寫在前面
本文為王樹森老師《小紅書推薦系統公開課》的課程筆記
- 課程來源: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個索引),線上計算量小(不需訪問上億個物品)
ItemCF召回通道的輸出,會跟其他召回通道的輸出融合起來做排序
如果取回的物品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除以物品的熱門程度
- 相似度sim就是兩個向量夾角的余弦
UserCF召回的完整流程
為了能在線上實時推薦,必須要事先做離線計算,建立兩個索引
事先做離線計算
建立 “用戶->物品” 的索引:
- 記錄每個用戶最近點擊、交互過的物品ID
- 給定任意用戶ID,可以找到他近期感興趣的物品列表
建立?“用戶->用戶” 的索引:
- 對于每個用戶,索引他最相似的k個用戶
- 給定任意用戶ID,可以快速找到他最相似的k個用戶
線上做召回
- 給定用戶ID,通過??“用戶->用戶” 的索引,找到topk相似用戶
- 對于每個top-k相似用戶,通過?“用戶->物品” 的索引,找到用戶近期感興趣的物品列表(last-n)
- 對于取回的nk個相似物品,用公式預估用戶對每個物品的興趣分數
- 返回分數最高的100個物品,作為召回結果
若取回的物品有重復的,去重,并把分數加起來
UserCF的原理:
- 用戶u1跟用戶u2相似,而且u2喜歡某物品,那么u1也可能喜歡該物品
- 用戶相似度:
- 如果用戶u1和u2喜歡的物品有很大的重疊,那么u1和u2相似
(分母是做歸一化,讓相似度分數介于0~1之間)
UserCF召回通道:
- 維護兩個索引:
- 用戶->物品列表:用戶近期交互過的n個物品
- 用戶->用戶列表:相似度最高的k個用戶
- 線上做召回:
- 利用兩個索引,每次取回nk個物品
- 預估用戶user對每個物品item的興趣分數:
- 返回分數最高的100個物品,作為召回結果
--------------------------------------------- 分割線,上面都是協同過濾召回 ---------------------------------------?
4、離散特征處理
- 協同過濾召回(以上內容)
- 向量召回(之后內容)
離散特征
如性別、國籍、英文單詞、物品ID、用戶ID
處理:
- 建立字典:把類別映射成序號
- 向量化:把序號映射成向量
- One-hot編碼:把序號映射成高維稀疏向量,序號對應位置的元素為1,其他位置元素都是0
- Embedding:把序號映射成低維稠密向量
One-Hot編碼
性別:男、女兩種類別
- 字典:男->1,女->2
- One-hot編碼:用2維向量表示性別
- 未知 -> 0 -> [0, 0]
- 男 -> 1 -> [1, 0]
- 女 -> 2 -> [0, 1]
國籍:中國、美國、印度等200種類別
- 字典:中國->1,美國->2,印度->3,...
- One-hot編碼:用200維稀疏向量表示國籍
- 未知 -> 0 -> [0, 0, 0, 0, ...,? 0]
- 中國 -> 1 -> [1, 0, 0, 0, ...,? 0]
- 美國 -> 2 -> [0, 1, 0, 0, ...,? 0]
- 印度 -> 3 -> [0, 0, 1, 0, ...,? 0]
局限:類別數量太大時,通常不用one-hot編碼
Embedding(嵌入)
把每個類別映射為一個低維的稠密向量
參數數量 = 向量維度 × 類別數量
- 設Embedding得到的向量都是4維的
- 一共有200個國籍
- 參數數量=800
編程實現:TensorFlow、Pytorch提供Embedding層
- 參數以矩陣的形式保存,矩陣大小是向量維度×類別數量
- 輸入是序號,比如美國的序號是2
- 輸出是向量,比如美國對應參數矩陣的第2列
兩者聯系

離散特征處理:One-hot編碼、embedding
類別數量很大時,用embedding,如:
- word embedding
- 用戶ID embedding
- 物品ID embedding
------------------------------------------------- 分割線,下面都是向量召回 ------------------------------------------
5、矩陣補充(Matrix Completion)、最近鄰查找
矩陣補充(Matrix Completion)是向量召回最簡單的一種,已經不太常用,是為了幫助理解雙塔模型
矩陣補充模型結構
輸出是一個實數(用戶對物品興趣的預估值),越大表示用戶對物品越感興趣
- 左邊的Embedding Layer:矩陣列的數量為用戶數量,每一列都是向量a這樣的對用戶的表征
- 右邊的Embedding Layer
- 兩個Embedding Layer不共享參數
矩陣補充模型訓練
(1)基本想法:

?
(2)數據集:是很多三元組的集合
(3)訓練:讓模型的輸出擬合興趣分數
- (u, i, y)三元組 是訓練集中的一條數據,表示 用戶u 對 物品i 的 真實興趣分數y
- 對目標函數求min,可以用隨機梯度下降,每次更新A和B的一列
(4)直觀解釋:
拿綠色位置的數據訓練模型,預估所有灰色位置的分數。給定一個用戶,選出用戶對應行中分數較高的物品,推薦給用戶?
矩陣補充的缺點?
矩陣補充在實踐中效果不好,在工業界不work
- 缺點1:僅用ID Embedding,沒利用物品、用戶屬性
- 物品屬性:類目、關鍵詞、地理位置、作者信息
- 用戶屬性:性別、年齡、地理定位、感興趣的類目
- 雙塔模型可以看做矩陣補充的升級版,會結合物品屬性和用戶屬性
- 缺點2:負樣本的選取方式不對
- 樣本:用戶-物品的二元組,記作(u, i)
- 正樣本:曝光之后,有點擊、交互(正確的做法)
- 負樣本:曝光之后,沒有點擊、交互(錯誤的做法)
- 缺點3:做訓練的方法不好
- 內積 <au, bi> 不如余弦相似度
- 用平方損失(回歸),不如用交叉熵損失(分類)
矩陣補充線上服務
模型存儲:
- 訓練得到矩陣A和B。A的每一列對應一個用戶,B的每一列對應一個物品
- 把矩陣A的列存儲到key-value表,key是用戶ID,value是A的一列。給定用戶ID,返回一個向量(用戶Embedding)
- 矩陣B的存儲和索引比較復雜,不能簡單地用key-value存儲
線上服務:
近似最近鄰查找(Approximate Nearest Neighbor Search)
避免暴力枚舉(暴力枚舉計算量正比于物品數量),近似最近鄰查找的結果未必最優,但不會比最優差多少
支持最近鄰查找的系統:
- 系統:Milvus、Faiss、HnswLib,等
- 衡量最近鄰的標準:
- 歐式距離最小(L2距離)
- 向量內積最大(內積相似度):矩陣補充用的就是這個
- 向量夾角余弦最大(cosine相似度):最常用,即夾角最小
有些系統不支持余弦相似度,可以把所有向量都做歸一化,讓它們的二范數等于1,那么內積就等于余弦相似度

?
在做線上服務前,把數據劃分為很多區域:
- cos相似度,劃分結果為扇形
- 歐式距離,劃分結果為多邊形
劃分后,每個區域用一個向量表示,這些向量的長度都是1。劃分區域后,建立索引,key為每個區域的向量,value為區域中所有點的列表
首先計算用戶a與索引向量的相似度,通過索引找出該區域內的所有點,再計算用戶a與該區域所有點的相似度
矩陣補充:
- 把物品ID、用戶ID做embedding,映射成向量
- 兩個向量的內積<au, bi>作為 用戶u 對 物品i 興趣的預估
- 讓<au, bi>擬合真實觀測的興趣分數,用回歸的方式學習模型的embedding層參數
- 矩陣補充模型有很多缺點,效果不好。工業界不用矩陣補充,而是用雙塔模型
線上召回:
- 把用戶向量a作為query,查找使得<a, bi>最大化的物品i(內積最大的 top k 物品i)
- 暴力枚舉速度太慢,實踐中用近似最近鄰查找
- Milvus、Faiss、HnswLib等開源向量數據庫支持近似最近鄰查找
6、雙塔模型(DSSM)
雙塔模型可以看做是矩陣補充模型的升級
模型結構
用戶離散特征:
- 所在城市、感興趣的話題等:Embedding,不同的離散特征要用不同的Embedding層得到向量
- 性別:對類別數量很少的離散特征,用One-hot編碼即可,可以不做Embedding
用戶連續特征:年齡、活躍程度、消費金額
- 歸一化:讓特征的均值為0,標準差為1
- 有些長尾分布的連續特征需要特殊處理,如取log、分桶
神經網絡可以是簡單的全連接網絡,也可以是更復雜的深度交叉網絡?
物品的表征類似處理
?
雙塔模型使用了ID之外的多種特征作為輸入,輸出介于-1~1的余弦相似度(余弦相似度相當于先對兩個向量做歸一化,再求內積,比內積更常用)
正負樣本的選擇
- 正樣本:歷史記錄顯示用戶喜歡的物品。如用戶點擊的物品
- 負樣本:用戶不感興趣的物品
- 沒有被召回的?
- 召回但是被粗排、精排淘汰的?
- 曝光但是未點擊的? —— 不能作為召回的負樣本,可以作為排序的負樣本
訓練方法
pairwise:Jui-Ting Huang et al. Embedding-based Retrieval in Facebook Search. In KDD, 2020.(Facebook)
listwise:
Xinyang Yi et al. Sampling-Bias-Corrected Neural Modeling for Large Corpus ItemRecommendations. In RecSys, 2019.(YouTube)
- pointwise:獨立看待每個正樣本、負樣本,做簡單的二元分類。即把正樣本和負樣本組成一個數據集,在數據集上做隨機梯度下降訓練雙塔模型
- pairwise:每次取一個正樣本、一個負樣本,組成一個二元組,損失函數為triplet hinge loss或triplet logistic loss
- listwise:每次取一個正樣本、多個負樣本,組成一個list,訓練方式類似于多元分類
pointwise訓練
- 把召回看做二元分類任務
- 對于正樣本,鼓勵 cos(a,b) 接近+1
- 對于負樣本,鼓勵 cos(a,b) 接近-1
- 控制正負樣本數量為1:2或者1:3(行業經驗)
pairwise訓練
輸入一個三元組,用戶的特征向量為a,兩個物品的特征向量為b+和b-。兩個物品塔共享參數
讓用戶對正樣本的興趣分數盡量高(最好接近+1),對負樣本的興趣分數盡量低(最好接近-1)
(1)Triplet Hinge Loss:孿生網絡?

?
(2)Triplet Logistic Loss:讓cos(a, b-)盡量小,讓cos(a,b+)盡量大??

listwise訓練
最大化正樣本的余弦相似度,最小化負樣本的余弦相似度?
?
不適用于召回的模型
上圖為粗排/精排的模型,不能應用于召回
- 前期融合,在進入全連接層之前就把特征向量拼起來了 —— 排序模型
- 雙塔模型屬于后期融合,兩個塔在最終輸出相似度時才融合 —— 召回模型
前期融合的模型不適用于召回,否則要把所有物品的特征挨個輸入模型,預估用戶對所有物品的興趣,無法使用近似最近鄰查找來加速計算。通常用于排序(從幾千個物品中選出幾百個)
雙塔模型:
- 用戶塔、物品塔各輸出一個向量
- 兩個向量的余弦相似度作為興趣的預估值,越大,用戶越有可能對物品感興趣
- 三種訓練方式:
- pointwise:每次用一個用戶、一個物品(可正可負)
- pairwise:每次用一個用戶、一個正樣本、一個負樣本。訓練目標是最小化triplet loss,即讓正樣本余弦相似度盡量大,負樣本余弦相似度盡量小
- listwise:每次用一個用戶、一個正樣本、多個負樣本。訓練時用softmax激活+交叉熵損失,讓正樣本余弦相似度盡量大,負樣本余弦相似度盡量小
正負樣本
選對正負樣本的作用 > 改進模型結構
正樣本:
- 正樣本:曝光而且有點擊的用戶-物品二元組(用戶對物品感興趣)
- 問題:二八法則,少部分物品占據大部分點擊,導致正樣本大多是熱門物品。拿過多熱門物品當正樣本,會對冷門物品不公平,讓熱門更熱、冷門更冷
- 解決方案:過采樣冷門物品,或降采樣熱門物品
- 過采樣(up-sampling):一個樣本出現多次
- 降采樣(down-sampling):一些樣本被拋棄。如以一定概率拋棄熱門物品,拋棄的概率與樣本的點擊次數正相關
負樣本:用戶不感興趣的物品,或鏈路上每一步被淘汰的物品
簡單負樣本:未被召回物品
(1)全體物品:從全體物品中做非均勻抽樣,作為負樣本
未被召回的物品,大概率是用戶不感興趣的。幾億個物品里只有幾千個被召回,故未被召回的物品 ≈ 全體物品

使用非均勻抽樣,熱門物品成為負樣本的概率大
(2)batch內負樣本
圖中是一個batch內的樣本。訓練時要鼓勵正樣本的余弦相似度盡量大,負樣本的余弦相似度盡量小
一個物品成為負樣本的概率越大,模型對這個物品打壓就會越狠?

這樣理解Batch內負樣本的修正:p越小,-logp越大,softmax結果s更高。如果是正樣本(s>0.5),那導數會偏小(輸出已經很接近正確標簽,不希望模型做出太大的調整);如果是負樣本,那導數會偏大。就形成了對低頻物品(p小)更強的負樣本的傾向,相當于是放大了冷門負樣本的梯度
-logp算是物品的先驗,模型實際上是非常容易擬合先驗的,所以要debias掉
注意,線上召回時,還是用原本的余弦相似度,不用做這種調整,不用減掉 logpi
困難負樣本:粗排/精排淘汰
- 被粗排淘汰的物品 (比較困難)
- 精排分數靠后的物品 (非常困難)
訓練雙塔模型,其實是對正負樣本做二元分類:
- 全體物品(簡單)分類準確率高
- 被粗排淘汰的物品 (比較困難) 容易分錯
- 精排分數靠后的物品 (非常困難) 更容易分錯
訓練數據
混合幾種負樣本,50%的負樣本是從全體物品隨機非均勻抽樣得到(簡單負樣本),50%的負樣本是沒通過粗排精排的物品,即從粗/精排淘汰的物品中隨機抽樣得到(困難負樣本)
常見的錯誤
選擇負樣本的原理:
- 召回的目標:快速找到用戶可能感興趣的物品,再交給后面的排序逐一甄別
- 全體物品(easy): 絕大多數是用戶根本不感興趣的
- 被排序淘汰(hard): 用戶可能感興趣,但是不夠感興趣。這樣的負樣本做分類時比較難以區分,所以是困難負樣本
- 有曝光沒點擊(不能作為負樣本): 用戶感興趣,可能碰巧沒有點擊【 可以作為排序的負樣本,不能作為召回的負樣本 】
-
正樣本:曝光而且有點擊的用戶-物品二元組
簡單負樣本:
- 全體物品中做非均勻隨機抽樣
- batch內負樣本
困難負樣本:被召回,但是被排序淘汰(跟正樣本很像,做分類會有困難)
錯誤:曝光但是未點擊的物品做召回的負樣本。這類負樣本只能用于排序,不能用于召回
線上服務/線上召回
訓練好的兩個塔,分別提取用戶特征和物品特征
- 向量數據庫存儲物品特征向量和ID的二元組,用作最近鄰查找
- 不要事先計算和存儲用戶向量。當用戶發起推薦請求時,調用神經網絡現算一個特征向量a,然后把向量a作為query,去向量數據庫中檢索查找最近鄰
離線存儲:把物品向量b存入向量數據庫
- 完成訓練之后,用物品塔計算每個物品的特征向量b
- 把幾億個物品向量b存入向量數據庫,比如Milvus、Faiss、HnswLib
- 向量數據庫建索引(把向量空間劃分成很多區域,每個區域用一個向量表示),以便加速最近鄰查找?
線上召回:查找用戶最感興趣的k個物品?
- 給定用戶ID和畫像,線上用神經網絡算用戶向量a
- 最近鄰查找:把向量a作為query,調用向量數據庫做最近鄰查找,返回余弦相似度最大的k個物品,作為召回結果
事先存儲物品向量b,線上現算用戶向量a,為什么?
- 每做一次召回,用到一個用戶向量a,幾億物品向量b(線上算物品向量的代價過大)
- 用戶興趣動態變化,而物品特征相對穩定 (可以離線存儲用戶向量,但不利于推薦效果)
訓練好雙塔模型之后,在開始線上服務之前,先要做離線存儲,在向量數據庫建好索引之后,可以做線上召回
跟ItemCF、Swing、UserCF等召回通道的結果融合
模型更新
全量更新(天級別)
今天凌晨,用昨天全天的數據訓練模型
- 在昨天模型參數的基礎上做訓練(不是隨機初始化)
- 用昨天全天的數據(要先random shuffle打亂,然后打包成TFRecord)訓練1個epoch,即每條數據只過一遍
- 訓練完成后,發布新的用戶塔神經網絡(在線上實時計算用戶向量,作為召回的query)和物品向量(存入向量數據庫,向量數據庫會重新建索引,在線上做最近鄰查找),供線上召回使用
- 全量更新對數據流、系統的要求比較低
- 不需要實時的數據流,對生成訓練數據的速度沒有要求,延遲一兩個小時也沒關系。只需要把每天的數據落表,在凌晨做個批處理,把數據打包成TFRecord格式即可
- 每天只需要把神經網絡和物品向量發布一次即可
- 只有做全量更新時才更新全連接層
增量更新(分鐘級別)
做online learning更新模型參數,每隔幾十分鐘就要把新的模型參數發布出去,刷新線上的用戶塔Embedding層參數
- 用戶興趣會隨時發生變化
- 實時收集線上數據,做流式處理,實時生成訓練模型用的TFRecord文件
- 對模型做online learning,梯度下降增量更新ID Embedding參數 (不更新神經網絡其他部分的參數,全連接層是鎖住的,不做增量更新;只有全量更新會更新全連接層參數)
- 發布用戶ID Embedding(哈希表),供用戶塔在線上計算用戶向量,可以捕捉用戶最新的興趣點。這個過程會有延遲,但可以被縮短到幾十分鐘
全量 vs 增量
今天凌晨的全量更新,是基于昨天凌晨全量訓練出來的模型,而不是用下面增量訓練出來的模型。在完成這次全量之后,下面增量訓練出來的模型就可以扔掉了?
能否只做增量更新,不做全量更新?
- 小時級數據有偏,統計值跟全天數據差別很大(不同時間段用戶的行為不同);分鐘級數據偏差更大
- 全量更新: random shuffle (為了消除偏差)一天的數據,做 1 epoch 訓練
- 增量更新:按照數據 從早到晚的順序 ,做 1 epoch 訓練
- 隨機打亂 優于 按順序排列數據 , 全量訓練 優于 增量訓練
全量訓練的模型更好,而增量訓練可以實時捕捉用戶的興趣?
雙塔模型:
用戶塔、物品塔各輸出?個向量,兩個向量的余弦相似度作為興趣的預估值,越大越好 三種訓練的方式:pointwise、pairwise、listwise 正樣本:用戶點擊過的物品 負樣本:從全體物品中隨機抽樣 (簡單負樣本)、被排序淘汰的物品 (困難負樣本)召回:
做完訓練,把物品向量存儲到向量數據庫,供線上最近鄰查找 線上召回時,給定用戶ID、用戶畫像,調用訓練好的用戶塔神經網絡 現算用戶向量a 把a 作為query,查詢物品的向量數據庫,找到余弦相似度最高的k個物品向量,返回k個物品ID更新模型:定期全量+實時增量
全量更新: 今天凌晨,用昨天的數據訓練整個神經網絡,做 1 epoch 的隨機梯度下降(每條數據只用一遍) 增量更新:用實時數據訓練神經網絡,只更新 ID? Embedding,鎖住全連接層不更新- 實際的系統:全量更新(每天)?& 增量更新(實時)?相結合。每隔幾十分鐘,發布最新的用戶 ID Embedding,供用戶塔在線上計算用戶向量(好處:捕捉用戶的最新興趣點)
7、雙塔模型+自監督學習
自監督學習:改進雙塔模型的方法,能提升業務指標,目的是把物品塔訓練的更好
雙塔模型的問題
- 推薦系統的頭部效應嚴重:
- 少部分物品占據大部分點擊
- 訓練雙塔模型時用點擊數據作為正樣本,模型學習物品的表征靠的就是點擊行為
- 大部分物品的點擊次數不高
- 高點擊物品的表征學得好,長尾物品的表征學的不好(長尾物品的曝光和點擊次數太少,訓練的樣本數量不夠)
- 自監督學習:對物品做data augmentation,更好地學習長尾物品的向量表征
雙塔模型的訓練(同時訓練用戶塔和物品塔)
用batch內負樣本
- 一個batch內有n對正樣本
- 組成n個list,每個list中有1對正樣本和n-1對負樣本
listwise訓練

讓模型給正樣本打的分數盡量高,給負樣本打的分數盡量低
損失函數:考慮batch內第i個用戶和全部n個物品

糾偏
batch內負樣本會過度打壓熱門物品,造成偏差。如果用batch內負樣本,需要糾偏,使熱門物品不至于被過分打壓。訓練結束,在線上做召回時,還是用原本的余弦相似度?
訓練雙塔模型
- i:第i個用戶
- n:batch內一共有n個用戶
自監督學習(訓練物品塔)
- 雙塔模型的listwise訓練方式,同時訓練用戶塔和物品塔
- 自監督學習訓練物品塔
Tiansheng Yao et al. Self-supervised Learning for Large-scale Item Recommendations. In CIKM, 2021.?【Google】
盡管對應不同的特征變換,但同一物品的兩個向量表征應有較高的相似度?
不同物品的向量表征的分布應盡量分散開?
如何變換物品特征
四種方法:
- Random Mask(隨機選一些離散特征,將它們的值換為default)
- Dropout(僅對多值離散特征生效)
- 互補特征(將物品的多個特征隨機分組,鼓勵兩組特征形成的物品表征向量相似)
- Mask一組關聯的特征(計算互信息來衡量特征兩兩之間的關聯。隨機選一個特征,mask該特征及其最相關的k/2種特征,其余保留)
(1)Random Mask
一個物品可以有多個類目
- 如果不做random mask,正常的特征處理方法是對數碼和攝影分別做embedding,得到兩個向量,再做求和或平均,得到一個向量,表征物品類目
- 如果對類目特征做mask,這個物品的類目特征就變成了default。default表示默認的缺失值,對default做embedding,得到一個向量表征類目。也就是說,做mask后,物品的類目特征直接被丟掉(數碼和攝影都沒了)
(2)Dropout(僅對多值離散特征生效)
多值離散特征:離散特征 + 一個物品可以有多個該特征的取值
random mask是對整個類目特征的取值都丟掉,dropout只丟掉類目特征取值的50%
(3)互補特征(complementary)

正常的做法是把四種特征的值分別做embedding,然后拼起來輸入物品塔,最終得到物品的向量表征
(4)mask一組關聯的特征
特征之間有較強的關聯,遮住一個特征并不會損失太多的信息,模型可以從其他強關聯特征中學到遮住的特征
兩個特征關聯越強,p(u,v)就比較大,它們的互信息就越大
- 好處:效果好,推薦的主要指標比random mask、dropout、互補特征等方法效果更好
- 壞處:方法復雜,實現的難度大,不容易維護(每添加一個新的特征,都需要重新計算所有特征的MI)
如何用變換后的特征訓練模型

?
這里冷熱門物品被抽樣到的概率是相同的(注意跟訓練雙塔的區別。訓練雙塔得到的數據是根據點擊行為抽樣的,熱門物品被抽到的概率大。雙塔抽的是用戶-物品二元組,這里只抽物品)?
考慮batch中第i個物品的特征向量bi',和全部m個物品的特征向量b'':
訓練時希望向量si接近yi,說明物品塔訓練的好,即使做隨機特征變換,對物品的向量表征也影響不大。用yi和si的交叉熵作為損失函數
雙塔模型存在的問題:
- 雙塔模型學不好低曝光物品的向量表征(其實這是數據的問題,而不是雙塔模型的問題。真實推薦系統都存在頭部效應,小部分物品占據了大部分的曝光和點擊)
Google提出一種自監督學習的方法,用在雙塔模型上效果很好(低曝光物品、新物品的推薦變得更準)
- 對一個物品用兩種特征變換,物品輸出兩個特征向量
- 同一物品的兩個不同特征變換得到的向量應該有高相似度
- 不同物品應讓物品的向量表征盡量分散在整個特征空間上
訓練:
對點擊做隨機抽樣,得到n對用戶-物品二元組作為一個batch。這個batch用來訓練雙塔,包括用戶塔和物品塔。根據點擊做抽樣,熱門物品被抽到的概率高 從全體物品中均勻抽樣,得到m個物品作為另一個batch。熱門和冷門物品被抽到的概率是相同的,這個batch用來做自監督學習,只訓練物品塔 做梯度下降使損失函數減小。α決定自監督學習起到的作用
8、Deep Retrieval召回
Deep Retrieval(字節):
Weihao Gao et al. Learning A Retrievable Structure for Large-Scale Recommendations. InCIKM , 2021.TDM(阿里): Han Zhu et al. Learning Tree-based Deep Model for Recommender Systems. In KDD , 2018.
- 經典的雙塔模型把用戶、物品表示為向量,線上做向量最近鄰查找
- Deep Retrieval把物品表征為路徑(path),線上查找用戶最匹配的路徑,從而召回一批物品(類似于阿里的TDM,但更簡單)
Deep Retrieval和TDM都用深度學習,但都不做向量最近鄰查找
索引:關聯路徑和物品
L1~L3表示結構的3層(深度depth),每一層里面有K個節點(寬度width)
路徑就是幾個節點連接起來,路徑可以有重合的節點
物品->路徑的索引:訓練神經網絡時要用到
- 一個物品對應多條路徑
- 用3個節點表示一條路徑:path = [a,b,c]
路徑->物品的索引:線上做召回時要用到
- 一條路徑對應多個物品(給定一條路徑,會取回很多個物品作為召回的結果)
預估模型-神經網絡(尋找用戶感興趣的路徑)
給定用戶特征,預估用戶對路徑的興趣分數(根據用戶特征,召回多條路徑)
預估用戶對路徑的興趣:
假設結構有3層?
過程:

- 如果結構的每一層有k個節點,那么p1就是一個k維向量
- 向量p1對應L1層,向量p1的k個元素是神經網絡對L1層k個節點打的分。分數越高,節點就越有可能被選中(用Beam Search的方法。假設選中節點a)
- 向量x不變,直接作為下一層的輸入,對節點a做embedding,與x做concat,輸入另一個神經網絡

- 假設根據向量p1,從k個節點中選出節點a。把向量x和節點a一起送入下一層神經網絡,輸出k維向量p2(對應結構中的第二層)
- 同理選出節點b,把向量x和節點a、b一起輸入下一層神經網絡。從3層中各選出一個節點,組成路徑 [a,b,c]
線上召回:user->path->item
給定用戶特征,召回一批物品
用Beam Search召回一批路徑:
beam size(每層選的節點數量)越大,計算量越大,但同時search的結果也會越好(最簡單的情況即beam size=1)
Beam Search相當于貪心算法,選中的節點分別最大化p1、p2、p3,但獨立對p1、p2、p3求最大化,未必會最大化這三項的乘積?

?

?
size大一些,結果會比貪心算法好一些,但計算量也變大?
步驟:
- 第一步:給定用戶特征,用神經網絡做預估,用beam search召回一批路徑
- 第二步:利用索引,召回一批物品
- 查看索引path->item(在線上做召回之前,線下已經把路徑和物品匹配好了)
- 每條路徑對應多個物品
- 第三步:對物品做排序,選出一個子集作為最終召回結果
- 用小的排序模型來打分排序,如雙塔模型,防止物品超出召回通道的配額
離線訓練(同時學習神經網絡參數和物品表征)
神經網絡可以判斷用戶對物品的興趣,物品表征則是把物品映射到路徑
訓練時只用正樣本,用戶-物品二元組,只要用戶點過物品就算正樣本
學習神經網絡參數
判斷用戶對路徑有多感興趣

學習物品表征
解釋:圖中的用戶都點擊過左邊的物品,如果其中很多用戶也對路徑感興趣(興趣分數是神經網絡預估出來的),我們就判斷物品和路徑有很強的關聯,可以把路徑作為物品的表征?

最小化損失,相當于根據score對path做排序,排序結果的top j,即對于每個物品item,選擇score最高的j條路徑,作為物品的表征?
為了防止非常多的物品集中在一條路徑上的情況,希望每條path上的item數量比較平衡,需要用正則項約束path。如果某條path上已經有了很多item,這條path就會受到懲罰,避免關聯到更多物品
物品與J條路徑的相關性越高,損失函數就越小;正則reg控制路徑l上的物品不要太多?
交替訓練神經網絡和物品表征
訓練神經網絡時,把物品當做中介,將用戶和路徑關聯起來
線上召回:用戶->路徑->物品(兩階段,先用神經網絡尋找用戶感興趣的路徑,路徑長度與神經網絡層數相關;再取回路徑上的物品)
這里也說明了為什么要用正則項控制一條path上的item不能太多,否則有時候召回的物品數量會特別多,有時候會特別少
Deep Retrieval召回的本質是用路徑作為用戶和物品之間的中介
雙塔模型召回的本質是用向量表征作為用戶和物品之間的中介
離線訓練:同時學習 用戶-路徑 和 物品-路徑 的關系
避免讓索引失去平衡
9、其他召回通道
地理位置召回
GeoHash召回(附近召回)
用戶可能對附近發生的事感興趣
- GeoHash:對經緯度的編碼(把經緯度編碼為二進制哈希碼),地圖上一個長方形區域(以GeoHash作為索引,記錄地圖上一個長方形區域內的優質筆記)
- 索引:GeoHash -> 優質筆記列表(按時間倒排,即最新的筆記排在最前面)
- 召回時,給定用戶的GeoHash,會取回這個區域內比較新的一些優質筆記
召回純粹只看地理位置,完全不考慮用戶興趣(就是因為沒有個性化,才需要考慮優質筆記。筆記本身質量好,即使沒有個性化,用戶也很可能會喜歡看;反之,既沒有個性化,又不是優質筆記,召回的筆記大概率通不過粗精排)
同城召回
用戶可能對同城發生的事感興趣
- 根據用戶所在城市和曾經生活過的城市做召回
- 索引:城市 -> 優質筆記列表(按時間倒排)
這條召回通道沒有個性化
作者召回
關注的作者召回
用戶對關注的作者發布的筆記感興趣
- 索引:用戶->關注的作者,作者->發布的筆記(按時間順序倒排,新發布的筆記排在最前面)
- 召回:用戶->關注的作者->最新的筆記
有交互的作者召回
即使沒關注作者,如果推送有交互作者發布的筆記也可能會感興趣而繼續看
如果用戶對某筆記感興趣(點贊、收藏、轉發),那么用戶可能對該作者的其他筆記感興趣
- 索引:用戶->有交互的作者
- 召回:用戶->有交互的作者->最新的筆記
作者列表需要定期更新,保留最新交互的作者,刪除一段時間沒有交互的作者
相似作者召回
如果用戶喜歡某作者(感興趣的作者包括用戶關注的作者+用戶有交互的作者),那么用戶喜歡相似的作者。取回每個作者最新的一篇筆記
相似性的計算類似于ItemCF:如果兩個作者的粉絲有很大重合,那么兩個作者相似
緩存召回
想法:復用前n次推薦精排的結果(精排前50但是沒有曝光的,緩存起來,作為一條召回通道)
- 精排輸出幾百篇筆記,送入重排;重排做多樣性的隨機抽樣(如DPP),選出幾十篇
- 精排結果一大半沒有曝光,被浪費
問題:緩存大小固定(比如最多存100篇筆記),需要退場機制。有很多條規則作為退場機制,如
- 一旦筆記成功曝光,就從緩存退場
- 如果超出緩存大小,就移除最先進入緩存的筆記
- 筆記最多被召回10次,達到10次就退場
- 每篇筆記最多被保存3天,達到3天就退場
還能再細化規則,如想要扶持曝光比較低的筆記,那么可以根據筆記的曝光次數來設置規則,讓低曝光的筆記在緩存中存更長時間
三大類、六條召回通道:
- 地理位置召回:用戶對自己附近的人和事感興趣
- GeoHash召回、同城召回
- 作者召回:
- 關注的作者、有交互的作者、相似的作者
- 緩存召回:把精排中排名高、但是沒有成功曝光的筆記緩存起來,再多嘗試幾次
10、曝光過濾 & Bloom Filter
曝光過濾通常在召回階段做,具體的方法就是用Bloom Filter(給用戶曝光過等價于用戶看過)
曝光過濾問題
實驗表明,重復曝光同一個物品會損害用戶體驗(抖音、小紅書);但YouTube這樣的長視頻就沒有曝光過濾,看過的可以再推薦??
在小紅書,n和r的量級都是幾千,暴力對比的計算量太大。實踐中不做暴力對比,而是用Bloom Filter
Bloom Filter
判斷一個物品ID是否在已曝光的物品集合中
- 如果判斷為no,那么該物品一定不在集合中
- 如果判斷為yes,那么該物品很可能在集合中(可能誤傷,錯誤判斷未曝光物品為已曝光,將其過濾掉)
概括一下:根據Bloom Filter的判斷做曝光過濾,肯定能干掉所有已經曝光的物品,用戶絕對不可能重復看到同一個物品;但是有一定概率會誤傷,召回的物品明明沒有曝光過,卻被Bloom Filter給干掉了?
Burton H. Bloom. Space/time trade-offs in hash coding with allowableerrors. Communications of the ACM, 1970.
Bloom Filter是一種數據結構?
- 把物品集合表征為一個m維二進制向量(每個元素是一個bit,要么是0要么是1)
- 每個用戶有一個曝光物品的集合,表征為一個向量,需要m bits的存儲
- 有k個哈希函數,每個哈希函數把物品ID映射成介于 0~m-1 之間的整數
- k和m都是需要設置的參數
最簡單的情況:k=1(只有1個哈希函數)?


- 如果Bloom Filter認為沒有曝光,那么這個物品肯定沒有曝光(不可能把已曝光的物品誤判為未曝光)
- 如果Bloom Filter認為已曝光,有可能是正確的,有可能是誤判(有一定概率把未曝光物品認為已曝光,導致未曝光物品被過濾掉)
k=3,用3個不同的哈希函數把物品ID映射到3個位置上,把3個位置的元素都置為1。同樣地,如果向量的某個位置元素已經是1,不需要修改數值??

?
如果物品已經曝光,那么其3個位置肯定都是1,如果其中某個位置為0,說明該物品實際未曝光
Bloom Filter誤傷的概率:?
用戶歷史記錄上有n個曝光物品,只需要 m=10n bits,就可以把誤差概率降低到1%以下
推薦系統中曝光過濾的鏈路
- 推薦系統的鏈路:多路召回、粗排、精排、重排,最終選出一批物品曝光給用戶
- 曝光過濾的鏈路:把曝光的物品記錄下來,更新Bloom Filter,用于過濾召回的物品
APP前端有埋點,所有曝光的物品都會被記錄下來。但這個落表的速度要足夠快,在用戶推薦界面下一刷之前,就要把本次曝光的結果寫到Bloom Filter上,否則下一刷很可能會出重復的物品,所以要用實時流處理,比如把曝光物品寫入Kafka消息隊列,用Flink做實時計算
Flink實時讀取Kafka消息隊列,計算曝光物品的哈希值,把結果寫入Bloom Filter的二進制向量。用這樣的實時數據鏈路,在曝光發生幾秒之后,這位用戶的Bloom Filter就會被修改,之后就能避免重復曝光
曝光過濾用在召回完成之后。召回服務器請求曝光過濾服務,曝光過濾服務把用戶的二進制向量發送給召回服務器,在召回服務器上用Bloom Filter計算召回物品的哈希值,再跟二進制向量對比,把已經曝光的物品過濾掉,剩余的物品都是未曝光的,發送給排序服務器
Bloom Filter缺點

?
只需要計算出k個哈希值(k是哈希函數的數量)
刪除物品,不能簡單把這個物品對應的k個元素從1改為0,否則會影響其他物品。這是因為向量的元素是所有物品共享的,如果把向量的一個元素改為0,相當于把很多物品都移除掉了。想要刪除一個物品,需要重新計算整個集合的二進制向量
推薦系統的曝光過濾問題、Bloom Filter數據結構
(三)排序
1、多目標排序模型
推薦系統的鏈路
- 有很多條召回通道,從幾億篇筆記中取出幾千篇
- 從中選出用戶最感興趣的,要用到粗排和精排,粗排給召回的筆記逐一打分,保留分數最高的幾百篇
- 然后用精排模型給粗排選出的幾百篇筆記打分,不做截斷,讓幾百篇筆記全都帶著精排分數進入重排
- 重排做多樣性抽樣,把相似內容打散,最終有幾十篇筆記被選中,展示給用戶
粗排、精排原理基本相同,只是粗排模型小,特征少,效果差一些,粗排的目的是做快速的初步篩選(如果不用粗排,直接把很大的精排模型用在幾千篇候選筆記上,計算代價大)
排序的依據
以小紅書為例,排序的主要依據是用戶對筆記的興趣,興趣可以反映在用戶與筆記的交互上。對于每篇筆記,系統記錄以下統計量:
- 曝光次數(impression):一篇筆記被展示給多少用戶
- 點擊次數(click):一篇筆記被多少用戶點開
- 點贊次數(like)
- 收藏次數(collect)
- 轉發(share)
可以用點擊率之類的消費指標衡量一篇筆記受歡迎的程度
- 點擊率 = 點擊次數 / 曝光次數
- 點贊率 = 點贊次數 / 點擊次數(收藏率、轉發率同理)。用戶點開筆記之后才會發生點贊、收藏、轉發等行為,故分母是點擊次數,而不是曝光次數
排序依據:?
- 在把筆記展示給用戶之前,需要事先預估用戶對筆記的興趣(排序模型預估點擊率、點贊率、收藏率、轉發率等多種分數);
- 融合這些預估分數,比如加權和(比如點擊率的權重是1,點贊率、收藏率、轉發率的權重是2),權重是做ab測試調出來的;
- 根據融合的分數做排序、截斷,保留分數高的筆記
簡單的多目標模型
排序模型的輸入是各種各樣的特征
- 用戶特征:用戶ID、用戶畫像
- 物品特征:物品ID、物品畫像、作者信息(筆記的作者)
- 統計特征:
- ??????????????用戶統計特征(用戶在過去30天中一共曝光/點擊...了多少篇筆記)
- 物品統計特征(候選物品在過去30天中一共獲得了多少次曝光/點擊...機會)
- 場景特征:隨著用戶請求傳過來的,包含
- 當前時間(如當前是否是周末或節假日)
- 用戶所在地點(候選物品和用戶是否在同一城市)

輸出4個預估值,都介于0~1之間,排序主要依靠這4個預估值?
?
模型訓練:
4個任務,每個任務都是一個二元分類,如判斷用戶是否會點擊物品。用交叉熵損失函數

?
實際訓練中會有很多困難,如類別不平衡(正樣本少,負樣本多)
- 每100次曝光,約有10次點擊、90次無點擊
- 每100次點擊,約有10次收藏、90次無收藏
解決方案:負樣本降采樣(down-sampling)
- 保留一小部分負樣本
- 讓正負樣本數量平衡,節約計算(比如原本一天積累的數據需要在集群上訓練10h,做降采樣后,負樣本減少很多,訓練只需3h)
預估值校準
給定用戶特征和物品特征,用神經網絡預估出點擊率、點贊率等分數之后,要對這些預估分數做校準,做完校準之后才能把預估值用于排序?
為什么要做校準?
α(采樣率)越小,負樣本越少,模型對點擊率的高估就會越嚴重
Xinran He et al. Practical lessons from predicting clicks on ads at Facebook. In the 8th International Workshop on Data Mining for Online Advertising.
對預估點擊率的校準公式:左邊的??表示校準之后的點擊率,右邊是對預估點擊率?
?做的變換,α為采樣率
在線上做排序時,首先讓模型預估點擊率,然后用上述公式做校準,拿校準之后的點擊率
做排序的依據
排序的多目標模型,用于估計點擊率、點贊率等指標。做完預估之后,要根據負采樣率對預估值做校準
2、Multi-gate Mixture-of-Experts(MMoE)
模型結構
Google的論文提出MMoE模型:Jiaqi Ma et al. Modeling Task Relationships in Multi-task Learning with Multi-gate Mixture-of-Experts. In KDD, 2018.
模型的輸入是一個向量,包含用戶特征、物品特征、統計特征、場景特征
- 3個神經網絡結構相同,都是由很多全連接層組成,但它們不共享參數
- 3個神經網絡各輸出一個向量,即3個“專家”(實踐中通常用4個或8個。專家神經網絡的數量是個超參數,需要手動調)

- softmax輸出向量的3個元素都>0且相加為1?
- p1~p3分別對應3個專家神經網絡,之后會將這3個元素作為權重,對向量x1~x3做加權平均。q1~q3同理
神經網絡可以有一個或多個全連接層,其輸出取決于具體的任務,比如輸出對點擊率的預估,是一個介于0~1之間的實數
這里假設多目標模型只有點擊率和點贊率兩個目標,所以只有兩組權重。
對神經網絡輸出的向量做加權平均,用加權平均得到的向量去預估某個業務指標。若多目標模型有n個目標,就要用n組權重
MMoE問題:極化現象
YouTube的論文提出極化問題的解決方案:Zhe Zhao et al. Recommending What Video to Watch Next: A Multitask Ranking System. In RecSys, 2019.
softmax會發生極化(Polarization):softmax輸出值一個接近1,其余接近0
如左邊softmax輸出值 ≈ [0,0,1],說明左邊的預估點擊率任務只使用了第三號專家神經網絡(沒有讓三個專家神經網絡的輸出融合,只是簡單使用了一個專家)
那么MMoE就相當于一個簡單的多目標模型,不會對專家做融合,失去了MMoE的優勢
解決極化問題:
如果有n個專家神經網絡,那么每個softmax的輸入和輸出都是n維向量。不希望看到其中一個輸出的元素接近1,其余n-1個元素接近0
在訓練時,對softmax的輸出使用dropout,這樣會強迫每個任務根據部分專家做預測
- softmax輸出的n個數值被mask的概率都是10%
- 每個專家被隨機丟棄的概率都是10%
如果用dropout,不太可能會發生極化,否則預測的結果會特別差。假如發生極化,softmax輸出的某個元素接近1,萬一這個元素被mask,預測的結果會錯得離譜。為了讓預測盡量精準,神經網絡會盡量避免極化的發生,避免softmax輸出的某個元素接近1。用了dropout,基本上能避免極化
用MMoE不一定會有提升,有可能是實現不夠好,也有可能是不適用于特定的業務場景
3、預估分數的融合公式
多目標模型輸出對點擊率、點贊率等指標的預估。怎么融合多個預估分數?介紹工業界排序的幾種融分公式
加權和
Pclick表示模型預估的點擊率,權重為1;其他以此類推
Pclick*1就是預估的點擊率,Pclick*Plike是曝光之后用戶點贊的概率
海外某短視頻APP的融分公式
Ptime是指預估短視頻的觀看時長(比如預測用戶會觀看10s),w1和α1都是超參數,通過線上ab選出合適的值
國內某短視頻APP(快手)的融分公式
不是直接用預估的分數,而是用每個分數的排名
α和β都是需要調的超參數。預估的播放時長越長,排名越靠前,越小,最終得分就越高
某電商的融分公式
指數α1~α4是超參數,需要調。若都為1,該公式就表示電商的營收,有很明確的物理意義
4、視頻播放建模
播放時長&完播率2個指標
指標1:視頻播放時長
- 圖文筆記排序的主要依據:點擊、點贊、收藏、轉發、評論...
- 視頻排序的依據還有播放時長和完播
視頻播放時長是連續變量,但直接用回歸擬合效果不好,建議用YouTube的時長建模
Paul Covington, Jay Adams, & Emre Sargin. Deep Neural Networks for YouTube Recommendations. In RecSys, 2016.
神經網絡叫做Share Bottom(被所有任務共享),每個全連接層對應一個目標(比如點擊、點贊、收藏、播放時長),這里只關心播放時長的預估:?
對z(實數,可正可負)做sigmoid變換得到p=sigmoid(z),讓p擬合y = t / (1+t)(?t表示用戶實際觀看視頻的時長,t越大,y也越大),訓練中用p和y的交叉熵作為損失函數【訓練完畢后,p就沒有用了】
CE(y, p) = y*logp + (1-y)*log(1-p)
可以用exp(z)作為播放時長的預估【線上做推理時,只用z的指數函數,把它作為對播放時長t的預估】
總結一下,右邊的全連接層輸出z,對z做sigmoid變換得到p,在訓練中用p;
用y(= t / (1+t),其中t為播放時長)和p的交叉熵作為損失函數訓練模型,訓練完成后p就沒有用了;
線上做推理時,只用z的指數函數,把它作為對播放時長t的預估?
建模
實踐中把分母1+t去掉也沒問題,相當于給損失函數做加權,權重是播放時長
最終把exp(z)作為融分公式中的一項,它會影響到視頻的排序
指標2:視頻完播率
2種建模方法:回歸;二元分類
回歸方法
y的大小介于0~1之間?
二元分類方法
需要算法工程師自己定義完播指標
預估的播放率會跟點擊率等指標一起作為排序的依據
?
有利于短視頻,而對長視頻不公平
視頻越長,函數值f越小,把調整之后的分數記作,可以反映出用戶對視頻的興趣,而且對長、短視頻是公平的
?與播放時長、點擊率、點贊率等指標一起,決定視頻的排序
視頻的排序;視頻有兩個獨特的指標:播放時長和完播率
5、排序模型的特征
排序所需特征
用戶畫像特征(User Profile)
召回和排序的模型中都有用戶屬性,用戶屬性記錄在用戶畫像中
- 用戶ID(在召回、排序中做embedding)
- 用戶ID本身不攜帶有用的信息,但作用很大,通常用32 or 64維向量
- 人口統計學屬性:性別、年齡等
- 賬號信息:新老(用戶注冊時間)、活躍度...
- 模型需要專門針對新用戶和低活用戶做優化
- 感興趣的類目、關鍵詞、品牌
- 屬于用戶興趣信息,這些信息可以是用戶自己填寫的,也可以是算法自動提取的
筆記畫像特征(Item Profile)
- 物品ID(在召回、排序中作embedding)
- 發布時間(或者年齡)
- 在小紅書,一篇筆記發布時間越久,價值就越低,尤其是強時效性話題
- GeoHash(經緯度編碼)、所在城市
- GeoHash表示地圖上一個長方形的區域
- 標題、類目、關鍵詞、品牌...
- 筆記的內容,通常對這些離散的內容特征做Embedding
- 字數、圖片數、視頻清晰度、標簽數...
- 筆記自帶的屬性,反映出筆記的質量,筆記的點擊和交互指標與之相關
- 內容信息量、圖片美學...
- 算法打的分數,事先用人工標注的數據訓練cv和nlp模型,當新筆記發布時,用模型給筆記打分,把分數寫到物品畫像中,可以作為排序模型的特征
統計特征(用戶/筆記)
(1)用戶統計特征:
- 用戶最近30天 (7天、1天、1小時) 的曝光數、點擊數、 點贊數、收藏數…
- 用各種時間粒度,可以反映出用戶的實時興趣、短期興趣和中長期興趣
- 按照筆記 圖文/視頻 分桶 (比如最近7天,該用戶對圖文筆記的點擊率、對視頻筆記的點擊率)
- 對圖文、視頻兩類筆記分開做統計,可以反映出用戶對兩類筆記的偏好
- 按照筆記類目分桶 (比如最近30天,用戶對美妝筆記的點擊率、對美食筆記的點擊率、對科技數碼筆記的點擊率)
(2)筆記統計特征:
- 筆記最近30天 (7天、1天、1小時) 的曝光數、點擊數、點贊數、收藏數…
- 統計量反映出筆記的受歡迎程度。如果點擊率、點贊率等指標都很高,說明筆記質量高,算法應該給這樣的筆記更多的流量
- 時間粒度是針對筆記的實效性。若筆記已經過時(如30天指標高,但1天指標低),不應該給更多的流量
- 筆記受眾:按照用戶性別、年齡、地域分桶
- 作者特征:發布筆記數、粉絲數、消費指標(曝光數、點擊數、點贊數、收藏數)
- 作者統計特征反映了作者的受歡迎程度以及作品的平均品質
場景特征(Context)
隨著推薦請求傳來的,不用從用戶畫像、筆記畫像等數據庫中獲取
- 用戶定位GeoHash (經緯度編碼) 、城市
- 用戶可能對自己附近的事感興趣
- 當前時刻 (分段,做embedding)
- 一個人在同一天不同時刻的興趣可能有所區別
- 是否是周末或節假日
- 周末或節假日用戶可能對特定的話題感興趣
-
- 手機品牌、手機型號、操作系統
- 安卓or蘋果
特征處理
離散特征:做embedding
- 用戶ID、筆記ID、作者ID(數量大,消耗內存大)
- 類目、關鍵詞、城市、手機品牌
連續特征:做分桶,變成離散特征
- 年齡(如把連續的年齡變為十個年齡段,做one-hot或Embedding)、筆記字數、視頻長度
連續特征:其他變換
- 曝光數、點擊數、點贊數等數值做log(1+x),可以解決異常值問題
- 曝光數等(大多數筆記只有幾百次,少數筆記能有上百萬次)為長尾分布,計算會出現異常(訓練梯度會很離譜,推理的預估值會很奇怪)
- 或者把點擊數、點贊數等轉化為點擊率、點贊率等值,并做平滑(去掉偶然性造成的波動)
- 實際處理中,兩種變換之后的連續特征都會作為模型的輸入,如log(1+點擊數)、平滑之后的點擊率都會被用到
特征覆蓋率
特征工程需要注重特征覆蓋率
- 理想情況下,每個特征都能覆蓋100%樣本,即不存在特征缺失
- 但實際很多特征無法覆蓋100%樣本,例:
- 很多用戶不填年齡,因此用戶年齡特征的覆蓋率遠小于100%
- 很多用戶設置隱私權限,APP不能獲得用戶地理定位,因此場景特征有缺失
-
提高特征覆蓋率,可以讓精排模型更準;另外還要考慮,當特征缺失時,用什么作為默認值
數據服務(線上服務的簡化版系統架構)
- 用戶畫像(User Profile)
- 物品畫像(Item Profile)
- 統計數據
以上3個數據源都存儲在內存數據庫中。線上服務時,排序服務器會從3個數據源取回所需的數據,然后把讀取的數據做處理,作為特征喂給模型,模型就能預估出點擊率、點贊率等指標
當用戶刷小紅書時,用戶請求會被發送到推薦系統的主服務器上,主服務器會把請求發送到召回服務器上。做完召回后,召回服務器會把幾十路召回的結果做歸并,把幾千篇筆記的ID返回給主服務器(召回需要調用用戶畫像)
主服務器把筆記ID、用戶ID、場景特征(1個用戶ID+幾千個筆記ID。筆記ID是召回的結果,用戶ID和場景特征都是從用戶請求中獲取的,場景特征包括當前時刻、用戶所在地點以及手機的型號和操作系統)發送給排序服務器
接下來,排序服務器要從多個數據源中取回排序所需的特征,主要是這3個數據源:用戶畫像、物品畫像、統計數據
- 用戶畫像數據庫線上壓力小,因為每次只讀1個用戶的特征
- 性別、年齡幾乎不變
- 用戶活躍度、興趣標簽等為天級別的刷新
- 物品畫像數據庫壓力大,因為粗排要給幾千篇筆記排序,讀取幾千篇筆記的特征
- 物品自身屬性、算法給物品打的標簽在很長一段時間內不會發生變化
對用戶畫像和物品畫像最重要的是讀取速度快,而不太需要考慮時效性,因為它們都是靜態的,甚至可以儲存在排序服務器本地,讓讀取變得更快
- 統計數據:存用戶統計值的數據庫壓力小,存物品統計值的數據庫壓力大
- 統計數據不能在本地緩存,因為它動態變化,時效性很強
在收集到排序所需的特征之后,排序服務器把特征打包傳給TF Serving。TensorFlow會給筆記打分,把分數返回給排序服務器,排序服務器會用融合的分數、多樣性分數、業務規則給筆記排序,把排名最高的幾十篇筆記返回給主服務器,這就是最終給用戶曝光的筆記
6、粗排模型
粗排 vs 精排
粗排犧牲準確性,以保證線上推理的速度快(目的是做初步篩選,從幾千篇筆記中選出幾百篇,而不是真正決定把哪些筆記曝光給用戶);精排的模型足夠大,犧牲更多的計算,確保預估的準確性足夠高
精排模型 & 雙塔模型
Shared Bottom含義為被多個任務共享【精排模型的代價主要在這,因為它很大,神經網絡也很復雜】
精排模型屬于前期融合(先對所有特征做concatenation,再輸入神經網絡),但線上推理代價大(如果有n篇候選筆記,整個大模型要做n次推理)
兩個向量的余弦相似度表明用戶是否對物品感興趣
在訓練好模型之后,把物品向量b存儲在向量數據庫,在線上不需要用物品塔做計算。線上推理只需要用到用戶塔,每做一次推薦,用戶塔只做一次推理,計算出一個向量a【雙塔模型的計算代價很小,適合做召回】
雙塔模型屬于后期融合(把用戶、物品特征分別輸入不同的神經網絡,不對用戶、物品特征做融合,直到最后才計算a和b的相似度),線上計算量小(用戶塔只需要做一次線上推理,計算用戶表征a;物品表征b事先儲存在向量數據庫中,物品塔在線上不做推理),但預估準確性不如精排模型
粗排的三塔模型
小紅書的粗排是三塔模型,效果介于雙塔和精排之間
借鑒【阿里媽媽】Zhe Wang et al. COLD: Towards the Next Generation of Pre-Ranking System. In DLP- KDD , 2020
交叉特征是指用戶特征和物品特征做交叉
訓練粗排模型的方法就是正常的端到端訓練,跟精排完全一樣
三塔模型介于前期融合(把底層特征做concatenation)和后期融合之間,是把三個塔輸出的向量做concatenation
- 物品的屬性相對比較穩定,可以把物品塔輸出向量緩存在PS,每隔一段時間刷新一次。由于做了緩存,物品塔在線上幾乎不用做推理,只有遇到新物品時才需要做推理。粗排給幾千個物品打分,物品塔實際只需要做幾十次推理,所以物品塔的規模可以比較大
- 統計特征會實時動態變化(每當一個用戶發生點擊等行為,其統計特征都發生變化;每當一個物品獲得曝光和交互,其點擊次數、點擊率等指標都會發生變化)。由于交叉塔的輸入會實時發生變化,不應該緩存交叉塔輸出的向量,交叉塔在線上的推理避免不掉,故交叉塔必須足夠小、計算夠快(通常來說交叉塔只有一層,寬度也比較小)
模型的上層結構:?
模型上層做n次推理,代價大于交叉塔的n次推理
三塔模型的線上推理
- 物品塔輸出的向量事先緩存在Parameter Server上,只有當沒有命中緩存時,才需要物品塔做推理(實際上緩存命中率非常高,99%的物品都能命中緩存,不需要做推理。給幾千個候選物品做粗排,物品塔只需要做幾十次推理)
- 交叉塔的輸入都是動態特征,不能做緩存
- 三個塔各輸出一個向量,融合起來作為上層網絡的輸入
粗排大部分的計算量都在上層網絡
粗排模型的設計理念:盡量減小推理的計算量,使得模型可以在線上給幾千篇筆記打分