论文主要内容
参考的paper是
在LR算法训练中,在第t个instance上的logloss为
loglosst(wt)=−ytlogpt−(1−yt)log(1−pt)
其中wt是训练当前这轮的权重,yt是label,pt是预估的结果,即sigmoid函数的计算结果。一般yt是0或者1。因此这个公式可以简化为
loglosst(wt)={−log(1−pt),if yt=0 −logpt,if yt=1
从而推倒出的gradient为
g=(pt−yt)xt,而instance在预处理之后对应的xt一般是libsvm的数据格式,即featid:1,所以xt的值一般是1,因此这里可以等价为g=pt−yt。
在FTRL算法中,这个是g是迭代的主要变量。n,z均是基于这个g进行构建。
Online Gradient Descent(OGD)被证明是有效的,但是不不擅长产出sparse模型。而在具体的线上服务,计算pctr的时候,sparsity决定了服务性能;并且简单的对logloss增加L1正则化参数并不能非常有效的产出0值权重;
像FOBOS类似的复杂算法是简单粗暴的砍掉接近0值的权重以产生sparsity。
RDA算法在sparsity和accuracy之间取的平衡,而google提出的FTRL算法比RDA有更好的效果(更sparse的基础上更有accuracy)。
当没有正则化参数时候,FTRL算法等价于OGD,但是FTRL中使用了称为lazy representation of model coefficient w,因此实现L1正则上更有效率。
对所有coordinate设置相同的学习率并不是很理想的。因此文章提出来per-coordindate learning rate。
这里贴出完整的ftrl算法
一般的实现上严格按照这里的表达式进行计算即可。在具体的实践中有几个trick:
第一个即sigmoid函数结果有个上限。
return 1.0 / (1.0 + math.Exp(-math.Max(math.Min(w, 35), -35)))
即累加值∑(w∗x)设置在[−35,35]之间即可。
工程实现
这里使用python以及golang分别实现ftrl算法。使用的数据下载自kaggle的train、test数据。大部分逻辑是参考以github相关开源的实现。记录下来供参考。
数据的格式为csv的格式,我下载的数据内容为这里avazu点击数据,压缩后数据大小为1G多,包含有40428968行。这里给出具体的实现内容并分
析。
1 |
|
我们跑了10w个数据,结果如下:
1 | date: 10 holdafter: 9 |
这里需要说明,date这一列的数据形式为14102100,即YY-MM-DD-HH,其中Hour设置为0,train数据中日期的分布是1021-1030。这里我们取出date后再减去20,得到的日期范围为1-10。其中10我们不参与训练,而是用在计算logloss上,作为model的validate。
代码初始花,我们设置了n,z两个变量用来存储训练中的迭代变量。
x对于一行instance,读取文件后,基于hash() %D,作为这列特征值的id,并以这个id作为索引在n,z中读写。
其中提供的fullindex(self, x)是基于原始x,构建cross特征的id。代码中设置任何两个变量之间进行cross。当然这里没有做太多的特征工程,只是简单的cross了所有特征。
如果interaction=False,那么训练的还是原始数据instance。
predict以及update严格按照google的paper中给出的算法
结语
相对于之前使用的LBFGS,或OWLQN,最大的不同是以前的的算法会严格提出stopping criterion。但是FTRL并没有这么做。我理解,首先作者已经给出了证明,这个算法是必然收敛的;其次这里给出的是简单的遍历所有instance进行train,实际的工程使用中,可能有其他的策略变化,这里待补充实际的工业实践。