0
点赞
收藏
分享

微信扫一扫

【华为AI比赛】广告信息流跨域CTR预估(持续更新)


学习总结

  • 暂时只是简单提交baseline,持续更新中。。。

文章目录

  • ​​学习总结​​
  • ​​一、赛题解读​​
  • ​​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的精准预估。

【华为AI比赛】广告信息流跨域CTR预估(持续更新)_CTR点击率预估

1.2 比赛评估指标

使用GAUC和AUC的加权求和作为评估指标。xAUC越高,代表结果越优,排名越靠前。

其中AUC为全体样本的AUC统计,GAUC为分组AUC的加权求和,以用户为维度分组,组权值为分组内曝光量 / 总曝光:
初赛:

1.3 比赛数据

数据下载:https://xj15uxcopw.feishu.cn/docx/doxcnufyNTvUfpU57sRyydgyK6c

本赛题提供 7 天数据用于训练,1 天数据用于测试,数据包括目标域(广告域)用
户行为日志,用户基本信息,广告素材信息,源域(信息流域)用户行为数据,源域(信
息流域)物品基本信息等。

希望选手基于给出的数据,识别并生成源域能反映用户兴趣,并能应用于目标域的用户行为特征表示,基于用户行为序列信息,进行源域和目标域的联合建模,预测用户在广告域的点击率。

(1)目标域用户行为数据

【华为AI比赛】广告信息流跨域CTR预估(持续更新)_CTR点击率预估_07


【华为AI比赛】广告信息流跨域CTR预估(持续更新)_用户兴趣_08

(2)源域用户行为数据

【华为AI比赛】广告信息流跨域CTR预估(持续更新)_CTR点击率预估_09


【华为AI比赛】广告信息流跨域CTR预估(持续更新)_用户兴趣_10

二、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)

提交LR baseline后说submission.csv行数不对,看来目标域和源域的数据都要用到,明天再看了。

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)特征提取

# 特征构建部分,均使用训练集数据进行构建,避免测试集使用未来数据
train_feeds = data_feeds[data_feeds.istest==0]
cols = [f for f in train_feeds.columns if f not in ['label','istest','u_userId']]
for col in tqdm(cols):
tmp = train_feeds.groupby(['u_userId'])[col].nunique().reset_index()
tmp.columns = ['user_id', col+'_feeds_nuni']
data_ads = data_ads.merge(tmp, on='user_id', how='left')

cols = [f for f in train_feeds.columns if f not in ['istest','u_userId','u_newsCatInterests','u_newsCatDislike','u_newsCatInterestsST','u_click_ca2_news','i_docId','i_s_sourceId','i_entities']]
for col in tqdm(cols):
tmp = train_feeds.groupby(['u_userId'])[col].mean().reset_index()
tmp.columns = ['user_id', col+'_feeds_mean']
data_ads = data_ads.merge(tmp, on='user_id', how='left')

特征提取部分围绕着​​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].dtypes
if 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**2
if 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)训练模型和保存提交结果

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(trn_x, trn_y, eval(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_pred
test += test_pred / kf.n_splits
cv_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, test

cat_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)

三、特征提取技巧

实践:本次比赛是一个经典点击率预估(CTR)的数据挖掘赛,任务是构建一种模型,根据用户的测试数据来预测这个用户是否点击广告。这是典型的二分类问题,模型的预测输出为 0 或 1 (点击:1,未点击:0)

机器学习中,关于分类任务我们一般会想到逻辑回归、决策树等算法,在本文实践代码中,我们尝试使用逻辑回归来构建我们的模型。我们在解决机器学习问题时,一般会遵循以下流程:

【华为AI比赛】广告信息流跨域CTR预估(持续更新)_用户兴趣_11

  • 统计相关特征:
  • 用户在各属性ID上,历史点击行为的target mean(平滑)
  • 用户在属性ID上信息熵、共现次数、比例偏好等统计特征
  • 商品统计特征:
  • 各属性ID,历史点击行为的target mean(平滑)
  • 各属性ID,曝光次数统计
  • 序列相关特征:
  • 由word2vec生成的用户、广告等ID的embedding(取平均)

【华为AI比赛】广告信息流跨域CTR预估(持续更新)_推荐算法_12


【华为AI比赛】广告信息流跨域CTR预估(持续更新)_数据_13

四、改进思路

  • 继续尝试不同的预测模型或特征工程来提升模型预测的准确度
  • 尝试模型融合等策略
  • 查阅广告信息流跨域ctr预估预测相关资料,获取其他模型构建方法

五、我的模型

待更新。

附:时间规划

任务

task内容

时间

完成情况

任务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
[5] 学习参考:https://xj15uxcopw.feishu.cn/docx/doxcnw5LGZfH5n1WSwcNV59VxTg
[6] ​​​catboost算法原理​​


举报

相关推荐

0 条评论