第一版的多目标排序采用的是共享参数层+任务独立参数层的结构。共同学习的多个目标是有内在联系的,对一个目标的训练会帮助到其他目标,通过共享底层参数可以实现这个目的。在共享参数层之上又加了任务独立的参数层,使各个训练任务之间可以独立更新参数,保证互不影响。需要注意的是,不同目标的训练数据是不一样的,转化类的目标比如阅读时长、点赞、收藏,这些要用户点击之后才能发生,所以只在有点击的数据上计算 Loss。另外由于阅读时长呈长尾分布,需要用对数时长作为训练目标。在多目标模型中每个目标都会对应一个模型打分,在线上预测是需要多多个分数进行加权融合。至于不同的目标对应的具体权重我们是通过 AB 实验确定的。
② 基于 MMOE 的第二版多目标排序
在去年年初的时候,我们采用 MMOE[2] 结构对多目标排序模型进行了升级。与 hard sharing 结构不同,MMOE 引入了 Expert,将共享参数层替换为多个 Experts 加权相加的方式,这些参数既是各个任务共享的,又通过独立权重的方式给了各个任务一定的独立性。这一版的更新在上线之后也是取得了一定的收益。
另一种思路就是考虑 Position Bias 的情况下,重新对用户点击进行建模。既然用户点击是由文档的真实点击率和文档的展示位置共同决定的,那就将这两部分分开预测,用文档的展示位置作为特征通过一个独立的 shallow tower 对 Position Bias 进行建模[3],将其他的 Query 和 Doc 本身的特征输入主模型结构建模文档的真实点击率,将两部分分数相加来预测用户点击。在线预测时,只需考虑真实点击率的部分。另外在训练的时候有两点需要注意,一个是最好在 shallow tower 的部分加一些 dropout 防止模型对 Position Bias 的过度依赖,另一个是只有点击目标会受到位置偏差的影响,所以只需要在点击目标上应用这个策略,在转化类目标上则无需这种处理。这一版的策略上线对点击比带来了显著的提升。
5. Context Aware LTR
对于排序任务可以有几种理解:
Point wise:确定每个文档独立打分,最终根据分数排序
Pair wise:确定两两文档对的相对顺序,推导出最终排序
List wise:直接优化待排序的文档的整体顺序
三种不同的方式都有对应损失函数,实践证明,对排序任务而言 List wise loss 优于 Pair wise loss 优于 Point wise loss。
从特征角度我们也可以通过构造一些 context 特征来实现 List wise,比如对待排序文档的特征取平均值或者中位数作为模型排序特征,实现起来比较简单,当然也是有效的。
常规的模型对不同文档的打分是相互独立的,我们希望可以实现一个模型在对单条文档打分时,同时可以考虑到其他的文档。在实践中我们借鉴了CV 中的 SE Block[4] 结构,具体到排序的场景就是:以全连接层输出 X 作为 SE Block 的输入,全连接层的输出维度是 L*C(L代表list size,C代表特征维度),然后先在 List维度上做Pooling,然后经过两层的全连接层之后将输出做Sigmoid 转换,映射到0到1之间,再跟乘回 X。通过List wise 的 Pooling操作就可以把一整个list信息融合到每个样本的打分里面,实现 List wise 打分。每一层全连接层后都可以接一个 SE Block 结构。在训练时有一个需要注意的点是,由于训练日志只是有展现的一小部分文档,但是在精排线上打分时候选集是比较大的,所以存在线上线下不一致的问题,而这种不一致对于 SE Block 的计算影响很大。为了解决这种的不一致的问题,我们在精排之后加了一个重排序阶段,把精排Top16的结果送到重排序模型中。保证Top16文档可以完整的加入到训练数据中,在离线训练时只有重排序模型应用 SE Block 结构,这样就保证了线上线下的一致性。这一版的策略更新对于头部位置的点击比提升比较明显。
Se Block结构之所以能发挥作用,本质上是因为在单条样本打分过程中融合了所有候选集的信息。另外有一种可能的解释是 SE Block结构实现了某种注意力机制,输出的 0 - 1 的打分可以看做是某个特征在当前 Query context 下的特征重要性,而同一个特征在不同 Query 下的特征重要性是一样的,所以通过这种结构模型可能可以学到这样的信息,来帮助模型更好的打分。