记录一次CTR比赛的实战,同时分享一下我CTR预估实战的过程。实战之前我先去调研了一下CTR这个任务的特点:
- 毫无疑问这个任务的是个二分类任务,预测点击与否。
- CTR 预估的特征一般是 用户的日志特征和画像特征,包含类别特征和数值型特征两种。
- 此任务的评估指标是 AUC 得分 或者 Logloss,facebook2014年的论文指出Logloss可能是相对来说较好的一个评估指标。
而这个任务有如下一些挑战:
- 特征稀疏性的问题如何解决。
- 特征直接的组合关系如何挖掘,交互特征如何学习。
- 如何感知用户兴趣随时间的变化。(这一点我没研究过所以暂且不谈)
- 最后一点是深度模型自带问题,就是如何利用好将不同层级的特征。(由于加入深度神经网络,会出现高层级的特征)
DeepCTR 简介
深度学习解决CTR模型天然的会有这些优势(笔者的个人看法):
- 数据稀疏的问题采用深度模型似乎会有着不错的效果。
- 特征之间的组合关系可以采用深度学习模型自动提取。
- 一些经典的FM,LR模型可以和深度学习模型做结合,能够产生出更好的效果。
这里我先介绍一下DeepCTR这个领域的发展过程,下图是知乎博主浅梦的学习笔记 画的一张DeepCTR的算法发展路线图,我们清晰的看到在CTR预估的深度算法领域有三条主要的路线:
- 1.基于模型并行 组合的 Wide&Deep这条路线
- 2.基于模型串行 组合的FNN 这条路线
- 3.融入历史行为感知变化的YouTube Net 这条路线
这里我简单解释一下1和2的区别:
- 模型并行的意思就是,浅层模型和深度学习模型共享同一个输入(一般是稀疏特征Embeding后的输出),然后分别做特征表示学习,最终把它们的结果相加。
- 模型串行的意思就是:浅层模型的输出会喂给深度模型。这部分模型的主要工作还是在如何利用浅层模型更好的挖掘特征。深度模型的作用好像是对浅层模型找的特征做再一次的表示学习。
深度学习模型毕竟是从图像发家的,如果仔细阅读看下图的左半边,会发现很多图像领域深度模型的影子。比如Attention,Senet等。如果你看右半边开始引入序列信息之后,则会出现循环网络等NLP领域经常会用到的技术。深度学习有些好的思想和技术好像在任何领域都能够work。
![](https://img.haomeiwen.com/i9168245/23d68e63eba3e895.png)
实验部分
这里笔者采用的数据:如图所示下,脱敏后的40个特征列,加一列label列。包含一笑广告信息,用户日志信息,用户设备信息,服务器信息。
![](https://img.haomeiwen.com/i9168245/e7cf1732069f0bd3.png)
DeepFM模型
笔者这次实验主要的算法还是deep的算法,采用的Python包是deepctr(一个对CTR预估有着深入研究的大佬开发的),里面封装了各种经典的CTR算法,而且简单易用,能够方便的测试不同的算法的性能和效果。使用的算法是deepfm,论文的地址在这:deepfm论文地址。模型细节我就不多介绍了,两句话概括:
- 模型的架构:如下图所示,这是一个典型的并行模型,将FM和DNN的结果进行相加后送入到激活函数。
- 模型的特征:快而不失准确率。我测试了NFFM,xDeepFM,DeepFM,发现DeepFM是最快的,而NFFM虽然准一点但是参数量极大,速度慢。
deepFM
import pandas as pd
from sklearn.metrics import log_loss, roc_auc_score
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder, MinMaxScaler
from deepctr.models import DeepFM,NFFM,xDeepFM
from deepctr.inputs import SparseFeat, DenseFeat, get_feature_names
data = pd.read_csv('train.csv')
sparse_features = []
dense_features = []
for i in data.columns:
if data[i].nunique()>1000:
dense_features.append(i)
else:
if i == "label":
break
sparse_features.append(i)
data[sparse_features] = data[sparse_features].fillna('-1', )
data[dense_features] = data[dense_features].fillna(0, )
target = ['label']
# # 1.Label Encoding for sparse features,and do simple Transformation for dense features
for feat in sparse_features:
lbe = LabelEncoder()
data[feat] = lbe.fit_transform(data[feat])
mms = MinMaxScaler(feature_range=(0, 1))
data[dense_features] = mms.fit_transform(data[dense_features])
# # 2.count #unique features for each sparse field,and record dense feature field name
fixlen_feature_columns = [SparseFeat(feat, data[feat].nunique())
for feat in sparse_features] + [DenseFeat(feat, 1,)
for feat in dense_features]
dnn_feature_columns = fixlen_feature_columns
linear_feature_columns = fixlen_feature_columns
feature_names = get_feature_names(linear_feature_columns + dnn_feature_columns)
print (feature_names)
# # 3.generate input data for model
train, test = train_test_split(data, test_size=0.3)
train_model_input = {name:train[name] for name in feature_names}
test_model_input = {name:test[name] for name in feature_names}
# 4.Define Model,train,predict and evaluate
model = DeepFM(linear_feature_columns,dnn_feature_columns,l2_reg_dnn=0.01,dnn_dropout=0.5,task='binary')
# model = FGCNN(dnn_feature_columns,l2_reg_dnn=0.01,dnn_dropout=0.5,task='binary')
model.compile("adam", "binary_crossentropy",
metrics=['binary_crossentropy'], )
history = model.fit(train_model_input, train[target].values,
batch_size=256, epochs=5, verbose=2, validation_split=0.2, )
pred_ans = model.predict(test_model_input, batch_size=256)
print("test LogLoss", round(log_loss(test[target].values, pred_ans), 4))
print("test AUC", round(roc_auc_score(test[target].values, pred_ans), 4))
上述代码中,我将各特征类别数大于1000的当做了数值特征,而小于1000的当做类别特征。经过 半天的调参(只能说深度学习调参真是一门手艺,当然也有很多自动调参的工具,这里笔者没用),最终在再test数据集上的AUC 得分为0.722
CatBoost模型
之后我又用catboost尝试了一下,没做任何调参,唯一的做法就是把所有的特征都当做类别特征输入(之前尝试了把一部分特征作为数值型,结果效果不好)。至于想了解catboost算法的同学可以通过这个链接catboost学习到算法的一些概要。最终代码如下,没想到在test数据集上的AUC得分居然为0.731,比deepfm高了千分之九。对比下来这里可以得到一个很小的结论就是:不要迷信深度模型,可能有些数据集就用boost算法反而更好。
from catboost import CatBoostClassifier
import pandas as pd
from sklearn.model_selection import train_test_split
import numpy as np
data = pd.read_csv("train.csv")
data = data.fillna(0)
categorical_features_indices=[i for i in range(40)]
X_train, X_validation, y_train, y_validation = train_test_split(data.iloc[:,:-1],data.iloc[:,-1],test_size=0.3 , random_state=1234)
print(categorical_features_indices)
model = CatBoostClassifier(iterations=8000,
depth=6,
cat_features=categorical_features_indices,
learning_rate=0.01,
loss_function='Logloss',
custom_metric="AUC",
use_best_model = True,
task_type="GPU",
logging_level='Verbose')
model.fit(X_train,y_train,eval_set=(X_validation, y_validation),plot=True)
结语
只不过是一次很小很不规范的实验,想体验一下深度模型在CTR预估中的使用过程。经过两天的学习,得到上述一下知识和感想,以及一些实验结果,也无从解释。留下一堆问题才是学习CTR真正的开始,接下来还是需要通过实践和阅读论文去感受这个领域的一些精妙之处。
参考文献
https://zhuanlan.zhihu.com/p/53231955
https://arxiv.org/pdf/1703.04247.pdf
https://www.jianshu.com/p/49ab87122562
网友评论