【华为AI比赛】广告信息流跨域CTR预估(持续更新)
學習總結
- 暫時只是簡單提交baseline,提交catboost后的線上成績為0.71左右,持續更新本貼。
文章目錄
- 學習總結
- 一、賽題解讀
- 1.1 CTR賽題
- 1.2 比賽評估指標
- 1.3 比賽數據
- (1)目標域用戶行為數據
- (2)源域用戶行為數據
- 二、baseline通關模型
- 2.1 Logistic baseline
- 2.2 Catboost算法
- (1)導入模塊和數據預處理
- (2)特征工程
- 1)自然數編碼
- 2)特征提取
- 3)內存壓縮
- 4)訓練集和測試集的劃分
- 5)訓練模型和保存提交結果
- 三、特征提取技巧
- 四、改進思路
- 五、其他模型
- 附:時間規劃
- Reference
一、賽題解讀
1.1 CTR賽題
廣告推薦主要基于用戶對廣告的歷史曝光、點擊等行為進行建模,如果只是使用廣告域數據,用戶行為數據稀疏,行為類型相對單一。而引入同一媒體的跨域數據,可以獲得同一廣告用戶在其他域的行為數據,深度挖掘用戶興趣,豐富用戶行為特征。引入其他媒體的廣告用戶行為數據,也能豐富用戶和廣告特征。
賽題任務:本賽題基于:
- 廣告日志數據
- 用戶基本信息
- 跨域數據
目標:優化廣告ctr預估準確率。
目標域為廣告域,源域為信息流推薦域,通過獲取用戶在信息流域中曝光、點擊信息流等行為數據,進行用戶興趣建模,幫助廣告域ctr的精準預估。
1.2 比賽評估指標
使用GAUC和AUC的加權求和作為評估指標。xAUC越高,代表結果越優,排名越靠前。
xAUC=α?GAUC+β?AUC\mathrm{xAUC}=\alpha^* \mathrm{GAUC}+\beta^* \mathrm{AUC} xAUC=α?GAUC+β?AUC
其中AUC為全體樣本的AUC統計,GAUC為分組AUC的加權求和,以用戶為維度分組,組權值為分組內曝光量 / 總曝光:GAUC?=∑k=inAUC?i??Impression?i∑k=inImpression?i\text { GAUC }=\frac{\sum_{k=i}^n \text { AUC }_{i *} * \text { Impression }_i}{\sum_{k=i}^n \text { Impression }_i} ?GAUC?=∑k=in??Impression?i?∑k=in??AUC?i????Impression?i??
初賽: α\alphaα 為 0.7,β0.7, \beta0.7,β 為 0.30.30.3
1.3 比賽數據
數據下載:https://xj15uxcopw.feishu.cn/docx/doxcnufyNTvUfpU57sRyydgyK6c
本賽題提供 7 天數據用于訓練,1 天數據用于測試,數據包括目標域(廣告域)用
戶行為日志,用戶基本信息,廣告素材信息,源域(信息流域)用戶行為數據,源域(信
息流域)物品基本信息等。
希望選手基于給出的數據,識別并生成源域能反映用戶興趣,并能應用于目標域的用戶行為特征表示,基于用戶行為序列信息,進行源域和目標域的聯合建模,預測用戶在廣告域的點擊率。
(1)目標域用戶行為數據
(2)源域用戶行為數據
二、baseline通關模型
2.1 Logistic baseline
需要內存:1G;時間:5min。
#安裝相關依賴庫 如果是windows系統,cmd命令框中輸入pip安裝,參考上述環境配置 #!pip install sklearn #!pip install pandas#--------------------------------------------------- #導入庫 import pandas as pd#----------------數據探索---------------- # 只使用目標域用戶行為數據 train_ads = pd.read_csv('./train/train_data_ads.csv',usecols=['log_id', 'label', 'user_id', 'age', 'gender', 'residence', 'device_name','device_size', 'net_type', 'task_id', 'adv_id', 'creat_type_cd'])test_ads = pd.read_csv('./test/test_data_ads.csv',usecols=['log_id', 'user_id', 'age', 'gender', 'residence', 'device_name','device_size', 'net_type', 'task_id', 'adv_id', 'creat_type_cd'])# 數據集采樣 train_ads = pd.concat([train_ads[train_ads['label'] == 0].sample(70000),train_ads[train_ads['label'] == 1].sample(10000), ])#----------------模型訓練---------------- # 加載訓練邏輯回歸模型 from sklearn.linear_model import LogisticRegression clf = LogisticRegression() clf.fit(train_ads.drop(['log_id', 'label', 'user_id'], axis=1),train_ads['label'] )#----------------結果輸出---------------- # 模型預測與生成結果文件 test_ads['pctr'] = clf.predict_proba(test_ads.drop(['log_id', 'user_id'], axis=1), )[:, 1] test_ads[['log_id', 'pctr']].to_csv('submission.csv',index=None)2.2 Catboost算法
- Catboost自動采用特殊的方式處理類別型特征(categorical features)。首先對categorical features做一些統計,計算某個類別特征(category)出現的頻率,之后加上超參數,生成新的數值型特征(numerical features)。這也是這個算法最大的motivtion,有了catboost,再也不用手動處理類別型特征了。
- catboost還使用了組合類別特征,可以利用到特征之間的聯系,這極大的豐富了特征維度。
- 采用ordered boost的方法避免梯度估計的偏差,進而解決預測偏移的問題。
- catboost的基模型采用的是對稱樹,同時計算leaf-value方式和傳統的boosting算法也不一樣,傳統的boosting算法計算的是平均數,而catboost在這方面做了優化采用了其他的算法,這些改進都能防止模型過擬合。
(1)導入模塊和數據預處理
#安裝相關依賴庫 如果是windows系統,cmd命令框中輸入pip安裝,參考上述環境配置 #!pip install sklearn #!pip install pandas #!pip install catboost #--------------------------------------------------- #導入庫 #----------------數據探索---------------- import pandas as pd import numpy as np import os import gc import matplotlib.pyplot as plt from tqdm import * #----------------核心模型---------------- from catboost import CatBoostClassifier from sklearn.linear_model import SGDRegressor, LinearRegression, Ridge #----------------交叉驗證---------------- from sklearn.model_selection import StratifiedKFold, KFold #----------------評估指標---------------- from sklearn.metrics import accuracy_score, f1_score, roc_auc_score, log_loss #----------------忽略報警---------------- import warnings warnings.filterwarnings('ignore')數據預處理:
# 讀取訓練數據和測試數據 train_data_ads = pd.read_csv('./2022_3_data/train/train_data_ads.csv') train_data_feeds = pd.read_csv('./2022_3_data/train/train_data_feeds.csv')test_data_ads = pd.read_csv('./2022_3_data/test/test_data_ads.csv') test_data_feeds = pd.read_csv('./2022_3_data/test/test_data_feeds.csv')# 合并數據 # 合并數據 train_data_ads['istest'] = 0 test_data_ads['istest'] = 1 data_ads = pd.concat([train_data_ads, test_data_ads], axis=0, ignore_index=True)train_data_feeds['istest'] = 0 test_data_feeds['istest'] = 1 data_feeds = pd.concat([train_data_feeds, test_data_feeds], axis=0, ignore_index=True)del train_data_ads, test_data_ads, train_data_feeds, test_data_feeds gc.collect()(2)特征工程
1)自然數編碼
# 自然數編碼 def label_encode(series, series2):unique = list(series.unique())return series2.map(dict(zip(unique, range(series.nunique()))))for col in ['ad_click_list_v001','ad_click_list_v002','ad_click_list_v003','ad_close_list_v001','ad_close_list_v002','ad_close_list_v003','u_newsCatInterestsST']:data_ads[col] = label_encode(data_ads[col], data_ads[col])2)特征提取
問:源域的信息為什么沒有直接進行自然數編碼,而且在之后把它變為nunique和mean數據再加進去?直接把feeds數據變為float然后和ads數據合一起再進行訓練可行嗎?
答:第一個問題是因為base只做了粗糙的特征提取和拼接,思路可以嘗試。
- 比如只有些具有數值意義的字段再去統計mean等相關屬性,對類別型特征統計頻次之類的屬性,包括對源域數據一些特征的編碼處理這樣。
- 目標域的每一條數據都是記錄用戶在特定時間針對某廣告任務的一次行為;
- 源域則是用戶在特定時間在某信息流場景下的一次行為。
- 直接拼接的一個難點在于,源域和目標域采用的時間戳記錄方式是不同的(一個是12小時計時且沒給上午和下午的標識符,另一個則是24小時計時的),因此想直接用表的原始主鍵進行特征拼接是很難的。
- 從應用的角度出發:選取user作為外鍵連接兩個表的話會有很多一對多的關系,因此需要進一步對特定特征做統計分析(相當于是構造用戶相關的一些行為習慣特征)
特征提取部分圍繞著train_feeds進行構建(添加源域信息,測試集也是train_feeds構建,避免使用未來數據),主要是nunique屬性數統計和mean均值統計。由于是baseline方案,所以整體的提取比較粗暴,大家還是有很多的優化空間。
3)內存壓縮
def reduce_mem_usage(df, verbose=True):numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']start_mem = df.memory_usage().sum() / 1024**2 for col in df.columns:col_type = df[col].dtypesif col_type in numerics:c_min = df[col].min()c_max = df[col].max()if str(col_type)[:3] == 'int':if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:df[col] = df[col].astype(np.int8)elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:df[col] = df[col].astype(np.int16)elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:df[col] = df[col].astype(np.int32)elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:df[col] = df[col].astype(np.int64) else:if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:df[col] = df[col].astype(np.float16)elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:df[col] = df[col].astype(np.float32)else:df[col] = df[col].astype(np.float64) end_mem = df.memory_usage().sum() / 1024**2if verbose: print('Mem. usage decreased to {:5.2f} Mb ({:.1f}% reduction)'.format(end_mem, 100 * (start_mem - end_mem) / start_mem))return df# 壓縮使用內存 data_ads = reduce_mem_usage(data_ads) # Mem. usage decreased to 2351.47 Mb (69.3% reduction)由于數據比較大,所以合理的壓縮內存節省空間尤為的重要,使用reduce_mem_usage函數可以壓縮近70%的內存占有。
4)訓練集和測試集的劃分
# 劃分訓練集和測試集 cols = [f for f in data_ads.columns if f not in ['label','istest']] x_train = data_ads[data_ads.istest==0][cols] x_test = data_ads[data_ads.istest==1][cols]y_train = data_ads[data_ads.istest==0]['label']del data_ads, data_feeds gc.collect()5)訓練模型和保存提交結果
catboost針對類別特征不用處理,baseline提前做了編碼,所以沒有在cat_features單獨指定,
def cv_model(clf, train_x, train_y, test_x, clf_name, seed=2022):kf = KFold(n_splits=5, shuffle=True, random_state=seed)train = np.zeros(train_x.shape[0])test = np.zeros(test_x.shape[0])cv_scores = []for i, (train_index, valid_index) in enumerate(kf.split(train_x, train_y)):print('************************************ {} {}************************************'.format(str(i+1), str(seed)))trn_x, trn_y, val_x, val_y = train_x.iloc[train_index], train_y[train_index], train_x.iloc[valid_index], train_y[valid_index]params = {'learning_rate': 0.3, 'depth': 5, 'l2_leaf_reg': 10, 'bootstrap_type':'Bernoulli','random_seed':seed,'od_type': 'Iter', 'od_wait': 50, 'random_seed': 11, 'allow_writing_files': False}model = clf(iterations=20000, **params, eval_metric='AUC')model.fit(trn_x, trn_y, eval_set=(val_x, val_y),metric_period=200,cat_features=[], use_best_model=True, verbose=1)val_pred = model.predict_proba(val_x)[:,1]test_pred = model.predict_proba(test_x)[:,1]train[valid_index] = val_predtest += test_pred / kf.n_splitscv_scores.append(roc_auc_score(val_y, val_pred))print(cv_scores)print("%s_score_list:" % clf_name, cv_scores)print("%s_score_mean:" % clf_name, np.mean(cv_scores))print("%s_score_std:" % clf_name, np.std(cv_scores))return train, testcat_train, cat_test = cv_model(CatBoostClassifier, x_train, y_train, x_test, "cat")結果保存:
x_test['pctr'] = cat_test x_test[['log_id','pctr']].to_csv('submission.csv', index=False)提交catboost后的線上成績為0.71左右。
三、特征提取技巧
實踐:本次比賽是一個經典點擊率預估(CTR)的數據挖掘賽,任務是構建一種模型,根據用戶的測試數據來預測這個用戶是否點擊廣告。這是典型的二分類問題,模型的預測輸出為 0 或 1 (點擊:1,未點擊:0)
機器學習中,關于分類任務我們一般會想到邏輯回歸、決策樹等算法,在本文實踐代碼中,我們嘗試使用邏輯回歸來構建我們的模型。我們在解決機器學習問題時,一般會遵循以下流程:
- 統計相關特征:
- 用戶在各屬性ID上,歷史點擊行為的target mean(平滑)
- 用戶在屬性ID上信息熵、共現次數、比例偏好等統計特征
- 商品統計特征:
- 各屬性ID,歷史點擊行為的target mean(平滑)
- 各屬性ID,曝光次數統計
- 序列相關特征:
- 由word2vec生成的用戶、廣告等ID的embedding(取平均)
問:在不同的時刻是廣告點擊不同的,訓練樣本包含了一天所有時刻的廣告點擊統計,如果用訓練出來的模型去預測9點和12點的點擊效果,按照直觀的想法是,最好是分別訓練兩個模型,一個是用9點左右的數據,一個是用12點左右的數據,如果這些數據都混在一起學習的話,怎么做效果會更好一點?
答:如果這兩部分數據分布差異很大的話,在9點數據集上效果好的模型在12點上應該就達不到預期效果,這種情況可以考慮把他們分開構建模型分別預測。如果二者分布差異不大的話其實混在一起也沒太大影響。如果想嘗試在混合的情況下考慮這種因素的話,可以考慮構建不同時刻粒度的特征(比如小時特征之類的),之后通過該特征字段,結合其他類別特征,就能夠統計并構造針對特定小時情況下的一些數據特征,然后可以對比不同特征下的模型效果進行探索。
四、改進思路
- 繼續嘗試不同的預測模型或特征工程來提升模型預測的準確度
- 嘗試模型融合等策略
- 查閱廣告信息流跨域ctr預估預測相關資料,獲取其他模型構建方法
五、其他模型
待更新。
附:時間規劃
| 任務1: | 報名并理解賽題任務(兩天) | 9月11、12號 | |
| 任務2: | 配置環境(一天) | 9月13號 | |
| 任務3: | baseline實踐(一天) | 9月14號 周三 | |
| 任務4: | 數據處理、算法應用等技能學習(兩天) | 9月15、16號 周五 | |
| 任務5: | 相關知識參考學習(一天) | 9月17號 周六 |
Reference
[1] https://xj15uxcopw.feishu.cn/docx/doxcnw5LGZfH5n1WSwcNV59VxTg
[2] 華為比賽官網: https://developer.huawei.com/consumer/cn/activity/starAI2022/algo/competition.html#/preliminary/info/006/data
[3] 愛奇藝-用戶留存預測挑戰賽
[4] 魚佬的catboost的baseline:https://blog.csdn.net/Datawhale/article/details/126079911
[5] 學習參考:https://xj15uxcopw.feishu.cn/docx/doxcnw5LGZfH5n1WSwcNV59VxTg
[6] catboost算法原理
總結
以上是生活随笔為你收集整理的【华为AI比赛】广告信息流跨域CTR预估(持续更新)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 图片放大镜详细教程(简单易懂、注释清晰明
- 下一篇: Android BLE 蓝牙开发-扫码枪