【推荐系统】CTR预估模型(一):LR+GBDT

5,855 阅读9分钟

在推荐系统中,CTR预估是一个非常常见的任务,他最开始用于计算广告中用于判断广告效果的优化情况。在搜索广告中,一般通过广告的点击数量进行效果的评估和结算,因此点击率预估的准确性在效果优化中起到非常关键的作用。如果最终评估效果的指标是转化,那么还需要同时对点击之后的转化率进行估计。而在很多场景中实际的转化数据非常稀少,很难直接利用转化数据对模型进行训练,所以经常退而求真次,对二次跳转、加入购物车等行 为进行建模。对转化、二次跳转、加入购物车等行为进行建模的原理与点击率预估十分类似,所以CTR预估可以作为一个常用任务来建模。

1. CTR的定义

点击率预估可以抽象成为一个二分类问题。它所解决的问题是:给定一个请求以及与该请求所匹配的广告,预测广告展示之后获得点击的概率。标注可以从实际的投放数据中获得,在历史的投放结果中,获得了点击的记录标注为1,真余标注为0。

ctr_1

上图为点击率预估攫型中的一条训练/测试数据记录的示意。左边的方框中列出了这条数据记录对应的特征,包括与用户(User tags)、上下文(Date, City, Ad exchange, Domain , ...)、广告主(Ad ID)、创意(Ad size)等相关的特征。在训练记录中,如果这条记录最终发生了点击,则记录为1,否则记录为0;在预测肘,我们需要预测这条记录发生点击的概率,即CTR (Click-Through Rate)。

2. LR+GBDT

这个模型是Facebook在2014年的论文中介绍通过GBDT模型解决LR模型的特征组合问题。思路很简单,特征工程分为两部分,一部分特征用于训练一个GBDT模型,把GBDT模型每颗树的叶子节点编号作为新的特征,加入到原始特征集中,再用LR模型训练最终的模型。在这个模型之前CTR预估的主要的benchmark是逻辑回归(LR)。

2.1 LR

因为CTR本身就是一个二分类问题,所以LR作为一个非常经典的分类模型很早就被拿来做CTR预估了。其本身结构简单,时间复杂度低,又可以大规模并行化,所以在早期CTR预估任务中,基本都是以手动设计交叉特征以及特征离散化来赋予LR线性模型对于数据集的非线性学习能力,高位离散特征+手动交叉特征构成了CTR预估的基本特征。

lr

模型结构f(x)=w0+i=1nwixiy=sigmoid(f(x))f\left ( x \right )=w_0+\sum_{i=1}^{n}w_ix_i,y=sigmoid(f(x))

目标函数J(w)=1mi=1m(yilogfw(xi)+(1yi)log(1fw(xi)))J(w)=-\frac{1}{m}\sum_{i=1}^{m}(y^i\log f_w(x^i)+(1-y^i) \log(1-f_w(x^i)))

优势

  • 模型简单,具备一定可解释性
  • 计算时间复杂度低
  • 工程上可大规模并行化

不足

  • 依赖于大量的特征工程,离入需要根据业务具备相关只是用过特征工程融入模型
  • 特征交叉难以穷尽
  • 对于训练集中没有出现的交叉特征无法进行参数学习

2.2 GBDT

GBDT是集成学习中boosting算法的一种,boosting算法的基本思路是弱分类器通过串联,根据上一轮弱分类器的结果,以拟合残差或者提升分类错误样本的权重形式来层层叠加弱分类器,最终各层的弱分类器结果加权得到最终结果。弱分类器本身是高偏差,高方差,boosting通过串行形式降低了集成分类器的偏差,从而能得到更精确的结果。

GBDT全称为梯度提升决策树(gradient boosting decision tree)。先训练一个初始决策树是ft1(x)f_{t-1}(x),损失函数L(y,ft1(x))L(y,f_{t-1}(x)),则本轮迭代目标数找到一个CART回归树学习器ht(x)h_t(x),让本轮的损失函数L(y,ft(x))=L(y,ft1(x)+ht(x))L(y,f_{t}(x))=L(y,f_{t-1}(x)+h_t(x))最小。也就是说本轮迭代找到的决策树,要让样本的损失尽量变得更小。而最终的预测结果是所有树的预测值之和。

提升树算法

  1. 提升树首先初始化f0(x)=0f_0(x)=0,

  2. 对于m=1,2,...,Mm=1,2,...,M

    1. 则残差rmi=yifm1(x),i=1,2,...,Nr_{mi}=y_i-f_{m-1}(x),i=1,2,...,N

    2. 拟合残差rmir_{mi}学习一棵回归树,得到hm(x)h_m(x)

    3. 更新fm(x)=fm1(x)+hm(x)f_m(x)=f_{m-1}(x)+h_m(x)

  3. 得到回归问题提升树fM(x)=m=1Mhm(x)f_M(x)=\sum_{m=1}^{M}h_m(x)

假设前一轮迭代得到的强学习器是ft1(x)f_{t-1}(x),损失函数是L(y,ft1(x))L(y,f_{t-1}(x)),则本轮迭代的目标数找到一个弱学习器ht(x)h_t(x)最小化本轮的损失

argminL(y,ft(x))=argminL(y,ft1(x)+ht(x))\arg \min L(y,f_t(x))=\arg \min L(y,f_{t-1}(x)+h_t(x))

当使用平方差为损失函数时

L(y,ft1(x)+ht(x))=(yft1(x)ht(x))2=(rht(x))2L(y,f_{t-1}(x)+h_t(x))\\=(y-f_{t-1}(x)-h_t(x))^2\\=(r-h_t(x))^2

这里的rr就是残差,所以提升树只需要简单的拟合当前模型残差,就可以达到最小化损失函数的目的。

梯度提升

在第tt轮第ii个样本的损失函数在选择平方损失函数时

[L(y,f(xi))f(xi)]f(x)=ft1(x)=yf(xi)-\left [\frac{\partial L(y,f(x_i))}{\partial f(x_i)} \right ]_{f(x)=f_{t-1}(x)}=y-f(x_i)

这时就会发现GBDT的负梯度就是残差,这也是Friedman提出的用损失函数的负梯度作为提升树算法的残差近似值。GBDT回归模型是要拟合残差,

GBDT回归算法

训练样本T=(x1,y1),(x2,y2),...,(xm,ym)T={(x_1,y_1),(x_2,y_2),...,(x_m,y_m)},最大迭代次数T,损失函数L

  1. 初始化弱学习器:f0(x)=argminci=1NL(yi,c)f_0(x)=\arg \min _c\sum _{i=1}^{N}L(y_i,c)

  2. t=1,2,...,Tt=1,2,...,T有:

    1) 对每个样本i=1,2,...,mi=1,2,...,m,计算负梯度(残差)

    rti=[L(yi,f(xi))f(xi)]f(x)=ft1(x)r_{ti}=-\left [\frac{\partial L(y_i,f(x_i))}{\partial f(x_i)} \right ]_{f(x)=f_{t-1}(x)}
    1. 将上步得到的残差作为样本新的真实值,并将数据(xi,rti)i=1,2,..m (x_i,r_{ti}), i=1,2,..m拟合一棵CART回归树,得到第tt棵回归树ft(x)f_{t} (x)其对应的叶子节点区域为Rtj,j=1,2,...,JR_{tj}, j =1,2,..., J。其中JJ为回归树t的叶子节点的个数。

    2. 对叶子区域j=1,2,...,Jj=1,2,...,J计算最佳拟合值

    ctj=argmincxiRtjL(yi,ft1(xi)+c)c_{tj}=\arg \min _c\sum_{x_i \in R_{tj}}L(y_i,f_{t-1}(x_i)+c)
    1. 更新强学习器
    ft(x)=ft1(x)+j=1JctjI(xRtj)f_t(x)=f_{t-1}(x)+\sum_{j=1}^{J}c_{tj}I(x \in R_{tj})
  3. 得到强学习器f(x)f(x)的表达式

    ft(x)=fT(x)=f0(x)+t=1Tj=1JctjI(xRtj)f_t(x)=f_{T}(x)=f_0(x)+\sum _{t=1}^{T}\sum_{j=1}^{J}c_{tj}I(x \in R_{tj})

GBDT分类算法

分类算法和回归算法的思想上是相同的,但是样本输出不是连续值,所以不能直接从输出类型去拟合类别输出的误差。则在分类算法中使用的损失函数有指数损失函数对数似然损失函数。而在二分类中使用的就是后者(GBDT+LR模型中就是二分类)

L(y,f(x))=log(1+exp(yf(x)))L(y,f(x))=log(1+\exp(-yf(x)))

则此时负梯度误差为

rti=[L(yi,f(xi))f(xi)]f(x)=ft1(x)=yi1+exp(yif(xi))r_{ti}=-\left [\frac{\partial L(y_i,f(x_i))}{\partial f(x_i)} \right ]_{f(x)=f_{t-1}(x)}=\frac{y_i}{1+\exp(y_if(x_i))}

则对于生成的决策树,各个叶子节点的最佳负梯度拟合值为

ctj=argminxiRtjlog(1+exp(yi(f(t1)+c)))c_tj=\arg \min \sum_{x_i \in R_{tj}}\log(1+\exp(-y_i(f_(t-1)+c)))

在优化上面的拟合值是用近似值代替

ctj=xiRtjrtixiRtjrti(1rti)c_{tj}=\frac{\sum _{x_i \in R_{tj}}r_{ti}}{\sum _{x_i \in R_{tj}}|r_{ti}|(1-|r_{ti}|)}

除了负梯度计算和叶子节点最佳负梯度拟合的线性搜索,二元GBDT分类和回归过程相同。

训练参数

GBDT的训练参数最基础需要:

  • 学习率(learning rate)
  • 迭代次数(n_trees)
  • 单棵树深度(max_depth)

优点

  • 树的生成过程可以理解成自动进行多维度的特征组合。从根结点到叶子节点上的整个路径(多个特征值判断),才能最终决定一棵树的预测值, 另外,对于连续型特征的处理,GBDT 可以拆分出一个临界阈值,比如大于 0.027 走左子树,小于等于 0.027(或者 default 值)走右子树,这样很好的规避了人工离散化的问题。这样就非常轻松的解决了逻辑回归那里自动发现特征并进行有效组合的问题, 这也是GBDT的优势所在。

缺点

  • 海量id类特征并不能有效存储

2.3 GBDT+LR

由于LR本身需要做大量的人工特征交叉,而GBDT可以自动化特征筛选组合,从而形成新的离散特征向量。所以将这两个模型连接起来,用GBDT对原特征进行特征筛选组合,然后输出给LR模型进行分类就是著名的GBDT+LR模型了。

gbdt_lr

如上图所示,训练时,GBDT 建树的过程相当于自动进行的特征组合和离散化,然后从根结点到叶子节点的这条路径就可以看成是不同特征进行的特征组合,用叶子节点可以唯一的表示这条路径,并作为一个离散特征传入 LR 进行二次训练。举例上图xx作为一条输入样本经过左右两棵GBDT分类树,分别落到两棵树的叶子节点上,每个叶子节点对应LR的一维特征,那么通过遍历树就得到了该样本对应的所有LR特征。举例两棵并行树会分别生成[0,1,0][0,1,0][1,0][1,0],然后他们连接起来是[0,1,0,1,0][0,1,0,1,0]然后输入给下面的LR模型以完成分类。

在预测时,会先走GBDT的每棵树,得到某个叶子节点对应的一个离散特征,然后把该特征以one-hot形式传入LR与原特征一起线性加权预测。

关键点

  1. 通过GBDT进行特征组合之后得到的离散向量是和训练数据的原特征一块作为逻辑回归的输入, 而不仅仅全是这种离散特征
  2. 建树的时候用ensemble建树的原因就是一棵树的表达能力很弱,不足以表达多个有区分性的特征组合,多棵树的表达能力更强一些。GBDT每棵树都在学习前面棵树尚存的不足,迭代多少次就会生成多少棵树。
  3. RF也是多棵树,但从效果上有实践证明不如GBDT。且GBDT前面的树,特征分裂主要体现对多数样本有区分度的特征;后面的树,主要体现的是经过前N颗树,残差仍然较大的少数样本。优先选用在整体上有区分度的特征,再选用针对少数样本有区分度的特征,思路更加合理,这应该也是用GBDT的原因。
  4. 在CRT预估中, GBDT一般会建立两类树(非ID特征建一类, ID类特征建一类), AD,ID类特征在CTR预估中是非常重要的特征,直接将AD,ID作为feature进行建树不可行,故考虑为每个AD,ID建GBDT树。
    1. 非ID类树:不以细粒度的ID建树,此类树作为base,即便曝光少的广告、广告主,仍可以通过此类树得到有区分性的特征、特征组合
    2. ID类树:以细粒度 的ID建一类树,用于发现曝光充分的ID对应有区分性的特征、特征组合

参考资料

  1. blog.csdn.net/Rock_y/arti…
  2. blog.csdn.net/olizxq/arti…
  3. blog.csdn.net/zpalyq110/a…
  4. zhuanlan.zhihu.com/p/35465875