写在前面
本文为王树森老师《小红书推荐系统公开课》的课程笔记
- 课程来源:ShusenWang的个人空间-ShusenWang个人主页-哔哩哔哩视频 (bilibili.com)
- 课程资料:GitHub - wangshusen/RecommenderSystem
由于篇幅较长,分为【上】【下】两篇文章来记录。其中【上】包括推荐系统基础、召回、排序,【下】包括特征交叉、行为序列、重排、物品冷启动、涨指标的方法
【上】部分,内容导航如下:
(一)推荐系统基础
1、推荐系统的基础概念
打开小红书APP,默认打开 “发现” 页面,展示推荐系统分发给你的内容,即用户自己创作的笔记,将它们展示给其他用户,形成陌生人社交的社区
小红书推荐系统的转化流程
推荐系统决定给用户曝光什么内容,用户自己决定是否点击、滑动到底、...
抖音没有曝光和点击,用户下滑一次只能看到一个视频
短期消费指标
反映出用户对推荐是否满意
- 点击率 = 点击次数 / 曝光次数
- 越高说明推荐越精准
- 不能作为唯一优化目标(标题党)
- 点赞率 = 点赞次数 / 点击次数
- 收藏率、转发率同理
- 阅读完成率 = 滑动到底次数 / 点击次数 * 归一化函数(跟笔记长度有关)
- 笔记越长,完成阅读的比例就越低
- 若没有归一化函数,对长笔记会不公平
短期消费指标容易竭泽而渔,用户很快会失去兴趣,不再活跃。故还需关注多样性(用户没看过的话题),有助于提高用户黏性,留住用户,让用户更活跃
北极星指标
衡量推荐系统好坏最重要的指标,根本标准
- 用户规模:日活用户数DAU(只要今天登录了一次小红书就增加了一次DAU)、月活用户数MAU(只要这个月登录了一次小红书就增加了一次MAU)
- 消费:人均使用推荐的时长、人均阅读笔记的数量
- 通常与点击率涨跌一致。若矛盾,应以北极星指标为准,如增加多样性,点击率不一定涨,但时长可能变多
- 发布:发布渗透率、人均发布量
- 激励作者发布通常是由冷启动来负责
实验流程
- 做离线实验不需要把算法部署到产品中,没有跟用户实际交互,没有占用线上流量
- 离线实验结果没有线上实验结果可靠(北极星指标都是线上指标,只能通过线上实验获得)
- AB测试:把用户随机分成实验组和对照组,实验组用新策略,对照组用旧策略,对比两者的业务指标,判断新策略是否会显著优于旧策略。若是,可以加大流量,最终推全
2、推荐系统的链路
推荐系统的目标是从物品的数据库中选出几十个物品展示给用户
召回(几亿->几千)
从物品的数据库中快速取回一些物品
几十条召回通道,每条召回通道取回几十~几百个物品,一共取回几千个物品作为候选集
- 召回通道:协同过滤、双塔模型、关注的作者等
- 融合、去重、过滤(排除掉用户不喜欢的作者、笔记、话题等)
让排序决定该把哪些物品曝光给用户,以及展示的顺序。为了解决计算量的问题,将排序分为粗排和精排(二者非常相似,但精排模型更大,用的特征更多)
粗排(几千->几百)
用规模较小的机器学习模型给几千个物品逐一打分,按照分数做排序和截断,保留分数最高的几百个物品送入精排(也会用一些规则,保证进入精排的笔记具有多样性)
- 排序分为粗排和精排,平衡计算量和准确性
- 召回和粗排是最大的漏斗
精排(几百->几百)
用大规模深度神经网络给几百个物品逐一打分,做排序和截断(截断可选)
- 精排相比粗排用的特征更多,计算量更大,模型打分更可靠
- 把多个预估值做融合(比如加权和)得到最终的分数,分数决定会不会展示给用户,以及展示的位置
重排(几百->几十)
根据精排分数和多样性分数做随机抽样,得到几十个物品。用规则把相似内容打散,并插入广告和运营推广内容,根据生态要求调整排序,即为最终展示给用户的结果
- 重排主要是做多样性抽样(如MMR、DPP)、规则打散、插入广告和运营内容
- 做多样性抽样(比如MMR、DPP),从几百个物品中选出几十个
- 用规则打散相似物品
- 插入广告、运营推广内容,根据生态要求调整排序
整条链路上,召回和粗排是最大的漏斗(候选物品从几亿->几千->几百)
3、AB测试
目的:
- 离线实验的指标有提升,不代表线上实验也会有收益。判断新策略能带来多大的业务指标收益
- 模型中有一些参数,需要用AB测试选取最优参数,如GNN网络的深度可以是1/2/3层
离线实验结果正向,下一步是做线上的小流量AB测试(一般10%用户)
随机分桶
若用户数量足够大,每个桶的DAU/留存/点击率等指标都是相等的
例:召回团队实现了一种GNN召回通道,离线实验结果正向。下一步是做线上的小流量A/B测试,考虑新的召回通道对线上指标的影响。模型中有一些参数,比如GNN的深度取值∈{1, 2, 3},需要用A/B测试选取最优参数
- 1~3桶的GNN深度分别为1~3层,4号桶没有用GNN做召回
- 计算每个桶的业务指标,比如DAU、人均使用推荐的时长、点击率等
- 如果某个实验组指标显著优于对照组,则说明对应的策略有效,如2桶的业务指标与对照组的diff有显著性,说明2层的GNN召回通道是有效果的,值得推全(把流量扩大到100%,给所有用户都使用2层GNN召回通道)
分层实验
目标:解决流量不够用的问题
- 信息流产品的公司有很多部门和团队,都需要做AB测试
- 推荐系统(召回、粗排、精排、重排)
- 用户界面
- 广告
- 如果把用户随机分成10组,1组做对照,9组做实验,那么只能同时做9组实验,无法满足产品迭代需求
分层实验:召回、粗排、精排、重排、用户界面、广告...(例如GNN召回通道属于召回层)
- 同层互斥:GNN实验占了召回层的4个桶,其他召回实验只能用剩余的6个桶
- 两个召回实验不会同时作用到同一个用户上
- 避免一个用户同时被两个召回实验影响,若两个实验相互干扰,实验结果将变得不可控
- 不同层正交:每一层独立随机对用户做分桶,每一层都可以独立用100%的用户做实验
- 一个用户可以同时受一个召回实验和一个精排实验的影响,因为它们的效果不容易相互增强或抵消
互斥 vs 正交:
- 如果所有实验都正交,则可以同时做无数组实验
- 同类的策略(例如精排模型的两种结构)天然互斥,对于一个用户,只能用其中一种
- 同类的策略(例如添加两条召回通道)效果会相互增强或相互抵消。互斥可以避免同类策略相互干扰
- 不同类型的策略(例如添加召回通道、优化粗排模型)通常不会相互干扰,可以作为正交的两层
Holdout机制
- 每个实验(召回、粗排、精排、重排)独立汇报对业务指标的提升
- 公司考察一个部门(比如推荐系统)在一段时间内对业务指标总体的提升
- 取10%的用户作为holdout桶(对照组),推荐系统使用剩余90%的用户做实验,两者互斥
- 10% holdout桶 vs 90% 实验桶的diff(需要对指标做归一化)为整个部门的业务指标收益
- 每个考核周期结束之后,清除holdout桶,让推全实验从90%用户扩大到100%用户
- 重新随机划分用户,得到holdout桶和实验桶,开始下一轮考核周期
- 新的holdout桶与实验桶各种业务指标的diff接近0
- 随着召回、粗排、精排、重排实验上线和推全,diff会逐渐扩大
实验推全
若业务指标的diff显著正向,则可以推全实验。如重排策略实验,取一个桶作为实验组,一个桶作为对照组,实验影响了20%用户,若观测到显著正向的业务收益,则可以推全
- 把重排层实验关掉,把两个桶空出来给其他实验
- 推全时新开一层,新策略会影响全部90%用户
- 在小流量阶段,新策略会影响10%用户,会微弱提升实验桶和hold桶的diff
- 推全后,新策略作用到90%用户上,diff会扩大9倍
- 如ab测试发现新策略会提升点击率9个万分点,小流量实验只作用10%用户上,所以只能把跟holdout桶的diff提升1个万分点,推全后,理论上可以把diff提升到9个万分点,跟ab实验得到的结果一致
反转实验
- 有的指标(如点击、交互)立刻受到新策略影响,有的指标(留存)有滞后性,需要长期观测
- 实验观测到显著收益后需要尽快推全新策略。目的是腾出桶供其他实验开展,或需要基于新策略做后续的开发
- 用反转实验解决上述矛盾,既可以尽快推全,也可以长期观测实验指标
- 在推全的新层中开一个旧策略的桶,可以把反转桶保留很久,长期观测实验指标
- 一个考核周期结束之后,会清除holdout桶,会把推全新策略用到holdout用户上,不影响反转桶;当反转实验完成时,新策略会用到反转桶用户上,实验真正推全,对所有用户生效
- 分层实验:同层互斥(不允许两个实验同时影响同一位用户)、不同层正交(实验有重叠的用户)
- Holdout:保留10%的用户完全不受实验影响,可以考虑整个部门对业务指标的贡献
- 实验推全:扩大到90%流量上,新建一个推全层,与其它层正交
- 反转实验:为了在尽早推全新策略的同时还能长期观测各种指标,在新的推全层上,保留一个小的反转桶,反转桶使用旧策略。反转桶可以保留很久,长期观测新旧策略的diff
(二)召回
1、基于物品的协同过滤(ItemCF)
ItemCF的原理
我喜欢看《笑傲江湖》,《笑傲江湖》与《鹿鼎记》相似,我没看过《鹿鼎记》——> 给我推荐《鹿鼎记》
推荐系统如何知道《笑傲江湖》与《鹿鼎记》相似?
- 基于知识图谱:
- 两本书的作者相同,故两本书相似
- 基于全体用户的行为:
- 看过《笑傲江湖》的用户也看过《鹿鼎记》
- 给《笑傲江湖》好评的用户也给《鹿鼎记》好评
ItemCF的实现
假设点击、点赞、收藏、转发四种行为各1分

逐一计算用户对候选物品的兴趣分数,返回分数高的topn个物品
物品相似度
两个物品的受众重合度越高,两个物品越相似
- 例如,喜欢《射雕英雄传》和《神雕侠侣》的读者重合度很高,可以认为它们相似

上述公式只要是喜欢就看作1,不喜欢就看作0,没有考虑用户喜欢的程度

考虑用户喜欢的程度,如点击、点赞、收藏、转发各自算1分,用户对物品的喜欢程度最多是4分
- 分子表示同时喜欢两个物品的用户v(如果兴趣分数的取值是0或1,那么分子就是同时喜欢两个物品的人数)
- 分母第一项表示用户对物品i1的兴趣分数,关于所有用户求连加,然后开根号
- 把一个物品表示成一个向量,向量每个元素表示一个用户,元素的值就是用户对物品的兴趣分数,两个向量的夹角的余弦就是这个公式

ItemCF的基本思想:根据物品的相似度做推荐
- 如果用户喜欢物品Item1,而且物品Item1和Item2相似
- 那么用户很可能喜欢物品Item2
预估用户对候选物品的兴趣:
计算两个物品的相似度:
- 把每个物品表示为一个稀疏向量,向量每个元素对应一个用户
- 相似度sim就是两个向量夹角的余弦
ItemCF召回的完整流程
为了能在线上实时推荐,必须要事先做离线计算,建立两个索引
事先做离线计算
建立 “用户->物品” 的索引:
- 记录每个用户最近点击、交互过的物品ID
- 给定任意用户ID,可以找到他近期感兴趣的物品列表
建立 “物品->物品” 的索引:
- 计算物品之间两两相似度
- 对于每个物品,索引它最相似的k个物品
- 给定任意物品ID,可以快速找到它最相似的k个物品
线上做召回
- 给定用户ID,通过 “用户->物品” 的索引,找到用户近期感兴趣的物品列表(last-n)
- 对于last-n列表中每个物品,通过“物品->物品” 的索引,找到top-k相似物品
- 对于取回的相似物品(最多有nk个),用公式预估用户对物品的兴趣分数
- 返回分数最高的100个物品,作为推荐结果(即ItemCF召回通道的输出,会跟其他召回通道的输出融合起来并排序,最终展示给用户)
索引的意义在于避免枚举所有的物品
- 记录用户最近感兴趣的n=200个物品
- 取回每个物品最相似的k=10个物品
- 给取回的nk=2000个物品打分(用户对物品的兴趣)
- 返回分数最高的100个物品作为ItemCF通道的输出
用索引,离线计算量大(需要更新2个索引),线上计算量小(不需访问上亿个物品)
如果取回的物品ID有重复的,就去重,并把分数加起来
ItemCF的原理:
- 用户喜欢物品i1,那么用户喜欢与物品i1相似的物品i2
- 物品相似度:
- 不是根据物品的内容判定物品相似,而是根据用户行为
- 如果喜欢i1、i2的用户有很大的重叠,那么i1与i2相似
ItemCF召回通道:
- 维护两个索引:
- 用户->物品列表:用户最近交互过的n个物品
- 物品->物品列表:相似度最高的k个物品
- 线上做召回:
- 利用两个索引,每次取回nk个物品
- 预估用户对每个物品的兴趣分数:
- 返回分数最高的100个物品,作为召回结果
2、Swing召回通道(ItemCF的变种)
与ItemCF非常像,区别就是如何定义物品的相似度
ItemCF:
ItemCF的问题:两篇笔记受众不同,但由于被分享到一个小圈子,导致很多用户同时交互过这两篇笔记。需要降低小圈子用户的权重
Swing模型即给用户设置权重,解决小圈子问题
α是个人工设置的参数;overlap为u1和u2的重叠,重叠大说明两个人是一个小圈子,对相似度的贡献应减小
Swing与ItemCF唯一的区别在于物品相似度
- ItemCF:两个物品重合的用户比例高,则判定两个物品相似
- Swing:额外考虑重合的用户是否来自一个小圈子
- 同时喜欢两个物品的用户记作集合v
- 对于v中的用户u1和u2,重合度记作overlap(u1, u2)
- 两个用户重合度大,则可能来自一个小圈子,权重降低
3、基于用户的协同过滤(UserCF)
UserCF原理
有很多跟我兴趣非常相似的网友,其中某个网友对某笔记点赞、转发,而我没看过这篇笔记,那么可能给我推荐这篇笔记
推荐系统如何找到跟我兴趣非常相似的网友呢?
- 点击、点赞、收藏、转发的笔记有很大的重合
- 关注的作者有很大的重合
UserCF实现
0代表用户没有看过物品,或对物品不感兴趣

用户相似度
用户有共同的兴趣点,即喜欢的物品有重合

上述公式同等对待热门和冷门的物品,需降低热门物品的权重
物品越热门, 越大,分子(即物品的权重)越小
UserCF的基本思想:
- 如果用户user1跟用户user2相似,而且user2喜欢某物品,那么用户user1也很可能喜欢该物品
预估用户user对候选物品item的兴趣:
计算两个用户的相似度:
- 把每个用户表示为一个稀疏向量,向量每个元素对应一个物品
- 如果用户对物品不感兴趣,向量元素为0;若感兴趣,元素为1,或1除以物品的热门程度