召回06:双塔模型——模型结构、训练方法_哔哩哔哩_bilibili
1. 双塔模型的结构与训练
上一讲的矩阵补充模型 是训练用户矩阵A和物品矩阵B;每列代表一个用户/物品 对应值用两列做内积可求得。但缺陷在于只用到用户和物品的ID 而没有利用他们底层的属性。
双塔模型把 用户ID 离散特征 连续特征分别处理并合成;再扔进神经网络得到向量 (像是两座塔)
三种正负样本数目选择 对应三种训练方式:
softmax概率 与理想只有正样本为1 进行交叉熵
2. 正负样本的选取
正样本:曝光而且有点击的用户—物品二元组(用户对物品感兴趣)。负样本未被点击。
问题:少部分物品占据大部分点击,导致正样本大多是热门物品。
解决方案:过采样冷门物品,或降采样热门物品。
简单负样本:未被召回的;因为召回很少 未被召回≈所有物品
因为总物品的绝大多数都是负样本,如果均匀抽样产生负样本,负样本大多是冷门物品。
(本来正样本大多数是热门物品,负样本大多数是冷门物品 所以希望负样本多一点热门 正样本多一点冷门)
非均抽采样:目的是打压热门物品; 负样本抽样概率与热门程度(点击次数)正相关。
batch 内负样本例子:n个人 每个人一个点击(正样本)没有点击其他物品(n-1个负样本)
但是:一个物品出现在 batch 内的概率 p∝ 点击次数。
物品成为负样本的概率本该是 ∝ (点击次数)^0.75,
但在这里是 ∝ 点击次数。 热门物品成为负样本的概率过大。对热门物品打压过大。
预估用户对物品 i 的兴趣: 训练的时候鼓励正样本的余弦相似度尽量大,负样本的余弦相似度尽量小。
做训练时,调整为: -logp 是一个正数 小的p 对应大的-logp
对于负样本来说,我们希望他的cos(a,bi)尽可能的小;对于热门的负样本,为了避免过度纠正,我们希望他的cos(a,bi)在线上召回的时候(训练结束)要比冷门物品的大一点(更远离-1)。所以在训练过程中,对于同一件物品bi而言,训练出的cos(a,bi)-logpi的值是一定的。如果他是热门物品,pi越大,logpi越大,cos(a,bi)要相应变大。
(线上应用做召回的时候,还是用原来的余弦相似度公式)
困难负样本:召回后没通过排序的;在过程越靠后越困难(越难二分类)
有曝光但是没点击不应该被作为负样本(应该是用户感兴趣的 但碰巧没点击),召回目的是快速找到用户可能感兴趣的物品。(可以作后面排序的负样本)
3. 线上召回和更新
把物品ID和对应的向量离线存储;向量库索引 加快最临近查找
在线算用户的特征向量(用户兴趣动态变化,而物品特征相对稳定)
把a作为query,查询向量数据库,找到余弦相似度最高的k个物品向量,返回k个物品ID。
更新分为:全量更新+增量更新。
全量更新:今天凌晨,用昨天的数据训练整个神经网络,做1epoch的随机梯度下降。
增量更新:用实时数据训练神经网络,只更新ID Embedding,锁住全连接层。
增量更新为了实时性更新用户兴趣(但短期小时分钟级数据会有偏差)
全量训练效果好于增量(一天的数据经过random shuffle 抛去顺序干扰)
4. 双塔模型+自监督模型(长尾物品向量表征)
双塔模型的问题 推荐系统的头部效应严重:
少部分热门物品占据大部分点击。 大部分长尾物品的点击次数不高,表征学得不好。
纠偏后为
自监督学习:做 data augmentation,更好地学习长尾物品的向量表征。
对物品做多个随机特征变换,同一物品的不同特征尽量相近,不同物品特征拉远。
特征变换:Mask一组关联的特征
·设一共有k种特征。离线计算特征两两之间MI,得到 k*k 的矩阵。
·随机选一个特征作为种子,找到种子最相关的k/2种特征。
·Mask种子及其相关的k/2种特征,保留其余的k/2种特征。
这是典型的 自监督学习中的 Mask 策略,类似 NLP 里的 BERT:
-
制造“不完整的输入”:
-
模型看到的信息被遮掉了一部分,必须依靠其他特征去恢复或预测。
-
-
迫使模型学习特征之间的关联性:
-
比如:如果把“视频标签”和“作者”mask掉,模型必须从“标题”和“简介”中学到信息。
-
这样训练出来的表示更鲁棒,不会只依赖最显著的某几个特征。
-
-
帮助长尾物品学到更好的表示:
-
对长尾物品,本来交互少,容易过度依赖某些强特征。
-
Mask 强相关特征后,模型被迫挖掘“其他信号”,学习到更丰富的特征关系。
-
结果:长尾物品的 embedding 不会因为数据少而“缺乏信息”。
-
5. Deep Retrieval 路径->item
经典的双塔模型把用户、物品表示为向量,线上做最近邻查找。
DeepRetrieval 把物品表征为路径(path),线上查找 用户最匹配的路径。(条件概率 马尔科夫)
选择后加入原用户特征,embedding后再进行选择
我们想要选出 p(a,b,c|x) 也就是沿着乘积最大的路径。
第一步:给定用户特征,用beam search召回一批路径。(束搜索就是分支k个)
如果贪心算法 就是每次选下一层连上的一个最好的 但在整个路径就不一定最好。
第一层到第i-1层有k个最好的路径;扩展第i层 就把i-1层的属于k个路径的端点 到第i层的路径;得到这些第一层到第i层中 再挑k个最好的。
第二步:利用索引“path -> List(item)”,召回一批物品。
第三步:对物品做打分和排序,选出一个子集。
把一个物品表征为多条路径[[a,b,c]},建立索引;只用点击的正样本进行训练 点击代表对这些路径都感兴趣。
神经网络:拟合用户对每条路径的打分。
click点击=0/1
用户是桥梁 如果用户点击了 用户对路径的兴趣程度就累加到物品上。
6. 地理/作者/缓存召回
地理位置GeoHash召回 可能对同城 / 地理位置比较近的感兴趣
1. 关注作者召回:用户-> 作者-> 最新的笔记
2. 如果用户对作者某篇笔记感兴趣 也可能对其他笔记感兴趣。
有交互的作者 对其作品有过点赞收藏转发。
3. 用户喜欢某作者 也可能喜欢其他类似的作者。
缓存召回:精排的已经是质量比较高的;但很多笔记没有通过重排 被召回;可以缓存起来下次用
缓存退场机制:设置缓存容量(满了就FIFO 先进先出);缓存天数上限;召回次数上限
7. 曝光过滤&Bloom filter(不重复曝光)
以前曝光给用户的 以后不能还曝光给他。对于每个用户,记录已经曝光给他的物品。
(小红书只召回1个月以内的笔记,因此只需要记录每个用户最近1个月的曝光历史。)
用哈希函数映射到一张 bool表 为1代表曝光过,如果映射对应bool为0说明一定没曝光过,如果bool为1则可能曝光过(因为哈希冲突误伤但概率较小)
曝光物品集合大小为n,二进制向量维度为m,使用k个哈希函数。
曝光后就会修改 Bloom filter;召回时过滤已经曝光过的
这种数据结构缺点是 只能添加元素无法移除元素。
因为绝对不会推荐1个月以上的,所以会从曝光物品集合移除(每天重建一下)进而降低n