推荐系统架构
候选池:可供推荐的物料候选池,对于图文、短视频、新闻等来说通常是最近N天的新物料组成的集合,定期进行物料更新。
召回:从候选池中选出和用户相关的物料,交给后面的排序模块进行打分。召回面对的候选集通常是数十万或者百万甚至千万的量级,检索过程必须轻量快速。通常由多路召回组成,保证不遗漏且相关性、多样性尽量好,可以分为非个性化召回和个性化召回。
粗排:对召回选择的几千或者几百候选物料进行排序,快速打分选出topN,减少精排的压力,兼顾精准与效率。
精排:对粗排的结果进行精准排序,这个环节需要特征尽可能丰富,样本尽可能干净,模型尽可能复杂,使得对物料的预估分数尽可能准确。
重排:对精排结果做一些小幅调整,包括类目多样性、作者多样性、图文/视频多样性打散
混排:对于广告插入、冷启物料插入,长尾物料加权、热门物料降权等需要进行策略调整。
召回分类
非个性化召回
热门召回
后验高ctr/cvr召回,高点赞/高评论召回,高完播召回。这些都是全局意义上面的热度物料。需要注意的是,ctr/cvr/完播率等需要根据已经下发的时间做一些衰减,还需要做平滑处理(例如威尔逊平滑),另外也可以对比值的分母(比如曝光)做一些阈值截断,杜绝一些由于低曝光导致比值虚高带来的数据偏差问题。
// 威尔逊平滑
def wilsonSlide(click: Long, n: Long): Double = {
if(n <= 0) 0.0 else {
val p = if (n == 0) 0.0 else click * 1.0 / n
val K = 1.96 // 95% confidence level
val divN = K * K / n
(p + divN / 2.0 - K * Math.sqrt(p * (1 - p) / n + divN / n / 4.0)) / (1 + divN)
}
}
时新召回
对于一些新产生的物料,由于没有曝光点击数据,没法从后验指标里面筛选出来,需要一个专门的新物料召回通道,满足对于新物料的拉取。
特殊通道召回
对于一些产品活动的物料、政策制定的物料,需要从特殊通道中召回。
个性化召回
人口属性召回(Demographic-based)
基于用户的基础属性的召回。
基于用户年龄段、性别、学历、地域等基础属性的召回。关键是找到不同基础属性对应的优质素材。这里注意,如果某一个属性(例如性别)不同取值(例如男、女)对应的热门物料差别不大,这个属性召回理论上效果不明显。
CB召回(Content-based)
基于物料内容的召回。
cate1/cate2召回:物料所属的一级类目/二级类目/标签信息作为倒排索引的key,倒排索引的value为这个key下面的优质物料集合。用户trigger为兴趣画像中对应的一级/二级/标签兴趣画像。
创作者召回:物料所属的创作者作为倒排索引的key,倒排索引的value为这个创作者所属的优质物料集合。
CF召回(Collaborative Filtering-based )
协同过滤召回,可以分为UserCF和ItemCF。
UserCF:基于目标用户,计算其相似用户集合,然后根据相似用户的正向行为作为候选集。
step1:计算目标用户的topN相似用户集合U。
step2:将相似用户集合U中每个用户的正向行为取出,过滤掉目标用户历史(已曝光/消费/点击)行为
关键是计算用户的相似度。例如要计算用户u和v的相似度,N(u)表示用户u正向行为集合,N(v)表示用户v的正向行为集合,可以通过Jaccard公式或者余弦公式来计算相似度
Jaccard公式:
余弦公式:
欧几里得距离公式:
=
=
皮尔逊相关系数:
ItemCF:基于目标用户,计算目标用户的正向行为物料的相似物料,这些相似物料作为目标用户的召回候选集。
step1:计算各物料之间的相似度。
step2:根据目标用户的历史正向行为物料集合,找到对应的相似物料,过滤掉目标用户历史(已曝光/消费/点击)行为
关键是计算物料的相似度。例如要计算物料i和物料j的相似度,N(i)表示对物料i有正向行为的用户集合,N(j)表示对物料j有正向行为的用户集合,下面用余弦公式计算i和j的相似度。
CF召回的优缺点。
优点:
- 计算速度较快,依赖较少
- 利用了用户、物料之间的相关性。
缺点: - 只利用了ID信息,除ID之外的其他信息(side information)未利用,损失较多信息精度较低,泛化能力较差。
- 对于热门物料或者活跃用户,由于出现的频次较高,容易形成马太效应,所有用户和这个用户相似/所有物料和这个物料相似,需要做热门打压。CF类召回容易出现下发物料类目上的聚集效应,即头部下发的物料类目/作者集中度变高。
- 对于行为稀疏的用户或者物料不友好,这类用户或者物料计算出的相似度数值存在置信度问题。
模型召回(Model-based )
基于用户特征、物料特征、上下文特征来用模型找到用户的相关候选物料。
线性模型:FM召回、FFM召回
FM通过因子分解的方式对每个类别特征引入一个维的隐向量,根据两个对应的隐向量的点积来确定二阶交叉特征的权重。
其中点积部分由如下方式计算,是隐向量长度的超参数,数值大小影响模型准确性及泛化能力。
我们把特征按照用户和物料对公式进行拆分,拆分为偏置项、用户侧一阶特征、物料册一阶特征、用户特征与物料特征交叉、用户特征和用户特征交叉、物料特征和物料特征交叉几个部分。
用户特征 ,
物料特征
对目标用户来说,用户相关特征的计算不影响物料的排序
不同的item,特征不同,对应的预估值不同,下面公式项决定item的分数排序,左边2项是物料侧得分,最后一项是交叉项得分。
交叉项得分可以写成两个向量的内积形式
可以看出这个内积是用户隐向量之和与物料隐向量之和的乘积。
问题就转换成了两个Embedding的ANN检索问题了,先离线构建好物料侧的向量Embedding,然后针对目标用户,计算出用户的Embedding,根据ANN检索方式求topN相似物料。
DSSM
原文是查询词query和文档doc的匹配问题,在推荐中对应用户和物料的匹配问题。DSSM基本思想是将query和doc映射到同一个低维向量空间,通过query向量和doc向量的相似度来表征两者的相关性。
应用在推荐中,结构图如下所示,一个塔为用户塔,一个塔为物料塔,输入是用户特征组成的embedding向量和物料特征组成的embedding向量(上下文特征embedding可以放在用户侧),然后通过中间的MLP映射层,最后将匹配层的用户embedding和物料embedding通过cosine来得到相关性结果。
用户塔和物料塔表示层的计算示例如下
user_emb = user_tower(layers=[256, 128, 32])
item_emb = item_tower(layers=[256, 128, 32])
user_output = tf.nn.l2_normalize(user_emb, axis=1, epsilon=1e-7, name='user_tower')
item_output = tf.nn.l2_normalize(item_emb, axis=1, epsilon=1e-7, name="item_tower")
dot = tf.reduce_sum(tf.multiply(user_output, item_output), axis=1, keepdims=True)
logits = tf.layers.dense(dot, 1, activation=None)
y_pred = tf.sigmoid(logits)
loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=logits, labels=y_label))
YouTubeDNN
模型结构图
模型包括三个激活函数为Relu的隐层结构,输入为用户的观看历史视频、搜索历史视频、人口属性等信息。
在时刻,为用户(上下文信息)在物料候选池中计算物料的类别,物料池中每个物料表示一个类,物料也是一个类。
离线训练输出层是softmax层,输层出公式如下
- 直接使用用户embedding和物料embedding做线上ANN检索,加快召回速度。用户embedding在输出层接的是softmax的全连接层,得到概率分布。其权重矩阵即个物料embedding组成的
用户embedding和物料embedding点积得到观看概率,点积越大,表示用户对这个物料越感兴趣 - 训练输出层做负采样
序列模型Mind
序列模型SASREC
图召回GraphEmbedding
端到端训练TDM
召回优化
评价指标
AUC 召回侧模型的auc指标一般较高,主要原因是加入了随机负样本,模型学习的时候是相对较为容易的,在某些头部物料较为集中的场景,如果负样本仅仅采用随机负采样,得到的auc指标可能会到0.95+,主要原因是模型能根据物料的一些反馈信号很容易区分出正负样例。因此对模型、样本、特征 进行升级,这个指标的变化往往不能真正反映模型效果的变化。一般使用HitRecall@K指标。
HR@K
例如用户A、B、C在测试集中真实行为物料分别有4个、3个、5个,三个用户推荐的top10列表与真实的交集分别是2个、1个、3个,那么
负样本优化
因为样本选择偏差问题,召回需要从全局来选择负样本。一个简单的方式是随机全局负采样,全局随机负采样优点是选择的负样本无偏,所有候选物料理论上都能选到,不会遗漏。缺点是对于大部分长尾物料来讲,用户行为较少,模型能根据一些信号很容易的区分出正例和负例的区别。
随机采的负样本是easy negetive,比较容易学习到的,一个有效的办法是加入hard negetive,增加模型的训练难度。
比较常见的增加hard 负样本的方式增加曝光未点击的负样本。这样负样本的组成如下
负样本 = 全局随机负样本 + 曝光未点击
第二部分也可以从全局曝光未点击集合中进行抽样,也就是从实际有行为的物料中进行抽样。
Airbnb有一篇paper讲到他们挑选负样本的方式,根据业务来,他们的场景是用户选择预定出租屋或者房间,他们选择了一部分和用户定位同城市的出租屋或者房间作为hard负样例,这里通过限定和用户定位地点相同的条件来显式增加模型学习的难度。
Facebook有一篇paper讲到他们选择hard负例的方式,通过将上一轮推荐的召回结果中,排名在101-500的物料作为hard负例。这部分负例和用户相关,但从排名上看不是强相关,可以增加模型学习的难度。
对于视频流、信息流等场景来说,从用户兴趣画像(一级/二级类目)出发,增加用户感兴趣的类目下的负例,也可能有一定的效果。
模型结构优化
样本优化
目标优化