part4:前面三部分主要介绍了model server的搭建以及recom server中如何构建client 来访问model服务,第四部分主要讲一下google 论文 wide and deep learning for Recomender Systems里提到的算法部分,以及所做的离线实验与model的导出。最后给出在线abtest的一些数据。[本文默认看官了解lr,dnn的一些基本常识如果不了解请自行补充学习再读下文]
(1)wide and deep model training 公式推导
在pctr领域,业界广泛应用的有lr以及树模型(gbdt)或者他们的组合,再加上今天我们的所要介绍的dnn模型,也就构成了learn to rank 的几种point wise打法。虽然各种模型各有优劣,但是一个成熟的算法工程师是不会沉迷模型而不自拔的,因为数据特征决定上限,而模型只不过是体现了逼近这个上限的程度。当你的任务数据样本还不是很有规律的时候也许一个简单的规则或者最简单的浅层模型就可以事半功倍,盲目的套用模型只能是东施效颦。这就如同国家男足的水平只有65分,你把世界冠军里皮请来他可以让你打出60分的水平(可能另一个比世界杯冠军教头里皮差一些的教练也可以让你打出58分,59分的水平),但是绝对打不出75分,成为世界top50.恭喜国家男子足球队战胜乌兹别克,保留出线希望。
在工业界领域,lr模型有天然的优势,就是简单快捷,而且无性能瓶颈,但是同样是劣势十分明显,需要大量的人工特征工程,为了增加非线性化需要增加大量的特征组合(feature cross),对于特征处理的基本功要求极高,这在我曾经做过的几个项目中深有体会。而且特征组合过程中不能保证组合特征的所有取值情况都出现在training data中,对于一些数据规律会学不到位。而且了解dnn的同学会发现,其实lr就是只有输入,输出和一个隐层的dnn模型。显然不能更好的抽象特征之间的组合关联关系,以及描述那些特征组合之上的二阶组合。关于论文中详述的 dnn与lr 分别在rank的generalize 和memorization这两方面的好处,这里我就不翻译给大家了。论文还是自己一手读,读的有感觉。论文在model 构建之后,并木有给出training的公式推导。根据自己的理解我尝试推导一下,如果有差错之处,请看官指出。
由下图一可知,wide&deep模型的输入层分为wide part 和deep part。其中wide part有离散特征,以及一些离散特征的组合。不过这些离散特征要经过embeding成一定的维度比如userid,docid。(关于embeding技术如果感兴趣,请参考google 给出的word2vec的数学推导,非常的详细,可以有助于理解)。deep part的输入层混合了一些离散特征和连续特征,可以放置一到多个隐层,隐层的激活函数是RELU函数,木有用常用的sigmod。最后一层隐层的输出直接和widepart 的输出线性加权一下,经过sigmod函数去线性化就是最后的点击率预估。这个model其实并不是十分复杂,但是它在训练的时候是joint training,有别于传统的boosting,也就是论文所说的ensemble。这里不是分别独立训练多个弱model,而是这俩model被看成了一个model,他们的参数一起迭代。
公式推导结合下面的图2,图3,图4,图5展开。图2是论文中唯一的提及model 公式的地方,给出了model的输出层的公式,激活函数是sigmod,然后wide part,deep part 线性加权。后面的公式推导同样也将w分成 wdeep以及w wide,w wide就是一个和wide part 特征维度相同的行向量。wdeep 向量都是带有上下标的,上标的l表示第l个隐藏层中的模型参数,L表示最后一层,也就是这里的输出层。下标是两个字母,如jk 则表示从l-1层的k节点指向l层的j节点的这条边。由于这里的输出层只有一个节点。输出层的描述直接用了下标out。下面分析一下如下几个公式。公式1是损失函数这里采用了对数似然损失函数也就是论文中的(logit function),为什么不用平方损失函数,请大家思考,公式中写的是n个样本的总损失然后平均,下面推导梯度的过程中用的Loss function,并木有取sum,而是用了一个样本的损失函数,这里并不影响。公式2是输出层的前向公式,也就是图2中的公式,是model的输出层。公式3,是损失函数对于wide part行向量中的某一个参数求梯度的过程,我写的应该很清晰。定义z为每一层激活函数去线性化之前的输出。公式4,5是Backpropagation中的误差反向传播公式。由公式5可以看出l层j节点误差可以有上一层的每个节点的误差以及j节点指向下一层的链接权重求出。只不过由于最后一层和前几层的激活函数不一样,所以后面的导数不一样。有了5,6就可以对每一层的每一个节点偏移以及链接权重求梯度了。既然model里每一个参数的梯度我们都得到了,就可以利用梯度下降来每一次迭代更新一次模型参数,直到达到迭代上限或者loss function收敛。当然这里只是简单的对核心部分的公式进行了求解。wide 和deep part 的参数迭代过程中少不了正则化。其实这个模型通过公式推导能够发现,相当于将最后一个隐藏分成了两部分,而两部分互不链接,输入也不交叉链接。所以google大神的想象力是可以的。
(2) model 的offline train
model的offline train整体遵循如图6的流程图,评估指标采用ave session auc。用户一次请求会话得到的视频是用顺序的根据用户点击不点击可以得到类似101000100。这样的序列,每一个都可以求一个auc,然后所有取平均。就是衡量的标准。训练完成的model 对测试样本进行重新打分,排序。也许就能将101000100变成111000000.这样就是变好了。也许会变成011000100,这样就是变差了。离线的训练请参考代码https://www.tensorflow.org/tutorials/wide_and_deep,非常详细,只需要将特征换成你的特征,但是需要特别注意请将tf.estimator.DNNLinearCombinedClassifier这个类包装到tf.contrib.learn.SKCompat这个类支持batch训练这样速度大大提高,否则速度太慢。不能满足真正的要求。关于模型导出请参考part3的参考文献。
采用了一个月的训练样本,3天的训练样本。其表现分别比线上base(lr与gbdt混合模型)。20170801,auc lift +0.4%,20170802,auc lift +0.5%。20170803 auc lift -0.5%。 由于我们的线上base是经过一版本规则,两版本模型迭代的,所以略有小胜也不是不可接受,决定推到生成环境小流量实验。
(3)线上效果短时分析
由于离线测评model表现还可以,所以决定推导线上实验,切分了一个流量不是很多的版本的15%流量dnn,15%流量lrgbdt混合模型,70%流量规则。来做了两周abtest。效果如下图。红颜色表示15%的dnn,紫颜色表示15%的treemodel,蓝颜色表示100%的流量。所以根据下面的结论,将wide and deep model 推到了一个大版本流量的70%来观察。后续会给出一个大流量版本上的表现。
致谢:特别感谢在这个项目开发过程中禚神在排期上的宽松。以及各位小伙伴的在特定工程问题上的指点。比如压测,比如环境搭建。比如从log中预处理出点击展示数据。谢谢大家。
最近百度外卖被饿了么合并了,但是我在lbs,百度外卖的两年多,从很多项目中学到了算法如何靠谱的落地到工业界。可以说是受益匪浅。谢谢先后与我合作并指导我的代老板,泉高工,邝高工以及禚神。虽然离职也有一年了,但是回忆起那时在物流调度算法,寻宝可视化数据挖掘平台,用户个性化营销那些激情燃烧的岁月,依然感觉收获满满。谨以此段,向岁月神偷致敬。
完成于20170902如需转载请注明出处。
全文第一稿完,王岳于20170902凌晨。