本文主要是介绍推荐算法实战五-召回(下),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一、FM的召回功能
(一)打压热门物料
FM主要应用于U2I召回场景,正样本采用与用户正向交互过的样本。负样本来源于两个途径,一个是随机采样,一个是曝光但未点击的负向物料。由于热门物料曝光率高,因此正负样本中热门物料参与度都不小,为了确保推荐结果的多样性,对正负样本分别采取不同的热门物料打压策略。
1、热门物料在正样本中要降采样
降低热门物料被选为正样本的概率,曝光率越高,选为正样本的概率就越低。定义一个物料t_i能被任何用户选为正样本的概率P_post(t_i)为:
α是一个超参数,可以认为是冷门物料的门槛,当f(t_i)<=α并且被用户点击过,那么可以认定为正样本。
2、热门物料在负样本中要过采样
负样本采样需要满足以下两个要求:需要尽可能广泛地采样负样本,覆盖尽可能多的物料样本;采集一些热门物料来抵消少数热门物料垄断正样本的情况。负采样概率如下所示:
其中V是所有物料的集合。当超参数b为1的时候,对热门物料的打压力度最大;当超参数b为0的时候,则是uniform sampling,任何物料选为负样本的概率都一样大。
(二)增广Embedding
拆解得到用户特征和物料特征后,用FM表示用户u和物料t的匹配程度如下式所示:
W_t是一阶物料特征权重之和,W_u是一阶用户特征权重之和,V_uu是用户特征集内部两两交叉,V_tt是物料特征集内部两两交叉,V_ut是用户特征和物料特征的两两交叉。
由于b、W_u、V_uu对于不同物料都是相同的,因此可以省略这三项,后面变成向量内积的形式求解:
E_u是在在线召回时实时计算得到的用户向量。E_t是物料向量,离线计算好存入faiss建立索引。面对新用户V_ut提供的信息有限,主要依赖W_t和V_tt。训练的时候没必要将用户向量和物料向量拆开,只在预测时使用上式。
FM召回的主力函数如下所示:
二、大厂主力:双塔模型
(一)不同场景下的正样本
1、I2I
同一个用户在同一个会话中交互过的两个物料可以组成为正样本。
2、U2I
用户和其交互过的物料可以组成正样本。
3、U2U
用户一半历史行为和另外一个用户一半历史行为,基于同一个兴趣爱好的,可以组成为正样本。
(二)简化负采样
1、Batch内负采样
- 方式:u_i交互过的物料表示为t_i,负样本则由一个batch中其他正样本中除了t_i以外的物料t_j和u_i组成。
- 优点:因为t_j在正样本中被计算过了,复用向量t_j避免了大量重复计算。
- 缺点:一个batch内大部分正样本都被热门物料垄断了,因此负采样得到的物料大多是热门物料,这是hard negative。缺少与用户兴趣毫不相干的easy negative。这种现象被称为样本选择偏差sample selection bias(SSB)。
2、混合负采样
为了解决batch内负采样造成的样本选择偏差,采用混合负采样策略(mixed negative sampling)。
主要思想如下:
- 额外建立了向量缓存,缓存多个Batch的物料向量。
- batch内负采样作为hard negative。
- 额外从向量缓存中取出之前计算好的物料向量作为easy negative。
(三)双塔结构特点
塔内可以复杂,塔间不能。
1、单塔可以很复杂
- 塔就是一个DNN。
- U2I的话就是将用户特征输入用户塔,物料特征喂给物料塔,输出embedding。
- 塔底座可以很宽,不局限于user ID,item ID这两种特征,可以接受的特征很丰富。
- 塔高可以足够高,实现充分的交叉。
2、双塔一定要解耦
- 解耦:①在特征上解耦:不使用物料特征和用户特征的交叉特征。②在结构上解耦:不能像DIN那样使用候选物料特征对用户行为序列做attention。③补充:用户特征向量和物料特征向量只有最后一步才点积交叉。
- 将用户行为序列接入用户塔:①最简单的方式是average pooling,但是这样会将所有历史行为视为相同重要。②由于无法使用候选物料对用户历史行为序列做attention,因此可以采用以下方式:(1)利用用户搜索文本当做query。(2)阿里巴巴将用户画像当做query给历史行为打分。(3)微信利用用户行为序列中最后交互的物料来体现用户最新行为兴趣,来衡量历史行为的重要性。
(四)sampled softmax loss的技巧
双塔模型常用的基于Batch内负采样的sampled softmax loss。
其中G(u,t)表示物料和用户的匹配程度。
1、L2正则化
已知u表示用户向量,t表示物料向量。每个向量都除以向量的L2正则,这样就将点积求匹配度转换成了cosine。由于cosine的范围在-1到1,更容易衡量匹配度。
2、温度调整难度
被称为温度,由Tower loss可知,应当使得正样本的匹配程度尽量大,负样本的匹配程度尽量小。因此当负样本训练得不够好的时候,1/就会放大这个问题,导致分母变大,损失增加,没被训练好的负样本就会被重新聚焦。
- 设置的足够小的时候,对错误放大的功能很强,会将与用户交互过的物料牢牢记住,而将没有交互过的物料与用户向量强行分开,这样推荐精度很高,但是兴趣覆盖不够。
- 设置的足够大的时候,对错误放大能力较弱,会突破信息茧房来为用户推荐更广兴趣范围的物料,但是有损精度。
3、采样概率修正
除了batch内负采样之外,引入向量缓存中的物料向量作为负样本。
(五)Tensorflow实现双塔
class MovielensModel(tfrs.models.Model):"""电影推荐场景下的双塔召回模型"""def __init__(self, layer_sizes):super().__init__()self.query_model = QueryModel(layer_sizes) # 用户塔self.candidate_model = CandidateModel(layer_sizes) # 物料塔self.task = tfrs.tasks.Retrieval(......) # 负责计算Lossdef compute_loss(self, features, training=False):# 只把用户特征喂入“用户塔”,得到user embedding "query_embeddings"query_embeddings = self.query_model({"user_id": features["user_id"],"timestamp": features["timestamp"],})# 只把物料特征喂入“物料塔”,生成item embedding "movie_embeddings"movie_embeddings = self.candidate_model(features["movie_title"])# 根据Batch内负采样方式,计算Sampled Softmax Lossreturn self.task(query_embeddings, movie_embeddings, ......)class Retrieval(tf.keras.layers.Layer, base.Task):def call(self, query_embeddings, candidate_embeddings,sample_weight, candidate_sampling_probability, ......) -> tf.Tensor:"""query_embeddings: [batch_size, dim],可以认为是user embeddingcandidate_embeddings: [batch_size, dim],可以认为是item embedding"""# query_embeddings: [batch_size, dim]# candidate_embeddings: [batch_size, dim]# scores: [batch_size, batch_size],batch中的每个user对batch中每个item的匹配度scores = tf.linalg.matmul(query_embeddings, candidate_embeddings, transpose_b=True)# labels: [batch_size, batch_size],对角线上全为1,其余位置都是0labels = tf.eye(tf.shape(scores)[0], tf.shape(scores)[1])if self._temperature is not None: # 通过温度,调整训练难度scores = scores / self._temperatureif candidate_sampling_probability is not None:# SamplingProbablityCorrection的实现就是# logits - tf.math.log(candidate_sampling_probability)# 因为负样本是抽样的,而非全体item,Sampled Softmax进行了概率修正scores = layers.loss.SamplingProbablityCorrection()(scores, candidate_sampling_probability)......# labels: [batch_size, batch_size]# scores: [batch_size, batch_size]# self._loss就是tf.keras.losses.CategoricalCrossentropy# 对于第i个样本,只有labels[i,i]等于1,scores[i,i]是正样本得分# 其他位置上的labels[i,j]都为0,scores[i,j]都是负样本得分# 所以这里实现的是Batch内负采样,第i行样本的用户,把除i之外所有样本中的正例物料,当成负例物料loss = self._loss(y_true=labels, y_pred=scores, sample_weight=sample_weight)return loss
这篇关于推荐算法实战五-召回(下)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!