【深度学习】推荐系统基础知识
Last updated on February 22, 2024 am
推荐系统学习资料
推荐系统简介
搜索弓|擎需要用户主动输入自己的意图,有时候,用户并不知道自己需要什么,有些需求、意愿,是用户自己都意识不到的。因为用户提不出需求,就"无所事事",显然这是对宝贵流量的巨大浪费,不利于建立用户粘性。将自己拥有的、用户可能喜欢的内容主动展示给用户,从而留住用户花费更多的时间与金钱。这就是推荐系统。
学习推荐系统算法需要注意以下的关键要点:
- 记忆性与拓展性
- 推荐系统的高维稀疏的类别特征
- 特征的Embedding
- 特征的交叉结构
推荐系统的例子
假设一个极简版的推荐系统中,也有两类角色:用户和物料。
- 用户(User) 就是推荐系统要服务的对象。用户也是推荐系统的重要贡献者。用户通过"大拇指投票" 帮推荐系统分辨出内容、信息的真假优劣。
- 物料(Item),用于统称要推荐出去的信息、内容。不同场景下,物料会有不同的内涵:电商推荐中,物料就是商品;内容推荐中,物料就是一篇文章、一首歌;社交推荐中,物料就是另一个人。
建立推荐系统的步骤如下:
- 第一步是给物料打标签。比如对一个视频《豆瓣评分9.3,最恐怖的喜剧电影:楚门的世界》,我们给它打上"电影、喜剧、真人秀、金凯瑞'这样的标签。
- 第二步,建立倒排索引,将所有物料组织起来,倒排索引类似一个HashMap, 键是标签,值是一个列表,包含着被打上这个标签的所有视频。
- 第三步,推荐系统接到一个用户的推荐请求。推荐系统根据从请求中提取出来的用户ID,从数据库中检索出该用户的兴趣爱好。假设该用户过去看过10个视频,其中7个带有"喜剧"的标签,3个带有"足球"的标签,则提取出来的该用户的兴趣爱好就是{"喜剧": 0.7, "足球": 0.3}, 其中键表示用户感兴趣的标签,后面的数字表示用户对这个标签的喜好程度。
- 第四步,召回,拿用户感兴趣的每个标签去倒排索引中检索。假设"喜剧"这个标签对应的倒排链中包含A、B两个视频,"足球"标签对应的倒排链中包含C、D、 E两个视频。汇总起来,推荐系统为该用户找到了5个他可能感兴趣的视频。这个过程叫作"召回" (Retrieval) ,查寻倒排索引只是其中一种实现方式。
- 第五步,推荐系统会猜测用户对这5个视频的喜爱程度,再按照喜爱程度降序排列,将用户最可能喜欢的视频排在最显眼的第一位,
让用户一眼就能看到。这个过程叫作"排序" (Ranking)
。至于如何猜测用户的喜爱程度,在这个极简版的推荐系统中,这里只使用一个简单的评价规则:\[Score(u,g,v)=Like(u,g)\times Q(v)\]
- u表示发出请求的用户
- g表示用户u喜欢的一个标签
- v表示,在第四步中,根据标签g从倒排索引中提取出来的一个视频
- Like(u,g)表示用户u对标签g的喜爱程度。比如在上面的例子中,Like(u,"喜 剧")=0.7
- Q(v)表示视频v的质量。可以由v的各种后验消费指标来表示,比如可以用点击率(CTR) 来表示
- Score(u, g, v)表示推荐系统猜测的用户u对视频v的喜爱程度。
- 第六步推荐系统对排序结果进行截断,只保留前4个视频,返回给用户。
- 第七步,用户按照[B, C, A,D]的顺序看到了4个视频,点击并观看了视频B。用户行为,即"用户u点 击视频B, A/C/D曝光未点击",被记录进日志,发送给推荐系统。
- 第八步,推荐系统接收到了用户反馈,并据此更新用户的兴趣爱好。
以上是比较简单的推荐系统的例子,具体的实现过程中可能会非常复杂,考虑的东西会非常多,因此具体实现上可能需要进一步的考虑。
推广搜简介
含义:推荐、广告、搜索。用户需求表达方式:推荐、搜索。信息服务对象:推搜、广告
相同点
- 功能架构相同:都遵循先召回再排序
- 数据架构相同:都遵循使用Lambda架构(后续会提到)
- 技术栈相同,面向的功能相同,都需要高度个性化设计(都十分依赖于用户的画像)
不同点
- 推荐&搜索
推荐和搜索的最大差异在于用户表达意图的方式不同,用户输入显式的查询语句来表明自己的意图 \[ F_{search}(t|q,u) \]
- u表示当前用户,q表示用户输入的查询语句,t表示某一个候选物料
- Fsearch表示搜索模型, 衡量物料t对用户u输入的查询q的匹配程度
- 用户信息u也是公式的输入条件。不同用户输入相同的查询语句q,得到的结果也是不一样的
推荐中,用户无须显式表达其意图。推荐系统通过自己的长期观察,猜测用户意图,完成推荐 \[ F_{recommend}(t|u) \]
- 表示推荐模型,衡量物料t对用户u的匹配程度
q表示物料对用户查询的匹配程度,搜索有显式的匹配程度,但是推荐没有,总结如下:
搜索:查询语句与物料信息交叉
推荐:用户信息与物料信息交叉
推搜&广告
最终目标性:(优化指标)
- 推搜是为了制造流量,给予用户最佳的使用体验
- 广告是为了变现流量,要兼顾用户、广告主、平台三方面的利益,参与方更多、更复杂,优化起来难度更高
实现流程:
- 推搜目标即刻完成,比如点击和播放
- 广告是深层次的转化,需要用户选择并下载APP,存在较大的延时反馈问题,成功转化的正样本越稀疏,建模难度越高。
预测精度的要求:
- 推荐与搜索对预测出来的CTR/CVR只要求"相对准确性",即它们的预测精度能够将用户最喜欢的物料排在最前面,这就足够了
- 广告对预测精度要求"绝对准确性"。在模型的预测结果出来之后,广告还需要对其修正、校准。毕竟制作、投放广告还存在一定技术、财力上的门槛。
推荐系统模型架构
根据划分角度不同,将分成“功能架构”和“数据架构”两个板块
功能架构
为了应对海量的候选集合,现代大型推荐系统都采用由"召回→粗排→精排→ 重排"四个环节组成的分级推荐模式,在推荐链路中越靠前的环节,面对的候选集合越大,因此要采用技术较简单、精度稍逊、速度较快的算法,牺牲部分精度换速度;反之,链路靠后的环节,面对的候选集合较小,有条件采用技术较复杂、精度高、速度较慢的算法,牺牲部分速度换精度。

召回
召回模块的第一要务就是"快",可以牺牲一部分精度, 只要能找到与用户兴趣比较匹配的物料就好,而非最匹配的,因为后者是下游排序模块的目标。
召回模块主要依赖"离线计算+在线缓存"模式来实现对上百万候选集的快速筛选。上百万的候选物料在离线就处理好,处理结果被存入数据库并建立好索引,线上召回时,只花费一个在索引中的检索时间,时间开销非常小。
离线处理物料时肯定不知道将来要访问的用户是谁,召回模型在结构与特征上,都不能出现用户信息与物料信息的交叉。这个特点限制了召回模型的表达能力,也就限制了制约了召回模型的预测精度。
为了弥补精度上的不足,召回模块一般采用多路召回的方式,以数量弥补质量。每路召回只关注用户信息或物料信息的一个侧面,比如有的只负责召回当下最火爆的内容,有的只根据用户喜爱的"标签"进行召回,虽然单独一路召回的视角是 片面的,但是多路召回的结果汇总起来,取长补短,查漏补缺,就能覆盖用户兴趣的方方面面。
区分召回和排序:
召回是从一大堆物料中排除与用户兴趣八杆子打不着的,留下还比较合用户品味的。举个例子,召回好比经历过社会历练,无论哪种"不靠谱",他都见识过。
精排是从一小拨儿还不错的物料中,精挑细选,优中选优,挑出对用户来说最好的物料。举个例子,精排好比还在校园中的乖学生,见过最不靠谱的人不过是借橡皮不还的同桌。
传统召回算法
- 基于物料属性的倒排索引
- 基于统计的协同过滤算法
- 基于用户的协同过滤:给用户A找到与他相似爱好的用户B,把B喜欢的东西推荐给A
- 基于物料的协同过滤:用户A喜欢物料C,找到与A相似的其他物料D,把D推荐给A
- 矩阵分解算法(Matrix Factorization, MF)
向量化召回统一建模框架
如何定义负样本、如何定义证样本、如何生成Embedding、如何定义优化目标
定义:将召回问题建模成向量空间中的近邻搜索问题
分类:两类实体QT,可以是物料-用户、用户-用户、物料-物料
方法流程:
- 训练一个模型,并将QT映射到同一个向量空间
- 构建起索引向量数据库
- 对于传入的Q实例先进行Embedding 再进行近邻搜索
建模方法:
- 如何定义正样本(哪些q和t在向量空间中应该近)
- qt都是物料:时间间隔很近的用户行为序列交互过的物料认为很近,相似性
- q是用户t是物料:用户交互过的物料认为是相近的,匹配性
- qt都是用户:比如孪生网络,q是用户一半的交互历史,t是同一用户另一半的交互历史,同一性
- 如何定义负样本(哪些q和t在向量空间中应该远)
- 负样本为王,负样本选择对模型很重要
- 离线训练的数据分布应该与线上服务的数据分布保持一致
- 注意 召回和排序 候选集完全不同:召回要让模型见过最匹配也要见最不靠谱,因此召回的负样本主要依靠随机采样生成,不能(只)拿“曝光未点击”作为负样本
- 注意Easy
Negative问题,负样本要和q有细节差异,增加难度;
easy:hard=100:1
- 如何将q和t映射成Embedding
- 排序鼓励特征交叉,召回要求解耦
- 排序使用了大量的交叉统计特征,排序将用户特征、物料特征、交叉统计特征拼接
- 召回解耦,不用知道用户的请求提前计算好物料向量,避免计算交叉特征耗时
- 如何定义优化目标和损失函数
- NCE Loss
- Sampled Softmax Loss
- Pairwise Loss
精排
任务目标:精排的任务是从上游层层筛选出来的千余号还比较符合用户兴趣的物料中,精挑细选出百余个最合用户品味的物料。
设计重点:精排的设计重点是提升预测精度。所以不同于召回、粗排不允许用户信息与物料信息交叉,精排模型的发力重点就是让物料信息与用户信息更加充分地交叉,为此业界在精排引入了更多更复杂的交叉特征,
特征交叉方法
- 传统方法:FTRL
- FM:引入了二阶特征交叉(手动二阶特征交叉)
- Wide&Deep:兼顾记忆与扩展
- DeepFM :融合二阶交叉(实现了自动二阶特征交叉)
- DCN:能够指定任意显式交叉
- Autolnt:基于Transformer作特征交叉
用户行为序列建模
本质:将用户行为序列提炼并压缩成用户兴趣的Embedding
- DIN:利用Attention,将当前的物料t作为用户历史序列的Query
- 双层Attention行为序列:第一层Attention历史行为序列内部的依赖关系;第二层是当前物料和历史行为序列
- SIM:将“软过滤”变成“硬过滤”,相当于从长序列选择短序列进行DIN
粗排
召回的精度不足,所以用数量弥补质量,倾向于召回更多物料,送往下游。精排为了提升预测精度,不断加大模型复杂度,而牺牲了模型的吞吐能力。 如果让召回直接对接精排,笨重的精排无法面对召回送来的越来越多的候选物料,粗排接在召回后面,一般将召回的10000个结果再过滤掉9成,再交给精排重点考察。粗排夹在召回与精排之间,又是一个速度与精度折衷妥协的产物。
- 一方面,由于候选集规模比召回小得多,相比召回,粗排模型可以接入更多特征,使用更复杂的结构。
- 另一方面,由于候选集比精排还大得多,粗排模型比精排又简单太多。比如主流粗排模型仍然依赖"离线计算+在线缓存"模式来处理候选物料,所以仍然不能使用用户信息与物料信息交叉的特征与结构。
基于改进的双塔模型
- 召回需要向量数据库建立索引,粗排不需要索引
- 负样本的选择,可以拿“曝光未点击”当负样本
- 损失函数,粗排的损失函数需要考虑用户实际反馈,召回不需要
- 召回ANN快速搜索近邻使用点积实现交叉,粗排可以选择任意的交叉方式
- 双塔结构的问题:细粒度信息在塔中被消耗,如何保留更多的塔中输出信息
- 动态调整输入信息,减少噪声信息的输入
- 重要的信息走捷径,ResNet
基于知识蒸馏
重排
相似内容(比如相同话题、相同标签)会被粗排模型打上相近的分数,从而在结果集中排在相近的位置。如果将这样的排序结果直接呈现给用户,用户连看几条相似内容,很容易审美疲劳,从而伤害用户体验。所以,精排结果还需要经过重排。重排的主要目的不是为了过滤筛选,而是为了调整精排结果的顺序,将相似内容打散,保证用户在一屏之内看到的推荐结果,丰富而多样。
基于启发式规则
滑动窗口打散法
一种常见的打散规则叫做"滑窗打散",即在一个长度为K的滑动窗口(Sliding Window, SW)内,相似物料最多出现n次。
下面是一个例子
- 初始时,所有物料按照精排打分从大到小排序;
- 在第1个滑窗内,物料2和3是相同类别,违反打散目标,物料4和3对调;
- 在第2个滑窗内,还是物料2和3违反打散目标,物料3和5对调;
- 在第3个滑窗内,每个类别只出现一次,满足打散目标,路过;
- 在第4个滑窗内,物料5和6违反打散目标,物料6和7对调;
- 如果滑动到最后一次窗口,还不满足打散目标,但是后面已经没有物料可对调了,不做处理。
分桶打散法
首先按照不同类别将排好序的物料放入按照某个指标划分的桶中,紧接着均匀地从每个桶中抽取对应的物料来保证物料不相似
基于贪心算法
\[N e x t=\underset{t \in R \backslash S}{\operatorname{argmax}} \lambda \operatorname{Sim}_1(u, t)-(1-\lambda) \operatorname{Sim}_2(S, t)\]
- $$$$ 是全体精排结果, 也就是重排的全体候选集。
- $\[$ 是当前的重排结果集。当 \]\[ 的长度达到指定长度时, 重排结束, 将 \]$$ 展示给用户。
- \[R \backslash \] 表示还未插入重排结果集的所有精排结果。
- \[\operatorname{Sim}_1(u, t\] 代表当前候选物料 $\[$ 与发起请求的用户 \]$$ 之间的相关性, 可以用精排打分表示。
- \[\operatorname{Sim}_2(S, t\] 是当前物料 $\[$ 与当前重排结果集合 \]$$ 的相似度。这个数值越小, 将加入 \(S\), 对重排结果多样性的提升越大。
- \[\lambda \operatorname{Sim}_1(u, t)-(1-\lambda) \operatorname{Sim}_2(S, t\] 代表将候选物料 $\[$ 加入 \]\[ 带来的边际收益, 是相关性与多样性的折中, \]$$ 是组合的权重。
其余的方法:基于行列式点过程、基于上下文感知等方法
数据架构
除了上面介绍的功能架构,推荐系统中的模块还可以按照数据生产、计算、存储的不同方式进行划分,也就是推荐系统的数据架构。
数据类型:
- 冷数据:存储在Hadoop分布式文件系统(Hadoop Distributed File System, HDFS)上的那部分日志数据
- 热数据:HDFS只支持批量读写的性质所限,还有许多用户行为未得及组成用户日志,或者未来得及落盘在HDFS上的数据
为了应对互联网大数据系统的复杂性,Lambda架构应运而生。
- 将数据请求拆解为分别针对冷、热数据的两个子请求。
- 针对冷数据的请求, 由"离线层"批量完成计算,其结果由"近线层"缓存并提供快速查询。
- 针对热数据的请求, 由"在线层"基于流式算法进行处理。
- 汇总从冷、热数据分别获得的子结果,得到最终的计算结果。
离线层
为了计算可以启动一个小时级的定时任务,每个小时都向前回溯一周的用户行为日志, 统计这个时间窗口内每个视频V的曝光数与点击数。为了加速,我们可以定时跑另一个小时级的批量任务,统计每个小时内每个视频的曝光、点击总数,并保存结果。另外,这些小时级的中间结果也能够被其他上层计算任务所复用,避免重复计算。
以上这些定时扫描日志的批量计算任务,就构成了Lambda架构中的"离线层"。技术上,这些批量计算任务可以凭借Hadoop、Spark、 Flink等大数据框架来完成,而多个任务之间的协同可以由Airflow来完成。
近线层
HDFS是一种擅长批量读写,但随机读写效率极低的存储介质,不利于线上快速读取。为了提高查询速度,我们将离线批量计算的结果导入Cassandra、Redis这样的"键 ~值" (Key-Value, KV) 型数据库。这些起缓存、加速访问作用的KV数据库就构成了Lambda架构中的"近线层"。
在线层
因为HDFS只支持批量读写,所以用户行为从发生到被记录在HDFS上,之间存在着小时级别的延时。用户最新的行为都未能体现在离线批量计算的结果中。
"在线层"正是为了弥补上这块短板。这一层凭借Storm、Flink等流式计算框架,对接用户的行为数据流,不等数据落地,就直接对它们进行分析计算,计算结果也缓存在Redis这样支持随机读写的数据库中,方便线上查询。
推荐系统的特征工程
好的特征工程能够将数据处理得更加适配模型,能够让模型发挥最大的性能
特征工程的必要性
- DNN万能函数模拟器并不可靠,输入数据未经处理会影响DNN性能的发挥
- DNN的自动化特征工程对于大量的数据存在耗时等问题
特征提取
这一部分会介绍如何对各类不同的特征提取的方法
物料特征
- 物料自身属性
- 在视频推荐场景下,视频的作者、作者等级、作者粉丝数、投稿栏目、视频标题与简介、上传时间、 时长、清晰度等信息,都属于物料属性。
- 在电商场景下,商品标题与简介、封面图片、所属商铺、商铺等级、品牌、价格、折扣、物料方式、上架时间等信息,都属于物料属性。
- 物料的唯一标识(tem ID)也是重要的特征(高维、稀疏)Item ID当成特征能够在物料侧提供最个性化的信息。
- 物料的类别与标签
所谓物料的静态画像,是指不依赖用户反馈,只通过分析物料内容就能获得的物料的类别、标签等信息。
可以用"自然语言处理" (Natural Language Processing, NLP) 算法,比如BERT[5], 分析物料的标题、摘要、评论等。如果是文章还可以分析正文,如果是视频还可以分析字幕。
我们可以用"计算机视觉" (ComputerVision, CV) 算法,比如CNN模型[6], 分析物料的封面,或者视频的关键帧。内容分析的结果就构成了物料的静态画像。
基于内容的Embedding
利用CNN或BERT之类的模型,从一篇文章、一个视频中提炼出几个标签,其结果是超级稀疏的。
这回拿模型的某一层的输出,当成物料特征,喂入上层模型。尽管这个向量不如那几个标签好理解,但是它有32位或64位那么长,里面蕴含的信息要比几个标签丰富一些。
- 物料的动态画像
物料的动态画像,指它们的后验统计数据,反映了物料的受欢迎程度,是物料侧最最重要的特征。物料的动态画像可以从以下两个维度来进行刻画:
时间粒度:全生命周期、过去一周、过去1天、过去1小时
统计对象: CTR、平均播放进度、 平均消费时长、排名
用户给物料反向标签
将消费过某个物料的用户身上的标签,传递积累到这个物料身上,丰富物料画像。
比如一篇关于某足球明星八卦绯闻的文章,由于该球星的名字出现频繁,NLP算法可能会给它打上"体育"标
签。但是后验数据显示,带"体育"标签的用户不太喜欢这篇文章,反而带"娱乐"标签的用户更喜欢,显然这
篇文章也应该被打上"娱乐"的标签。类似的,给物料打上诸如"文青喜欢的电影榜第3名"、或者"数码迷最喜
欢的手机"这样的反向标签,都包含了非常重要的信息,能够帮助提升模型性能。
用户特征
- 用户的静态画像
静态画像就是人口属性(e.g., 性别、年龄、职业、籍贯)、用户安装的APP列表等比较稳定的数据信息。
由于互联网大厂的主打APP的存量老用户居多,行为丰富,能够提供足够的训练数据,因此在大厂的实践中还是非常喜欢拿User ID当特征的。
- 用户的动态画像
用户的动态画像就是从用户的历史行为中提取出来的他的兴趣爱好。
最简单直接的动态画像就是,将用户一段时间内用户交互过的物料的Item ID按时间顺序组成的集合。将这个集合扔进模型,让模型自动从中提取出用户兴趣。最简单的提取方式无非就是将每个ltemID先Embedding,再把多个Embedding聚合(也称"池化", Pooling,比如采用加和或平均)成一个向量,这个向量就是用户兴趣的抽象表达。
至于可以统计哪些指标来反映用户兴趣,我们可以从以下6个维度展开,做到不重不漏:
- 用户粒度,可以是单个用户,也可以是一群用户。针对一个用户群体的统计,有利于新用户冷启。
- 时间粒度,比如最近的100次曝光,再比如过去1小时、1周、1月。
- 物料属性,比如视频的一二级类别、标签、作者,再比如商品的分类、品牌、店铺、价位。
- 动作类型,可以是正向的,比如点击、点赞、转发等,也可以是负向的,比如忽略、点踩。
- 统计对象,比如次数、时长、金额等。
- 统计方法,比如加和、求平均、计算各种比例等。
通过以上6个维度的交叉,我们可以构造出一系列的统计指标来反映用户在各个时间跨度、各个维度上的兴趣
交叉特征
DNN并非万能,其交叉能力也有限;另一方面,手动交叉的特征犹如加工好的食材,个中信息更容易被模型消化吸收。因此,在推荐模型的深度学习时代,交叉特征依然大有可为,值得重视。
在具体交叉方式上,又有做"笛卡尔积"与做"内积"两种方式。
- 笛卡尔积交叉
笛卡尔交叉就是将两个Field内的Feature两两组合,组成一个新的Field。 比如用户感兴趣的电影类别有{"动作片"、科幻片"},而当前候选物料的标签是("施瓦辛格"、"终结者" 、"机器人"}, 这两个Field做笛卡尔交叉的结果就是{"动作片+施瓦辛格",“动作片 +终结者","动作片+机器人", "科幻片+施瓦辛格",科幻片+终结者","科幻片+机器人"},显然"动作片+施瓦辛格"、"科幻片+机器人"都是非常强烈的信号,有助于模型判断用户与物料间的匹配程度。
- 内积交叉
点积,即选定一个画像维度(比如标签、分类),将用户在这个维度上的兴趣,和物料在这个维度上的属性,想像成两个稀疏向量,这两个向量做点积结果反映出用户和物料在这个画像维度上的匹配程度。
点积结果越大,说明用户与候选物料在这个维度上越匹配。
比如用户感兴趣的标签是Tagsuser ={"坦克": 0.8, "足球": 0.4, "二战": 0.6, "台球": -0.3}, 每个标签后面的数字表示用户对这个标签的喜爱程度,可以拿用户在这个标签上的后验指标来表示,而当前候选物料的标签是Tagsitem ={"坦克": 1, "二战": 0.5,"一战": 0.8}。 将Tagsuser与是Tagsitem'做点积,也就是将共同标签对应的分数相乘再相加,结果是1.3,表示用户与当前候选物料在"标签"这个维度上的匹配程度。
偏差特征
- 无法做到绝对公平,没点击不代表不喜欢,点击的未必喜欢,偏差Bias
- Above Click: 只有在点击物料上方的未点击的物料才被当作负样本,没被点击的且不在点击上方的就不放进训练的集合中,就不当成训练样本
- 训练的过程中,会将这个偏差特征不能和正常特征一起喂入DNN,否则会改变排序结果
数据特征处理
处理缺失值
- 训练模型来预测缺失值
比如对于新用户,我们可以构建一个模型,利用比较容易获得的人口属性(比如性别、年龄等)预测新用户对某个内容分类、标签的喜爱程度(比如对某类内容的CTR)。再比如对于新物料,我们可以训练一个模型,利用物料的静态画像(比如分类、标签、品牌、价位)预测它的动态画像(CTR、 平均观看时长、平均销售额等)。
标准化处理
标准化的目的是将不同量纲、不同取值范围的数值特征都压缩到同一个数值范围内,使它们彼此可比。最常用的标准化是z-score标准化
\[x^*=(x-\mu)/\sigma\]
- x是某条样本在特征F的原始取值。
- μ、σ分别是特征F在训练数据集上的均值和标准差。
- x*是某条样本在特征F的标准化结果,称为z-score。
标准化数据
- 目的:将不同量纲、不同取值范围的数值压缩到一个数值范围内
- 标准化z-score方法:\[x^*=(x-\mu)/\sigma\]分别是均值和标准差
数据平滑与消偏
目标:克服小样本的负面影响,提高计算结果的置信区间
推荐系统中经常要计算各种比率作为特征,比如点击率、点赞率、购买率、复购率等。计算这些比率时,我们经常遇到的一个问题就是样本太少,导致计算结果不可信。比如计算一件商品, 只被曝光了一次并被购买了,由此我们就说它的购买率是100%,从而认定它是爆款,应该大力推荐,显然这是站不住脚的。为克服小样本的负面影响,提高计算结果的置信水平,我们可以采用"威尔逊区间平滑":
\[p^*=\frac{p+\frac{z^2}{2n}-z(\frac{p(1-p)}{n}+\frac{z^2}{4n^2})^{1/2}}{1+{2n}\frac{z^2}{n}}\]
- z是一个超参,代表对应某个置信水平的z-score。比如当我们希望计算结果有95%的置信水平时,z应该
等于1.96。p是用简单方法计算出的比率。比如当p代表点击率时,就是拿点击样本数除以曝光样本数。
- \[p^*\]是平滑后的比率。n是样本数量。
分桶离散化
在推荐模型中,使用类别特征具有能更好反映非线性关系、便于存储与计算等多方面优势,因此在实践中,我们更喜欢将实数特征离散成类别特征。
离散方法就是分桶,即将实数特征的值域划分为若干区间,又称为"桶",看实数特征落进哪个桶,就以那个桶的桶号作为类别特征值。比如,某用户在最近一小时看了5个视频,如果用实数特征描述,特征是"最近1小时看的视频数",特征值是5。而如果离散成类别特征,整个特征可以表示成"ast1hour_ 0 _10"这 个字符串,表示该用户在最近1小时看的视频数在0~10之间。
分桶有三种实现方式:
- 等宽分桶:即将特征值域平均划分为了N等份,每份算一个桶。
- 等频分桶:将整个值域的N个分位数(Percentile) 作为各桶的边界,保证落入各个桶的样本个数大致要相等。
- 模型分桶:对实数特征F分桶,。第1阶段,单独拿特征F与目标值拟合一棵简单的决策树。第2阶段才进行分桶,将某个特征中F的实数取值f喂进决策树,f最终落进的那个叶子节点的编号就是f的离散化结果。
类别特征的处理
首先明确一下推荐系统的特征空间:高维、稀疏的特点
增强类别特征表达
- Embedding自动学习并拓展内涵,学习隐语义拓展单个特征的内涵,扩展单个特征的内涵
- 多特征交叉,选择用户多个不同的特征进行交叉一次来增强特征的表达能力,比如“20岁”和“程序员”的特征交叉,“格子衬衫”作为推荐选择
类别特征的高维性
- 数据位的很高,需要上百万的标签,多维特征交叉
- Parameter Server架构缓解高维数据的难度
- 分散了参数存储检索的压力,降低了带宽资源与时间开销
类别特征的稀疏
- FTRL自适应调整学习率,常见的特征受训机会多,罕见特征受训机会少
- DIN模型提出自适应调整正则系数
特征如何表征
- 建立字符串到数字的映射表,缺点在于需要更新维护且对于大规模而言比较难以负担
- 特征哈希Feature Hashing
- Feature Hashing负责将输入的字符串映射成一个[0, N)之间的整数,N是Embedding矩阵的总行数,映射得到的整数代表该类别特征的Embedding在Embedding矩阵中的行号。
- Feature Hashing可以简单理解为,先计算输入的字符串的哈希值,再拿哈希值对Embedding矩阵行数N取余数。当然实际实现要更复杂一些,以减少发生"哈希冲突" (Hash Collision)的可能性。
- 只要Embedding的长度相同,若干Field可以共享一个Feature Hash模块与背后的Embedding矩阵。相比于让各个Field拥有独立的Embedding矩阵,这种共享方式对空间的利用率更高,是大型推荐模型的主流作法。
推荐系统的Embedding
Embedding的必要性
对于推荐系统而言:记忆+拓展
记忆能够处理80%的需求,但是个性化太弱(评分)
- Logistic Regression 能够很好的实现记忆功能,记住一些比较经典的关联标签
- 所有的模式,都依赖人工输入。所以在推荐模型的LR时代,特征程既需要创意,同时也是一项体力活。
- LR本身并不能够发掘出新模式,它只负责评估各模式的重要性。这个重要性是通过大量的历史数据拟合得到的。
- LR (评分卡)模型,强于记忆,但是弱于扩展。、中国顾客来了推饺子,美国客户来了推.火鸡,效果都不错,毕竟LR记性好。但是,当一个中国客户来了,你的推荐系统会给他推荐一只火鸡吗?如果你的推荐系统只有LR,只有记忆功能,答案是:不会。
- 因为<中国人,火鸡>毕竟属于小众模式,在历史样本罕有出现,LR的L1正则直接将打分置为0,从而被从评分卡中剔除。
拓展将细粒度变成粗粒度,借助深度学习的Embedding
- 在训练LR模型的时候,每条样本除了将原来细粒度的概念<春节, 中国人,饺子>和<感恩节,美国人,火鸡>作为特征,也将扩展后的<节日,和节日相关的食物>作为特征,一同喂入LR模型。
- 这样训练后的"评分卡"工作量大,劳神费力。比如饺子、火鸡这两个概念,还能不能从其他角度拆解,从而发现更多的相似性?这就要受到工程师的业务水平、理解能力、创意水平的制约。
- 使用Embedding就是很有必要的
共享/独占Embedding
共享Embedding
所谓共享Embedding,是指同一套Embedding要喂入模型的多个地方,发挥多个作用。共享Embedding的好处有二:
- 能够缓解由于特征稀疏、数据不足所导致的训练不充分。
- Embedding矩阵一般都很大, 复用能够节省存储空间。
再比如,召回模型中的双塔模型例子,
- Item ID Embedding既是重要的物料特征,要喂入ltem Tower;
- 同时,用户行为序列作为最重要的用户侧特征,也是由一系列的Item ID组成, 因此ltem ID Embedding也要喂入User Tower。
如果选择让喂入User Tower和Item Tower的Item ID Embedding共享同一个Embedding矩阵,模型结构如图
独占Embedding
共享Embedding最大的优点,就是缓解因为数据不足而导致的稀疏特征训练不充分的问题。但是各互联网大厂最不缺的就是数据,这时共享Embedding的缺陷就暴露出来,即不同目标在训练同一套Embedding时可能相互干扰。
例子:APP的安装、 启动、卸载,对于要学习的APP Embedding有着不同的要求。理想情况下,"安装"与"启动"两个Field要求APP Embedding能够反映出APP为什么能够招人喜欢,而"卸载"这个Field要能够反映出APP为什么招人烦,所以大厂一般选择让"装/启/卸"三个Field各自拥有独立的Embedding矩阵。
更有甚者,大厂的推荐系统都是多目标的,比如要同时优化点击率、购买率、转发率、.... 等多个目标。有一些重要特征, 在参与不同目标的建模时,也要使用不同的Embedding。
- 不同的任务使用不同的Embedding的方法
- 如果在特征交叉的时候使用的是共享Embedding,会产生相互干扰的情况(不同需求要求不同Embedding)
- FFM算法:每个特征在与不同特征交叉的时候,根据对方特征所属的Field要使用不同的Embedding
- CAN算法:既要使用不同的Embedding进行特征交叉,但是又不想要太多的参数导致训练的难度增加
Parameter Server训练框架
传统的训练方式
传统利用Hadoop/Spark的分布式训练方法忽略了高维稀疏特征空间,直接让master处理会参数量很大,传统的训练方式:
- 将训练数据分散到所有Slave节点。
- Master节点将模型的最新参数广播到所有Slave节点。
- 每个Slave节点收到最新的参数后,用本地训练数据,先前代再回代,计算出梯度并.上传至Master。
- Master节点收集齐所有Slave节点发来的梯度后,平均之,再用平均后的梯度更新模型参数。
- 回到步骤1,开始下一轮训练。
"高维稀疏的特征空间",造成了两个困难:
- 推荐系统的特征动辄上亿、上十亿,每个特征的Embedding是16位、 32位甚至更长,这么大的参数量是一台Master所容纳不下的。
- 每轮训练中,Master节点都要将这么大的参数量广播到各Slave节点,每个Slave还要将相同大小的梯度回传,占据的带宽、造成的时延绝对达不到在线实时训练的需求。
PS训练方式
训练步骤:
PS训练模式是Data Parallelism(数据并行)和Model Parallelism(模型并行)的计算范式的结合,具体的训练流程见如下图:
PS并发策略
- 同步并发(BSP)
各Worker完成自己的本轮计算,将梯度汇报给Server,然后阻塞等待。
Server在收集齐所有Worker上报的梯度后,聚合梯度,用SGD算法更新自己负责的那部分模型参数。
Server通知各Worker解除阻塞。
Worker接到解除阻塞的通知,从Server拉取更新过的模型参数,开始下一轮训练。
这种模式的优点是,多个Worker节点更新Server,上的参数时不会发生冲突,所以分布式训练的效果赞同于单机训练的效果。缺点是,一轮迭代中,速度快的节点要停下来等待速度慢的节点,从而形成了"短板效应",慢节点就能拖累整个集群的计算速度。
- 异步并发(ASP)
ASP在每台worker推送完自己的梯度之和,不用等待其他worker,就可以开始训练下一个batch的数据,没有短板效应
但是由于缺乏同步控制,ASP可 能发生"梯度失效" (Stale Gradient)的问题,从而影响收敛速度。举一个极度简化的例子:
当前Server端 上模型参数的版本是\[\theta_0\],有两个Worker节点,都从Server拉取\[\theta_0\],同时开始一轮训练。
Worker 1的速度比较快,很快训练完本地数据并向Server上报梯度g1。
Server收到g1后,根据SGD算法迭代一 步(步长为\[\lambda\]),将Server端的参数值由\[\theta_0\]更新为\[\theta_1=\theta_0-g_1\]。
此时Worker 2才完成计算并向Server报了自己的梯度g2。
Server收到g2后,如果像\[\theta_2=\theta_1-\lambda g_2\]这样更新模型参数,反而可能损害收敛。这是因为g2是Worker 2基于\[\theta_0\]计算得到的,而Server端的参 数此时已经变成了\[\theta_1\],g2已经失效。
得益于推荐系统中的特征超级稀疏的特点,在一轮迭代中,各个Worker节点的局部训练数据所包含的非零特征,相互重叠得并不严重。多个Worker节点同时更新同一个特征的参数的可能性非常小,所以Server端 的冲突也就没有那么频繁和严重,ASP模式在推荐系统中依然比较常用的。
- 半同步半异步并发(SSP)
半同步半异步(Staleness Synchronous Parallel, SSP) 是BSP与 ASP的折衷方案。SSP允许各Worker节点在一定迭代轮数之内保持异步。如果发现最快Worker节点与最慢Worker节点的迭代步数之差已经超过了允许的最大值,所有Worker都要停下来进行一次参数同步。SSP希望通过折衷,实现"计算效率"与"收敛精度"之间的平衡。
冷启动
推荐系统的冷启问题是指: 针对较少消费记录的新用户、新物料的推荐。声明一下, 本章提到的新用户,既包括那些初次使用APP、没有任何历史行为的纯新用户,也包括那些虽然APP安装了很久、但偶尔使用的低活跃用户。新物料的定义也类似。
冷启是困扰推荐系统的一大难题。
- 一方面, 冷启非常重要。对新用户而言, 现在互联网行业竞争激烈, 拉新、获客成本居高不下, 好不容易拉来的新用户, 如果模型承接做得不好而让其流失掉, 前面的努力也就白白浪费了; 对新物料而言,新物料推荐不出去, 既会让用户失去新鲜感而加速流失, 也会打击创作者的积极性, 不利于建立良好的内容生态。
- 另一方面, 冷启又相当困难。毕竟"巧妇难为无米之炊", 再强大的模型, 没有信息喂进去, 也发挥不出什么作用。而且, 很多经典的推荐算法从根本上就不支持新用户与新物料, 比如Item \[2 \mathrm{Vec\] 对不曾在训练集中出现的物料就无法获得其Embedding, 比如DIN/SIM要对用户历史行为序列做Attention, 在新用户身上也就没有了用武之地。
既然冷启是如此重要且艰巨, 业界涌现出了许多方法来应对这一难题。其中不乏一些简单、经典的策略,比如:
- 给新用户推荐全网最热门的物料。
- 不依赖消费记录, 重视使用基本属性(比如用户的性别与年龄, 物料的分类与标签)。
代表算法
Bandit算法
多臂老虎机问题:本质就是平衡探索和利用
多臂老虎机问题与"冷启动"问题是非常相似的:
- 对于新用户冷启动, 每个新用户就是一台老虎机, 每个兴趣大类(比如:电影、音乐、军事、体育、......) 就是老虎机的一个手柄。向该新用户展示某个兴趣类目下的物料, 相当于拉动某一根手柄。用户的反馈(比如点击)犹如老虎机吐出的金币。我们希望通过有限次试探, 使得到的用户正反馈最大化,也就摸清了用户兴趣,使用户获得了良好的初体验,增强APP对新用户的粘性。
- 对于新物料冷启动, 所有用户组成一台老虎机, 候选新品池中的每个新物料相当于一根手柄。曝光某个新物料相当于拉动一次手柄。我们希望通过有限流量的试探, 找到新品池中最优质的候选物料, 犹如在多臂老虎机中找到那根能吐出最多金币的手柄。
因此,我们可以借鉴MAB问题的成熟算法来解决推荐系统中的冷启动问题。
MAB问题最朴素、最初级的解法就是将N次尝试划分成"探索(Explore)"与"开发(Exploit)"先后两个阶段:
- 先"探索", 也就是将每个手柄都拉动 $\[$ 次。统计 \]{R}(i\[ 为拉动第 \]\[ 根手柄 \]$$ 次得到的平均收益。
- 再"开发", 找到平均收益最大的那根手柄 \[a_{\max }=\operatorname{argmax}_i \bar{R}(i)\],然后将剩余的机会全部用来拉动 \[a_{\max \text { 。 }\]
Epsilon Greedy
UCB算法,每次尝试都选择收益最高的
第 $$$$根手柄的收益上限,如公式所示。
\[U C B(i)=\bar{R}(i)+c \sqrt{\frac{2 \log N}{n_i}}\]
- \[\bar{R}(i\] 表示第 $$$$根手柄的平均收益
- \[\sqrt{\frac{2 \log N}{n_i}\] 表示第 $\[$根手柄的收益的不确定性。 \]\[ 是到目前为止一共尝试的总次数, \]n_\[ 是其中拉动第 \]\[根手柄的次数。可见, \]n_\[ 越小, 即第 \]$$根手柄尝试得越少,其收益的不确定性越高, 也就是潜力越大, 尝试的机会也就应该增加。
- $$$$ 表示"收益均值"与"收益潜力"之间的调节权重, 和Decay Epsilon Greedy一样, c也随时间衰减,后期"探索"应该降低,而主要以"开发"为主。
从公式(8-1)可以看出, 一根手柄的收益上限高, 只有两种可能性:
- 要么是这根手柄的平均收益高。此时, 选择上限最高的手柄, 就是在"开发"。
- 要么是这根手柄的收益潜力高。此时, 选择上限最高的手柄, 就是在"探索"。
Bayesian Bandit
基于Bayesian的MAB问题求解方法如下:
- 假定第 $\[$ 根手柄的平均收益遵循先验概率 \]p({R}(i)$$
- 经过若干次实验, 第 $\[$ 根手柄收到一批反馈 \]D_i={r_1, r_2, , r_{n_i}$$
- 根据Bayes公式, 第 $\[$ 根手柄的平均收益的后验概率 - \]p({R}(i) D_i) p(D_i {R}(i)) p({R}(i)$$
- 此时让我们选择手柄时, 我们只需要从各手柄收益的后验概率中随机采样一个数字, 然后选择采样数字最大的那根手柄去拉动即可。
当各手柄的收益非 0 即 1 (这一点非常适用于推荐场景, 比如点击与否)时, 我们可以用Bernoulli分布来描述。而这个Bernoulli的均值(即每根手柄的平均收益)可以用Bernoulli分布的共轭分布Beta分布来描述,好处是先验分布与后验分布都遵循同样的形式,方便Bayes公式的计算。这种Bayesian Bandit算法被称为 Thompson Sampling, 可用于试探新用户的兴趣分布, 如代码代码 8-3所示。
- 将每个新用户设想成一台老虎机, 假设一共有K个兴趣分类(比如:军事、历史、电影、音乐、......),相当于每个新用户的老虎机有 $$$$ 个手柄可选择。
- 第 2 行:向当前新用户展示第 \[\mathrm{k\] 个兴趣分类的平均收益用Beta分布来描述, 涉及到两个参数 \[\alpha_\] 和 \[\beta_\] 。这里将 \[\alpha_\] 和 \[\beta_\] 都初始化为 1 , Beta分布退化成平均分布
- 第7行:选择采样随机数最大的那个兴趣分类。 三根手柄采样到的随机数分别为 [0.1,0.75,0.2],所以应该选择第 2 个手柄代表的兴趣分类。
- 第 8 行:将选中的兴趣分类 \[c_\] 中优质物料推荐给新用户。至于如何获得一个兴趣分类下的优质物料, 方法就多种多样了, 可以通过大数据统计, 也可以让运营团队人工管选。
元学习(Meta Learning)
元学习介绍
首先注意, 喂入Meta Learning的基本数据单位不再是一条条单独的样本, 而是一个个"任务"(Task)。一个Task内部又包含两个数据集, 一个训练集(元学习领域又称Support Set), 一个测试集(元学习领域又称Query Set)。
如图所示, 第一个Task是用于分类水果图片。训练Task 1时, 将其中的训练集 (一批水果图片和标注) 喂入模板\[ F_{\phi}\], 训练得到一个水果分类器 \[f_{\theta_1^*}, \theta_1^\] 是训练得到的最优权重。这个步骤只用到单独一个 Task的数据, 所以被称为"任务内学习"Within-Task Learning。
再将Task 1 中的测试集喂入训练好的模型\[f_{\theta_1^*}\], 计算出在测试集上的损失 \[l_\] 。
同理, 将Task 2 中的训练集 (交通工具的图片和标注) 喂入模板 \[ F_{\phi}\], 训练得到一个交通工具分类器 \[f_{\theta^{2 *}\] , \[\theta_2^\] 是训练得到的最优权重。再拿Task 2 中的测试集喂入 \[f_{\theta^{2 *}\] , 计算得到测试集的损失 \[l_\] 。
假设训练一个批次 (Batch) 有 $$$$ 个任务, 总损失就是所有任务的测试集上的损失之和。这个步骤用到了一个Batch内所有任务的数据, 所以被称为"跨任务学习"(Across-Task Learning)。
\[L_{\text {meta }}(\phi)=\sum_{n=1}^N l_n=\sum_{n=1}^N L\left(D_n^{\text {test }} \mid \theta_n^*\right)\]
- L是所有任务共用的损失函数
- \[l_\] 是第 $$$$ 个任务在其测试集上的损失
- \[D_n^{\text {test }\] 是第 $$$$ 个任务中的测试集(即Query Set)
- \[\theta_n^\] 是第 $$$$ 个任务训练得到的最优参数
MAML算法
Model-Agnostic Meta-Learning (MAML)是一类特殊的元学习, 有两个特点:
模板配置 \[\ph\] 仅限于模型参数 \[\thet\] 的初始值。
损失函数 \[L_{\text {meta }}(\phi\] 对 \[\ph\] 可导, 从而可以通过 SGD的方式求解出最优 \[\ph\] , 也就是最优的 \[\thet\] 初值。
提出MAML是为了解决"小样本训练"(Few-Shot Learning)的问题, 也就是新任务没有足够多的数据将模型参数从头训练好。MAML的解决思路是:
通过若干组任务(比如Task 1 是分辨不同水果, Task 2 是分辨不同的交通工具), 学习出一套高质量的参数初值 \[\ph\]
当面对一个新任务(比如分辨不同动物时)时, 由这段高质量的参数初值 \[\ph\] 出发, 只需要经过少量样本的迭代, 就能达到适合新任务的最优参数 \[\theta^\], 从而解决了新任务样本不足的问题。
具体解法上, 尽管理论上从初值 \[\ph\] 出发, 需要经过多轮训练迭代才能得到最优参数 \[\theta^\] 。但是, 从减少训练样本数的实际目标出发, 我们假设初值 \[\ph\] 只经过一次梯度下降就得到最优参数 \[\theta^\], 如公式所示。
\[\theta_n^*=\phi-\alpha \frac{\partial L\left(D_n^{\text {train }} \mid \phi\right)}{\partial \phi}\]
\[\ph\] 是所有任务共享的参数初值
\[\theta_n^\] 是第 $\[$ 个任务的最优模型参数, 假设由 \]$$ 通过一次梯度下降就能得到
L是所有任务共享的损失函数
\[L\left(D_n^{\text {train }} \mid \phi\right\] 是模型以初值 \[\ph\] 为参数, 在第 $\[$ 个任务的训练集 \]D_n^{$$ 上的损失
\[\alph\] 是迭代步长
如何作用于冷启动的推荐系统任务呢?
我们可以得到将MAML应用于推荐场景所要做的第一个改进, 就是修正其应用范围:
对于推荐模型的大部分参数, 包括DNN权重和常规特征的Embedding, 新任务(即新用户/新物料)应该直接复用老任务(即老用户/老物料)已经训练好的,这样既能保证参数的质量,又能节省资源。所以, MAML完全没必要学习这些参数的最优初值。
每个新任务只有ID Embedding是这个任务独有的, 是无法复用老任务的, 希望能够从一个最优初值出发只经过少量数据就快速迭代至最优状态。而这个最优的User ID Embedding初值, 或最优的Item ID Embedding的初值, 是唯一需要MAML学习的模板配置。
优化目标改造
MAML在推荐场景下的唯一任务就是将最优的ID Embedding初值 \[\ph\] 学习出来, 而在冷启的不同阶段发挥着不同作用。以新用户冷启为例:
第1个阶段是Cold-Start(为了和通篇所指的广义冷启相区别, 我称之为"纯冷启"):用户生平第一次向本推荐服务发出请求,预测程序在线上服务的模型的Embedding层找不到该用户User ID对应的 Embedding, 就拿 \[\ph\] 代替喂进模型进行预测。此时, \[\ph\] 直接影响了新用户的初体验。
第2个阶段是Warm-Up(热身):第1个阶段的用户反馈回传至在线学习 (Online Learning) 程序, 训练程序在Parameter Server中查不到新用户User ID对应的Embedding, 就拿 \[\ph\] 当初值, 利用新用户的反馈数据, 通过一次梯度下降就得到了该新用户User ID Embedding的最新值 \[\theta^\] 。 \[\theta^\] 被打到线上, 作为新的 User ID Embedding, 为该用户的第"二"次(理想了一点, 假设在线更新足够及时)请求服务。
对比学习(Contrastive Learning)
对比学习属于自监督学习的一类
一个完整的分类模型可以由"特征编码"(Encoding)与"分 (Classification) 两阶段。
- 特征编码(Encoding)阶段:一张图片可以由一个长度等于 \[H \times W \times \] 的大向量表示, 其中 \[H / W / \] 分别是图片的高/宽/通道个数(比如RGB三色可以理解为 3 个通道)。其中单个维度的信息含量都有限, 而且难免包含噪声。Encoder或是过滤掉原始输入中的噪声, 或是将若干弱信息的原始特征交叉、聚合成一个强信息的特征, 从而将原来 \[H \times W \times \] 的原始特征压缩成一个 $\[$ 维的"有效特征", 其中 \]K H W $$ 。"有效特征"虽然长度变短了, 但是却保留了原始特征中绝大部分的信息, 是原始特征的"精华"。
- 分类 (Classification) 阶段:将前一阶段提取出来的"有效特征",经过简单映射,就得到了最终分类。
常规机器学习中, "特征编码"与"分类"是由一个模型通过端到端学习来完成的。但是由于现在标注稀疏, 我们只好将"特征编码"与"分类"物理拆分成两个独立的模型。
- "特征编码"阶段, 通过"自监督"(Self-Supervised Learning, SSL)方式来学习。所谓"自监督学习", 是指不依赖人工标注, 通过挖掘未标注样本内部存在的结构、关联, 将"特征编码"这个模块训练出来。传统的降噪自编码器(Denoising AutoEncoder),Word2Vec和Transformer中通过句子的一部分预测另一部分, 和这里要讲的对比学习, 都属于"自监督学习"的范畴。
- "分类"阶段, 还是需要通过"监督" (Supervised) 方式来学习。但是由于编码阶段提取出来的"有效特征"的长度已经大大缩短, 所以“分类"模型只需要少量标注数据就能被充分训练, 从而缓解了标注稀疏的问题。
- 我们有一张原始图片 $\[$ 尽管我们很容易知道这是一张狼犬的图片, 但是 \]\[ 没有被标注。模型并不知道,也无需知道 \]$$ 的类别。
- 我们通过一些手段, 从原始图片衍生出多张与其相似的图片, 这个过程被称为"数据增强"(Data Augmentation) 。比如中的数据增强, 就是将原始图片 \[P^{\prime \prime\] 黑白化"得到增强版图片 \[P_{a u\] 。其他对于图片的数据增强方式还包括:旋转、镜像、剪裁等。
- 再从全体图片中随机抽取一张图片 \[P_{r a\] 。假设候选集足够庞大, 我们不太可能再抽到同一类别的图片,比如中就抽到一张雪鸮的图片。当然模型同样无需知道其类别。
- 将原始图片 $\[$ 、增强图片 \]P_{a u\[ 和随机图片 \]P_{r a\[ , 都喂入Encoder进行提炼压缩, 得到三者的"有效特征"向量 \]V 、 {a u\[ 和 \]{r a$$ 。
- 我们计算 \[\boldsymbol{V\] 与 \[\mathbf{V}_{a u\] 之间的相似度 \[s_{+}=\operatorname{Sim}\left(\mathbf{V}, \mathbf{V}_{a u}\right)\], 模型的训练目标是最大化 \[s_{+}\], 即原样本与其增强版在向量空间里, 应该越近越好。
- 我们计算 \[\mathbf{V\] 与 \[\mathbf{V}_{r a\] 之间的相似度 \[s_{-}=\operatorname{Sim}\left(\mathbf{V}, \mathbf{V}_{r a}\right)\], 模型的训练目标是最小化 \[s_{-}\], 即原样本与随机抽取的其他样本在向量空间里, 应该越远越好。
再将训练好的分类器用于小样本学习中
如何将对比学习用于推荐系统
推荐系统中的海量数据标注存在贫富差距的问题,就是所谓的二八法则,少数的物料或者用户拥有了大量的标注信息,但是大量的物料或者用户所拥有的标注信息很少
对比学习在推荐系统的主要作用就是“纠偏”
通过"数据增强", 我们从少数用户/物料衍生出更多样本, 放大少数群体在训练样本中的音量。
对比学习作为辅助任务, 要让模型多见识一些平日里被其忽视的少数人群和小众物料。让平常听惯了"阳春白雪"的模型, 也多多感受一下"下里巴人"。
因为在训练阶段与少数群体都"亲密接触"过了, 被对比学习调教过的模型线上预测时, 会少一份势利,对小众人群与物料友好一些。
既然明确了对比学习的目标是为了Debias, 那么训练时, 我们必须注意以下两点
第一点, 参与对比学习的样本, 和参与主任务的样本, 最好来自不同的样本空间
- 主任务, 需要拟合用户与物料之间的真实互动, 训练数据还是以曝光数据为主, 也就是以老用户、老物料为主。
- 对比学习, 既然是为了放大少数群体样本的影响力, 其训练样本应该以鲜有曝光机会的少数人群和小众物料为主。比如, 越少曝光的用户或物料, 他们的样本被衍生、增强得应该越多; 反之, 就应该少增强或不增强。
第二点, 主任务与对比学习任务之间, 必须共享参数。
- 近年来给我的感觉, 参数共享、结构共享在推荐算法中, 越来越不受待见。比如, 多任务场景下,流行让同一个特征对不同目标, 拥有不同Embedding; 通篇都在讲参数独立性, 同一个特征与不同特征交叉时, 都要使用不同的Embedding。
- 但是, 对于对比学习, 参数共享是必须的。否则, 主模型与对比学习辅助模型, 各学各的, 主模型中的Bias依旧存在, 对比学习学了个寂寞。
区分对比学习和向量化召回
- 首先, 向量召回属于"有监督学习"。U2|召回中, 用户与其点击过的物料在向量空间是相近的。在 121 召回中, 被同一个用户点击过的物料在向量空间中是相近的。这些正样本都来源于用户反馈(标注)。反之,对比学习属于"自监督学习", 不需要用户标注。用户与其增强版本, 物料与其增强版本, 这些正样本都是我们根据一定规则制造出来的。
- 其次, 向量召回重点关注的是负样本。大型推荐系统中的用户反馈源源不断, 正样本从来都不是问题。反之, 对于对比学习, 重点、难点恰恰是如何制造正样本。也就是给定用户或物料, 如何"增强"出与其相似的用户或物料信息。因为推荐模型中的特征, 以类别特征为主, 高维、稀疏且相互关联(比如被一个用户点击过的多个物料之间可能存在时序、因果关系),简单粗暴地"增强",反而降低了产生的正样本的可信度。因此, 阅读将对比学习应用于推荐场景的文章, 重点是看其"数据增强"方法有何创新, 其他方面如负样本策略、模型结构、损失设计往往都是向量召回中的常规套路,无甚新意。
- 最后, 向量化召回是主任务, 比如替用户找到他喜欢的物料, 对推荐效果负直接责任。反之, 对比学习的目的, 仅仅是为了纠正模型对小众用户、冷门物料这些少数派的偏见。对比学习作为辅助任务, 只存在于训练阶段,并不上线,间接影响推荐效果。
推荐系统的多任务多场景
多任务推荐
多任务建模(Multi-Task) , 有时也被称为多目标建模(Multi-Objective)
- 比如视频推荐场景下, 我们推荐出去的结果,既想让用户点击,点击之后又希望观看的时间尽量长,还想让用户多多评论、转发。因此,需要同时建模点击率、观看时长、评论率和转发率这4个目标。
- 比如电商场景下,我们推荐出去的商品,既想让用户多多点击,还希望用户多多下单购买(术语叫转化)。因此,我们要同时建模三个目标:一件商品从曝光到点击的概率(点击率,CTR)、从点击到购买的概率(转化率, CVR)和从曝光到购买的概率(CTCVR)。
为什么不为每个目标单独建模?
这么做太浪费资源,大厂的推荐模型本来就对内存、算力消耗巨大。如果每个目标单独建模,需要将内存、算力的消耗都乘上10,这笔预算恐怕很难批得下来。
其次,用户转化是一个链条,比如先点击,再加入购物车,最后购买。在这个链条越靠后的环节,价值越大,但是可用于训练的正样本也就越少,非常有必要将所有环节放在一起联合训练
为什么不直接建模终极目标?
以电商场景为例,用户最终没有购买(即未转化),并不代表用户就一定不喜欢推荐结果,也很有可能是因为商品价格超出了这名用户的消费能力。如果只以提高CTCVR为唯一目标,APP推荐给这名用户的就都是在他消费能力之内的中低端商品。可能会暂时提高销售额,但是会带来两方面的危害:
- 容易造成用户的审美疲劳,对用户的长期留存不利。
- 也失去给用户"种草"的机会。万一哪一天,用户狠下心来想剁手,APP却推荐不出来高端商品,
也就白白浪费了一次提高销售额的机会。
多任务方法
并发建模
这种模式下,每个目标独立建模,忽略了不同目标之间的因果关系。
- Share Bottom共享底层
- 底层结构比如Embedding层和底层的DNN,为所有任务共享的。
- 每个任务有自己独立的"塔" 结构,Shared Bottom的输出作为每个塔的输入。
这种结构的优点是实现了多任务之间的"知识迁移"。比如,任务A的正样本多,任务B的正样本少。如果任务B单独训练,对"共享底层"的训练不会太充分。而联合任务A与B,数据丰富的任务A能够将"共享底层"训练到一个比较好的状态,让B在此基础上继续训练,事半功倍。
很多时候,不同任务之间的关系,并非我们想像的那般相辅相成。不同任务对底层共享参数的梯度方向存在分歧,虽说还不至于南辕北辙、相互拆台,但也做不到"心往一块想,劲往一处使"。
- Multi-gate Mixture-of-Experts
沿着拆解共享部分的思路,Mixture-of-Experts (MoE) 将"共享底层"拆分成若干小型DNN,每个DNN称为一个Expert,再由一个门控网络" (Gate) 控制每个Expert对某个任务的参与程度。
MoE中第 $$$$ 个目标的预测值, 如下所示。
\[y_k=h_k\left(\sum_{i=1}^n g(\mathbf{x})_i \operatorname{Expert}_i(\mathbf{x})\right)\]
- \[\mathrm{x\] 是输入的特征向量, \[y_\] 是根据计算出的第 \[\mathrm{k\] 个目标的预测值。
- 一共创建了n个Expert, Expert \[t_\] 代表第 \[\mathrm{i\] 个Expert的网络模型。
- \[\mathrm{g\] 代表门控模型, \[g(\mathbf{x}\] 将输入特征映射成一个 $\[$ 维长的数组, \]g()_\[ 是其中的第 \]\[ 位, 表示第 \]\[ 个Expert的权重。具体实现上, \]$$ 就是一个普通的多层全链接网络 (MLP), 其最后一层使用Softmax做激活函数, 使各Expert的权重之和等于 1 。
- \[h_\] 代表第 $\[$ 个任务的Tower结构(图 7-2中的Tower A和Tower B), 喂入 \]h_$$ 的是各个Expert输出的加权和。
Multi-gate Mixture-of-Experts (MMoE) 在MoE的基础上, 进一步拆解, 结构如图所示:
- MoE中只有一个Gate, 替所有任务决定各Expert的权重。
- MMoE中, 每个任务都有自己的Gate, 衡量各Expert对于本任务的重要性。
- Progressive Layered Extraction
- 首先, 对模型的共享部分继续拆解。在MMoE中, 所有Expert为所有任务所共享。而在PLE中, 将所有 Exper划分为"任务独占"(Task Specific)和"任务共享"(Task Shared)两大类,前者只参与单一任务,后者参与所有任务。比如图7-5中, "Experts A"中的所有Experts, 只参与对任务A的建模。而建模任务 A,是由"Experts A"与"Experts Shared"中的所有Experts共同参与的。
- 其次, MMoE中只有一层Experts, Experts之间的交互比较弱。而PLE中引入了多层Experts, Experts之间的交互层层递进、深化。
PLE的第 $\[$ 层要输出 \]N+$$ 个向量, 如公式(7-3)所示
\[\left[\mathbf{x}_1^k, \ldots, \mathbf{x}_N^k, \mathbf{x}_s^k\right]\]
- $$$$ 是所有任务的个数
- \[\mathbf{x}_t^k, t \in[1, \ldots, N\] 表示第 $\[$ 层对任务 \]$$ 的建模结果
- \[\mathbf{x}_s^\] 表示第 $$$$ 层对共享信息的建模结果
在第 \[\mathrm{k\] 层建模时, 首先将下层的输出喂入本层的各Experts
\[\begin{aligned} & E O_t^k=\left[E_{t, 1}^k\left(\mathbf{x}_t^{k-1}\right), \ldots, E_{t, m_t}^k\left(\mathbf{x}_t^{k-1}\right)\right], t \in[1, \ldots, N] \\ & E O_s^k=\left[E_{s, 1}^k\left(\mathbf{x}_s^{k-1}\right), \ldots, E_{s, m_s}^k\left(\mathbf{x}_s^{k-1}\right)\right] \end{aligned}\]
- \[\mathrm{x}_t^{k-1\] 是第 \[k-\] 层对任务 $$$$ 的建模结果
- \[E O_t^\] 表示第 $\[$ 层任务 \]$$ 独占的那些Experts的输出向量的集合
- \[E_{t, i}^\] 代表第 $\[$ 层任务 \]\[ 独占的第 \]$$ 个Expert
- \[m_\] 是任务 $$$$ 独占的所有Experts的个数
- \[\mathbf{x}_s^{k-1} 、 E O_s^k 、 E_{s, i}^k 、 m_\] 代表类似含意, 只不过是针对共享信息的
最终参与第 $\[$ 层任务 \]\[ 建模的是 \]E O_t^\[ 和 \]E O_s^\[ 中的所有 \]m_t+m_\[ 个Experts。先计算这些Experts的权重, 如公式 \](7-5$$ 所示。
\[\mathbf{C}_t^k=G_t^k\left(\mathbf{x}_t^{k-1}\right) \in R^{m_t+m_s}\]
- \[G_t^\] 是第 $\[$ 层针对任务 \]\mathrm{t$$ 的门控函数
- \[\mathbf{C}_t^\] 是一个长度为 \[m_t+m_\] 的数组, 表示对参与第 $\[$ 层任务 \]\[ 建模的是 \]E O_t^\[ 和 \]E O_s^$$ 中所有Experts的权重。
然后, 将权重与Experts的输出加权加和, 得到第 $\[$ 层对任务 \]$$ 的建模结果 \(\mathbf{x}_t^k\), 如公式(7-6)所示, 其中符号"||"表示将两个数组拼接一起。
\[\mathbf{x}_t^k=\sum_{i=1}^{m_t+m_s} \mathbf{C}_t^k[i] \times\left(E O_t^k \| E O_s^k\right)[i]\]
第 $\[$ 层对共享信息 \]_s^$$ 的建模, 与对某个特定目标的建模类似, 只不过要让本层所有Experts参与其中。
\[\begin{aligned} \mathbf{C}_s^k & =G_s^k\left(\mathbf{x}_s^{k-1}\right) \in R^{T E} \\ \mathbf{x}_s^k & =\sum_{i=1}^{T E} \mathbf{C}_s^k[i] \times\left(E O_1^k\|\ldots\| E O_N^k \| E O_s^k\right)[i] \end{aligned}\]
串行建模
串行建模主要用于电商场景。如同前面提到的,电商业务需要将三个概率CTR/CVR/CTCVR都预估清楚,在预测时,我们需要对尚未曝光的物料预测包括CVR在内的分数。这样一来,训练数据中的物料与预测时的物料集合存在明显差异,导致"样本选择误差" (Sample Selection Bias, SSB),严重影响模型效果。
- ESMM
为了克服以上样本选择偏差,阿里的Entire Space Multi- task Model (ESMM) 的解决思路是:
- 将CVR与CTR、CTCVR一样,都建模在"曝光样本空间"上(理论上,这些任务都应该建模在粗排结果集上,但是实现起来有难度,所以大家都约定俗成建模在"曝光样本"上了,其中的SSB就忽略不计了)。
- 但是,毕竟"曝光未点击"的样本不符合CVR的定义,因此CVR只能作为隐藏目标,在其他目标被优化的同时,被间接优化。
- 整个模型由"CTR模块"与"CVR模块"组成, 喂入的都是曝光样本
- 图 7-7中的Embedding Layer、Field-wise Pooling、MLP都是常规操作。值得注意的是, CTR模块与CVR 模块的底层Embedding是共享的, 有利于正例丰富的CTR任务向正例稀疏的CVR任务进行"知识迁移"。
- 在分别预测得到"曝光 \[\rightarro\] 点击"概率 \(\mathrm{pCTR}\), 和"点击 \[\rightarro\] 购买"概率pCVR后, ESMM根据条件概率公式, 得到"曝光 \[\rightarro\] 购买"概率 \[p C T C V R=p C T R \times p C V \] 。
- 由于 pCTR和pCTCVR都是建立在"曝光样本空间"上的,可以被直接优化,总损失函数如公式(7-9)所示。注意这里CVR没有被直接优化, 而是作为 \[\mathrm{PCTCVR\] 的组成部分被间接优化。而且CVR也是被全体"曝光数据"训练出来的, 消除了训练与预测两阶段间的"样本选择偏差"。
\[\begin{aligned} L\left(\mathbf{W}_{c t r}, \mathbf{W}_{c v r}\right) & =\sum_{i=1}^N B C E\left(y_i, d n n_{c t r}\left(\mathbf{x}_i ; \mathbf{W}_{c t r}\right)\right) \\ & +\sum_{i=1}^N B C E\left(z_i, d n n_{c t r}\left(\mathbf{x}_i ; \mathbf{W}_{c t r}\right) \times d n n_{c v r}\left(\mathbf{x}_i ; \mathbf{W}_{c v r}\right)\right) \end{aligned}\]
- \[\mathbf{x}_\] 是第涤样本的特征, \[y_\] 代表第 $\[$ 条样本是否点击, \]z_\[ 代表第 \]\[ 条样本是否购买, \]$$ 代表样本总数
- \[d n n_{c t r\] 和 \[d n n_{c v r\] 分别CTR模块与CVR模块, \[\mathbf{W}_{c t r}, \mathbf{W}_{c v r\] 分别代表两模块中要学习的权重
- BCE(Binary Cross Entropy)代表二阶交叉熵函数。
- ESM2
ESM2要预测 4 个概率, 在图 7-10中从左至右分别是:
- "曝光 \[\rightarro\] 点击"的概率, CTR, 记为 \[y_1=P(c=1 \mid v=1)\], 其中 $\[$ 表示曝光, \]$$ 表示点击。
- "点击 \[\rightarro\] 直接行为"的概率, 记为 \[y_2=P(a=1 \mid c=1\] 。其中 $$$$ 表示"直接行为" (Direct Action, DAction), 是指像"加入购物车"、"加入愿望清单"这样与购买强相关的行为。
- "直接行为 \[\rightarro\] 购买"的概率, 记为 \[y_3=P(b=1 \mid a=1)\], 其中 $$$$ 表示购买。
- "其他行为 \[\rightarro\] 购买"的概率, 记为 \[y_4=P(b=1 \mid a=0\] 。
基于以上 4 个概率, ESM2要优化三个目标。最简单的就是"曝光 \[\rightarro\] 点击", 损失函数如公式 \[(7-10\] 所示。
\[L o s s_1=\sum_{i=1}^N B C E\left(c_i, p_i^{c t r}\right)=\sum_{i=1}^N B C E\left(c_i, y_{1 i}\right)\]
- \[c_\] 代表第涤样本是否点击
- \[p_i^{c t r}=y_{1 i\] 是模型预测的第 $$$$ 条样本的CTR
- $$$$ 是所有样本数量, BCE代表二阶交叉熵损失函数
第二个目标是"曝光 \[\rightarro\] 直接行为", 如公式(7-11)和公式(7-12)所示。
\[\begin{aligned} p_i^{\text {ctavr }}= & P\left(a_i=1 \mid v_i=1\right) \\ = & P\left(a_i=1 \mid c_i=1\right) \times P\left(c_i=1 \mid v_i=1\right) \\ = & y_{2 i} y_{1 i} \\ & \text { Loss }_2=\sum_{i=1}^N B C E\left(a_i, p_i^{\text {ctavr }}\right) \end{aligned}\]
- \[p_i^{c t a v r\] 代表模型预测出的第 $\[$ 个样本"曝光 \]$$ 直接行为"的概率
- \[a_\] 代表第 $$$$ 条样本发生了直接行为
第三个目标是"曝光 \[\rightarro\] 购买",如公式(7-13) (7-15)所示。
\[\begin{gathered} p_i^{c v r}=P\left(b_i=1 \mid c_i=1\right) \\ =P\left(b_i=1 \mid a_i=0\right) \times P\left(a_i=0 \mid c_i=1\right)+P\left(b_i=1 \mid a_i=1\right) \times P\left(a_i=1 \mid c_i=1\right) \\ =y_{4 i}\left(1-y_{2 i}\right)+y_{3 i} y_{2 i} \\ p_i^{\text {ctcvr }}=p_i^{c t r} \times p_i^{c v r}=y_{1 i}\left(y_{4 i}\left(1-y_{2 i}\right)+y_{3 i} y_{2 i}\right) \\ \operatorname{Loss}_3=\sum_{i=1}^N B C E\left(b_i, p_i^{c t c v r}\right) \end{gathered}\]
- \[p_i^{c v r\] 代表模型预测出的第 $\[$ 条样本"点击 \]$$ 购买"的概率
- \[p_i^{\text {ctcvr }\] 代表模型预测出的第 $\[$ 条样本"曝光 \]$$ 购买"的概率
- \[b_\] 代表第 $$$$ 条样本是否购买
最终ESM2要优化的目标是三个目标的损失之和,其中 \[w_1 、 w_2 、 w_\] 是三个用于调节损失权重的超参数。和ESMM一样, 这里CVR是作为隐藏目标, 被间接优化。
\[\text { Loss }=w_1 \text { Loss }_1+w_2 \text { Loss }_2+w_3 \text { Loss }_3\]
- 除此之外还有例如ESCM2、知识迁移等等
多场景推荐
多场景推荐,关注的是指使用推荐服务的用户中,存在着差异明显的不同消费模式。
- 同一个视频APP,"单列模式"让用户有"沉浸式"检验,每次只看到当前视频,看不到其他候选视频;而"双列模式"允许用户一次性看到多个候选视频,有更多选择自由。这两种产品模式下的用户行为模式,存在显著差异。
- 一个提供全球服务的APP,不同国家的用户的消费模式,明显不同。
- 同一个APP,不同生命周期的用户差异明显,需要推荐系统有不同的应对策略。对低活跃用户,推荐结果要以热门物料为主;
注意多场景推荐与几个相关概念的异同:
- 多场景推荐vs.多任务/多目标推荐。多场景研究的是如何用一个模型将行为模式有明显差异的不同用户群体都服务好,而无论用户来自哪个群体,模型可能都要预测多个目标。所以,多场景与多目标是相互正交的两个维度
- 多场景推荐vs. 跨场景(跨域)推荐。一般来说,多场景推荐指的是用一套模型来服务所有用户, 而跨场景推荐需要用不同模型来服务不同用户,而多个模型之间存在知识迁移。比如某公司已经有一款图文APP积累了大批活跃用户,现在该公司新推出了一款视频APP。
特征位置
要想模型能够识别出不同场景、不同用户群体并区别对待, 首先要设计出"场景指示" (Scenario Indicator)特征。比如:
- "APP模式"能够区分用户请求是来自"单列模式"还是"双列模式"。
- 为了区分不同国家的用户,国籍、语言应当被纳为特征。
- 为了区分"低活用户"与"高活用户"。"近7天用户活跃天数"、"是否新注册用户"、"用户是否登陆"这些都应该被用作特征。
但是仅仅设计出以上特征, 还是远远不够的。如何将这些特征加入模型, 也大有讲究。本书前面曾经反复强调, "DNN是万能函数模拟器"的神话已经破灭。如果把"场景指示"特征加到DNN底部,让它们的信息"按部就班"层层上传, 恐怕再重要的信息到达顶部时, 也不剩下多少了。另外, DNN的底层往往由许多Field Embedding拼接而成, 动辄上千维是小意思。这时你再新加入一两个"场景指标"特征, "泯然众人矣",恐怕也不会太奇怪。
为解决以上问题, 业界常见的作法是将"场景指示"特征加到离最终目标近一点的地方。
- "场景指示"特征,通过一个非常浅的网络, 得到logit scene
- 其他对场景不敏感的特征,按照常规处理,经过比较复杂的网络,得到logit common
这样做, 使"场景指示"特征对最终预测结果的影响直接有力, 避免自DNN底部层层上传带来的信息损失,更有机会将如此重要的先验知识贯彻到"顶"。
模型结构
多场景推荐模型由两大部分组成:
- 场景共享部分: 需要共享结构和参数来建模来建模多场景之间的共性, 让数据丰富的场景将共享参数充分训练, 借此向数据稀少的场景迁移知识。
- 场景独立部分:各场景也需要独立的结构与参数, 以建模该场景的特殊性。
代表算法:
- Split&Merge:将共享结构和各场景独有的结构串联起来
- HMoE
在普通"串联"结构的基础上, 阿里于2020年提出了Hybrid Mixture-of-Experts(HMoE)结构。HMoE的理论假设是, 对某个样本, 除了其所在场景的模型的打分, 其他场景的模型打分也有借鉴意义。所以, HMoE在"场景独立部分"引入了MoE结构, 每个场景下的模型都相当于一个Expert。一条样本要经过多个Expert打分, 再对各Expert的打分加权相加, 得到最终得分。
- STAR
阿里于2021年提出STAR型结构, 突破了传统的串联模式, 将"场景共享部分"与"场景独立部分"更紧密地"交织"在一起。计算过程如公式(7-37)所示, 网络结构如图7-16所示。
\[\begin{aligned} y_p & =D N N\left(\mathbf{x}_p ; \mathbf{W}_p^*\right) \\ \mathbf{W}_p^* & =\left[\mathbf{W}_{p, 1}^*, \ldots, \mathbf{W}_{p, K}^*\right] \\ \mathbf{W}_{p, i}^* & =\mathbf{W}_{p, i} \otimes \mathbf{W}_i \end{aligned}\]
- 第 $\[$ 个场景的输入 $\mathbf{x}_p$, 经过一个DNN结构, 得到第 \]\[ 个场景的输出 \]y_\[ 。这个DNN的参数是 \]_p^$$ 。
- \[\mathbf{W}_p^*=\left[\mathbf{W}_{p, 1}^*, \ldots, \mathbf{W}_{p, K}^*\right\] 是长度为 $\[$ 的数组, \]_{p, i}^\[ 是DNN第 \]\[ 层的权重, \]$$ 是DNN的总层数。
- \[\mathbf{W}_{p, i}^\] 是由第 $\[$ 个场景独有结构的第 \]\[ 层权重 \]{p, i}\[, 与共享结构的 \]\[ 层权重 \]{i}\[,通过按位相乘 (用 \]$$ 表示)得到。
模型参数
动态权重法
- 把"场景指示"(Scenario Indicator)特征 \[\boldsymbol{z\],喂进"权重生成器"(Weight Generator, WG,生成动态权重向量 \[\mathbf{D W}=W G(\mathbf{z}\] 。
- 再将 \[\boldsymbol{D} \boldsymbol{W\] 变形(Reshape)成一个合适形状的DNN, 记为 \[F_{\mathrm{DW}\] 或 \[F_{W G(\mathbf{z})\]。比如, 假设 \[\boldsymbol{D} \boldsymbol{W\] 的长度是\[640=32 \times 16+16 \times 8\], 所以 \[\boldsymbol{D} \boldsymbol{W\] 可以变形为一个三层的MLP,每层的神经元个数分别为 \[[32,16,8\]。
- 将这个根据"场景指示"特征动态生成的网络\[F_{W G(\mathbf{z})}\],应用于在整个推荐模型的关键位置。
大型推荐系统经常要同时解决"多场景+多目标"的推荐问题, 即不仅一个模型要应对多个场景, 而且在每个场景下还要同时预测多个目标。阿里于2022年提出M2M(Multi-scenario Multi-task)结构,运用"动态权重"模式解决这一问题。
M2M整体上还是遵循了经典的MMoE结构, 只不过在两个关键位置, "评估多个 Expert的重要性的Gate"与"各任务独有的Tower", 采用了根据"场景指示"特征动态生成的权重, 以更好地适应不同场景的特点。