開篇
今天的文章還是由一個我親身經歷的故事來開篇。在一次面試中,面試官問我怎麽處理「新使用者冷啟動」的問題,當時,我是從演算法的角度來回答的,比如透過online learning,加快線上模型的更新頻率;再比如,我當時想把PinSAGE由item-2-item升級為user-2-item,這樣只要新使用者點選過一個item A,我就可以將A的資訊、點選過A的其他user的資訊、點選過A的其他使用者點選過的item資訊、......都聚合到新使用者身上。巴拉巴拉講了一堆,面試官顯得不是很滿意,只見他大手一揮,中氣十足地說到:「沒那麽復雜,處理新使用者,歸根結底就一句話,
盡可能多地找這個使用者的數據!!!
」。當時我還是有點小受震撼的,感嘆道,面試官一眼看到問題本質,怪不得是人家面試我呢。於是,我立刻切換到產品思路,比如構建產品矩陣,從其他產品了解使用者;透過彈窗問卷的方式,收集使用者的興趣愛好;......,這下面試官方才滿意,於是面試愉快地進行下去。
現在回過頭來,重新思考「新使用者冷啟」這個問題。毫無疑問,針對我們所知甚少的新使用者做推薦,是一個極其困難的問題,盡一切努力收集使用者資訊,也絕對是top priority。但是,因此我們演算法就無事可做,等著產品和前端同學把新使用者的數據送過來?
根據我近年來的經驗,新使用者的數據是極其重要的,但是並非問題的全部。比如,新老使用者在app中的行為模式有很大不同(e.g., 比如一些產品設計不允許新使用者做點贊、評論等動作)。但是,在訓練的時候,老使用者的樣本在數量級上碾壓新使用者的樣本(本來dau就主要是由老使用者貢獻,再加上一個老使用者能夠貢獻多條樣本),導致
模型主要擬合老使用者的行為模式,忽略了新使用者
。在這種情況下,增加幾個對新使用者友好的特征(比如年齡、性別等),因為主導模型的老使用者不care(影響老使用者推薦結果的主要是其過去歷史,而非人口內容),加了也是白加。
噢,看似是一個樣本不均衡的問題,也不是毫無辦法。比如:
給新使用者的樣本加權
。行是行,但是有兩個問題不太好解決,一是如何設定給這些新使用者的權重,二是,畢竟老使用者才是給我們貢獻KPI的主力,給新使用者加權,等於變相削弱老使用者,可能得不償失。
幹脆分家,就只拿新使用者的數據單獨訓練一套模型,只服務新使用者
。也不是不行。但是,也有兩問題不好解決,一是新使用者數據量少,底層embedding和中間的dense weight可能訓練不好;二來,單獨訓練,單獨部署,有點浪費資源。
對付新使用者,以上方法雖然簡單,但是真的不妨一試。如果也覺得以上方法的缺點無法接受,不妨向下讀,看看本文替你總結出的一些從演算法層面助力新使用者冷啟的技巧。
正文開始之前,先提前聲明幾點:
本文中所提及的新使用者是一個泛稱,未必是第一次登入、毫無動作的純新使用者,也包括那些雖然距首次登入已經過去許久,但是鮮有活動的使用者。其實,文中的概念算是「低活使用者」,但是為了行文方便,下文中還是用「新使用者」統一稱呼這些使用者;
本文中所提觀點,和「
收集使用者資訊是新使用者冷啟top priority
」的觀點並不矛盾。相反,以下演算法技巧的目的,就是為了能夠讓模型更重視那些對新使用者友好的特征,讓這些特征真正發揮作用。
演算法畢竟是實驗科學,就像股票點評之後總要加上一句「不構成投資建議」一樣,以下介紹的技巧未必顆顆是銀彈。究竟哪個管用,就讓GPU和AB平台告訴我們答案吧。
加強重要區分性特征
既然我們透過數據分析,先驗已知新老使用者的行為模式有著較大差異,我們希望把這一先驗知識告訴模型,讓模型對新老使用者能夠區別對待。至於如何註入這樣的先驗知識,我已經在我的另一篇文章【
先入為主:將先驗知識註入推薦模型
】介紹過了,簡述如下。
首先,我們要構造能夠顯著標識新使用者的特征,比如:
一個標識當前使用者是否是新使用者的0/1標誌位
使用者是否登入
首次登入距今時間
互動過的item的個數
......
強bias特征加得淺
可以把這些強bias特征加得離final logit近一些,讓它們對最終loss的影響更直接一些。比如:Logit_{final}=Logit_{ordinary} + Logit_{bias}
由其他特征獲得Logit_{ordinary},
需要經過復雜且深的DNN結構
而由以上強bias特征貢獻的Logit_{bias}
,只是將這些強bias特征透過一個LR或一層fully connection就得到
強bias特征當裁判
方法二,將這些強bias的特征,餵入SENet或或LHUC(Learn Hidden Unit Contribution,如下圖所示),用來決定其他特征的重要性
強bias特征作為LHUC的輸入,經過sigmiod啟用函式後,輸出是一個N維度向量,N是所有fileld的個數
N維向量就是各field的重要性,將其按位乘到各field的embedding上,起到增強或削弱的作用。比如:
比如產品設計不允許未登入使用者點贊、評論,那麽新使用者樣本中的相關特征的重要性「應該」為0
使用者點選哪個站外廣告被引入app,這個資訊只對新使用者有用(權重應該大),對老使用者意義就不大了(權重應該小)
加權後的各field embedding再拼接起來,餵入上層DNN
這樣做,相比於將所有特征「一視同仁」、一古腦地餵入DNN最底層等候上層DNN篩選,更能發揮那些對新使用者友好的重要特征的作用。
借鑒多領域學習
正如開篇所說,
為了避免新使用者數據被老使用者淹沒,幹脆分家,用新使用者的數據單獨訓練一個模型只服務新使用者
。但是有兩個問題不好解決,一是新使用者數據少,單獨訓模型可能訓不好;二是,單獨訓練+部署,有點浪費資源。
其實這個問題屬於
multi-domain learning
的解決範疇。不要和multi-task learning相混淆,multi-domain learning研究的是如何將多個相關渠道的數據放在一起訓練,而能夠相互促進,而非相互幹擾。這方面的工作並不多,讓我們看看阿裏近年來在這一領域發表的兩篇文章。
提醒讀者,盡管兩篇文章的初衷都不是針對新使用者,但是在閱讀過程中,我們可以代入new user domain vs. old user domain的場景
。
STAR結構
一篇是【One Model to Serve All: Star Topology Adaptive Recommender for Multi-Domain CTR Prediction】。這篇文章研究的是,如何訓練並部署一個模型,就能勝任「首頁推薦」、「猜你喜歡」等多個領域。前兩個改進也很「直覺」(嘿嘿,知道我模仿的是誰嗎?)
每個域學習各自的batch normalization
\gamma
, \beta
是要全域學習的scaling和bias
\gamma_p
, \beta_p
是某個domain "p"獨有的scaling和bias
將domani id這樣的bias特征,透過一個淺層網路,加得離final logit近一些。這一點就是我們在上一章節介紹的技巧。
第三個改進,就是所謂的星型拓撲結構,也就是論文標題的來源。簡單來說,
就是每個domain要經過的final dense weight是domain specific part和shared part融合而成
。
W_p^*
是domain 'p'的樣本最終要經過的final weight,
W_p
是domain "p"獨有的weight,只讓domain "p"的樣本參與叠代更新
W是shared weight,所有domain的數據都要參與更新。
\otimes
是element-wise multiplication
我有點不太好評價這種結構。其實想結合domain specific information與shared information也不算什麽新概念,比如下面兩種:
domain specific weight與shared weight是並聯的關系,樣本資訊同時經過domain specific weight和shared weight向上傳遞,Logit_{final}=Logit_{domain}+Logit_{share}
模仿早先multi-task learning中常見的share bottom結構,先有共享的底層shared weight,各個domain再在上面串聯上domain specific weight。預測時,樣本先經過shared weight,再經過domain specific weight向上傳遞,Logit_{final} = F_{domain}(F_{share}(x))
文中並沒有給出W_p \otimes W
這種融合方式,相對於我在上文提出的兩種融合方式的優勢,或許沒啥大道理,又是實驗得出的結論。我自己給出的理由只能是:
無論是domain specific weight與shared weight是並聯還是串聯,前代回代中,資訊所經過的每一層,都只屬於單一通道(要麽是domain specific,要麽是share),融合得並不徹底;
而W_p \otimes W
,資訊流過的每一層都是domain specific weight和shared weight融合後的,融合得更充分。
HMoE結構
AliExpress的文章【Improving Multi-Scenario Learning to Rank in E-commerce by Exploiting Task Relationships in the Label Space】聚焦於多國家場景下的推薦。這個場景,和我們新使用者的場景更加類似一些,多個國家的人口迥異,市場進入有早有晚,相對於先發國家龐大的使用者基數和豐富的行為,
後發國家的使用者少、行為少,可以類比於「新使用者冷啟」的場景
。
這篇文章提出的HMoE與STAR完全相反,沒有所謂的shared weight。其原理假設是:
A國家中,有些使用者a的行為和B國的使用者很像,所以,讓B國的DNN給a打分也很靠譜,異域模型打分的借鑒意義大;反之,異域模型打分的借鑒意義小。
回到我們新使用者的場景下,
用老使用者模型給新使用者打分,可能對部份新使用者也是大有裨益的
。
具體做法上:
無論哪個域的樣本,都要經過所有domain的DNN打分(下面公式中的S),
最終得分,是在各個域打分上的加權平均。權重(下面公式中的W)由一個gate netowrk根據輸入樣本,個人化決定。
訓練時,
domain=t的樣本,只能更新自己的DNN_t
,對其他domain的DNN都要stop gradient
。這一點也相當直覺,畢竟其他domain的DNN幫domain t打分是副業,不能強求其他domain的DNN為domain t上的loss負責。
獲得對新使用者友好的初值
這種方法的思路是,新使用者訓練困難是因為數據少。但是,
如果新使用者的初始值並非隨機,而是已經非常靠譜了,透過少量數據+幾次叠代,就能收斂到不錯的使用者表示
,豈不妙哉!
強烈吐槽MeLU
上述思路簡直就是為
MAML
(Model-Agnostic Meta-Learning)量身定做的。MAML主打的招牌就是Fast Adaptation
先透過多個task的學習,將參數學習到一個不錯的狀態\theta
;
有了新任務1(or 2 or 3),該新任務只需要提供少量數據,就能夠從\theta
出發,快速收斂到這個新任務的最優參數\theta_1^*
(or \theta_2^*
or \theta_3^*
)
於是基於MAML,南韓人提出了MeLU想學習出對新使用者友好的初始值。文章的細節,我就不多說了,因為如果基礎錯了,剩下的實作細節也就毫無價值,浪費筆墨罷了。
我就問兩個問題:
註意上圖中的\theta_1^*
、 \theta_2^*
、\theta_3^*
是從\theta
衍生出三組完全不同的參數。而MeLU中的task粒度是每個使用者算一個task。這也就意味著,哪怕\theta
真有fast adapation的能力,
來了N個新使用者,就要衍生出N組不同的參數
?!想想在一個真實的推薦系統中,\theta
要包括所有dense weight和一些能夠跨user共享的embedding(e.g., tag embedding之類的),參數量還是不小的,
而MeLU竟然要為每個新使用者都學習出單獨的一套出來???!!!
新使用者訓練和預測的難點就在於,他的一些特征是之前從來就沒出現過的。比如一個使用者最具個人化的特征就是user id embedding,新使用者的user id embedding壓根就不會在\theta
中出現,
\theta
adapt to nowhere !!!
我也就納悶了,
這種脫離推薦系統實際、毫無實戰價值的文章,是怎麽被KDD錄入的?還被那麽多講新使用者冷啟的文章參照!簡直是誤人子弟!!!
用Group Embedding代替新使用者的Id Embedding
介紹一種我自己設計的方法,用來獲取良好的new user id embedding初值,在一些場景下的效果還不錯。
我的方法的前提假設是:
user id embedding是最具個人化的特征輸入,在老使用者推薦中發揮了極其重要的作用,DNN也非常重視這個特征(e.g., 接在user id embedding上方的weight比較大)。
但是問題就在於,由於新使用者的user id embedding出現次數少,經過有限次訓練,
new user id embedding還離最初隨機初始化的結果不遠
;而在預測的時候,由於PS有特征準入規則,新使用者的id embedding壓根就沒有被PS收錄,
導致inference server向PS請求該新使用者的id embedding時返回一個全0向量
,導致DNN所期待的重磅特征user id embedding落了空。
既然user id embedding那麽重要,而
新使用者的user id embedding品質卻那麽差(訓練時得到的隨機向量,預測時得到的是全0向量)
,那麽能不能找到一個替代方案?這時,Airbnb的經典文章【Real-time Personalization using Embeddings for Search Ranking at Airbnb】給我提供了一種思路。在Airbnb的實踐中,由於booking這個行為太稀疏,所以絕大多數使用者都是「新使用者」,而Airbnb的思路就是根據規則將使用者劃分為若幹使用者組(比如,
「講英語,使用蘋果手機」的使用者群,和「講西班牙語,使用Android手機」的使用者群
),然後
用user group embedding代替單獨的user id embedding參與後續訓練與預測
。
基於以上假設,解決思路是:
將user id embedding(無論新老使用者)拆解成E_{uid} = E_{InitGroup} + E_{PersonalResidual}
E_{InitGroup}
是「初始的使用者群」的embedding。
註意這個分群是「初始使用者群」,劃分時,只能依靠那些對新使用者友好的特征
,比如:年齡、性別、安裝那些(種類)的app,是被哪個(類)廣告拉新進來的、......
E_{PersonalResidual}
代表自使用者進入app之後,隨著在app內部行為越來越豐富,他距離當初「初始使用者群」embedding的個人化殘留誤差
對於老使用者,原來的方法是直接學習每人各自的user id embedding,現在是改學E_{PersonalResidual}
,所有個人化資訊都包含在這個個人化殘留誤差裏了。
對於新使用者
訓練時,盡管一開始E_{PersonalResidual}
還是隨機向量,但是
畢竟是在已經訓練好的E_{InitGroup}
周圍
(怎麽訓練,下面會講),對於上層的DNN來說,也算是一個不錯的初始值
預測時,inference server遇到純新使用者,由於未收錄該新使用者的E_{PersonalResidual}
從而返回全0向量,結果從DNN的角度來看,
E_{uid} = E_{InitGroup}
。也就是,新使用者的user id embedding被使用者群embedding代替
,強於之前模型用全0向量填充的方法。
目前還遺留一個問題,就是E_{InitGroup}
怎麽得到?我的方法是采用
預訓練
的方法
如上所述,分群是「初始使用者群」,劃分時,只能依靠那些對新使用者友好的特征,比如:年齡、性別、安裝那些(種類)的app,是被哪個(類)廣告拉新進來的、......
借鑒Airbnb的方法,用類似word2vec的方法獲得各E_{InitGroup}
,具體來說,
正樣本:某個新使用者的E_{InitGroup}
,與其互動過的item embedding,在向量空間上應該足夠近
負樣本:隨機采樣一些item embedding
註意,在我的實踐中,進行以上預訓練,
只采用新使用者的數據,不用老使用者的數據
。
這麽做是因為,老使用者互動的item,主要是由推薦系統根據使用者歷史推薦出去的,和使用者的初始元資訊(比如,年齡、性別、App等)關系已經不大了。引入老使用者數據,反而引入了雜訊
預訓練完畢,將各init user group embedding保存起來
註意,在正式訓練的時候,
E_{InitGroup}
對於老使用者要stop gradient
,畢竟年代久遠,init user group embedding不應該再為老使用者推薦結果上的loss負責。也防止規模巨大的老使用者數據,將已經訓練好的E_{InitGroup}
帶偏
至於新使用者,最好對E_{InitGroup}
也stop gradient
,一來新老使用者一致,方便實作;二來,我們希望資訊都積累在E_{PersonalResidual}
上,而不是在group embedding上。
最後提一下,既然要利用new user friendly feature分群,再用user group embedding代替新使用者的user id embedding,那為什麽不直接將這些特征接入DNN,直接讓DNN學習不就完了,何必這麽麻煩?如果有小夥伴能夠想到這一層,首先要為你自己的獨立思考能力點贊。
問題的關鍵還是這些new user friendly feature的接入位置
:
就像我在開篇中所說,直接將new user friendly feature接入DNN底層,很可能起不到提升的作用。還是因為老使用者數據已經主導了模型,模型對新使用者特征壓根不重視(e.g., 接在這些特征的weight都很小)。
但是模型是非常重視user id embedding這個特征的(e.g., 接在它上面的weight都很大),因此
只有將user group embedding接在user id embedding相同的位置,才能真正發揮作用
。
總結
本文聚集於新使用者冷啟這一讓推薦演算法工程師頭疼的問題,從以下幾個方面總結了我個人的看法與經驗:
對於新使用者冷啟,top priority肯定還是盡力收集使用者的數據。
但是僅靠收集數據也是不夠的。比如:在一個成熟的app中,老使用者的樣本碾壓新使用者樣本,主導了模型。徒增了幾個new user friendly的特征,模型也不會重視。
因此,也還需要一些演算法上的技巧,讓收集到的新使用者資訊,能夠真正發揮作用。
技巧1:要把new user friendly特征加到合適的位置,比如加得離loss更近一些,再比如作為gate的輸入,衡量其他特征的重要性。
技巧2:將新老使用者看成兩個domain,借鑒multi-domain learning的經驗,讓老使用者模型能夠助力新使用者,又不至於過分主導。
技巧3:新使用者的初值非常重要。先吐槽了MeLU這種毫無實戰價值、誤人子弟的文章;接著介紹了我自己設計的用user group embedding代替user id embedding的演算法,幫大家開啟思路。
本人的其他文章,感興趣的小夥伴可以繼續閱讀:
負樣本為王:評Facebook的向量化召回演算法
無中生有:論推薦演算法中的Embedding思想
萬變不離其宗:用統一框架理解向量化召回
先入為主:將先驗知識註入推薦模型
久別重逢話雙塔
少數派報告:談推薦場景下的對比學習
刀功:談推薦系統特征工程中的幾個高級技巧
- END -