Ad Click Prediction: a View From the Trenches
1 简介
FTRL是一种新的参数学习算法,利用它可以对传统的监督学习进行提升,并且利用了特征自适应(per-coordinate)的学习率。作者还探讨了其他非传统机器学习研究的内容,主要包含了内存节省的技巧,性能评估和可视化,预测概率的信心评估的方法,标准化方法以及自动特征管理。另外还列出了作者试验的一些并没有提升的方法。
2 系统概述
在广告竞价领域,一个查询的结果怎么返回给用户,返回的结果以何种顺序返回,以及如果它们被点击了怎么收费这些都是通过竞价的方式决定的,而竞价的决定因素之一就是返回结果的点击率。点击率预测的输入包含了几种数据,包含查询字段,广告的创新性以及各种广告相关的数据,这些数据很多时候都是极度稀疏的,即只有一小部分的字段是非0的。正则化的逻辑回归之类的算法是对这种数据进行拟合的直觉选择,每天都会观测到很多的数据,模型需要在数据观测到之后进行快速更新。另外,在部署在线上进行预测的时候我们需要的是一个稀疏的模型。
3 在线学习和稀疏性
在大规模的学习场景下,正则化的线性模型的学习算法有很多优势。尽管样本x有上亿维的特征,但是很多时候只有几百维数据是非零的。该特性让我们能够将数据从磁盘流式地输入到网络中来对大数据集进行有效的训练,这样的原因是每个训练样本只需要被训练一次。
符号设定:$g_{t}\in R^{d}$
,其中t表示当前训练样本的下标,它的第i个入口表示$g_{t,i}$
,并且也用简化的求和符号$g_{1:t}=\sum_{s=1}^{t} g_{s}$
。
以逻辑回归为例,作者用它对问题建模。对第t个样本,作者对 $x_{t} \in R^{d}$
进行预测$p_{t}=\sigma\left(w_{t}*x_{t}\right)$
,然后作者观测到标签$y_{t} \in \left\{ 0,1 \right\} $
,损失函数是LogLoss
l_{t} = -y_{t}\log p_{t} -\left( 1-y_{t} \right)\log\left( 1-p_{t} \right)
显然,$\bigtriangledown l_{t} =\left(\sigma\left(w*x_{t}\right)-y_{t}\right)x_{t}=\left(p_{t}-y_{t}\right)x_{t}$
,这个梯度就是优化目标所需要的。
OGD(Oline Gradient Descent)已经对这种方式提供了有效的解法,然而在现实中一个考虑点是最终模型的大小,$w$
的非零元素可以稀疏地存储,所以$w$
的非零元素的个数决定了内存节省的百分比。
OGD并不能产生稀疏的模型,实际上,简单地对损失函数加上L1惩罚项的次梯度不会产生0值的参数。许多成熟的方法如FOBOS和truncated gradient(截断的梯度,直接截断方法是每隔k次就不更新参数,截断的梯度稍稍复杂点,见下图)在产生稀疏性上取得了成功,Regularized Dual Averaging算法比FOBOS更好地在准确性和稀疏性之间进行取舍。然而作者观测到在他们的数据集上基于梯度下降的方法比RDA能够产生更好的准确性。FTRL算法能够同时获得梯度下降的准确性和稀疏性。如果不加正则,该算法和标准的在线梯度下降算法等价,但是由于它用了其他的模型参数w的懒惰表示(alternative lazy representation of the model coefficents w),L1正则化实现的更为高效。
Truncated Gradient算法简介:
考虑到一系列的梯度
$g_{t}\in R^{d}$
,OGD更新参数方式为
w_{t+1} = w_{t} - \eta_{t}g_{t}
其中,$\eta_{t}$
是一个非增的学习率,如$\eta_{t} = \frac{1}{\sqrt{t}}$
,然而FTRL用以下的更新方式,(证明等价于SGD)
w_{t+1}=\mathop{\arg\min}_{w}\left(g_{1:t}\cdot w+\frac{1}{2}\sum_{s=1}^{t} \sigma_{s}\left \|w-w_{s} \right\|_{2}^{2}+\lambda_{1}\left\| w\right\|_{1} +\lambda_2 \left\| w\right\|_{2}^{2}\right)
$ \sigma_{s}$
是学习率因此$ \sigma_{1:t} = \frac{1}{\eta_{t}}$
。表面上来看这两种更新方式完全不一样,但实际上我们设置$\lambda_{1}=0$
它们就会产生相同的参数。然而FTRL在$\lambda_1>0$
时会产生稀疏解。
从公式来看,有人可能会觉得实现FTRL比实现OGD更难或者需要存储过去的所有参数。但实际上在整个过程中只需要存储一份参数,我们可以将用来更新$w\in R^{d}$
的argmin函数重写为
\left(g_{1:t}-\sum_{s=1}^{t}\sigma_{s}w_{s} \right)\cdot w +\frac{1}{2}\left( \frac{1}{\eta_{t}}+\lambda_2\right)\left\|w\right\|_{2}^{2}+\lambda_{1} \left\| w \right\|_{1}+\left(constant\right)
如果我们已经存储了$z_{t-1}=g_{1:t}-\sum_{s=1}^{t-1}\sigma_{s}w_s$
,我们在t轮更新的时候令$z_t = z_{t-1}+g_t -\left( \frac{1}{\eta_{t}}-\frac{1}{\eta_{t-1}}\right)w_t$
,并且用闭包形式求得$w_{t+1}$
(证明见链接或者附录)
w_{t+1,i}=\left\{
\begin{array}{lcl}
0 & & {|z_{t,i}| < \lambda_1}\\
-\frac{\left(z_{t,i}-sgn\left(z_{t,i}\right)\lambda_{1}\right)}{\frac{1}{\eta_t}+\lambda_2} & & {otherwise}
\end{array} \right.
因此,FTRL存储$z\in R^{d}$
在内存中,然而OGD存储$w\in R^{d}$
。算法1 采用了这种方法,但是它也加入了per-coordinate学习率规划项并且加入了支持L2正则的参数$\lambda_2$
。我们也能直接存储$\eta_{t}z_t$
而不存储$z_t$
。注意当$\eta_t$
是一个常量并且$\lambda_1=0$
时,可以看到公式就等价于在线梯度下降,因为$w_{t+1}=-\eta z_t = -\eta\sum_{s=1}^{t}g_s$
正是梯度下降做的事情。
3.1 每个特征独有学习率
在线梯度下降建议用所有特征共有的学习率$\eta_t=\frac{1}{\sqrt{t}}$
。在某些任务上,每个特征不同的学习率有重要的好处,考虑到$g_{s,i}$
是$g_s=\bigtriangledown l_s \left(w_s\right)$
的第i项特征的梯度,那么
\eta_{t,i}=\frac{\alpha}{\beta+\sqrt{\sum_{s=1}^tg_{s,i}^2}}
在某种感觉上该学习率是近似最优的【why?】。实际上$\alpha$
根据特征和数据集大小进行调整,$\beta$
通常设置为1就够了,这样就能保证开始的学习不会太大。正如所描述的那样,该算法要求存储每个特征的梯度的和以及梯度平方的和。4.5节会提供另一种梯度平方和在许多模型上分期计算的内存节省形式。
4 大规模情况下的内存节省
正如前面所描述的那样,利用L1正则项能够在预测的阶段能节省内存,在此节作者主要介绍在训练时的内存节省。
4.1 概率特征包含
在许多高维特征数据领域,大部分特征出现次数都很少,实际上在作者的一些模型上,一半的独特的特征都只在整个数十亿样本中出现一次。
对这些稀疏且实际上可能对模型并没有用的特征进行存储统计权重是不划算的,但是在训练过程中我们不知道那些权重是稀疏的。一种方法是在预处理的时候对出现次数较少的特征进行剔除,但是读取和写入一遍这些数据的代价有时候会很大,并且如果一些特征被剔除后就不能用这些特征去构建模型来评价这些特征被剔除后的准确率损失了。
L1正则化的算法都不需要对参数为0的特征进行存储。这允许包含信息少的特征被移除,然而我们发现这种稀疏化的方法会导致与FTRL相比的令人难以接受的准确率损失。另外一种常用的方法——散列哈希(见9.1节),在这个问题上也没有任何改善。
作者尝试的另一类方法是概率特征包含(probabilistic feature inclusion),这种方法对特征像首次出现那样对其进行概率地引入(new features are included in the model probabilistically as they are first occur)。这种方法达到了对数据进行预处理的效果,但是却能在线处理。作者尝试了两种方法:
Poison Inclusion:当我们遇到一个模型里没有的特征时,根据概率p将它添加到模型中。添加到模型中后我们像OGD一样对其进行学习,一个特征在它被添加到模型中之前被遇到的次数期望是$\frac{1}{p}$
。
Bloom Filter Inclusion:我们用一组计数Bloom过滤器检测特征在训练时被遇到n次。只要特征遇到次数超过n次我们将其加入模型。这个方法也是概率性的,因为记数Bloom过滤器有“假正”却没有“假负”。这意味着我们有时候会加入一个出现次数少于n次的特征。
4.2 更少的编码位
因为几乎所有的特征权重都在(-2,+2)区间内,所以用新的编码方式即更少的bit位去存储这些权重。
4.3 训练多个相似模型
大致意思是训练多个高度相似的模型的时候可以将所有特征用哈希表存储起来。
4.4 一种单值结构
在训练多个模型的时候只保存每个特征的一份参数,然后模型在预测的时候进行加权平均。
4.5 用次数计算学习率
假设每个样本对特定特征的包含概率相同,并且假设模型已经对概率进行进行了准确的估计。P个正样本,N个负样本,如果我们使用的是逻辑回归的话,正样本的梯度是p-1,负样本的梯度是p-1,所以所有样本的梯度之和
\sum g_{t,i}^2 = \sum_{positive\,events}\left(1-p_t\right)^2+\sum_{negative\,events}p_t^2
\approx P\left(1-\frac{P}{N+P}\right)^2+N\left(\frac{P}{N+P}\right)^2
= \frac{PN}{N+P}
4.6 训练数据子采样
像CTR预估中正负样本不均匀,所以以概率进行采样
w_t=\left\{
\begin{array}{lcl}
1 & & {clicked}\\
\frac{1}{r} & & {otherwise}
\end{array} \right.
其中,$r\in \left(0,1\right]$
是所有搜索结果都没被点击的概率。
在计算损失函数的时候将权重乘以每个样本的损失,所以也会同时乘上损失函数的梯度。为了验证这样做的效果,考虑从未采样的数据集中选择一个样本t到下采样的目标函数中。令$s_t$
为样本t被采样额概率,按照定义,$s_t=\frac{1}{w_t}$
,因此
E\left[l_t\left(w_t\right)\right] = s_t w_t l_t\left(w_t\right)+\left(1-s_t\right)0 = s_t \frac{1}{s_t}l_t\left(w_t\right)=l_t\left(w_t\right)
这样对未点击数据进行下采样对准确率有轻微的影响,并且预测表现并不特别受r的大小的影响。
5 模型表现评估
因为不同的评价标准使得模型朝着不同的方向变化,作者用多元化的性能评估方法来评估模型。
5.1 渐进验证
作者并没有使用交叉验证的方法,因为在计算梯度的时候会对样本进行预测,可以将某些样本留到后面进行分析。作者同样在样本的多种子集上计算多种评价方法。在线评价标准计算是一种很好的线上预测准确率的替代,因为这些标准只在我们训练之前的数据上计算,这些数据和模型接下来要预测的很类似。因为可以同时使用样本进行训和测试,在线损失也有很好的统计结果。
另外,作者提到基于值的评估变化太大,比如用对数损失在50%和2%变化太大,所以作者说在他们的经验中使用模型的百分比(相对变化,用相对上一个模型提升了多少百分比)变化更稳定。并且在模型评估的时候最好在同一个时段的数据上进行评估,否则在不同时段两个不同模型的评估结果并没有说服力。
5.2 可视化的深层解读
6. 信心估计
在很多应用场景中,预测CTR不只是唯一的标准,预测准确率期望的结果的质量也很重要。预测结果的质量经常被用在衡量和控制E&E问题中。一般预测结果信心的评估在没有正则的收敛的模型中,但是在线模型并没有假设是IID的数据,并且带正则化,另外标准统计的计算方法要求对n x n大小的矩阵求逆,但是在广告中n是数十亿大小,这种方法不太可行。
其实,信心计算可以在预测的时候很容易地被计算,和预测的时间差不多。作者提出了一种启发式算法——不确定得分,每个特征的
|x \cdot w_t - x \cdot w_{t+1}| = \sum_{i:|x_i|>0}\eta_{t,i}|g_{t,i}|
\leq \alpha \sum_{i:|x_i|>0}\frac{x_{t,i}}{\sqrt{\eta_{t,i}}}=\alpha \eta \cdot x \equiv u\left(x\right)