本文主要是介绍第一届腾讯社交广告高校算法大赛经验分享,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
第一届腾讯社交广告高校算法大赛经验分享
一、简介
去年的5月,我和两个队友参加了《第一届腾讯社交广告高校算法大赛》,在那之前我们实际上完全没有相关的竞赛经验,三个毫无经验的菜鸟暴力提取特征,凭借训练神经网络的经验玄学调参,竟然也获得了还不错的成绩,最终初赛 10/1000,决赛 39/1000,第一次比赛分工比较混乱,每个人都参与了特征工程和模型调参。初赛和决赛都是线下赛,即用自己的机器跑,提交结果即可。本文是对我的第一次比赛中,获得的一些经验和思考的分享,旨在总结自身,寻求进步和创新。
二、赛题背景
2.1 赛题简介
详细赛题可以看这里(可能已经找不到了)。本题的业务场景是腾讯的广告推荐系统,广告主通过腾讯向用户发布广告以推荐自己的产品,腾讯根据广告主的汇报和反馈搜集用户的点击与转换记录,赛题给我们提供了这样的数据,要求我们根据过去两周内的记录,预测接下来一天内每一条记录是否发生转化,核心问题正是CVR预测。
2.2 赛题数据
2.2.1 提供特征
赛题提供的原始特征由三部分构成:广告相关特征(从广告主、推广计划一直到app的各Categorical类字段之间的对应关系)、用户特征(个人信息)、上下文特征。
广告特征
序号 | 字段名 | 描述 |
---|---|---|
1 | advertiserID | 广告主 |
2 | campaignID | 推广计划ID |
3 | adID | 广告ID |
4 | creativeID | 素材ID |
5 | appID | AppID |
6 | appCategory | App分类,使用3位数字分级编码 |
7 | appPlatform | App平台(Android,iOS) |
用户特征
序号 | 字段名 | 描述 |
---|---|---|
1 | userID | 用户ID,唯一标识一个用户 |
2 | age | 年龄 |
3 | gender | 性别 |
4 | education | 用户当前最高学历 |
5 | marriageStatus | 用户当前感情状况 |
6 | haveBaby | 用户当前孕育宝宝状态 |
7 | hometown | 出生地,使用二级编码 |
8 | residence | 常住地,使用二级编码 |
9 | appInstallList | App安装列表,截止到某一时间点用户全部的App安装列表(appID) |
10 | App安装流水 | 最近一段时间内用户安装App行为流水 |
上下文特征
序号 | 字段名 | 描述 |
---|---|---|
1 | positionID | 广告曝光的具体位置 |
2 | sitesetID | 站点集合ID,多个广告位的聚合 |
3 | positionType | 广告位类型 |
4 | connectionType | 移动设备联网方式 |
5 | telecomsOperator | 移动设备当前使用的运营商 |
2.2.2 评估方式
CVR预测,以交叉熵为评估方式:
2.2.3 理解与思考
- 点击与转化
这里要明确一下点击和转化之间的关系, 样本中的每一个instance就是一次点击,但是只有少数的点击发生了转化,转化意味着用户发生了购买或者从app下载行为。赛题要求预测转化,因此实际上是一个二分类问题。 - 转化的虚假性和延迟性
转化具有一定虚假性和延迟性,虚假性产生于广告主的虚假反馈(例如明明发生了多次转化,却反馈说没有发生,从而少付钱,当然腾讯的计费不是那么简单的),延迟性产生于广告主的反馈习惯(隔若干天再向系统反馈的行为是被允许的)以及用户实际情况(有的用户点击了广告,但过了一段时间才发生购买或下载)。虚假性我们无从处理,但延迟性影响较大,特别是训练集最后一天的转化记录很可能是不准的。
三、数据观察与预处理
在进入特征工程之前,首先要粗略地观察一波数据,这里并不要求观察 label l a b e l 的分布,那是在特征选择的时候需要的,要观察的实际上是数据的取值,包括而不局限于:连续 or 离散,是否有缺失,是否有异常数据等。
连续与离散
首先,连续 or 离散。连续特征可以考虑数据的归一化、量化,以及一些值域变换(包括取log、取指数、Box-Cox等)。离散特征一般是ID类特征(Categorical feature),ID类特征可能是分级的(例如本次比赛中的常住地特征以“大类-小类”的形式存在),因此需要分割。此外,根据要使用的模型,可以考虑对ID类特征进行 One-hot(LR和FFM特别需要,树模型可以不做,因为树实际上会把ID当成实数处理,又或者根据梯度学习分裂方向)或者 Label-Encoding 编码(Label-Encoding编码得到的输出可以直接作为树模型的输入)。然而,编码过后常会产生维度爆炸的问题,这时就需要考虑另一个问题,降维。降维分为预降维和后降维,预降维指在One-hot之前,观察一下特征在不同取值下样本量的分布,可以将出现次数少的ID归为同类,再做编码;后降维指在编码以后,使用PCA(本质上是特征值分解)或 LDA(实际上是用SVD实现)等降维方法进行降维,或者根据树模型给出的 feature importance 进行特征选择。在比赛中,我们并没有对连续数值做变换,但部分ID类特征做了Onehot,以及在特征选择阶段根据 feature importance 进行了后降维。
缺失值
其次,观察是否有缺失值。特征缺失(或称稀疏)是常见的现象,如果使用提升树模型,可以选择不处理,XGBoost和LightGBM在实现上有为缺失特征自动选择分裂方向的功能。具体地,在分裂结点时,分别假设特征值缺失的样本被分到左右两侧,根据信息增益的多少决定默认分类方向。如果一定要进行缺失值处理,就要继续根据连续和离散特征考虑。连续特征 可以填充均值,具体地可以是样本总体均值,也可以是对应的instance_id的样本的均值(例如用户老马有三次点击,特征A在第二次处缺失,则用第一次和第三次的均值补充),还可以是当天或者前几天内的均值。离散特征 可以填充众数(相当于voting),同样可以选择全局众数或局部众数。至于统计的范围如何选择,就属于特征工程的范畴了。值得注意的一点是,有时候直接舍弃有缺失特征的样本是更好的做法。孰好孰坏,应该让模型性能帮忙做决定。比赛中我们对缺失值的处理是,不处理。
异常数据
最后,检查是否存在异常数据。这个是最难的,因为每一个instance_id的每一个统计量都有可能出现异常。例如,某广告主的日转化率(日转化/日点击)在某天前一直很低,某一天之后激增,且之后一直很高,由于要预测接下来一天,因此必定假设新的一天转化分布是与历史分布基本一致的,该广告主在该天之前的信息显然不正常,因此应该删除掉那一天之前的所有记录。同理,若广告主在某一天之后日转化率骤减,且没有回复的迹象,也应该做相似的处理。为什么说很难呢?因为nstance_id的构成呈组合爆炸,每一个instance_id又可以分别观察点击量、转化率等各种分布,想要人工遍历是几乎不可能的。此外,异常数据还可能体现在各方各面,主要还是靠观察来挖掘。本次比赛中,存在着一种样本重复异常,即除了label外,其他信息都完全一样的多个样本,我们认为它出现的原因在于时间戳的秒位忽略,实际样本的排序正是按照时间排序的,对此我们做的处理是为这些重复的样本按先后顺序设置一个rank。此外,由于存在上面提到的转化延迟问题,我们观察到训练集最后一天的转化率远远低于前面几天,对此我们的处理是直接舍弃这一天的样本。
数据预处理总结
总结一下,我们对于数据的预处理包含下面几个方面:
- 为了保证数据分布的一致性,去除部分分布骤变的样本。
- 为了忽略转化延迟问题,去除训练集最后一天的样本。
- 部分ID类特征分级且Onehot编码处理。
四、特征工程
在本次比赛中,主要包含四部分,单特征、组合特征、泄露特征和基于其他模型构造的特征。
4.1 原始单特征
每一个原始字段都可以作为instance_id,从而统计相关统计量(例如在过去几天内的点击量、转化次数、转化率等)。以素材creative_id为例,它是用户最直接看到的内容,对某个特定的 creative_id,我们统计过去若干天内该 creative_id 的总点击量、转化次数和转化率,作为该 creative_id 取值的三个特征。其物理意义在于,量化地描述了该素材是否更能吸引用户(点击量)和发生转化(转化率)。
4.2 组合特征
部分原始字段之间存在着明显的树状层级,详细可见下图。例如,每个广告主下面有若干推广计划,每个推广计划下面有多个广告,每个广告又包含多个素材。他们之中,包含一对一关系(例如一个推广计划只能属于一个广告主),也存在多对多关系(例如同一个推广计划可以为不同的app发出广告,同一个app也可能收到来自不同推广计划的广告)。对于各种instance_id组合而成的新instance_id,仍可以像原始单特征那样统计相关统计量。具体地,以 positionID : connectionType 为例,对它的某个取值(指pair取值,比如positionID=0且connectionType=1)统计点击量和转化率,其物理意义在于,量化地描述“位置0上的广告对于连WIFI的用户所具备的吸引力”。虽然我们不知道位置具体是什么,也不知道1是不是代表WIFI,但是这么统计后,将发现在不同的取值下样本label的分布变得十分不均匀,也就是说该特征是具有较好的分类效果的。事实也证明,positionID : connectionType 确实是一个强特征。
4.3 leakage特征
在这类比赛中,我们可以获取预测当天的所有点击记录(正是测试集样本本身),对于预测当天提取的相关特征,我们称之为 leakage(泄露),因为在实际的实时系统中,即便可以获得当前以前的记录,却不能获得当天剩下的记录,由于泄露了未来的信息,因此称之为 leakage。具体地,上述的每一个 instance_id(包括单特征和组合特征)的某个取值,都可获得在当天内距离上一次取该所值经过的时间,以及下一次出现所需要的时间,还有当天内在本次出现之前和之后出现过的总次数。例如,某一个instance user_id=A且creative_id=0 其时间戳是中午12点,经过统计我们发现中午12点之前的它点击次数为10次,12点之后为1次,如果模型从样本中学习到的规律是“转化更偏向于发生在最后几次点击”,那么该 instance 发生转化的概率应该比较大。说到底,这些特征只是对用户习惯的一种描述,最终决策往往不是取决于单一特征的。
4.4 基于其他模型的特征
4.4.1 tf-idf特征
tf-idf特征主要针对用户App安装列表。将用户安装列表看成一个 词袋模型bag-of-words,每个用户的安装列表看作一篇文章,而app就是构成文章的单词。统计 tf-idf 值,即可得到一个具有一一对应关系的三元组:用户 : app : tf-idf,以 user_id : app_id 为instance_id,即可直接将 tf-idf 作为特征。实际上,该特征带来的提升并不算太高,我们认为这是由于安装列表的稀疏造成的(大多数app在一个列表中只出现过 1 次)。
4.4.2 贝叶斯平滑
在CTR估计中,一般会统计历史的展示数 N N 和点击数,然后用 C/N C / N 作为广告CTR的估计。这种估计方法是有问题的,例如投放100次广告,点击2次,CTR=2%,于是加大投放广告到1000次,但点击只有10次,CTR=1%,同一个广告CTR相差了一倍,显然是不合理的。产生该现象的根源来自于样本量不足,应当默认广告有一个历史的展示量和点击量,在估计CTR时,分别将新的统计量加到分子和分母的常数上,这样统计出来的CTR误差才会比较小。贝叶斯平滑的目的,就是根据不同广告展示和点击的实际统计值估计出CTR的分布参数,也就是估计分子和分母上的常数。在本题中,我们将CTR的贝叶斯平滑迁移到CVR中。
具体地,我们假设 “广告主 : 推广计划” pair 的 CVR 服从 Beta 分布,即同一 “广告主 : 推广计划” pair之下,所有 creative_id 服从参数为 CVR 的伯努利分布,也就是说,伯努利中的概率参数 CVR 本身也是一个随机变量,它服从Beta分布。我们将CTR中的 N N 和迁移为: N N 表示点击量,表示转化量。通过统计每个 “广告主 : 推广计划” pair 取值下,不同 creative_id 的点击次数 Ni N i 和转化次数 Ci C i ,代入Beta的极大似然估计,可以求出该 “广告主 : 推广计划” pair 分布的两个常数,并代入到CVR的估计中,达到平滑的效果。总而言之,认为每一个 “广告主 : 推广计划” pair 各有一个CVR分布,用下面不同素材的统计量估计该分布的值,以更新素材的CVR。
4.5 线下验证集的构建
赛题的训练集为某月的第17天至30天,测试集为第31天。线下验证集的构造,其原则是确保在模型或特征发生变化时,线下验证集的准确率与线上的准确率基本符合“同增同减”的规律。如上面所描述,31天存在严重的转化率回溯延迟,带来了巨大的干扰,因此我们舍去了该天。
- 线下测试:17~22是特征提取区间,23~28作为训练集,29作为线下验证
- 线上测试:17~29作为特征提取区间
4.5 特征的选择
特征选择上,我们主要采取包裹式和嵌入式相结合的方式。
- 包裹式,指针对不同的特征子集测试模型的性能,选择性能最佳的模型对应的特征子集。具体地我们实现了方便热插拔不同特征的代码,然后暴力遍历特征子集。
- 嵌入式,指根据提升树模型输出的feature importance来决定部分特征的去留。
4.6 特征工程总结
总结一下,本次比赛中,我们的特征工程主要分为四个部分,单特征、组合特征、泄露特征和基于其他模型构造的特征。构造线下验证集,根据特征在验证集上的表现筛选特征,同时参考了模型提供的feature importance。
特征工程包含了特征的提出、特征生成和特征选择。其核心在于特征提出,这往往需要基于对数据的观察和对业务的理解,当然运气也是很重要的。在有限的比赛时间中遍历所有的可能性是不现实的,但走投无路之时也只有这种方法而已。
下面是我参考这里总结的一点关于特征工程的知识图谱,本次赛题并没有完全用到上面的所有东西,该图谱也仍在完善当中,仅供参考。
五、模型
5.1 LR
LR 适用于特征维度比较高的情况,ID类特征需要先进行Encoding,例如One-hot。LR还可以作为 stacking 的二阶段模型,即将一阶段训练的树模型关于训练集的输出作为LR的输入训练LR模型。本次比赛中似乎没有尝试过。
5.2 树模型
本次比赛中主要使用的模型是提升树GBDT,RF也有试过,但性能上略输GBDT。
5.2.1 RF 随机森林
属于一种bagging。每次从样本中有放回地抽取足够量的样本,然后训练树,树在结点分裂选择最佳特征时,首先抽取k个特征(一般选取特征数开平方根),再从中根据信息增益等指标进行选择,以防止过拟合。当若干树训练完成后,使用平均、加权平均或者投票的方式组合成最终模型。
5.2.2 GBDT 梯度提升树
GBDT,梯度提升树。主要使用xgboost和lightgbm,xgboost在传统的gbdt的基础上做出了一些改进,包括增益的重新定义、引入L2正则、近似法选取分裂点、预读取和排序以及并行化加速等。Xgboost的优势在于能减少过拟合、速度快。Lightgbm则进一步提升了速度并减少内存的开销。根据比赛中的经验,二者在准确率上是相近的,但lightgbm要快得多(主要是直方图算法分裂以及直方图相减减少了计算量),因此我们后面基本上选择的是lightgbm。
5.2.3 因子分解机 FM
一种多项式模型,给我的感觉是很像LR,它也一般被用作stacking的二阶段模型。它的输入一般是Onehot过的非常稀疏的ID类特征。我们主要使用libffm,但不知为何效果总是不佳,所以最终弃用了。
5.2.4 模型融合
几乎到了比赛的大后期,我们才发现有模型融合这样的操作,但那时已经基本上来不及了。在比赛的最后一天,我通宵做了两个LightGBM的融合,它们分别使用不同的特征子集:目前单模型最好的特征子集,以及筛选中被舍弃的部分特征,提升了1个千分点。因此我们在模型融合方面,其实仍有巨大的提升空间,只不过经验不足且时间不够罢了。
六、最重要的部分
我认为在本次比赛中最重要的环节有两点:特征工程与模型融合。
特征工程中,虽然相当一部分是暴力组合测出来的,但在没有思路的时候也不失为一种办法。而一旦增进了对数据和赛题的理解,得到的相关特征就能大幅度提升性能,比如组合特征 positionID : connectionType, 比如贝叶斯平滑 。
模型融合是此类比赛中相当常见的做法,多个“好而不同”的模型通过加权或voting的方式融合在一起,以及模型之间做stacking,都能增加模型的鲁棒性和准确性。可惜当时太young太naive,我们在模型融合方面能提升的空间还是存在的。
七、我们和前十名的差距在哪里
比赛结束后,我去听了前十名的最终答辩,总结了我们和他们的差距如下。
- 没有过多地进行数据清洗,保留了太多缺失值。
- 对业务的思考还不够,没有去仔细地观察数据的规律,也不知道如何观察,按什么顺序。有点盲目地暴力遍历特征。
- 没有早一些尝试模型融合。
- 名次不够高的时候,没有尝试技术创新。
- 代码写得不够高效,应该模块化
八、面试中被问到过的问题
- Leakage特征为什么这么提?你是说用户当天点击地越多他越不可能转化吗?
特征只是反映了用户行为习惯的某一个方面,只是参与了模型的决策,用户点击地越多他越不可能转化?只能说有这个可能性,而模型正是通过这个特征学习特征与该可能性的关系。 - 你们有没有考虑正负样本不均匀的问题?
没有考虑,现在想起来,我认为这可能也是我们成绩不够高的原因之一。应该保留正样本,对负样本随机抽取。 - 用户的家乡?你怎么提取特征,ID类是可以直接扔进树模型的吗?
我们并没有直接把ID类送入树模型,而是围绕ID提取了统计特征,来代替特征本身。如果要用到ID本身,可以Onehot后送入FFM或者LR,不适合用树模型。 - 介绍RF和GBDT的区别,推导一下
附录:广告推荐中的贝叶斯平滑
预备小知识
二项分布( n n 重伯努利实验)
Beta分布
估计 n n 重伯努利实验的概率
把二项分布的概率密度当做 μ μ 的似然函数,为了估计 μ μ ,需要给 μ μ 设置一个先验分布。选什么分布呢?选二项分布的共轭分布:Beta分布。
计算后验概率:
把系数带上
所以后验分布也是一个Beta分布: B(a+C,b+n−C) B ( a + C , b + n − C )
Beta分布的期望是
一、问题提出
数据的层级结构
许多场景下,数据呈现层级结构(理解为树状)。假设事件(兄弟)之间并不相互独立,而层级结构中距离近的两个事件之间的相关性大于距离远的两个事件。具体到广告场景,叶子结点就是不同的广告,父结点就是广告主/推广计划,从概率的角度讲,兄弟广告之间的CTR应该会相互影响(例如肯德基广告可能会提升加多宝广告的点击率),而同一广告主的不同广告应该有相近的CTR分布,或者说广告风格(肯德基和麦当劳属于同一集团,它们的CTR分布应该是相似的)。本质上就是说,“不同ID下的广告的CTR同分布”。
CTR估计不准
根据历史点击统计转化率CTR存在着历史CTR与真实CTR相差很大的问题。例如投放100次广告,发生了两次点击,CTR是2%,若增加投放至1000次,点击只有10次,CTR是1%,两个CTR相差了一倍。产生这个问题的根源在于投放量不足,而通常的做法是在分子和分母各加一个较大的常数,表示广告在投放之前就有一个默认的展示次数和点击次数,从而平滑CTR。
按照层级结构的理论,可以预想一下,为了平滑CTR,应该需要去估计不同ID各自分布(核心是估计分布的参数),由于它是CTR的分布,只要求期望就能得到CTR了(称为平滑)。
二、理论
现在假设,ID={广告主=老王,推广计划=A计划} 下有 N N 个不同的广告。
单个广告
用表示其中某个广告的CTR, C C 表示广告被点击的次数,表示广告被展示的次数,一般来说我们会用 μ̂ =C/n μ ^ = C / n 估计该广告的CTR,这其实是不准的。
首先,广告的CTR μ μ 是一个随机变量,它服从Beta分布
其次,若已知广告的CTR μ μ ,则它的点击次数 C C 服从伯努利分布
伯努利分布是似然函数,而Beta函数则是先验分布,二者共同决定了后验分布,即:似然函数 + 先验分布 = 后验分布。
N个广告
父节点 ID={广告主=老王,推广计划=A计划} 下 N N 个广告的联合分布
极大似然估计 a a 和
取对数似然函数后分别关于 a a 和求偏导数(极大似然估计):
于是迭代更新 a a 和,直到收敛
最终利用 a a 和修正CTR:
总结
基于CTR在样本量少时测不准的问题,作出了CTR的相关概率假设,即同广告主的广告具有相同的CTR分布(Beta分布),通过极大似然估计获得超参数 a a 和(实际上它们描述了广告主的风格),就可以重新估计CTR,从而达到平滑的效果。
参考
- 【博客】Kaggle干货-陈成龙
- 【博客】MLWave: Kaggle Ensembling Guide
- 【知乎】特征工程是什么
这篇关于第一届腾讯社交广告高校算法大赛经验分享的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!