本次所涉及的模型用于推荐系统中的召回环节,该环节主要是一个embedding和筛选,本次所涉及的模型主要用于embedding过程。
DSSM双塔模型是指,user和item的embedding向量分别出自两个网络。模型并不复杂,由两个dnn流再加相似度计算构成。需要主要负样本采样及归一化/温度系数以保证欧氏空间的问题。
而YoutubeDNN则是单塔模型,user和item的embedding向量出自一个网络。模型由一个DNN构成,但对于特征的处理比较讲究。
1.精排模型 DeepFM&DIN
本次所涉及的模型为召回模型,其在推荐系统中的位置常在排序之前。
召回模型的功能在于快速地筛选及形成值得注意的数据的过程,具体为实现从推荐池中选取几千上万的item,送给后续的排序模块。
召回+排序这样分阶段漏斗体系诞生的原因,主要是算力局限所致。
如果在排序阶段选择所有的item作为候选集,则训练的耗时及内存开销巨大,服务响应时间都是灾难性的。
召回模块非常重要,该模块决定了整个系统最终表现的天花板,需要保证能召回高质量(高点击率或高转化率)的候选item。
因此如何创新和发展找回技术是对业务有重大意义的问题,也是业界和学术界关注的重点问题。
近几年的召回算法可以分为两类:
双塔模型在推荐领域中是一个十分经典的模型,无论是在召回还是粗排阶段,都会是首选。
双塔模型的主要优势在于其结构,劣势也在于其结构
DSSM(Deep Structured Semantic Model)是由微软研究院于CIKM在2013年提出的一篇工作。该模型并不是针对推荐系统提出的,因而下面部分会先介绍模型的背景,再介绍其在推荐系统中的具体形式。
问题的背景是NLP领域语义相似度,doc和query的匹配。
该模型主要用来解决NLP领域语义相似度任务 ,利用深度神经网络将文本表示为低维度的向量,用来提升搜索场景下文档和query匹配的问题。
DSSM 模型的原理主要是:通过用户搜索行为中query 和 doc 的日志数据,通过深度学习网络将query和doc映射到到共同维度的语义空间中,通过最大化query和doc语义向量之间的余弦相似度,从而训练得到隐含语义模型,即 query 侧特征的 embedding 和 doc 侧特征的 embedding,进而可以获取语句的低维语义向量表达 sentence embedding,可以预测两句话的语义相似度。
感觉上面这段话没有讲人话。理解了一下大概就是,DSSM是用来训练一个隐含语义模型的,目标是匹配语义相似的doc和query。
双塔模型的结构如下:
该网络结构比较简单,是一个由几层DNN组成网络。
而在推荐系统中,user和item间的匹配是关键。
而在推荐系统中,最为关键的问题是如何做好用户与item的匹配问题,因此对于推荐系统中DSSM模型的则是为 user 和 item 分别构建独立的子网络塔式结构,利用user和item的曝光或点击日期进行训练,最终得到user侧的embedding和item侧的embedding。
从模型结构上来看,主要包括两个部分:user侧塔和item侧塔,对于每个塔分别是一个DNN结构。
如何匹配相似度,定义最终的输出?
在召回模型中,这个问题类似于多分类问题(如YouTubeDNN模型)。此处将物料库中的一个item视为一个类别,因此损失函数需要计算每个类的概率值:
其中 s ( x , y ) s(x,y) s(x,y)表示两个向量的相似度, P ( y ∣ x ; θ ) P(y|x;\theta) P(y∣x;θ)表示预测类别的概率, M M M表示物料库所有的item。
但是在实际场景中,由于物料库中的item数量巨大,在计算上式时会十分的耗时,因此会采样一定的数量的负样本来近似计算,后面针对负样本的采样做一些简单介绍。
以上就是推荐系统中经典的双塔模型,之所以在实际应用中非常常见,是因为在海量的候选数据进行召回的场景下,速度很快,效果说不上极端好,但一般而言效果也够用了。
在实际的工业应用场景中,分为离线训练和在线服务两个环节。
- 在离线训练阶段,同过训练数据,训练好模型参数。然后将候选库中所有的item集合离线计算得到对应的embedding,并存储进ANN检索系统,比如faiss。为什么将离线计算item集合,主要是因为item的会相对稳定,不会频繁的变动,而对于用户而言,如果将用户行为作为user侧的输入,那么user的embedding会随着用户行为的发生而不断变化,因此对于user侧的embedding需要实时的计算。
- 在线服务阶段,正是因为用户的行为变化需要被即使的反应在用户的embedding中,以更快的反应用户当前的兴趣,即可以实时地体现用户即时兴趣的变化。因此在线服务阶段需要实时的通过拼接用户特征,输入到user侧的DNN当中,进而得到user embedding,在通过user embedding去 faiss中进行ANN检索,召回最相似的K个item embedding。
可以看到双塔模型结构十分的适合实际的应用场景,在快速服务的同时,还可以更快的反应用户即时兴趣的变化。
之所以双塔模型在服务时速度很快,是因为模型结构简单(两侧没有特征交叉),但这也带来了问题,双塔的结构无法考虑两侧特征之间的交互信息,在一定程度上牺牲掉模型的部分精准性。
例如在精排模型中,来自user侧和item侧的特征会在第一层NLP层就可以做细粒度的特征交互;
而对于双塔模型,user侧和item侧的特征只会在最后的內积计算时发生,这就导致很多有用的信息在经过DNN结构时就已经被其他特征所模糊了,因此双塔结构由于其结构问题先天就会存在这样的问题。
接下来提到的模型,都尝试弥补user侧和item侧特征交互的问题。
SENet由Momenta在2017年提出,当时是一种应用于图像处理的新型网络结构。后来张俊林将SENet引入了精排模型FiBiNET中,其作用是为了将大量长尾的低频特征抛弃,弱化不靠谱低频特征embedding的负面影响,强化高频特征的重要作用。
我们先看看SENet的结构,如下
从上图可以看出SENET主要分为三个步骤Squeeze, Excitation, Re-weight:
以上简单的介绍了一下SENet结构,可以发现这种结构可以通过对特征embedding先压缩,再交互,再选择,进而实现特征选择的效果。
张俊林将SENet应用于双塔模型中,模型结构如下所示:
具体地是将双塔中的user塔和Item侧塔的特征输入部分加上一个SENet模块,通过SENet网络,动态地学习这些特征的重要性,通过小权重抑制噪音或者无效低频特征,通过大权重放大重要特征影响的目的。
为什么加入SENet是有效的呢?
张俊林老师的解释是:双塔模型的问题在于User侧特征和Item侧特征交互太晚,在高层交互,会造成细节信息,也就是具体特征信息的损失,影响两侧特征交叉的效果。
而SENet模块在最底层就进行了特征的过滤,使得很多无效低频特征即使被过滤掉,这样更多有用的信息被保留到了双塔的最高层,使得两侧的交叉效果很好;
同时由于SENet模块选择出更加重要的信息,使得User侧和Item侧特征之间的交互表达方面增强了DNN双塔的能力。
也就是说SENet双塔模型主要是从特征选择的角度,提高了两侧特征交叉的有效性,减少了噪音对有效信息的干扰,进而提高了双塔模型的效果。(也不失为一个不错的思路)
主要是因为实际场景中业务复杂(如点击,评论,收藏,关注,转发等),所以针对不同的任务各独有一个tower。
这种模型结构,可以针对多目标进行联合建模,通过多任务学习的结构,一方面可以利用不同任务之间的信息共享,为一些稀疏特征提供其他任务中的迁移信息,另一方面可以在召回时,直接使用一个模型得到多个目标预测,解决了多个模型维护困难的问题。也就是说,在线上通过这一个模型就可以同时得到多个指标,例如视频场景,一个模型就可以直接得到点赞,品论,转发等目标的预测值,进而通过这些值计算分数获得最终的Top-K召回结果。
关于双塔模型,其模型结构相比排序模型来说很简单,没有过于复杂的结构。但除了结构,有一些细节部分容易被忽视,而这些细节部分往往比模型结构更加重要
归一化与温度系数
在Google的双塔召回模型中,重点介绍了两个trick,将user和item侧输出的embedding进行归一化以及对于內积值除以温度系数,实验证明这两种方式可以取得十分好的效果。
归一化的操作主要原因是因为向量点积距离是非度量空间,不满足三角不等式,而归一化的操作使得点击行为转化成了欧式距离。
那为啥非要转为欧式距离呢?这是因为ANN一般是通过计算欧式距离进行检索,这样转化成欧式空间,保证训练和检索一致。
负样本采样
对于召回模型而言,其负样本并不能和排序模型一样只使用展现未点击样本,因为召回模型在线上面临的数据分布是全部的item,而不仅仅是展现未点击样本。因此在离线训练时,需要让其保证和线上分布尽可能一致,所以在负样本的选择样要尽可能的增加很多未被曝光的item。
常见的采样方法有
教程给出了使用一点资讯提供数据的DSSM召回模型实践
训练数据基于一点资讯获取,详见教程
分为以下几步:
代码详见教程。
模型的结构其实并不复杂,主要实现分为:
代码详见教程。
需要针对输入数据有一些处理,分为以下几步:
双塔模型生成了embedding之后,具体到推荐系统中,应用是召回。
代码详见教程。
这里采用的召回模型是ANN。
分为以下两步:
YouTubeDNN模型是2016年的一篇文章,虽然离着现在有些久远, 但这篇文章无疑是工业界论文的典范, 完全是从工业界的角度去思考如何去做好一个推荐系统,并且处处是YouTube工程师留给我们的宝贵经验, 由于这两天用到了这个模型,今天也正好重温了下这篇文章,所以借着这个机会也整理出来吧, 王喆老师都称这篇文章是"神文", 可见其不一般处。
首先是从工程的角度去剖析了整个推荐系统,讲到了推荐系统中最重要的两大模块: 召回和排序, 这篇论文对初学者非常友好,之前的论文模型是看不到这么全面的系统的,总有一种管中规豹的感觉,看不到全局,容易着相。 其次就是这篇文章给出了很多优化推荐系统中的工程性经验, 不管是召回还是排序上,都有很多的套路或者trick,比如召回方面的"example age", “负采样”,“非对称消费,防止泄露”,排序方面的特征工程,加权逻辑回归等, 这些东西至今也都非常的实用,所以这也是这篇文章厉害的地方。
更具体地讲述了一下召回和精排的联系和区别。
对于召回,大致上有两大类召回方式,
召回那边对于每个用户, 给出了几百个比较相关的候选视频, 把几百万的规模降到了几百, 当然,召回那边利用的特征信息有限,并不能很好的刻画用户和视频特点,所以, 在精排侧,主要是想利用更多的用户,视频特征,刻画特点更加准确些,从这几百个里面选出几个或者十几个推荐给用户。 而涉及到准, 主要的发力点一般有三个:特征工程, 模型设计以及训练方法。 这三个发力点文章几乎都有所涉及, 除了模式设计有点审时度势之外,特征工程以及训练方法的处理上非常漂亮,具体的后面再整理。
精排侧,这一块的大致发展趋势,从ctr预估到多目标, 而模型演化上,从人工特征工程到特征工程自动化。主要是三大块, CTR预估主要分为了传统的LR,FM大家族,以及后面自动特征交叉的DNN家族.
而多目标优化,目前是很多大公司的研究现状,更是未来的一大发展趋势,如何能让模型在各个目标上面的学习都能"游刃有余"是一件非常具有挑战的事情,毕竟不同的目标可能会互相冲突,互相影响,所以这里的研究热点又可以拆分成网络结构演化以及loss设计优化等, 而网络结构演化中,又可以再一次细分。 当然这每个模型或者技术几乎都有对应paper,我们依然可以通过读paper的方式,把这些关键技术学习到。
对模型的评估,
我们往往也会用一些策略,比如修改模型的优化目标,损失函数这种, 让线下的这个目标尽量的和A/B衡量的这种指标相关性大一些。
模型的问题背景是:在大量YouTube视频中检索出数百个和用户相关的视频来。
这个问题,可以看成一个多分类的问题,即用户在某一个时刻点击了某个视频, 可以建模成输入一个用户向量, 从海量视频中预测出被点击的那个视频的概率。
问题的数学描述借鉴了word2vec。
DNN的输入是下列用户侧的特征整理而成的长向量:
[item_id5, item_id2, item_id3, ...]
, 是高维稀疏特征本部分代码的原文件见github
colab版本在这里
MovieLens数据集是电影网站提供的一份数据集,原数据分为三个文件,users.dat
, movies.dat
, ratings.dat
,包含了用户信息、电影信息和用户对电影的评分信息。
教程对数据进行了一个处理,和采样(在全量数据中取出前100个样本),数据的开头如下:
数据的基本信息如下:
在教程的DSSM模型中,使用了两种类别的特征,分别是稀疏特征(SparseFeature)和序列特征(SequenceFeature)。
对应的处理方式包括:
对于稀疏特征,是一个离散的、有限的值(例如用户ID,一般会先进行LabelEncoding操作转化为连续整数值),模型将其输入到Embedding层,输出一个Embedding向量。
对于序列特征,每一个样本是一个List[SparseFeature](一般是观看历史、搜索历史等),对于这种特征,默认对于每一个元素取Embedding后平均,输出一个Embedding向量。此外,除了平均,还有拼接,最值等方式,可以在pooling参数中指定。
具体到本例,Sparse Feature是把genres特征第一个作为cate_id特征,其他的sparse feature还包括’user_id’, ‘movie_id’, ‘gender’, ‘age’, ‘occupation’, ‘zip’。
对Sparse Feature进行label encoder处理,对比处理前后结果如下
除了常规的输入数据定义,还需要定义塔的特征
在DSSM中,分为用户塔和物品塔,每一个塔的输出是用户/物品的特征拼接后经过MLP(多层感知机)得到的。 下面我们来定义物品塔和用户塔都有哪些特征
# 定义两个塔对应哪些特征
user_cols = ["user_id", "gender", "age", "occupation", "zip"]
item_cols = ['movie_id', "cate_id"]
# 从data中取出相应的数据
user_profile = data[user_cols].drop_duplicates('user_id')
item_profile = data[item_cols].drop_duplicates('movie_id')
对序列特征,本数据集中的序列特征为观看历史,根据timestamp形成。
generate_seq_feature_match
函数