Real Recommender System For Industry

推荐系统

基础

基本概念

转化流程

消费指标

  • 点击率 = 点击次数 / 曝光次数
  • 点赞率 = 点赞次数 / 点击次数
  • 收藏率 = 收藏次数 / 点击次数
  • 转发率 = 转发次数 / 点击次数
  • 阅读完成率 = 滑动到底次数 / 点击次数 X f(笔记长度)

北极星指标

  • 用户规模:
    • 日活用户数(DAU)、月活用户数(MAU)。
  • 消费:
    • 人均使用推荐的时长、人均阅读笔记的数量。
  • 发布:
    • 发布渗透率、人均发布量。

实验流程

  • 离线实验:收集历史数据,在历史数据上做训练、测试。算法没有部署到产品中,没有跟用户交互。
  • 小流量 AB 测试:把算法部署到实际产品中,用户实际跟算法做交互。
  • 全流量上线

推荐系统的链路

  • 召回:从物品的数据库中快速取回一些物品,比如小红书有上亿篇笔记,当用户刷新小红书的时候,系统会同时调用几十条召回通道,每条召回通道取回几十到几百篇笔记,一共取回几千篇笔记。做完召回之后,接下来要从几千篇笔记中选出用户最感兴趣的。
  • 粗排:用规模比较小的机器学习模型,给几千篇笔记逐一打分,按照分数做排序和截断,保留分数最高的几百篇笔记。
  • 精排:用大规模的深度神经网络给几百篇笔记逐一打分,精排的分数反映出用户对笔记的兴趣,在精排之后可以做截断,也可以不做截断。
  • 重排:根据精排分数和多样性分数做随机抽样,得到几十篇笔记,然后把相似内容打散,并且插入广告和运营内容。

召回通道:推荐系统有很多条召回通道,常见的包括协同过滤、双塔模型、关注的作者等。小红书的推荐系统有几十条召回通道,每条召回通道取回几十到几百篇笔记,这些召回通道一共会返回几千篇笔记。然后推荐系统会融合这些笔记,并写作去重和过滤,过滤的意思是排除掉用户不喜欢的作者、不喜欢的笔记、不喜欢的话题。

排序:用机器学习模型预估用户对笔记的兴趣,保留分数最高的笔记。如果直接用一个大规模的神经网络,逐一对几千篇笔记打分,花费的代价会很大。为了解决计算量的问题,通常把排序分为粗排和精排两步骤。粗排用比较简单的模型快速给几千篇笔记打分,保留分数最高的几百篇笔记。精排用一个较大的神经网络给几百篇笔记打分,不用做截断。精排模型比粗排模型大很多,用的特征也更多,所以精排模型打的分数更可靠。但是精排的计算量很大,先用粗排做筛选,然后才用精排,可以比较好的平衡计算量和准确性。做完粗排和精排得到几百篇笔记,每篇笔记有一个分数,表示用户对笔记的兴趣有多高,可以直接把笔记按照模型打的分数做排序,然后展示给用户。但此时的结果还存在一些不足,需要做一些调整,这一步叫做重排。重排主要是考虑多样性,要根据多样性做随机抽样,从几百篇笔记中选出几十篇,然后还要用规则把内容相似的笔记打散。稍后我会解释重排。重排的结果就是最终展示给用户的物品,比如把前 80 的物品展示给用户,其中包括笔记和广告。

粗排和精排模型:粗排和精排非常相似,唯一的区别就是精排模型更大,用的特征更多。模型的输入包括用户特征、候选物品的特征,还有统计特征。假如想要判断小王同学是否对某篇笔记感兴趣,就要把笔记的特征、小王的特征还有很多统计特征输入神经网络。神经网络会输出很多数值,比如点击率、点赞率、收藏率、转发率,这些数值都是神经网络对用户行为的预估,这些数值越大,说明用户对笔记越感兴趣。最后把多个预估值做融合,得到最终的分数,比如求加权和这个分数决定了笔记会不会被展示给用户,以及笔记展示的位置是靠前还是靠后。

重排:最重要的功能是多样性抽样,需要从几百篇笔记中选出几十篇笔记,常见的方法有 MMR 和DPP。抽样的时候有两个依据,一个依据是精排分数的大小,另一个依据是多样性。做完抽样之后会用规则打散相似内容,不能把内容过于相似的笔记排在相邻的位置上。举个例子,根据精排得到的分数,排前五的笔记全都是 NBA 的内容。即使用户是个篮球迷,他也未必希望看到同质化的内容。如果排第一的是 NBA 的笔记,那么接下来几个位置就不能放 NBA 的内容。相似的笔记会往后挪。重排的另一个目的是插入广告和运营推广的内容,还要根据生态的要求调整排序。

AB 测试

  • 召回团队实现了一种 GNN 召回通道,离线实验结果正向。
  • 下一步是做线上的小流量的 A/B 测试,考察新的召回通道对线上指标的影响。
  • 模型中有一些参数,比如 GNN 的深度取值 ∈{1,2,3},需要用 A/B 测试选取最优参数

随机分桶

  • 分 b = 10 个桶,每个桶中有 10% 的用户
  • 首先用哈希函数把用户 ID 映射成某个区间内的整数,然后把这些整数均匀随机分成 b 个桶。

  • 计算每个桶的业务指标,比如 DAU、人均使用推荐的时长、点击率等。
  • 如果某个实验组指标显著优于对照组,则说明对应的策略有效,值得推全。推全的意思是把流量扩大到 100%,给所有用户都使用两层 GNN 召回通道。

分层实验

  • 分层实验:召回、粗排、精排、重排、用户页面、广告,例如 GNN 召回通道属于召回层。
  • 同层互斥:GNN 实验占了召回层的 4 个桶,其他召回实验只能用剩余的 6 个桶。
  • 不同层正交:每一层独立随机对用户做分桶。每一层都可以独立用 100% 的用户做实验。

互斥 VS 正交

  • 如果所有实验都正交,则可以同时做无数次实验。
  • 同类的策略(例如精排模型的两种结构)天然互斥,对于一个用户,只能用其中一种。
  • 同类的策略(例如添加两条召回通道)效果会相互增强(1 + 1 > 2)或相互抵消(1 + 1 < 2)。互斥可以避免同类策略相互干扰。
  • 不同类型的策略(例如添加召回通道、优化粗排模型)通常不会相互干扰(1 + 1 = 2),可以作为正交的两层。

Holdout 机制

  • 每个实验(召回、粗排、精排、重排)独立汇报对业务指标的提升。
  • 公司考察一个部门(比如推荐系统)在一段时间内对业务指标总体的提升。
  • 取 10% 的用户作为 holdout 桶,推荐系统使用剩余 90% 的用户做实验,两者互斥。
  • 10% holdout 桶 VS 90% 实验桶的 diff(需要归一化)为整个部门的业务指标收益。

  • 每个考核周期结束之后,清除 holdout 桶,让推全实验从 90% 用户扩大到 100% 用户。
  • 重新随机划分用户,得到 holdout 桶和实验桶,开始下一轮考核周期。
  • 新的 holdout 桶与实验桶各种业务指标的 diff 接近 0。
  • 随着召回、粗排、精排、重排实验上线和推全,diff 会逐渐扩大。

反转实验

  • 有的指标(点击、交互)立刻受到新策略影响,有的指标(留存)有滞后性,需要长期观测。
  • 实验观测到显著收益后尽快推全新策略。目的是腾出桶供其他实验使用,或需要基于新策略做后续的开发。
  • 用反转实验解决上述矛盾,既可以尽快推全,也可以长期观测实验指标。
  • 在推全的新层中开一个旧策略的桶,长期观测实验指标。

召回

基于物品的协同过滤

物品相似度

  • 两个物品的受众重合度越高,两个物品越相似。
  • 例如:
    • 喜欢《射雕英雄传》和《神雕侠侣》的读者重合度很高。
    • 可以认为《射雕英雄传》和《神雕侠侣》相似。

计算物品的相似度

  • 喜欢物品 i1 的用户记作集合 W1。
  • 喜欢物品 i2 的用户记作集合 W2。
  • 定义交集 V = W1 ∩ W2。
  • 两个物品的相似度:

sim(i1,i2)=VW1W2sim(i_1, i_2) = \frac{|V|}{\sqrt{|W_1| \cdot{|W_2|}}}

  • 考虑喜欢的程度 like(user, item):

sim(i1,i2)=sVlike(v,i1)like(v,i2)u1W1like2(u1,i1)u2W2like2(u2,i2)sim(i_1, i_2) = \frac{\sum_{s \in V} \text{like}(v,i_1) \cdot \text{like}(v,i_2)}{\sqrt{\sum_{u_1 \in W_1} \text{like}^2(u_1,i_1)} \cdot \sqrt{\sum_{u_2 \in W_2} \text{like}^2(u_2, i_2)}}

ItemCF

  • 基本思想:

    • 如果用户喜欢物品 item1,而且物品 item1 与 item2 相似,那么用户很可能喜欢物品 item2
  • 预估用户对候选物品的兴趣:

    jlike(user,itemj)×sim(itemj,item)\sum_{j} \text{like}(\text{user}, \text{item}_j) \times \text{sim}(\text{item}_j, \text{item})

  • 计算两个物品的相似度:

    • 把每个物品表示为一个稀疏向量,向量每个元素对应一个用户。
    • 相似度 sim 就是两个向量夹角的余弦。

事先离线计算

建立“用户 -> 物品” 的索引

  • 记录每个用户最近点击、交互过的物品 ID。
  • 给定任意用户 ID,可以找到近期感兴趣的物品列表。

建立 “物品 -> 物品” 的索引

  • 计算物品之间两两相似度。
  • 对于每个物品,索引它最相似的 k 个物品。
  • 给定任意物品 ID,可以快速找到它最相似的 k 个物品。

线上做召回

  • 给定用户 ID,通过 “用户 -> 物品” 索引,找到用户近期感兴趣的物品列表(last-n)。
  • 对于 last-n 列表中每个物品,通过 “物品 -> 物品” 的索引,找到 top-k 相似物品。
  • 对于取回的相似物品(最多有 nk 个),用公式预估用户对物品的兴趣分数。
  • 返回分数最高的 100 个物品,作为推荐结果。

索引的意义在于避免枚举所有的物品。

用索引,离线计算量大,线上计算量小。

Swing 模型

  • 用户 u1 喜欢的物品记作集合 J1。

  • 用户 u2 喜欢的物品记作集合 J2。

  • 定义两个用户的重合度:

    overlap(u1,u2)=J1J2\text{overlap}(u_1,u_2) = |J_1 \cap J_2|

  • 用户 u1 和 u2 的重合度高,则他们可能来自一个小圈子,要降低他们的权重。

  • 两个物品相似度:

    sim(i1,i2)=u1Vu2V1α+overlap(u1,u2)sim(i_1,i_2) = \sum_{u_1 \in V} \sum_{u_2 \in V} \frac{1}{\alpha + \text{overlap}(u_1,u_2)}

总结

  • Swing 和 ItemCF 唯一的区别在于物品相似度。
  • ItemCF:两个物品重合的用户比例高,则判定两个物品相似。
  • Swing:额外考虑重合的用户是否来自一个小圈子。
    • 同时喜欢两个物品的用户记作集合 V。
    • 对于 V 中的用户 u1 和 u2,重合度记作 overlap(u1,u2)。
    • 两个用户重合度大,则可能来自一个小圈子,权重降低。

基于用户的协同过滤

计算用户相似度

  • 用户 u1 喜欢的物品记作集合 J1。
  • 用户 u2 喜欢的物品记作集合 J2。
  • 定义交集 I = J1 ∩ J2。
  • 两个用户的相似度:

sim(u1,u2)=IJ1J2sim(u_1, u_2) = \frac{|I|}{\sqrt{|J_1| \cdot{|J_2|}}}

  • 降低热门物品权重:

    sim(u1,u2)=lI1log(1+nl)J1J2sim(u_1, u_2) = \frac{\sum_{l \in I} \frac{1}{\text log (1 + n_l)}}{\sqrt{|J_1| \cdot{|J_2|}}}

  • n_l:喜欢物品 l 的用户数量,反应物品的热门程度。

UserCF

  • 基本思想:

    • 如果用户 user1 跟用户 user2 相似,而且 user2 喜欢某物品,那么用户 user1 也可能喜欢该物品。
  • 预估用户 user 对候选物品 item 的兴趣:

    jsim(user,itemj)×like(itemj,item)\sum_{j} \text{sim}(\text{user}, \text{item}_j) \times \text{like}(\text{item}_j, \text{item})

  • 计算两个用户的相似度:

    • 把每个用户表示为一个稀疏向量,向量每个元素对应一个物品。

事先离线计算

建立“用户 -> 物品” 的索引

  • 记录每个用户最近点击、交互过的物品 ID。
  • 给定任意用户 ID,可以找到近期感兴趣的物品列表。

建立 “用户 -> 用户” 的索引

  • 对于每个用户,索引他最相似的 k 个用户。
  • 给定任意物品 ID,可以快速找到他最相似的 k 个用户。

线上做召回

  • 给定用户 ID,通过 “用户 -> 用户” 索引,找到 top-k 相似用户。
  • 对于每个 top-k 相似用户,通过 “用户-> 物品” 的索引,找到用户近期感兴趣的物品列表(last-n)。
  • 对于召回的 nk 个相似物品,用公式预估用户对每个物品的兴趣分数。
  • 返回分数最高的 100 个物品,作为召回结果。

离散特征处理

离散特征

  • 性别:男、女两种类别。
  • 国籍:中国、美国、印度等 200 个国家。
  • 英文单词:常见的英文单词有几万个。
  • 物品 ID:小红书有几亿篇笔记,每篇笔记有一个 ID。
  • 用户 ID:小红书有几亿个用户,每个用户有一个 ID。
  1. 建立字典:把类别映射成序号。
    • 中国 -> 1
    • 美国 -> 2
    • 印度 -> 3
  2. 向量化:把序号映射成向量。
    • One-hot 编码:把序号映射成高维稀疏向量。
    • Embedding:把序号映射成低维度稠密向量。

One-hot 编码的局限

  • 自然语言处理中,对单词做编码。
    • 英文有几万个常见单词。
    • 那么 one-hot 向量的维度是几万。
  • 推荐系统中,对物品 ID 做编码。
    • 小红书有几亿篇笔记。
    • 那么 one-hot 向量的维度是几亿。

Embedding

  • 参数数量:向量维度 X 类别数量。
    • 设 embedding 得到的向量都是 4维的。
    • 一共有 200 个国籍。
    • 参数数量 = 4 X 200 = 800
  • 编程实现:TensorFlow、PyTorch 提供 embedding 层。
    • 参数以矩阵的形式保存,矩阵大小是 向量维度 X 类别数量
    • 输入是序号,比如 “美国” 的序号是 2。
    • 输出是向量,比如 “美国” 对应参数矩阵的第 2 列。

总结

  • 离散特征处理:one-hot 编码、embedding。
  • 类别数量很大时,用 embedding。
    • Word embedding。
    • 用户 ID embedding。
    • 物品 ID embedding。

矩阵补充

基本想法

  • 用户 embedding 参数矩阵记作 A。第 u 号 用户对应矩阵第 u 列,记作向量 a_u。
  • 物品 embedding 参数矩阵记作 B。第 i 号物品对应矩阵第 i 列,记作向量 b_i。
  • 内积 <a_u, b_i> 是第 u 号用户对第 i 号物品兴趣的预估值。
  • 训练模型的目的是学习矩阵 A 和 B,使得预估值拟合真实观测的兴趣分数。

数据集

  • 数据集:(用户ID,物品ID,兴趣分数)的集合,记作 Ω = {(u,i,y)}。
  • 数据集中的兴趣分数是系统记录的,比如:
    • 曝光但是没有点击 -> 0 分。
    • 点击、点赞、收藏、转发 -> 各算 1 分。
    • 分数最低是 0,最高是 4。

训练

  • 把用户ID、物品ID 映射成向量。

    • 第 u 号用户 -> 向量 a_u。
    • 第 i 号物品 -> 向量 b_i。
  • 求解优化问题,得到参数 A 和 B。

    minA,B(u,i,y)Ω(y<au,bi>)2\underset{A,B}{\text{min}} \sum_{(u,i,y) \in \Omega}(y - <a_u, b_i>)^2

模型存储

  1. 训练得到矩阵 A 和 B。
    • A 的每一列对应一个用户。
    • B 的每一列对应一个物品。
  2. 把矩阵 A 的列存储到 key-value 表。
    • key 是用户 ID,value 是 A 的一列。
    • 给定用户 ID,返回一个向量(用户的 embedding)。
  3. 矩阵 B 的存储和索引比较复杂。

线上召回

  • 把用户向量 a 作为 query,查找使得 <a, b_i> 最大化的物品 i。
  • 暴力枚举速度太慢。实践中用近似最近邻查找。
  • Milvus、Faiss、HnswLib 等向量数据库支持近似最近邻查找。

总结

  • 把物品ID、用户ID 做 embedding,映射成向量。
  • 两个向量的内积 <a_u,b_i> 作为用户 u 对物品 i 兴趣的预估。
  • 让 <a_u,b_i> 拟合真实观测的兴趣分数,学习模型的 embedding 层参数。
  • 矩阵补充模型有很多缺点,效果不好。

双塔模型 - 模型结构、训练方法

训练

  • Pointwise:独立看待每个正样本、负样本,做简单的二元分类。
  • Pairwise:每次取一个正样本、一个负样本 [1]。
  • Listwise:每次取一个正样本、多个负样本 [2]。

正负样本的选择

  • 正样本:用户点击的物品。
  • 负样本 [1, 2]:
    • 没有被召回的?
    • 召回但是被粗排、精排淘汰的?
    • 曝光但是未点击的?

Pointwise 训练

  • 把召回看作二元分类任务。
  • 对于正样本,鼓励 cos(a, b) 接近 + 1。
  • 对于负样本,鼓励 cos(a, b) 接近 - 1。
  • 控制正负样本数量为 1:2 或者 1:3。

Pairwise 训练

  • 如果 cos(a, b+) 大于 co(a, b-) + m,则没有损失。

  • 否则,损失等于 cos(a, b-) + m - cos(a, b+)。

  • Triplet hinge loss:

    L(a,b+,b)=max{0,cos(a,b)+mcos(a,b+)}L(a, b^+, b^-) = \text{max} \lbrace 0, \cos(a, b^-) + m - \cos(a, b^+) \rbrace

  • Triplet logistic loss:

    L(a,b+,b)=log(1+exp[σ(cos(a,b)cos(a,b+))])L(a, b^+, b^-) = \log(1 + \exp[\sigma \cdot(\cos(a, b^-)- \cos(a, b^+))])

Listwise 训练

  • 一条数据包含:
    • 一个用户,特征向量记作 a。
    • 一个正样本,特征向量记作 b+。
    • 多个负样本,特征向量记作 b1-, …, bn-。
  • 鼓励 cos(a, b+) 尽量大。
  • 鼓励 cos(a, b1+), …, cos(a, bn+) 尽量小。

总结

  • 用户塔、物品塔各输出一个向量。
  • 两个向量的余弦相似度作为兴趣的预估值。
  • 三种训练方式:
    • Pointwise:每次用一个用户、一个物品(可正可负)。
    • Pairwise:每次用一个用户、一个正样本、一个负样本。
    • Listwise:每次用一个用户、一个正样本、多个负样本。

双塔模型 - 正负样本

正样本

  • 正样本:曝光而且有点击率的用户-物品二元组(用户对物品感兴趣)。
  • 问题:少部分物品占据大部分点击,导致正样本大多是热门物品。
  • 解决方案:过采样冷门物品,或降采样热门物品。
    • 过采样(up-sampling):一个样本出现多次。
    • 降采样(down-sampling):一些样本被抛弃。

简单负样本:全体物品

均匀抽样:对冷门物品不公平

  • 正样本大多是热门物品、
  • 如果均匀抽样产生负样本,负样本大多是冷门物品。

非均抽采样:目的是打压热门物品

  • 负样本抽样概率与热门程度(点击次数)正相关。
  • 抽样概率 ∝**(点击次数)^ 0.75**

简单负样本:Batch内负样本

  • 一个物品出现在 batch 内的概率 ∝(点击次数)

  • 物品成为负样本的概率本该是 ∝(点击次数)^ 0.75,但在这里是 ∝(点击次数)。

  • 热门物品成为负样本的概率过大。

困难负样本

  • 困难负样本:
    • 被粗排淘汰的物品(比较困难)。
    • 精排分数靠后的物品(非常困难)。
  • 对正负样本做二元分类:
    • 全体物品(简单)分类准确率高。
    • 被粗排淘汰的物品(比较困难)容易分错。
    • 精排分数靠后的物品(非常困难)更容易分错。

训练数据

  • 混合几种负样本。
  • 50% 的负样本是全体物品(简单负样本)。
  • 50% 的负样本是没通过排序的物品(困难负样本)。

选择负样本的原理

召回的目标:快速找到用户可能感兴趣的物品。

  • 全体物品(easy):绝大多数是用户根本不感兴趣的。
  • 被排序淘汰(hard):用户可能感兴趣,但是不够感兴趣。
  • 有曝光没点击(没用):用户感兴趣,可能碰巧没有点击。(可以作为排序的负样本,不能作为召回的负样本)

总结

  • 正样本:曝光而且有点击。
  • 简单负样本
    • 全体物品。
    • batch 内负样本。
  • 困难负样本:被召回,但是被排序淘汰。
  • 错误:曝光、但是未点击的物品做召回的负样本。

双塔模型 - 线上服务、模型更新

召回

离线存储:把物品向量 b 存入向量数据库。

  1. 完成训练之后,用物品塔计算每个物品的特征向量 b。
  2. 把几亿个物品向量 b 存入向量数据库(比如 Milvus、Faiss、HnswLib)。
  3. 向量数据库建索引,以便加速最近邻查找。

线上召回:查找用户最感兴趣的 k 个物品。

  1. 给定用户ID 和画像,线上用神经网络算用户向量 a。
  2. 最近邻查找:
    • 把向量 a 作为 query,调用向量数据库做最近邻查找。
    • 返回余弦相似度最大的 k 个物品,作为召回结果。

事先存储物品向量 b,线上现算用户向量 a,why?

  • 每做一次召回,用到一个用户向量 a,几亿物品向量 b。(线上算物品向量的代价过大)
  • 用户兴趣动态变化,而物品特征相对稳定。(可以离线存储用户向量,但不利于推荐效果)

更新模型

全量更新:今天凌晨,用昨天的数据训练模型。

  • 在昨天模型参数的基础上做训练。(不是随机初始化)
  • 用昨天的数据,训练 1 epoch,即每天数据只用一遍。
  • 发布新的 用户塔神经网络物品向量,供线上召回使用。
  • 全量更新对数据流、系统的要求比较低。

增量更新:做 online learning 更新模型参数。

  • 用户兴趣会随时发生变化。
  • 实时收集线上数据,做流式处理,生成 TFRecord 文件。
  • 对模型做 online learning,增量更新 ID Embedding 参数。(不更新神经网络其他部分的参数)
  • 发布用户 ID Embedding,供用户塔在线上计算用户向量。

能否只做增量更新,不做全量更新?

  • 小时级数据有偏;分钟级数据偏差更大。
  • 全量更新:random shuffle 一天的数据,做 1 epoch 训练。
  • 增量更新:按照数据从早到晚的顺序,做 1 epoch 训练。
  • 随机打乱优于按顺序排列数据全量训练优于增量训练

自监督模型

双塔模型的问题

  • 推荐系统的头部效应严重:
    • 少部分物品占据大部分点击。
    • 大部分物品的点击次数不高。
  • 高点击物品的表征学得好,长尾物品的表征学得不好。
  • 自监督学习:做 data augmentation,更好地学习长尾物品的向量特征。

特征变换

Random Mask:

  • 随机选一些离散特征(比如类目),把它们遮住。
  • 例:
    • 某物品的类目特征是 u = {数码,摄影}。
    • Mask 后的类目特征是 u’ = {default}。

Dropout:

  • 一个物品可以有多个类目,那么类目是一个多值离散特征。
  • Dropout:随机丢弃特征中 50% 的值。
  • 例:
    • 某物品的类目特征是 u = {美妆,摄影}。
    • Mask 后的类目特征是 u’ = {美妆}。

互补特征(complementary):

  • 假设物品一共有 4 种特征:
    • ID,类目,关键词,城市
  • 随机分成两组:
    • {ID,关键词} 和 {类目,城市}
  • {ID,default,关键词,default} -> 物品表征
  • {default,类目,default,城市} -> 物品表征

Mask 一组关联的特征:

  • p(u):某特征取值为 u 的概率。

  • p(u, v):某特征取值为 u,另一个特征取值为 v,同时发生的概率。

  • 离线计算特征两两之间的关联,用互信息(mutual information)衡量:

    MI(U,V)=uUvVp(u,v)logp(u,v)p(u)p(v)\text{MI}(U,V) = \sum_{u \in U} \sum_{v \in V}p(u,v) \cdot \log \frac{p(u,v)}{p(u) \cdot p(v)}

  • 好处:比 random mask、dropout、互补特征等方法效果更好。

  • 坏处:方法复杂,实现的难度大,不容易维护。

训练模型

  • 对点击做随机抽样,得到 n 对用户-物品二元组,作为一个 batch。

  • 从全体物品中均匀抽样,得到 m 个物品,作为一个 batch。

  • 做梯度下降,使得损失减少:

    1ni=1nLmain[i]+α1mj=1mLself[j]\frac{1}{n} \sum_{i=1}^n \text{L}_{\text{main}}[i] + \alpha \cdot \frac{1}{m} \sum_{j=1}^m \text{L}_\text{self}[j]

总结

  • 双塔模型学不好低曝光物品的向量表征。
  • 自监督学习:
    • 对物品做随机特征变换。
    • 特征向量 b_i’ 和 b_i’’ 相似度高(相同物品)。
    • 特征向量 b_i’ 和 b_j’’ 相似度低(不同物品)。
  • 实验效果:低曝光物品、新物品的推荐变得更准。

Deep Retrieval 召回

  • 经典的双塔模型把用户、物品表示为向量,线上做最近邻查找。
  • Deep Retrieval [1] 把物品表征为路径(path),线上查找用户最匹配的路径。
  • Deep Retrieval 类似于阿里的 TDM [2]。

物品到路径的索引

索引:item -> List

  • 一个物品对应多条路径。
  • 用 3 个节点表示一条路径:path = [a, b, c]。

索引:path -> List

  • 一条路径对应多个物品。

预估用户对路径的兴趣

  • 用 3 个节点表示一条路径:path = [a,b,c]。

  • 给定用户特征 x,预估用户对节点 a 的兴趣 p_1(a|x)。

  • 给定 x 和 a,预估用户对节点 b 的兴趣 p_2(b|a;x)。

  • 给定 x,a,b,预估用户对节点 c 的兴趣 p_3(c|a,b;x)。

  • 预估用户对 path = [a,b,c] 兴趣:

    p(a,b,cX)=p1(aX)×p2(ba;X)×p3(ca,b;X)p(a,b,c|X) = p_1(a|X) \times p_2(b|a;X) \times p_3(c|a,b;X)

线上召回

召回:用户 -> 路径 -> 物品

  • 第一步:给定用户特征,用 beam search 召回一批路径。
  • 第二步:利用索引 “path -> List”,召回一批物品。
  • 第三步:对物品做打分和排序,选出一个子集。
  • 假设有 3 层,每层 K 个节点,那么一共有 K^3 条路径。
  • 用神经网络给所有 K^3 条路径打分,计算量太大。
  • 用 beam search,可以减少计算量。
  • 需要设置超参数 beam size。

训练

同时学习神经网络参数和物品表征

  • 神经网络 p(a,b,c|x) 预估用户对路径 [a,b,c] 的兴趣。
  • 把一个物品表征为多条路径 {[a,b,c]},建立索引:
    • item -> List
    • path -> List
  • 正样本(user, item): click(user, item) = 1。

学习物品表征

  • 用户 user 对路径 path = [a,b,c] 的兴趣记作:

    p(pathuser)=p(a,b,cX)p(\text{path}|\text{user}) = p(a,b,c|X)

  • 物品 item 与路径 path 的相关性:

    score(item, path)=userp(path | user)×click(user, item)\text{score(item, path)} = \sum_{\text{user}}p(\text{path | user}) \times \text{click(user, item)}

  • 根据 score(item, path) 选出 J 条路径作为 item 的表征。

  • 损失函数(选择与 item 高度相关的 path):

    loss(item,Π)=log(j=1Jscore(item,pathj))\text{loss(item,Π)} = -\text{log}(\sum_{j=1}^J\text{score}(item,path_j))

  • 正则项(避免过多的 item 集中在一条 path 上):

    reg(pathj)=(number of items on pathj)4\text{reg}(\text{path}_j) = \left(\text{number of items on } \text{path}_j\right)^4

地理位置、作者、缓存召回

GeoHash 召回

  • 用户可能对附近发生的事感兴趣。

  • GeoHash:对经纬度的编码,地图上一个长方形区域。

  • 索引:GeoHash -> 优质笔记列表(按时间倒排)。

  • 这条召回通道没有个性化。

同城召回

  • 用户可能对同城发生的事感兴趣。
  • 索引:城市 -> 优质笔记列表(按时间倒排)。
  • 这条召回通道没有个性化。

关注作者召回

  • 用户对关注的作者发布的笔记感兴趣。
  • 索引:
    • 用户 -> 关注的作者
    • 作者 -> 发布的笔记
  • 召回:
    • 用户 -> 关注的作者 -> 最新的笔记

有交互的作者召回

  • 如果用户对某笔记感兴趣(点赞、收藏、转发),那么用户可能对该作者的其他笔记感兴趣。
  • 索引:用户 -> 有交互的作者
  • 召回:用户 -> 有交互的作者 -> 最新的笔记

相似作者召回

  • 如果用户喜欢某作者,那么用户喜欢相似的作者。
  • 索引:作者 -> 相似作者(k 个作者)
  • 召回:用户 -> 感兴趣的作者(n 个作者) -> 相似作者(nk 个作者)-> 最新的笔记(nk 篇笔记)

缓存召回

想法:复用前 n 次推荐精排的结果。

  • 背景:
    • 精排输出几百篇笔记,送入重排。
    • 重排做多样性抽样,选出几十篇。
    • 精排结果一大半没有曝光,被浪费。
  • 精排前 50,但是没有曝光的,缓存起来,作为一条召回通道。

缓存大小固定,需要退场机制。

  • 一旦笔记成功曝光,就从缓存退场。
  • 如果超出缓存大小,就移除最先进入缓存的笔记。
  • 笔记最多被召回 10 次,达到 10 次就退场。
  • 每篇笔记最多保存 3 天,达到 3 天就退场。

总结

  • 地理位置召回

    • GeoHash 召回、同城召回。
  • 作者召回:

    • 关注的作者、有交互的作者、相似的作者。
  • 缓存召回

曝光过滤 & Bloom Filter

曝光过滤问题

  • 如果用户看过某个物品,则不再把该物品曝光给该用户。
  • 对于每个用户,记录已经曝光给他的物品。(小红书只召回 1 个月以内的笔记,因此只需要记录每个用户最近 1 个月的曝光历史。)
  • 对于每个召回的物品,判断它是否已经给该用户曝光过,排除掉曾经曝光过的物品。
  • 一位用户看过 n 个物品,本次召回 r 个物品,如果暴力对比,需要 **O(nr)**的时间。

Bloom Filter

  • Bloom filter 判断一个物品 ID 是否在已曝光的物品集合中。

  • 如果判断为 no,那么该物品一定不在集合中。

  • 如果判断为 yes,那么该物品很可能在集合中。(可能误伤,错误判断未曝光物品为已曝光,将其过滤掉)

  • Bloom filter 把物品集合表征为一个 m 维二进制向量。

  • 每个用户有一个曝光物品的集合,表征为一个向量,需要 m bit 的存储。

  • Bloom filter 有 k 个哈希函数,每个哈希函数把物品 ID 映射成介于 0m - 1 之间的整数。

  • 曝光物品集合大小为 n,二进制向量维度为 m,使用 k 个哈希函数。

  • Bloom filter 误伤的概率为:

    δ(1exp(knm))k\delta \approx (1 - \text{exp}(-\frac{kn}{m}))^k

    • n 越大,向量中的 1 越多,误伤概率越大。(未曝光物品的 k 个位置恰好都是 1 的概率大)
    • m 越大,向量越长,越不容易发生哈希碰撞。
    • k 太大、大小都不好,k 有最优取值。
  • 设定可容忍的误伤概率为 δ,那么最优参数为:

    k=1.44ln(1δ),m=2nln(1δ)k = 1.44 \cdot \ln(\frac{1}{\delta}), \qquad m = 2n \cdot \ln(\frac{1}{\delta})

Bloom Filter 缺点

  • Bloom filter 把物品的集合表示成一个二进制向量。
  • 每往集合中添加一个物品,只需要把向量 k 个位置的元素置为 1。(如果原本就是 1,则不变)
  • Bloom filter 只支持添加物品,不支持删除物品。从集合中移除物品,无法消除它对向量的影响。
  • 每天都需要从物品集合中移除年龄大于 1 个月的物品。(超龄物品不可能被召回,没必要把它们记录在 Bloom filter,降低 n 可以降低误伤率)

排序

多目标排序模型

用户-笔记的交互

  • 对于每篇笔记,系统记录:
    • 曝光次数(number of impressions)
    • 点击次数(number of clicks)
    • 点赞次数(number of likes)
    • 收藏次数(number of collects)
    • 转发次数(number of shares)

排序的依据

  • 排序模型预估点击率、点赞率、收藏率、转发率等多种分数。
  • 融合这些预估分数。(比如加权和)
  • 根据融合的分数做排序、截断。

训练

  • 困难:类别不平衡
    • 每 100 次曝光,约有 10 次点击、90 次无点击。
    • 每 100 次点击,约有 10 次收藏、90 次无收藏。
  • 解决方案:负样本降采样(down-sampling)
    • 保留一小部分负样本。
    • 让正负样本数量平衡,节约计算。

预估值校准

  • 正样本、负样本数量为 n+ 和 n-。

  • 对负样本做降采样,抛弃一部分负样本。

  • 使用 α · n- 个负样本,α∈(0,1)是采样率。

  • 由于负样本变少,预估点击率大于真实点击率

  • 真实点击率和预估点击率:

    ptrue=n+n++n(期望)ppred=n+n++αn(期望)\text{p}_\text{true} = \frac{n_+}{n_+ + n_-}(期望) \qquad \text{p}_\text{pred} = \frac{n_+}{n_+ + \alpha \cdot n_-}(期望)

  • 由上面两个等式可得校准公式:

    ptrue=αppred(1ppred)+αppred\text{p}_\text{true} = \frac{\alpha \cdot \text{p}_\text{pred}}{(1-\text{p}_\text{pred}) + \alpha \cdot \text{p}_\text{pred}}

Multi-gate Mixture-of-Experts(MMoE)

极化现象(Polarization)

解决极化问题

  • 如果有 n 个 “专家”,那么每个 softmax 的输入和输出都是 n 维向量。
  • 在训练时,对 softmax 的输出使用 dropout
    • Softmax 输出的 n 个数值被 mask 的概率都是 10%。
    • 每个 “专家” 被随机丢弃的概率都是 10%。

预估分数融合

融合预估分数

简单的加权和

pclick+w1plike+w2pcollect+...\text{p}_\text{click} + w_1 \cdot \text{p}_\text{like} + w_2 \cdot \text{p}_\text{collect} + ...

点击率乘以其他项的加权和

pclick(1+w1plike+w2pcoolect+...)\text{p}_\text{click} \cdot (1 + w_1 \cdot \text{p}_\text{like} + w_2 \cdot \text{p}_\text{coolect} + ...)

短视频 APP 融分公式

(1+w1ptime)α1(1+w2plike)α2...(1 + w_1 \cdot \text{p}_\text{time})^{\alpha_1} \cdot (1 + w_2 \cdot \text{p}_\text{like})^{\alpha_2} \quad...

  • 根据预估时长 p_time,对 n 篇候选视频做排序。

  • 最终融合分数:

    w1rtimeα1+β1+w2rtimeα2+β2+w3rtimeα3+β3+...\frac{w_1}{r_{time}^{\alpha_1} + \beta_1} + \frac{w_2}{r_{time}^{\alpha_2} + \beta_2} + \frac{w_3}{r_{time}^{\alpha_3} + \beta_3} + ...

电商的融分公式

  • 电商的转换流程:

    • 曝光 -> 点击 -> 加购物车 -> 付款
  • 模型预估:p_click、p_cart、p_pay

  • 最终融分公式:

    pclickα1×pcartα2×ppayα3×priceα4p_{click}^{\alpha_1} \times p_{cart}^{\alpha_2} \times p_{pay}^{\alpha_3} \times price^{\alpha_4}

视频播放建模

图文 VS 视频

  • 图文笔记排序的主要依据:

    • 点击、点赞、收藏、转发、评论 …
  • 视频排序的依据还有播放时长和完播。

  • 直接用回归拟合播放时长效果不好。建议用 YouTube 的时长建模。

视频播放时长

  • 把最后一个全连接层的输出记作 z。设 p = sigmoid(z)。

  • 实际观测的播放时长记作 t。(如果没有点击,则 t = 0)

  • 做训练:最小化交叉熵损失

    (t1+tlogp+11+tlog(1p))-(\frac{t}{1+t}\cdot logp + \frac{1}{1+t}\cdot log(1-p))

  • 做推理:把 exp(z) 作为播放时长的预估。

  • 把 exp(z) 作为融分公式中的一项。

视频完播

回归方法

  • 例:视频长度 10 分钟,实际播放 4 分钟,则实际播放率为 y = 0.4。

  • 让预估播放率 p 拟合 y:

    loss=ylogp+(1y)log(1p)\text{loss} = y \cdot logp + (1-y) \cdot log(1-p)

  • 线上预估完播率,模型输出 p = 0.73,意思是预计播放 73%。

二元分类方法

  • 定义完播指标,比如完播 80%。

  • 例:视频长度 10 分钟,播放 >8 分钟作为正样本,播放 < 8 分钟作为负样本。

  • 做二元分类训练模型:播放 >8 分钟 vs 播放 < 8 分钟

  • 线上预估完播率,模型输出 p = 0.73

    P(播放>80%)=0.73\mathbb{P}(\text{播放} > 80\%) = 0.73

  • 线上预估完播率,然后做调整:

    pfinish=预估完播率f(视频长度)\text{p}_\text{finish} = \frac{\text{预估完播率}}{f(视频长度)}

  • 把 p_finish 作为融分公式中的一项。

排序模型的特征

用户画像(User Profile)

  • 用户 ID(在召回、排序中做 embedding)。
  • 人口统计学属性:性别、年龄。
  • 账号信息:新老、活跃度 …
  • 感兴趣的类目、关键词、品牌。

物品画像(Item Profile)

  • 物品 ID(在召回、排序中做 embedding)。
  • 发布时间(或者年龄)。
  • GeoHash(经纬度编码)、所在城市。
  • 标题、类目、关键词、品牌 …
  • 字数、图片数、视频清晰度、标签数 …
  • 内容信息量、图片美学 …

用户统计特征

  • 用户最近 30 天(7天、1天、1小时)的曝光数、点击数、点赞率、收藏数 …
  • 按照笔记图文/视频分桶。(比如最近 7 天,该用户对图文笔记的点赞率、对视频笔记的点击率)
  • 按照笔记类目分桶。(比如最近 30 天,用户对美妆笔记的点击率、对美食笔记的点击率、对科技数据笔记的点击率)

笔记统计特征

  • 笔记最近 30 天(7天、1天、1小时)的曝光数、点击数、点赞数、收藏数 …
  • 按照用户性别分桶、按照用户年龄分桶 …
  • 作者特征:
    • 发布笔记数
    • 粉丝数
    • 消费指标(曝光数、点击数、点赞数、收藏数)

场景特征(Context)

  • 用户定位 GeoHash(经纬度编码)、城市。
  • 当前时刻(分段,做 embedding)。
  • 是否是周末、是否是节假日。
  • 收集品牌、手机型号、操作系统。

特征处理

  • 离散特征:做 embedding。
    • 用户 ID、笔记 ID、作者 ID。
    • 类目、关键词、城市、收集品牌。
  • 连续特征:做分桶,变成离散特征。
    • 年龄、笔记字数、视频长度。
  • 连续特征:其他变换。
    • 曝光数、点击数、点赞数等数值做 log(1+x)。
    • 转化为点击率、点击率等值,并做平滑。

特征覆盖率

  • 很多特征无法覆盖 100% 样本。
  • 例:很多用户不填年龄,因此用户年龄特征的覆盖率远小于 100%。
  • 例:很多用户设置隐私权限,APP 不能获得用户地理定位,因此场景特征有缺失。
  • 提高特征覆盖率,可以让精排模型更准。

数据服务

粗排模型

粗排 VS 精排

粗排

  • 给几千篇笔记打分。
  • 单次推理代价必须小。
  • 预估的准确性不高。

精排

  • 给几百篇笔记打分。
  • 单次推理代价很大。
  • 预估的准确性更高。

精排模型

  • 前期融合:先对所有特征做 concatenation,再输入神经网络。
  • 线上推理代价大:如果有 n 篇候选笔记,整个大模型要做 n 次推理。

双塔模型

  • 后期融合:把用户、物品特征分别输入不同的神经网络,不对用户、物品特征做融合。
  • 线上计算量小:
    • 用户塔只需要做一次线上推理,计算用户表征 a。
    • 物品表征 b 事先储存再向量数据库中,物品塔在线上不做推理。
  • 预估准确性不如精排模型。

粗排的三塔模型

三塔模型的推理

  • 从多个数据源取特征:
    • 1 个用户的画像、统计特征。
    • n 个物品的画像、统计特征。
  • 用户塔:只做 1 次推理。
  • 物品塔:未命中缓存时需要做推理。
  • 交叉塔:必须做 n 次推理。
  • 上层网络做 n 次推理,给 n 个物品打分。

特征交叉

因式分解机(FM)

线性模型

  • 有 d 个特征,记作 x = [x_1, …, x_d]。

  • 线性模型:

    p=b+i=1dwixip = b + \sum_{i=1}^d w_ix_i

  • 模型有 d + 1 个参数:w = [w_1, …, w_d] 和 b。

  • 预测是特征的加权和。(只有加,没有乘)

二阶交叉特征

  • 有 d 个特征,记作 x = [x_1, …, x_d]。

  • 线性模型 + 二阶交叉特征:

    p=b+i=1dwixi+i=1dj=i+1duijxixjp = b + \sum_{i=1}^d w_ix_i + \sum_{i=1}^d\sum_{j=i+1}^d u_{ij} x_i x_j

  • 模型有 O(d^2)个参数。

  • Factorized Machine(FM):

    p=b+i=1dwixi+i=1dj=i+1d(viTvj)xixjp = b + \sum_{i=1}^d w_ix_i + \sum_{i=1}^d\sum_{j=i+1}^d (v_i^Tv_j) x_i x_j

  • FM 模型有 O(kd)个参数。(k << d)

Factorized Machine

  • FM 是线性模型的替代品,能用线性回归、逻辑回归的场景,都可以用 FM。
  • FM 使用二阶交叉特征,表达能力比线性模型更强。
  • 通过做近似 u_{ij} ≈ v_i^T v_j,FM 把二阶交叉权重的数量从 O(d^2) 降低到 O(d)。

深度交叉网络(DCN)

交叉层

Deep & Cross Network

LHUC(PPNet)

语音识别中的 LHUC

SENet 和 Bilinear 交叉

SENet

  • SENet 对离散特征做 field-wise 加权。
  • Field:
    • 用户 ID Embedding 是 64 维向量。
    • 64 个元素算一个 field,获得相同的权重。
  • 如果有 m 个 fields,那么权重向量是 m 维。

Field 特征交叉

  • 向量内积

  • 哈达玛乘积

  • Bilinear cross

FiBiNet

行为序列

用户历史行为序列建模

LastN 特征

  • LastN:用户最近的 n 次交互(点击、点赞等)的物品 ID。
  • 对 LastN 物品 ID 做 embedding,得到 n 个向量。
  • 把 n 个向量取平均,作为用户的一种特征。
  • 适用于召回双塔模型、粗排三塔模型、精排模型。

小红书的实践

DIN 模型(注意力机制)

DIN 模型

  • DIN 用 加权平均代替平均,即注意力机制(attention)。
  • 权重:候选物品与用户 LastN 物品的相似度。

  • 对于某候选物品,计算它与用户 LastN 物品的相似度。

  • 以相似度为权重,求用户 LastN 物品向量的加权和,结果是一个向量。

  • 把得到的向量作为一种用户特征,输入排序模型,预估(用户,候选物品)的点击率、点赞率等指标。

  • 本质是注意力机制(attention)

简单平均 VS 注意力机制

  • 简单平均注意力机制都适用于精排模型。
  • 简单平均适用于双塔模型、三塔模型。
    • 简单平均只需要用到 LastN,属于用户自身的特征。
    • 把 LastN 向量的平均作为用户塔的输入。
  • 注意力机制不适用于双塔模型、三塔模型。
    • 注意力机制需要用到 LastN + 候选物品。
    • 用户塔看不到候选物品,不能把注意力机制用在用户塔。

SIM 模型(长序列模型)

DIN 模型的特点

  • 注意力层的计算量 ∝n(用户行为序列的长度)。
  • 只能记录最近几百个物品,否则计算量太大。
  • 缺点:关注短期兴趣,遗忘长期兴趣。

如何改进 DIN?

  • 目标:保留用户长期行为序列(n 很大),而且计算量不会过大。
  • 改进 DIN:
    • DIN 对 LastN 向量做加权平均,权重是相似度。
    • 如果某 LastN 物品于候选物品差异很大,则权重接近零。
    • 快速排除掉与候选物品无关的 LastN 物品,降低注意力层的计算量。

SIM 模型

  • 保留用户长期行为记录,n 的大小可以是几千。
  • 对于每个候选物品,在用户 LastN 记录中做快速查找,找到 k 个相似物品。
  • 把 LastN 变成 TopK,然后输入到注意力层。
  • SIM 模型减小计算量(从 n 降到 k)。

第一步:查找

  • 方法一:Hard Search
    • 根据候选物品的类目,保留 LastN 物品中类目相同的。
    • 简单,快速,无需训练。
  • 方法二:Soft Search
    • 把物品做 embedding,变成向量。
    • 把候选物品向量作为 query,做 k 近邻查找,保留 LastN 物品中最接近的 k 个。
    • 效果更好,编程实现更复杂。

第二步:注意力机制

使用时间信息

  • 用户与某个 LastN 物品的交互时刻距今为 δ。
  • 对 δ 做离散化,再做 embedding,变成向量 d。
  • 把两个向量做 concatenation,表征一个 LastN 物品。
    • 向量 x 是物品 embedding。
    • 向量 d 是时间的 embedding。

为什么 SIM 使用时间信息?

  • DIN 的序列短,记录用户近期行为。
  • SIM 的序列长,记录用户长期行为。
  • 时间越久远,重要性越低。

总结

  • 长序列(长期兴趣)优于短序列(近期兴趣)。
  • 注意力机制优于简单平均。
  • Soft search 还是 hard search?取决于工程基建。

重排

推荐系统中的多样性

相似性的度量

  • 基于物品属性标签
    • 类目、品牌、关键词 …
  • 基于物品向量表征
    • 用于召回的双塔模型学到的物品向量(不好)。
    • 基于内容的向量表征(好)。

基于物品属性标签

  • 物品属性标签:类目、品牌、关键词 …

  • 根据一级类目、二级类目、品牌计算相似度。

    • 物品 i:美妆、彩妆、香奈儿

    • 物品 j:美妆、香水、香奈儿

    • 相似度:

      sim1(i,j)=1,sim2(i,j)=0,sim3(i,j)=1sim_1(i, j) = 1, sim_2(i, j) = 0, sim_3(i, j) = 1

基于图文内容的物品向量表征

  • CLIP 是当前公认最有效的预训练方法。
  • 思想:对于图片-文本二元组,预测图文是否匹配。
  • 优势:无需人工标记。小红书的笔记天然包含图片 + 文字,大部分笔记图文相关。

  • 一个 batch 内有 m 对正样本。
  • 一张图片和 m - 1 条文本组成负样本。
  • 这个 batch 内一共有 m(m - 1) 对负样本。

MMR 多样性算法

多样性

  • 精排给 n 个候选物品打分,融合之后的分数为:reward_1, …, reward_n
  • 把第 ij 个物品的相似度记作 sim(i, j)
  • n 个物品中选出 k 个,既要有高精排分数,也要有多样性。

  • 计算集合 R 中每个物品 i 的 Marginal Relevance 分数:

    MRi=θrewardi(1θ)maxjSsim(i,j)\text{MR}_i = \theta \cdot \text{reward}_i - (1 - \theta) \cdot \max_{j \in S} \text{sim}(i, j)

  • Maximal Marginal Relevance(MMR):

    argmaxiRMRi\text{argmax}_{i \in R} \text{MR}_i

MMR 多样性算法

  1. 已选中的物品 S 初始化为空集,未选中的物品 R 初始化为全集 {1, …, n}。

  2. 选择精排分数 reward_i 最高的物品,从集合 R 移到 S。

  3. 做 k - 1 轮循环:

    a. 计算集合 R 中所有物品的分数 {MR_i}_{i ∈ R}。

    b. 选出分数最高的物品,将其从 R 移到 S。

滑动窗口

  • 已选中的物品越多(即集合 S 越大),越难找出物品 i ∈ R,使得 i 与 S 中的物品都不相似。
  • 设 sim 的取值范围是 [0, 1]。当 S 很大时,多样性分数 max_{j ∈ S} sim(i, j) 总是等于 1,导致 MMR 算法失效。
  • 解决方案:设置一个滑动窗口 W,比如最近选中的 10 个物品,用 W 代替 MMR 公式中的 S。

总结

  • 标准 MMR:

    argmaxiR{θrewardi(1θ)maxjSsim(i,j)}\text{argmax}_{i \in R} \left\{ \theta \cdot \text{reward}_i - (1 - \theta) \cdot \max_{j \in S} \text{sim}(i, j) \right\}

  • 用滑动窗口:

    argmaxiR{θrewardi(1θ)maxjWsim(i,j)}\text{argmax}_{i \in R} \left\{ \theta \cdot \text{reward}_i - (1 - \theta) \cdot \max_{j \in W} \text{sim}(i, j) \right\}

业务规则约束下的多样性算法

重排的规则

规则:最多连续出现 k 篇某种笔记

  • 小红书推荐系统的物品分为图文笔记、视频笔记。
  • 最多连续出现 k = 5 篇图文笔记,最多连续出现 k = 5 篇视频笔记。
  • 如果排 i 到 i + 4 的全都是图文笔记,那么排在 i + 5 的必须是视频笔记。

规则:每 k 篇笔记最多出现 1 篇某种笔记

  • 运营推广笔记的精排分会乘以大于 1 的系数(boost),帮助笔记获得更多曝光。
  • 为了防止 boost 影响体验,限制每 k = 9 篇笔记最多出现 1 篇运营推广笔记。
  • 如果排第 i 位的是运营推广笔记,那么排 i + 1 到 i + 8 的不能是运营推广笔记。

规则:前 t 篇笔记最多出现 k 篇某种笔记

  • 排名前 t 篇笔记最容易被看到,对用户体验最重要。(小红书的 top 4 为首屏)
  • 小红书推荐系统有带电商卡片的笔记,过多可能会影响体验。
  • 前 t = 1 篇笔记最多出现 k = 0 篇带电商卡片的笔记。
  • 前 t = 4 篇笔记最多出现 k = 1 篇带电商卡片的笔记。

MMR + 重排规则

  • 重排结合 MMR 与规则,在满足规则的前提下最大化 MR。
  • 每一轮先用规则排除掉 R 中的部分物品,得到子集 R’
  • MMR 公式中的 R 替换成 R’,选中的物品符合规则。

DPP 多样性算法

多样性问题

  • 精排给 n 个物品打分:reward_1, …, reward_n。
  • n 个物品的向量表征:v_1, …, v_n ∈ R^d。
  • 从 n 个物品中选出 k 个物品,组成集合 S。
    • 价值大:分数之和 Σj∈s reward_j 越大越好。
    • 多样性好:S 中 k 个向量组成的超平形体 P(S) 的体积越大越好。

行列式点过程(DPP)

  • DPP 是一种传统的统计机器学习方法:

    argmaxS:S=klogdet(VSTVS)\text{argmax}_{S:|S|=k} \log \det(V_S^T V_S)

  • Hulu 的论文将 DPP 应用在推荐系统:

    argmaxS:S=kθ(jSrewardj)+(1θ)logdet(VSTVS)\text{argmax}_{S : |S| = k} \, \theta \cdot \left( \sum_{j \in S} \text{reward}_j \right) + (1 - \theta) \cdot \log \det \left( V_S^T V_S \right)

  • DPP 是个组合优化问题,从集合 {1, …, n} 中选出一个大小为 k 的子集 S。

  • 用 S 表示已选中的物品,用 R 表示未选中的物品,贪心算法求解:

    argmaxiRθrewardi+(1θ)logdet(AS{i})\text{argmax}_{i \in R} \, \theta \cdot \text{reward}_i + (1 - \theta) \cdot \log \det(A_{S \cup\{i\}})

规则约束

  • 贪心算法每轮从 R 中选出一个物品:

    argmaxiRθrewardi+(1θ)logdet(AW{i})\text{argmax}_{i \in R} \, \theta \cdot \text{reward}_i + (1 - \theta) \cdot \log \det(A_{W \cup\{i\}})

  • 有很多规则约束,例如最多连续出 5 篇视频笔记(如果已经连续出了 5 篇视频笔记,下一篇必须是图文笔记)。

  • 用规则排除掉 R 中的部分物品,得到子集 R’,然后求解:

    argmaxiRθrewardi+(1θ)logdet(AW{i})\text{argmax}_{i \in R'} \, \theta \cdot \text{reward}_i + (1 - \theta) \cdot \log \det(A_{W \cup\{i\}})

物品冷启动

优化目标 & 评价指标

物品冷启动

研究 UGC 的物品冷启

  • 小红书上用户新发布的笔记。
  • B 站上用户新上传的视频。
  • 今日头条上作者新发布的文章。

新笔记冷启动

为什么要特殊对待新笔记?

  • 新笔记缺少与用户的交互,导致推荐的难度大、效果差。
  • 扶持新发布、低曝光的笔记,可以增强作者发布意愿。

优化冷启的目标

  1. 精准推荐:克服冷启的困难,把新笔记推荐给合适的用户,不引起用户反感。
  2. 激励发布:流量向低曝光新笔记倾斜,激励作者发布。
  3. 挖掘高潜:通过初期小流量的试探,找到高质量的笔记,给与流量倾斜。

评价指标

  • 作者侧指标:
    • 发布渗透率、人均发布量。
  • 用户侧指标:
    • 新笔记指标:新笔记的点击率、交互率。
    • 大盘指标:消费时长、日活、月活。
  • 内容侧指标:
    • 高热笔记占比。
作者侧指标

发布渗透率(penetration rate)

  • 发布渗透率 = 当日发布人数 / 日活人数
  • 发布一篇或以上,就算一个发布人数。
  • 例:
    • 当日发布人数 = 100万
    • 日活人数 = 2000万
    • 发布渗透率 = 100 / 200 = 5%

人均发布量

  • 人均发布量 = 当日发布笔记数 / 日活人数

  • 例:

    • 每日发布笔记数 = 200万
    • 日活人数 = 2000万
    • 人居发布量 = 200 / 2000 = 0.1
  • 发布渗透率、人均发布量反应出作者的发布积极性。

  • 冷启的重要优化目标是促进发布,增大内容池。

  • 新笔记获得的曝光越多,首次曝光和交互出现得越早,作者发布积极性越高。

用户侧指标

新笔记的消费指标

  • 新笔记的点击率、交互率。
    • 问题:曝光的基尼系数很大。
    • 少数头部新笔记占据了大部分的曝光。
  • 分别考察高曝光、低曝光新笔记。
    • 高曝光:比如 > 1000 次曝光。
    • 低曝光:比如 < 1000 次曝光。

大盘消费指标

  • 大盘的消费时长、日活、月活。
  • 大力扶持低曝光新笔记会发生什么?
    • 作者侧发布指标变好。
    • 用户侧大盘消费指标变差。
内容侧指标

高热笔记占比

  • 高热笔记:前 30 天获得 1000+ 次点击。
  • 高热笔记占比越高,说明冷启阶段挖掘优质笔记的能力越强。

冷启动的优化点

  • 优化全链路(包括召回和排序)。
  • 流量调控(流量怎么在新物品、老物品中分配)。

简单的召回通道

召回的依据

✔ 自带图片、文字、地点。

✔ 算法或人工标注的标签。

❌ 没有用户点击、点赞等信息。

❌ 没有笔记 ID embedding

冷启召回的困难

  • 缺少用户交互,还没学好笔记 ID embedding,导致双塔模型效果不好。
  • 缺少用户交互,导致 ItemCF 不适用。

召回通道

❌ ItemCF 召回(不适用)

❓ 双塔模型(改造后适用)

✔ 类目、关键词召回(适用)

✔ 聚类召回(适用)

✔ Look-Alike 召回(适用)

ID Embedding

改进方案1:新笔记使用 default embedding

  • 物品塔做 ID embedding 时,让所有新笔记共享一个 ID,而不是用自己真正的 ID。
  • Default embedding:共享的 ID 对应的 embedding 向量。
  • 到下次模型训练的时候,新笔记才有自己的 ID embedding 向量。

改进方案2:利用相似笔记 embedding 向量

  • 查找 top k 内容最相似的高曝笔记。
  • 把 k 个高曝笔记的 embedding 向量取平均,作为新笔记的 embedding。
多个向量召回池
  • 多个召回池,让新笔记有更多曝光机会。
    • 1 小时新笔记
    • 6 小时新笔记
    • 24 小时新笔记
    • 30 天笔记
  • 共享同一个双塔模型,那么多个召回池不增加训练的代价。
用户画像
  • 感兴趣的类目

    美食、科技数码、电影 …

  • 感兴趣的关键词

    纽约、职场、搞笑、程序员、大学 …

基于类目
  • 系统维护类目索引:
    • 类目 -> 笔记列表(按时间倒排)
  • 用类目索引做召回:
    • 用户画像 -> 类目 -> 笔记列表
  • 取回笔记列表上前 k 篇笔记(即最新的 k 篇)。
基于关键词
  • 系统维护关键词索引:

    • 关键词 -> 笔记列表(按时间倒排)
  • 根据用户画像上的关键词做召回。

缺点
  • 只对刚刚发布的新笔记有效。
    • 取回某类目 / 关键词下最新的 k 篇笔记。
    • 发布几小时之后,就再没有机会被召回。
  • 弱个性化,不够精准。

聚类召回

聚类索引

  • 一篇新笔记发布之后,用神经网络把它映射到一个特征向量。
  • 从 1000 个向量(对应 1000 个 cluster)中找到最相似的向量,作为新笔记的 cluster。
  • 索引:
    • cluster -> 笔记 ID 列表(按时间倒排)

线上召回

  • 给定用户 ID,找到他的 last-n 交互的笔记列表,把这些笔记作为种子笔记。
  • 把每篇种子笔记映射到向量,寻找最相似的 cluster。(知道了用户对哪些 cluster 感兴趣)
  • 从每个 cluster 的笔记列表中,取回最新的 m 篇笔记。
  • 最多取回 mn 篇新笔记。
内容相似度

模型的训练

<种子笔记,正样本>

方法一:人工标注二元组的相似度

方法二:算法自动选正样本

  • 筛选条件:

    • 只用高曝光笔记作为二元组(因为有充足的用户交互信息)。
    • 两篇笔记有相同的二级类目,比如都是 “菜谱教程”。
  • 用 ItemCF 的物品相似度选正样本。

  • 从全体笔记中随机选出满足条件的:

    • 字数较多(神经网络提取的文本信息有效)。
    • 笔记质量高,避免图文无关。
总结
  • 基本思想:根据用户的点赞、收藏、转发记录,推荐内容相似的笔记。
  • 线下训练:多模态神经网络把图文内容映射到向量。
  • 线上服务:
    • 用户喜欢的笔记 -> 特征向量 -> 最近的 Cluster -> 新笔记

Look-Alike 召回

  • 点击、点赞、收藏、转发 —— 用户对笔记可能感兴趣。
  • 把有交互的用户作为新笔记的种子用户。
  • 用 look-alike 在相似用户中扩散。

流量调控

扶持新笔记的目的

  • 促进发布,增大内容池。
    • 新笔记获得的曝光越多,作者创作积极性越高。
    • 反映在发布渗透率、人均发布量。
  • 挖掘优质笔记。
    • 做探索,让每篇新笔记都能获得足够曝光。
    • 挖掘的能力反映在高热笔记占比。

工业界做法

  • 假设推荐系统只分发年龄 < 30 天的笔记。
  • 假设采用自然分发,新笔记(年龄 < 24小时)的曝光占比为 1 / 30。
  • 扶持新笔记,让新笔记的曝光占比远大于 1 / 30。

流量调控技术的发展

  1. 在推荐结果中强插新笔记。
  2. 对新笔记的排序分数做提权(boost)。
  3. 通过提权,对新笔记做保量。
  4. 差异化保量。

新笔记提权

  • 目标:让新笔记有更多机会曝光。

    • 如果做自然分发,24 小时新笔记占比为 1/ 30。
    • 做人为干涉,让新笔记占比大幅提升。
  • 干涉粗排、重排环节,给新笔记提权。

  • 优点:容易实现,投入产出比好。

  • 缺点:

    • 曝光量对提权系数很敏感。
    • 很难精确控制曝光量,容易过度曝光和不充分曝光。

新笔记保量

  • 保量:不论笔记质量高低,都保证 24 小时获得 100 次曝光。

  • 在原有提权系数的基础上,乘以额外的提权的系数,比如:

动态提权保量

用下面四个值计算提权系数

  • 目标时间:比如 24 小时。
  • 目标曝光:比如 100 次。
  • 发布时间:比如笔记已经发布 12 小时。
  • 已有曝光:比如笔记已经获得 20 次曝光。

提权系数=f(发布时间目标时间已有曝光目标曝光)=f(0.5,0.2)提权系数 = f \left(\frac{发布时间}{目标时间},\frac{已有曝光}{目标曝光} \right) = f(0.5, 0.2)

保量的难点

保量成功率远低于 100%

  • 很多笔记在 24 小时达不到 100 次曝光。

  • 召回、排序存在不足。

  • 提权系数调得不好

线上环境变化会导致保量失败

  • 线上环境变化:新增召回通道、升级排序模型、改变重排打散规则 …
  • 线上环境变化后,需要调整提权系数。

差异化保量

  • 保量:不论新笔记质量高低,都做扶持,在前 24 小时给 100 次曝光。
  • 差异化保量:不同笔记有不同保量目标,普通笔记保 100 次曝光,内容优质的笔记保 100 ~ 500 次曝光。
  • 基础保量:24 小时 100 次曝光。
  • 内容质量:用模型评价内容质量高低,给予额外保量目标,上限是加 200 次曝光。
  • 作者质量:根据作者历史上的笔记质量,给与额外保量目标,上限是加 200 次曝光。
  • 一篇笔记最少有 100 次保量,最多有 500 次保量。

总结

  • 流量调控:流量怎么在新老笔记之间分配。
  • 扶持新笔记:单独的召回通道、在排序阶段提权。
  • 保量:帮助新笔记在前 24 小时获得 100 次曝光。
  • 差异化保量:根据内容质量、作者质量,决定保量目标。

冷启的 AB 测试

用户侧实验

缺点

  • 限定:保量 100 次曝光。
  • 假设:新笔记曝光越多,用户使用 APP 时长越低。
  • 新策略:把新笔记排序时的权重增大两倍。
  • 结果(只看用户消费指标):
    • AB 测试的 diff 是负数(实验组不如对照组)。
    • 如果推全,diff 会缩小(比如 -2% -> -1%)。

作者侧实验:方案一

缺点:新笔记之间会抢流量

  • 设定:
    • 新老笔记走各自队列,没有竞争。
    • 重排分给新笔记 1 / 3 流量,分给老笔记 2 / 3 流量。
  • 新策略:把新笔记的权重增大两倍。
  • 结果(只看作者发布指标):
    • AB 测试的 diff 是正数(实验组优于对照组)。
    • 如果推全,diff 会消失(比如 2% -> 0)。

缺点:新笔记和老笔记抢流量

  • 设定:新老笔记自由竞争。
  • 新策略:把新笔记排序时的权重增大两倍。
  • AB 测试时,50%新笔记(带策略)跟 100% 老笔记抢流量。
  • 推全后,100% 新笔记(带策略)跟 100% 老笔记抢流量。
  • AB 测试结果与推全结果有差异。

作者侧实验:方案二

方案二比方案一的优缺点

  • 优点:新笔记的两个桶不抢流量,实验结果更可信。
  • 相同:新笔记和老笔记抢流量,AB 测试结果与推全结果有差异。
  • 缺点:新笔记池减小一半,对用户体验造成负面影响。

作者侧实验:方案三

总结

  • 冷启的 AB 测试需要观测作者发布指标用户消费指标

  • 各种 AB 测试的方案都有缺点。

  • 设计方案的时候,问自己几个问题:

    • 实验组、对照组新笔记会不会抢流量?

      在进行新笔记的AB测试时,实验组和对照组的新笔记可能会出现流量竞争的情况。如果实验组的新笔记通过提权等策略获得更多曝光,可能会抢占对照组新笔记的流量,导致实验结果出现偏差。这种竞争可能使实验组的发布指标上升,而对照组的发布指标下降,从而产生差异。

    • 新笔记、老笔记怎么抢流量?

      在推荐系统中,新发布的笔记(新笔记)和已有的笔记(老笔记)在获取用户曝光时可能存在竞争关系。由于老笔记通常已积累一定的用户反馈数据,推荐系统更容易评估其质量,从而在排序中占据优势。相比之下,新笔记缺乏用户交互数据,可能在推荐中处于劣势,导致曝光机会减少。

    • 同时隔离笔记、用户,会不会让内容池变小?

      在AB测试中,同时对用户和笔记进行隔离,即将用户和新笔记分别分配到实验组和对照组,确实会导致每个组内的内容池缩小。具体而言,实验组用户只能访问实验组的新笔记,对照组用户只能访问对照组的新笔记。这种隔离方式减少了每个用户可接触的内容数量,可能影响推荐效果,进而降低用户体验。

    • 如果对新笔记做保量,会发生什么?

      实验结果偏差:在AB测试中,如果对实验组的新笔记进行保量,而对照组未实施相同策略,实验组的新笔记可能获得更多曝光,导致实验结果不准确。

      流量竞争:新笔记的保量曝光可能与老笔记争夺用户注意力,影响老笔记的曝光机会,进而影响整体推荐效果。

      用户体验影响:如果保量策略导致低质量的新笔记频繁曝光,可能降低用户体验,影响用户对平台的满意度。


Real Recommender System For Industry
https://www.renkelin.vip/2024/11/12/RS2/
Author
Kolin
Posted on
November 12, 2024
Licensed under