作者:易雲天
1 背景
在雲音樂早期機器學習推薦場景中,大多數是以離線機器學習為主,模型是天級別(T+1)更新的。 隨著使用者、主播、ugc內容等變動頻繁,以及外部環境發生突變如產品形態、熱點爆點等情況下,離線方式存在嚴重的滯後性,而模型實時化能從全域性快速捕捉變化,提高流量轉化效率,減少流量曝光浪費,更加實時精準個性化推薦。具體背景可詳見上一篇:https://mp.weixin.qq.com/s/uu...
雲音樂模型實時化過程主要包含三個階段:實時樣本生成、實時模型訓練、實時推送上線。 我們可以看到首當其衝的就是實時樣本生成,也稱之為線上樣本。只有樣本實時秒級別生成,模型才能實時訓練,最後才能實時推送上線進行預估排序。
本篇重點介紹下實時樣本生成過程,實時模型訓練以及實時推送上線後續文章再分享。在介紹之前我們先了解下什麼是樣本。
2 什麼是樣本
樣本是機器學習訓練模型所需要的資料集,它通常是由user與item物件的不同屬性組成並帶有標註的資料集。
這裡的物件屬性即為特徵,是經過處理後能直接給模型學習訓練所需要的特徵,並非是原始特徵;這裡的標註代表著每條資料屬於哪種結果,這就衍生出正負樣本之分了。
- 正樣本:依據訓練學習的目標決定樣本的歸屬為正向的則為正樣本,例如:首頁直播場景,如果學習目標是ctr,那麼主播曝光後有點選即可為正樣本,標記label = 1
- 負樣本:依據訓練學習的目標決定樣本的歸屬為負向的則為負樣本,例如:首頁直播場景,如果學習目標是ctr,那麼主播曝光後沒有點選即可為負樣本,標記label = 0
而樣本的生成方式與週期不同,又可以分為離線樣本與實時樣本。
- 離線樣本:以離線機器學習為主,採用spark批處理生成樣本,生成的樣本是T+1週期的樣本。
- 實時樣本:以實時(增量) 機器學習為主,採用flink方式實時生成樣本,生成的樣本是秒級週期的樣本。
因此組成樣本的兩大關鍵變數就是特徵與label,所以樣本的生成過程就分為三塊:特徵生成、label生成、以及特徵與label關聯。瞭解了什麼是樣本後,我們來看下在實際樣本生產過程中,通常會面臨著什麼問題呢?
3 不一致性
線上模型預估過程(predict)與線下模型訓練(train)過程其實是因果相對過程,其邏輯、資料結構是相同的。可以簡單理解為如下示意圖:
這裡Feature1....n指的經過抽取後的模型特徵,它是從原始特徵開始經過一系列的特徵抽取計算得出的。如下示意圖:
從上面兩個示意圖,我們可以看出來,train與predict的共同點都是Feature1...n,所以線上線下不一致引起的變數就在於這裡。而不一致會直接造成演算法模型效果有損失的,最直接的一種表現就是線下模型評估AUC效果不錯,但模型上線後實際AB效果表現卻往往不盡人意,如下圖所示:
而引起feature這個變數不一致,主要可能由特徵輸入(上圖的原始特徵X)與特徵抽取計算不一致(上圖的F(X)),其中特徵穿越是造成特徵輸入不一致的典型情況。
3.1 特徵穿越
在樣本特徵拼接時候,應該是T時刻的樣本關聯T時刻的特徵,而在原來實際做法中,無法避免特徵穿越發生。我們來看下特徵穿越是如何產生的:
發生穿越的情況主要有兩種:
- 實時特徵穿越:實時特徵不斷在變化,通常是統計類特徵或者序列特徵,這種也是難以控制的,有可能上一分鐘是這個特徵值,下一分鐘這個特徵的值已經發生變化了。比如使用者U1的在t-1預估推薦時刻的點選item序列特徵: u_click_seq:[1001,1002,1005],模型預估出item:1006,1008 推薦曝光後,使用者U1點選了item:1008,在ua迴流拼接樣本時,可能實時特徵任務已提前完成更新,而這時拿到的U1點選序列特徵: u_click_seq : [1001,1002,1005,1008],而合理的樣本中U1點選序列特徵本應該依舊是:u_click_seq:[1001,1002,1005] 。這是其中一種特徵穿越的現象。
- 系統延遲穿越:一般離線特徵都是按照天處理的,考慮各種資料 Pipeline 的流程,處理時間一般都會有延遲,離線特徵處理完之後導到線上供線上模型預估時請求使用。例如3月18日這天,線上預估請求用的特徵是3月17號的特徵資料。到了3月19日凌晨0點,特徵 Pipeline 開始處理資料,到了凌晨5點,離線特徵處理完了導到線上儲存。那么在3月19日0點-5點,這段時間線上請求的特徵使用的是老的特徵資料,也就是3月17日的特徵資料。3月19日5點-24點,線上特徵使用的是3月18日的資料。在離線樣本生成過程中,如果是按天拼接的,那麼3月19號這天的所有樣本,都會使用3月18日的特徵。這也是一種特徵穿越的現象。
模型訓練學習F(x)的是在T-1時刻特徵預估後T+1時刻結果疊加T-1時刻的特徵進行訓練學習,而不是在T-1時刻預估後T+1時刻結果疊加T+1時刻的特徵進行訓練學習,不然就會“因果倒置”。
3.2 計算不一致
除了特徵穿越外,線上線下特徵計算不一致也會造成線下auc不錯線上實驗效果差的情況。我們看一下特徵計算不一致是如何發生的:
我們可以看出線上線下同樣都要做特徵查詢、特徵計算過程。而線上線下之間沒有任何關聯的,是相互獨立的,正因為兩套程式碼不同的計算過程,往往會導致計算邏輯出現不一致現象發生。
- 線上計算:採用c++語言在預估計擎中進行特徵計算的
- 線下計算:採用scala或者java在flink、spark引擎中進行特徵計算的。
那麼如何解決樣本生產過程中的不一致性問題呢?首先看下如何解決特徵穿越的問題,通常業界上有兩種做法:一種是Feature Point-In-Time,典型的例子就是Time-Travel with Tecton。 另一種做法就是預估快照迴流。
我們先看第一種方式:Feature Point-In-Time
這種方式就是要求每一份特徵都帶有timestamp,每次變化時不時直接覆蓋,而是以多版本方式存在,當離線進行關聯時,會根據timestamp來去選擇關聯哪個時刻的特徵。這種方式優點就是:準確率高、容易回溯。 但是由於歷史包袱情況,雲音樂的資料特徵體系還非常不成熟,不具備帶有timestamp能力,並且從底層數倉體系開始做,成本巨大。基於這種原因,我們從線上預估著手。
採用第二種做法:預估快照迴流(predict snapshot)
綜合了不一致性問題,以及模型實時化的訴求,要實現樣本實時化生成,我們基於snapshot做了模型實時化完整工程方案實踐。
4 實時樣本
以下是實時模型工程的基本架構圖,我們可以看到線上預估、線下訓練是形成了資料流閉環的。
就實時樣本這塊主要包含三個階段:實時特徵快照、實時樣本歸因、實時樣本拼接。
4.1 實時特徵快照(snapshot)
實時特徵快照,是指將線上預估時所用的特徵給落下,經過採集處理後實時寫入kv中,然後與生成好的label進行實時關聯,這個就是樣本中特徵生成部分。但具體在實施過程中,也面臨了不少問題:
1、特徵快照超大
當我們直接將線網預估時候幾百上千item與user的特徵以日誌的方式進行本地落盤,然後再通過採集器實時採集本地日誌傳送到kafka,經過Flink處理後實時寫入KV儲存中。但是雲音樂的場景流量很大,每秒超5W的寫入,單條體積都超過50KB了,每小時就達到上TB的資料,本地磁碟及網路IO根本扛不住,KV消耗也大,而且還影響正常請求線網預估。
為了解決這個問題,我們提出了旁路TopN的方案,具體如下:
由於最終與label進行關聯的只可能是最後被推薦給使用者的TopN item物件,因此我們線上投放系統最後一環節將勝出的TopN item非同步構造同樣的請求再次轉發到旁路環境的排序預估系統,與線網環境的排序預估系統唯一區別是不做inference模型計算,這樣將原來幾百上千的Item縮小到TopN 10以內的量,幾乎縮小了50倍~100倍,並且與線網請求進行解耦,不再影響正常的請求。另一方面,去掉本地日誌,直連kafka,減少了IO的壓力,預估快照採用protobuf協議,結合snappy壓縮,單條體積也縮小了50%以上。
在kv儲存方面,結合實時的特性,我們在也做了基於rocksdb之上進行了定製化-Tair-FIFO-RDB,它的優點很明顯,具體的詳細介紹且聽下回分享。
2、特徵選擇多樣性
不同場景對特徵的選擇是不同的,有些場景需要原始特徵,有些場景需要特徵抽取後的特徵,甚至兩種情況都同時存在,還有就是有些特徵是不需要給到線下的。如果每次都需要程式碼去開發選擇什麼的特徵進行落快照,極大的影響了開發效率。面對這種情況,我們將特徵選擇進行DSL配置化,具體如下:
我們可以看到,使用者通過配置這樣一份XML去靈活選擇什麼特徵,極大的滿足快速上線變更的需求,無需進行程式碼開發。
3、recId
recId是snapshot實時特徵快照與Label準確關聯的重要手段,如果僅僅通過userId+itemId的話,可能存在重複情況或者關聯錯誤情況,極大影響著樣本的準確置信度。
recId是由線上投放系統來生成的,代表著每一個使用者請求唯一ID。在演算法推薦鏈路最後一個重排層環節生成recId, 一方面將recId填充到轉給演算法預留的埋點欄位alg中透出到上游客戶端,客戶端會在每個item曝光、使用者對item發生點選、播放等行為上報,在上報ua中都會帶上recId;另一方面將recId傳到snapshot旁路預估系統中,在隨著特徵快照一起dump到kafka中,隨後按照recId_userId_itemId落到kv儲存中。
在實時樣本拼接階段時,通過key=recId_userId_itemId,就能將label與snapshot關聯拼接成功。
4.2 實時樣本歸因(Label)
樣本歸因即是樣本打標過程,根據使用者的對item所表現的行為結果來判斷是正樣本還是負樣本,樣本歸因對於樣本的真實準確是極為重要的,也是直接影響到學習到的模型是否偏置。
業內常見的樣本歸因方式有兩種:
- 負樣本cache:facebook提出的負樣本cache歸因法,國內蘑菇街採用這種方式。負樣本Cache,等待潛在正樣本做選擇, 樣本歸因準確,訓練成本較低, 存在視窗,準實時。
- Twice Fast-Train:twitter提出的樣本矯正法(FN校準/PU loss),國內愛奇藝也是採用這種方式。正負兩次快速更新訓練,樣本無需顯示歸因、無等待視窗,更加實時,對流式訓練要求高,且強依賴矯正策略,準確性難以保證。
結合我們現有工程特點及業務特性,在實時性與準確性上做了折中權衡,採用了負樣本Cache+增量視窗修正方式,具體如下:
我們以一個首頁直播場景為例,這個場景的學習目標是ctcvr,ctr_label、cvr_label分別是是否有點選、是否有效觀看的樣本標籤,對於每條樣本來說,會存在以下三種情況:
ctr_label | cvr_label | feature{1...n} | 樣本說明 |
---|---|---|---|
0 | 0 | {fea1:[0,1,3],fea2:[100]} | 曝光無點選 |
1 | 0 | {fea1:[0,1,3],fea2:[100]} | 曝光有點選,但無有效觀看 |
1 | 1 | {fea1:[0,1,3],fea2:[100]} | 曝光有點選,且有效觀看 |
同一個使用者對同一個item可能會產生多條ua日誌,比如itemId=10001主播來說,推薦曝光之後會產生一條impress ua,如果userId=50001使用者對它進行了點選就會產生一條click ua,如果userId=50001使用者點選之後有效觀看了就產生一條play ua 。但在最後生成樣本時,只可能保留一條ua,也就是隻可能存在上面三種情況的其中一種,如果是離線樣本的話,樣本歸因通常在spark按照GroupBy(recId_userId_itemId)進行count,然後保留行為鏈路中最後一條ua。但如果是實時樣本的話,樣本歸因該怎麼做呢?
在延遲處理環節(延遲處理根據目標轉化時間來暫時評估的,後續可通過監控統計來調整或者離線統計如果95%都能在設定的時間視窗完成轉化即可),比如首頁直播場景評估為10分鐘。
- 預存kv: 只存曝光之後的行為標誌,如click、playend,key:recId_userId_itemId_action,value: 1。
- 當impress ua 來到時,根據recId_userId_itemId_1查預存KV,如果能查到有click行為,就丟棄當前的ua,否則標記ctr_label=0,cvr_label=0;
- 當click ua來到時,然後根據recId_userId_itemId_2查預存KV,如果能查到有play行為,就丟棄當前的ua, 否則標記ctr_label=1,cvr_label=0;
- 當play ua來到時,根據播放時長,如果播放時長小於有效播放時長,則標記ctr_label=1,cvr_label=0. 如果播放時長大於等於有效播放時長,則標記ctr_label=1,cvr_label=1;
在技術實現上通過flink的keyBy+state+timer來實現樣本歸因過程的:
- 對輸入的流按照join的key:recId_userId_itemId_ts進行keyBy操作,如果keyBy操作有資料傾斜情況,可在操作之前加一個隨機數。
- 對keyBy後的流進行KeyedProcessFunction處理,在KeyedProcessFunction中定義一個ValueState,重寫processElement方法,在processElement方法中判斷,如果value state為空,則new 一個新的state,並將資料寫到value state中,並且為這條資料註冊一個timer(timer會由Flink按key+timestamp自動去重)。
- 重寫onTimer方法,在onTimer方法中主要是定義定時器觸發時執行的邏輯 : 處理邏輯即為如上的歸因邏輯。
當然也存在一些特殊情況,如某些使用者對item存在跨視窗的行為,或者因為埋點上報的機制有關,比如首頁息屏再開啟,可能不會重新請求重新整理item,而item是上一次推薦的結果。這個直接影響到實時歸因樣本的效果,可能對同一個使用者同一個item來說,上一個視窗它可以歸因到負樣本,下一個視窗可能歸因到正樣本。通常我們可以在離線再進行一次小批量歸因處理。
- 統計在groupby(recid,userid,itemId)後,是否存在>1 。 如果有,只保留Max(sum(ctr_label,cvr_label))那條樣本。
4.3 實時樣本拼接(Sample)
有了實時特徵快照Snapshot與實時Label,接下來就是進行實時樣本拼接,主要工作包含Join關聯,特徵抽取處理,樣本輸出。
1、Join關聯
在Flink任務中,拿到label的一條記錄後,按照key=recId_userId_itemId查詢kv,查到snapshot後拼成一條寬記錄。
2、特徵抽取
join關聯拼接成寬記錄後,但有的snapshot特徵它是原始特徵,需要進行特徵抽取計算,計算的邏輯要保持與線上預估時候的一致。因此需要將線上的特徵抽取打成JAR給到線下Flink中使用,這樣保證了上面提到的線上線下特徵計算一致性。
3、樣本輸出
按照不同的訓練格式進行Format,常見的如tfrecord、parquet,然後輸出到hdfs中去。
4.4 Flink任務開發
由於實時樣本的流程及任務比較多,想要快速實現實時樣本快速落地,快速覆蓋更多的業務場景,我們也在任務開發上也做了一些工作。
1、模板化開發,CICD
將任務抽象成四個,分別為:snapshot採集任務、Label任務、Join任務、Sampling取樣任務(可選),並每個任務進行介面封裝,實現模板化開發,簡化任務開發流程,自動化建立工程、編譯打包、建立任務,具備可持續整合CICD的能力。
2、任務血緣
同一個場景,DAG串聯,讓任務血緣更加清晰。
4.5 樣本監控
實時樣本生成過程是實時化的,涉及到環節特別多,而樣本的穩定性、樣本的內容如何對於樣本最後的效果及其重要的。 所以樣本監控是主動發現樣本的異常情況,那麼監控可分為系統監控、內容監控兩個方面。這裡重點強調內容監控。
- 系統監控:線上旁路系統監控、kv儲存監控、Flink監控。
內容監控:拼接率監控、特徵分佈監控、時間跨度監控
- 拼接率監控:即label與snapshot能通過recId關聯成功率,反映了樣本真實有效的水平。
- 特徵分佈監控:比如性別、年齡分佈的監控,可以反映特徵內容特徵分佈情況,來判斷當前的樣本是否可靠。
時間跨度分佈:推薦出去一次item到ua迴流的時間差、snapshot旁路採集到落kv的時間差
5 線上效果
我們整個模型實時模型方案上線,也取得比較喜人的結果。結合上述樣本歸因的處理、離線模型熱啟動重啟以及特徵准入方案,我們最終在首頁直播模組推薦場景取得轉化率:平均 24 天相對提升 +5.219% ;點選率:平均 24 天相對提升 +6.575%的效果。
並且我們針對不同的模型更新頻率進行了多種方案的測試,如下圖,ABTest t1 組為離線日更模型,每天更新替換模型檔案;t2 組為 2 小時更新模型,模型每兩個小時增量訓練;t8 組為 15 分鐘更新模型,模型每 15 分鐘增量訓練模型。經過我們多次測試,發現模型更新越快效果更佳也更佳穩定。
6 總結與展望
直播推薦業務有著其不同於其他業務的場景特色,推薦的不僅是一個 Item 更是狀態,進而直播推薦需要更快、更高、更強的推薦演算法來支援業務的發展。本文從工程落地角度來闡述模型實時化中實時樣本生產的過程,分享我們雲音樂在工程實踐過程中遇到的一些問題的經驗,當然不同公司的背景可能不同,找到一些適合自己能實際落地的才是正確的路線。接下來我們會做更多的業務覆蓋,持續為業務實現突破,另外也希望從工程上為演算法建模流程提供更多的能力。
參考文獻
- Cheng Li, Yue Lu, Qiaozhu Mei, Dong Wang, and Sandeep Pandey. 2015. Click-through Prediction for Advertising in Twitter Timeline. In Proceedings of the 21th ACM SIGKDD International Conferen
- Xinran He, Junfeng Pan, Ou Jin, Tianbing Xu, Bo Liu, Tao Xu, Yanxin Shi, Antoine Atallah, Ralf Herbrich, Stuart Bowers, and Joaquin Quiñonero Candela. 2014. Practical Lessons from Predicting Clicks on Ads at Facebook. In Proceedings of the Eighth International Workshop on Data Mining for Online Advertising (ADKDD’14). ACM, New York, NY, USA, , Article 5 , 9 pages.
- 淘寶搜尋模型如何全面實時化?首次應用於雙11[1]
- 螞蟻金服核心技術:百億特徵實時推薦演算法揭祕.[2]
- 線上學習在愛奇藝資訊流推薦業務中的探索與實踐.[3]
- 蘑菇街首頁推薦視訊流——增量學習與wide&deepFM實踐(工程+演算法)[4]
參考資料
[1]淘寶搜尋模型如何全面實時化?首次應用於雙11: https://developer.aliyun.com/...[2]螞蟻金服核心技術:百億特徵實時推薦演算法揭祕.: https://zhuanlan.zhihu.com/p/...[3]線上學習在愛奇藝資訊流推薦業務中的探索與實踐.: https://www.infoq.cn/article/...[4]蘑菇街首頁推薦視訊流——增量學習與wide&deepFM實踐(工程+演算法): https://zhuanlan.zhihu.com/p/...
本文釋出自網易雲音樂技術團隊,文章未經授權禁止任何形式的轉載。我們常年招收各類技術崗位,如果你準備換工作,又恰好喜歡雲音樂,那就加入我們staff.musicrecruit@service.ne...