當前位置: 華文星空 > 知識

有哪些解決推薦系統中冷啟動的思路和方法?

2011-09-07知識

開篇

今天的文章還是由一個我親身經歷的故事來開篇。在一次面試中,面試官問我怎麽處理「新使用者冷啟動」的問題,當時,我是從演算法的角度來回答的,比如透過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 -