召回06:双塔模型——模型结构、训练方法
本博文引自王树森老师推荐系统。
视频地址:召回06:双塔模型——模型结构、训练方法_哔哩哔哩_bilibili
课件地址: https://github.com/wangshusen/RecommenderSystem
复习
这节课后面两节课的内容是双塔模型,双塔模型可以看作是矩阵补充的升级版。
这是上节课介绍的矩阵补充模型,模型的输入是一个用户ID和一个物品ID模型,用两个embedding层把两个ID映射到两个向量,用两个向量的内积,来预估用户对物品的兴趣分数,这个模型比较弱,只用到了用户和物品的ID,没有用到用户和物品的属性。
双塔模型
这节课介绍的双塔模型,可以看作是矩阵补充模型的升级,下面开始讲双塔模型的结构。
我们先来看用户的特征,我们知道用户ID还能从用户填写的资料,和用户行为中获取很多特征,包括离散特征和连续特征,所有这些特征不能直接输入神经网络,而是要先做一些处理。比如用embedding层把用户ID映射到一个向量,跟上节课的做法相同。用户还有很多离散特征,比如所在城市感兴趣的话题等等,用embedding层把用户的离散特征映射成向量,对于每个离散特征,用单独一个embedding层得到一个向量。比如用户所在城市用一个embedding层,用户感兴趣的话题用另一个embedding层,对于性别这样类别数量很少的离散特征,直接用one-hot编码就行,可以不做embedding。用户还有很多连续特征,比如年龄,活跃程度,消费金额等等,不同类型的连续特征有不同的处理方法。最简单的是做归一化,让特征均值是零,标准差是一,有些长尾分布的延续特征需要特殊处理,比如取log,比如做分桶。做完特征处理,得到很多特征向量,把这些向量都拼起来,输入神经网络。神经网络可以是简单的全连接网络,也可以是更复杂的结构,比如深度交叉网络。神经网络输出一个向量,这个向量就是对用户的表征,做召回要用到这个向量。
物品的特征也是用类似的方法处理,用embedding层处理物品ID和其他离散特征,用归一化,取对数,或者分桶等方法处理物品的连续特征。把得到的特征输入一个神经网络,神经网络输出的向量就是物品的表征,用于召回。
这样的模型就叫双塔模型,左边的塔提取用户的特征,右边的塔提取物品的特征,跟上节课的矩阵补充模型相比,双塔模型的不同之处,就在于使用了ID之外的多种特征作为双塔的输入。两个塔各输出一个向量,记作A和B。两个向量的内积就是模型最终的输出。
预估用户对物品的兴趣,现在更常用余弦相似度。两个塔的输出分别记作向量A和B,余弦相似度,意思是两个向量夹角的余弦值,它等于向量内积除以A的二范数,再除以B的二范数。其实就相当于先对两个向量做归一化,然后再求内积。余弦相似度的大小,介于-1到+1之间。
双塔模型的训练
这节课剩余的内容是双塔模型的训练,有三种训练双塔模型的方式。
第一种是pointwise,训练独立看待每个正样本和负样本,做简单的二元分类训练模型,把正样本负样本组成一个数据集,在数据集上做随机梯度下降训练双塔模型。
第二种训练双塔模型的方式是pairwise,每次取一个正样本,一个负样本组成一个二元组,损失函数用triplet hinge loss或者triplet logistic loss,可以参考下面这篇FACEBOOK发的论文。
第三种训练双塔模型的方式是listwise,每次取一个正样本和多个负样本组成一个list,训练方式,类似于多元分类,参考下面的文献二,这是YOUTUBE发的论文。
正负样本选择
训练的时候要用到正样本和负样本,该如何选择正负样本呢。
正样本很简单,就是用户点击过的物品,用户点击过一个物品,就说明用户对这个物品感兴趣,负样本的意思是用户不感兴趣的,负样本的选择没有那么显然,在实践中,负样本的选择是比较讲究的,有几种看起来比较合理的负样本,比如没有被召回的;召回了但是被粗排精排淘汰的;还有曝光了但是用户没有点击的,该用哪种呢。下节课再详细讲解如何选择正负样本,感兴趣的话可以自己阅读,刚才提到的两篇论文,论文讲解了FACEBOOK,YOUTUBE如何选择正负样本,我们小红书基本是照着做的,再加一些自己的小技巧,取得了很好的效果。
Pointwise训练
下面我具体讲解三种训练双塔模型的方式,第一种是pointwise训练,pointwise是最简单的训练方式,我们把召回看作简单的二元分类任务,正样本意思是历史记录显示用户对物品感兴趣,对于正样本,我们要鼓励向量A和B的cos相似度接近+1。负样本的意思是用户对物品不感兴趣,对于负样本,我们要鼓励向量A和B的cos相似度接近-1。这就是个很典型的二元分类问题。如果做pointwise训练,可以把正负样本的数量控制在一比二或者一比三,我也不知道为什么,但是互联网大厂的人都这么做,这算是业内的经验。
Pairwise训练
第二种训练双塔模型的方式是pairwise。做训练的时候,每一组的输入是一个三元组,包括一个用户和两个物品,左边的物品是正样本,就是用户感兴趣的物品,右边的物品是负样本,是用户不感兴趣的物品,把用户的特征和物品的特征各自做变换,然后输入神经网络,最终输出三个向量,用户的特征向量记作a,两个物品的特征,向量记作b+和b-。两个物品塔是相同的,里面的embedding层和全连接层都用一样的参数。分别计算用户对两个物品的兴趣,用户对正样本的兴趣是向量a和向量b+的余弦相似度,这个值越大越好,最好是接近+1;用户对负样本的兴趣是,向量a和向量b-的余弦相似度,这个值越接近-1越好。
做pairwise训练的基本想法,是让用户对正样本的兴趣尽量大,对负样本的兴趣尽量小,也就是让向量a和b-的余弦相似度,大于向量a和b+的余弦相似度,而且两者之差越大越好。我们来推导一下损失函数,我们希望看到用户对正样本的兴趣很大,而对负样本的兴趣很小,最好是前者比后者大m这么多。这个m是个超参数,需要调,比如设置成一。如果前者比后者大了m那么就没有损失,否则如果用户对正样本的兴趣不够大,没有比负样本的兴趣大m这么多,就会有损失,损失等于$\cos(a,b^-)+m-\cos(a,b^+)$,这样就推导出了下面的triplet hinge loss,如果熟悉孪生网络,Siamese network,应该见过这种损失函数。训练的时候,每个训练样本都是个三元组向量,a是用户的表征,b+和b-分别是物品正样本和负样本的表征,我们希望损失函数越小越好,训练的过程就是对损失函数求最小化,用梯度更新双塔神经网络的参数.
triplet hinge loss只是一种损失函数,还有别的损失函数起到同样的作用。triplet logistic loss是这样定义的,其实就是把logistic函数作用到$\cos(a,b^-)-\cos(a,b^+)$。最小化logistic函数会鼓励这一项尽量小,也就是让$\cos(a,b^-)$尽量小,让$\cos(a,b^+)$尽量大。跟上面的triplet hinge loss,道理是一样的,都是让用户对正样本的兴趣分数尽量高,用户对负样本的兴趣分数尽量低。这里的$\sigma$是个大于零的超参数,控制损失函数的形状,$\sigma$需要手动设置。我们已经推导出了,triplet hinge loss和triple logistic loss,可以通过最小化这样的损失函数来训练双塔模型。训练样本都是三元组,其中一个用户,一个正样本物品,还有一个负样本物品。
Listwise模型训练
第三种训练双塔模型的方式是listwise。做listwise训练的时候,每次取一个正样本和很多负样本,需要一个用户,把它的特征向量记作a,取一个正样本,意思是历史记录显示用户喜欢这个物品,把这个物品的特征向量记作b+,还需要取N个负样本,把它们的特征向量及做$b_1^-$到$b_n^-$,做训练的时候,要鼓励a和正样本的余弦相似度尽量大,鼓励a和负样本的余弦相似度尽量小。
下面我演示一下listwise训练具体怎么做。向量a和b+的余弦相似度,是个介于-1到+1之间的实数,意思是用户对正样本物品兴趣的预估分数,这个分数越大越好,最好是接近+1,这些cos相似度对应负样本,他们是用户对$n$个负样本兴趣的预估分数,这$n$个分数越小越好,最好是接近-1。把这$n+1$个分数输入softmax激活函数,激活函数输出$n+1$个分数,这些分数都介于0~1之间。最左边的分数$s^+$对应正样本,我们希望这个分数越大越好,最好是能接近1,这$n$个分数对应负样本,我们希望这些分数越小越好,最好都接近0。$y^+=1$是正样本的标签,意思是鼓励$s^+$接近1,负样本的标签是$y_1^-$到$y_n^-$,把它们都设置成0,意思是鼓励$s_1^-$到$s_n^-$都接近零,我们用$y$和$s$的交叉熵作为损失函数,训练的时候,最小化交叉熵,意思是鼓励softmax输出$s$接近标签$y$,其实交叉熵就等于$-\log s^+$。训练的时候最小化交叉熵,也就是最大化$s^+$,这就等价于最大化正样本的余弦相似度,最小化负样本的余弦相似度。
总结
我已经讲完了双塔模型和三种训练方式,最后总结一下这节课的内容。
这节课详细讲解了双塔模型,双塔模型顾名思义,有两个塔,一个用户塔,一个物品塔,两个塔各输出一个向量。向量的余弦相似度就是对兴趣的预估值,这个值越大,用户就越有可能对物品感兴趣。
有三种训练双塔模型的方式,一种是pointwise,每次用一个用户和一个物品,物品可以是正样本,也可以是负样本。第二种训练方式是pairwise,每次取一个用户,一个正样本物品,一个负样本物品,训练的目标是最小化Triplet loss,也就是让正样本的余弦相似度尽量大,负样本的余弦相似度尽量小。第三种训练方式是listwise,每次取一个用户,一个正样本物品,很多个负样本物品,训练的时候,用softmax激活函数和交叉熵损失函数,也是鼓励正样本的余弦相似度尽量大,负样本的余弦相似度尽量小。
在结束这节课内容之前,我们讨论一种错误的召回模型的设计,大家一看到这种结构就应该知道,这是粗排或者精排的模型,而不是召回的模型,这种模型没办法应用到召回。下面这块结构跟双塔模型是一样的,都是分别提取用户和物品的特征,得到两个特征向量,但是上层的结构就不一样了,这里直接把两个向量做concatenation,然后输入一个神经网络,神经网络可以有很多层。这种神经网络结构属于前期融合,在进入全连接层之前就把特征向量拼起来了,这种前期融合的神经网络结构,跟前面讲的双塔模型有很大区别,双塔模型属于后期融合,两个塔在最终输出相似度的时候才融合起来。
看一下图中的神经网络,这个神经网络最终输出一个实数作为预估分数,表示用户对物品的兴趣。再看一下这个部分,把两个特征向量拼起来,输入神经网络,这种前期融合的模型不适用于召回,假如把这种模型用于召回,就必须把所有物品的特征都挨个输入模型,预估用户对所有物品的兴趣,假设一共有1亿个物品,每给用户做一次召回,就要把这个模型跑1亿次,这种计算量显然不可行。如果用这种模型,就没办法用近似最近邻查找来加速计算,这种模型通常用于排序,从几千个候选物品中选出几百个,计算量不会太大,以后大家一看到这种前期融合的模型就要明白,这是排序模型,不是召回模型,召回只能用双塔那样的后期融合模型。