記得我入算法這一行的第一份工作面試的時候,最終的boss面的面試官是前微軟工程院的副院長。面試進行得很順利,不免向前院長賣弄一番,談了談我對算法的理解。我說算法工程師就好比廚師,模型是灶上功夫,而數據預處理+特徵工程就好比刀工。再好的食材,不切不洗,一古腦地扔下鍋,熟不熟都會成問題,更甭提味道了。好的刀工能夠將食材加工成合適的形狀,無需烈火烹油,也能做出好味道。同理,特徵工程做得好,簡單模型也能做出不錯的效果,當然有了nb的模型,「或許」能夠錦上添花。(這個故事的後半段是,前副院長聽了我的比喻,問了我一個問題:有沒有比刀工更重要的呢?我不記得當時是怎麼回答的了,應該是答得不好。副院長說,好的廚師要擅於挑選食材,所以好的算法工程師要會挑選數據。嗯,這個道理等到我寫《負樣本為王》的時候,理解得就更通透了。負樣本挑錯了,再好的特徵工程也無法挽救你的召回模型。)
講這個故事是為了引出今天的主題,即推薦系統中的特徵工程。這個話題比較大,是單單一篇文章無法涵蓋的,所以也要先做一下限定。
首先,本文不介紹特徵工程的基礎套路。所謂基礎套路,就是大家耳熟能詳的那些常規原始特徵。
用戶側無非就是人口屬性(性別、年齡、職業、位置等)、用戶安裝的app列表、用戶的各種觀看+互動歷史那一堆;物料側無非就是基本屬性(作者、長度、語言),還有基於內容理解(thanks to nlp and cv)技術打上的一堆一/二級分類、標籤、關鍵詞等之所以不講這些,一來獲得這些信息,更多依賴產品、前端、內容理解等團隊,算法工程師的作用有限,等着用就是了;二來,根據我的個人經驗,這些未經加工的原始信息發揮的作用有限。即使是對於新用戶,一些產品同學往往以為知道了用戶的性別、年齡,甚至安裝的app,就能猜出用戶興趣,從而提升冷啟動中的個性化成分,結果往往令人失望。
其次,我還是要再次批判「深度學習使特徵工程過時」的論調。比如,用了阿里的DIxN家族(DIN/DIEN/DSIN)能夠捕捉用戶的短期興趣,用了SIM能夠捕捉用戶的長期興趣,那麼未來是不是沒必要再對用戶歷史做特徵工程了,把一堆用戶觀看+互動過的item id扔進DIxN+SIM不就行了,省時省力,效果也好?我的答案是否定的,原因有二:
2021年留給我最深印象的就是DCN作者的那一句話「People generally consider DNNs as universal function approximators, that could potentially learn all kinds of feature interactions. However, recent studies found that DNNs are inefficient to even approximately model 2nd or 3rd-order feature crosses.」。DNN的「火力」沒那麼強,原材料一古腦扔進去,有時熟不熟都會成問題,更何況各種滋味的融會貫通。各種強大的網絡結構好是好,但並不是無代價的。SIM中用target item去檢索用戶的長期歷史再做attention,都是線上inference的耗時大頭。做做候選item只有幾百的精排倒還可以,但是讓候選item有成千上萬的粗排和召回,情何以堪?難道不用SIM就不能刻畫用戶的長期興趣了嗎?當然不是,接下來要介紹的幾個特徵工程上的技巧就能夠派上用場。根據Lambda架構中「線上層--近線層--離線層」的劃分,將計算壓力從線上轉移到線下,離線挖掘用戶長期興趣,使召回+粗排環節也能夠用得上。接下來,就介紹推薦系統特徵工程中的幾個高級技巧,「奇技淫巧」,將原始特徵加工成「讓模型更好吸收」的形狀。
用戶特徵無疑,描述用戶興趣最有用的信息就是各種用戶歷史。如上所述,除了將用戶觀看+交互過的item id一古腦地扔進DIN/DIEN/SIM,讓模型自己學出用戶各種長短期歷史,我們還可以從這些用戶歷史中手工挖掘出一些有用的信號。
我的一個同事提出從以下6個維度挖掘用戶的長短期興趣
某一群用戶:比如相同年齡段、同性別、同地域、安裝了同款app的人群。基於人群的統計,對於新用戶冷啟意義重大。最近、過去x小時、過去1天、過去1周、過去1月、從用戶首次使用app至今、...太長的時間粒度(e.g.,首次使用至今)在統計的時候,會考慮時間衰減長期歷史的統計,會通過離線批量任務(hadoop/spark)的形式完成也可以是item上的屬性,比如一二級分類、標籤、關鍵詞、...正向:點擊、有效觀看、完整觀看、點讚、轉發、評論、...通過以上6個維度的交叉,我們可以構造出一系列特徵來描述用戶的長期(e.g., 首次使用app至今)、短期(e.g. 過去1天)、超短期(e.g., 剛剛觀看的x個視頻)的興趣。比如:
用戶A,在過去1天,對tag="坦克"的CTR(i.e., 系統在過去1天,給該用戶推了10篇帶tag="坦克"的文章,該用戶點擊了6篇,ctr=0.6)用戶A,在過去1天,對tag=「坦克」的點擊占比(i.e., 在過去1天,用戶一共點擊了10文章,其中6篇帶tag="坦克",點擊占比=0.6)用戶A,在過去1天,對tag=「坦克」的時長占比(i.e., 在過去1天,用戶一共播放了100分鐘,其中60分鐘消費在帶tag="坦克"的物料上,時長占比=0.6)用戶A,在過去1天,忽略(隱式負反饋)category=「時尚」的item個數用戶A,自首次使用app至今,對tag='坦克'的CTR(統計時,曝光數與點擊數都要經過時間衰減)男性用戶,在過去1月,對tag="坦克"的文章的CTR注意:
以上6個維度只是為我們手工挖掘用戶興趣提供了一個框架,使我們添加特徵時更有章法。至於具體要離線挖掘哪些特徵,也要根據算力和收益,綜合考慮;這些手工挖掘的用戶興趣信號,可以作為DIN/DIEN/SIM挖掘出來的用戶興趣的補充。而在召回/粗排這種計算壓力大的環節,由於可以離線挖掘而節省線上耗時,以上這些手工挖掘出的用戶長短期興趣可以(局部)代替DIxN/SIM這些「強但重」的複雜模型。物料特徵對於item側,我認為最重要的特徵就是這些item的後驗統計數據,
時間粒度:全部歷史(帶衰減)、過去1天,過去6小時,過去1小時、......統計對象:CTR、平均播放進度、平均消費時長、......比如:某文章在過去6小時的CTR,某文章在過去1天的平均播放時長、......
但是也要謹記,
這些統計數據肯定是有偏的,一個item的後驗指標好,只能說明推薦系統把它推薦給了對的人,並不意味着把它推給任何人都能取得這麼好的效果。其實這個問題其實也不大,畢竟交給精排模型打分的都已經通過了召回+粗排的篩選,多多少少是和當前用戶相關的,之前的統計數據還是有參考意義。利用這些後驗統計數據做特徵,多少有些縱容馬太效應,之前後驗數據好的item可能會被排得更靠前,不利於新item的冷啟。那麼新item沒有後驗證數據怎麼辦?填寫成0豈不是太受歧視了?其實有一個辦法就是建立一個模型,根據物料的靜態信息(e.g., 作者、時長、內容理解打得各種標籤等基本穩定不變的信息)來預測它們的後驗數據。另外再介紹一個由用戶給物料反向打標籤的trick。
一般畫像的流程,都是先有物料標籤,再將用戶消費過的物料的標籤積累在用戶身上,形成用戶畫像。反向打標籤是指,將消費過這個物料的用戶身上的標籤積累到這個物料身上。比如:一篇關於某足球明星八卦緋聞的文章,由於該明星的名字出現頻繁,NLP可能會將其歸為「體育新聞」,但是後驗數據顯示,帶「體育」標籤的用戶不太喜歡這篇文章,反而帶「娛樂」標籤的用戶更喜歡,顯然這篇文章也應該被打上「娛樂」的標籤。類似的,給物料打上「小資文青喜歡的top10電影之一」,或者「在京日本人光顧最多的日料店」等,都是由用戶消費反向給物料打上的極其重要的標籤。交叉特徵說到交叉特徵,受「DNN萬能論」的影響,近年來已經不再是研究+關注的熱點。既然DNN"能夠"讓餵入的特徵「充分」交叉,那何必再費神費力地去做交叉特徵。
我的答案還是那兩條,一來,不要再迷信DNN"萬能函數模擬器"的神話;二來,手動交叉的特徵猶如加工好的食材,這麼強烈的信號遞到模型的嘴邊上,模型沒必要費力咀嚼,更容易被模型消化吸引。
比較簡單的一種交叉特徵就是計算用戶與物料在某個維度上的相似度。以「標籤」維度舉例:
某用戶過去7天在「標籤」維度上的畫像,可以寫成一個稀疏向量,u={『坦克』:0.8, 『足球』:0.4, '二戰':0.6, '檯球':-0.3}。每個標籤後面的分數是由第2節的方法統計出的用戶在某個標籤上的興趣強度(可以根據xtr/時長等指標計算出);某篇文章的標籤,可以寫成一個稀疏向量,d={'坦克':1, '二戰':0.5, '一戰':0.8}。每個標籤後面的分數是NLP在打這個標籤時的置信度;拿這兩個稀疏向量做點積,u x d={『坦克』:0.8, 『足球』:0.4, '二戰':0.6, '檯球':-0.3} x {'坦克':1, '二戰':0.5, '一戰':0.8}=0.8*1+0.5*0.6=1.3,就可以表示這個用戶對這篇文章在「標籤」維度上的匹配程度。另一個複雜一點的交叉特徵是任意一對兒<user特徵,item特徵>上的消費指標,比如<男性用戶,item標籤=「足球」>這一特徵對上的xtr。要得到這樣的特徵,離線統計當然是可以的,數數<男性用戶,item標籤=「足球」>共同出現的樣本有多少,其中帶正標籤的樣本又有多少,兩者一除就能得到。但是這麼做,有兩個缺點:
數量太多了,我們要統計、存儲的特徵對兒的量是"所有用戶特徵 * 所有物料特徵",要耗費大量的資源,線上檢索起來也是個麻煩擴展性太差。只有對共現超過一定次數的特徵對上的xtr才置信,才有保留價值。對於共現次數較少,甚至沒有共現過的特徵對上的xtr,我們也拿不到。為了克服以上缺點,阿里媽媽在SIGIR 21《Explicit Semantic Cross Feature Learning via Pre-trained Graph Neural Networks for CTR Prediction》一文中提出了用預訓練來解決以上難題的思路
圖的頂點就是樣本中出現過的categorical feature,樣本中常見共現特徵之間建立邊,邊上的值就是這一對兒特徵離線統計出的xtr按照link prediction的方式來訓練GNN線下訓練和線上預測時,將每個用戶特徵與每個物料特徵兩兩組合,每對兒特徵餵入訓練好的GNN模型,返回預估xtr作為特徵。這種作法,即節省了存儲+檢索海量特徵組合的開銷,又因為GNN本身也是基於embedding的,對於罕見或少見特徵對的擴展性也比較好。此篇文章的重點是「用預估代替統計+存儲」的思路,是否必須用GNN模型,我看倒也未必。如果擔心GNN線上預估耗時問題,我看用FM模型也不錯:
模型訓練好後,將每個categorical feature的embedding存儲起來線下訓練或線上預估時,將所有用戶特徵與所有物料特徵,兩兩組合。針對每一對兒特徵,獲取各自的feature embedding,點積再sigmoid,就得到這一對兒特徵上的預估xtr,供訓練或預測後記多多少少受「DNN萬能函數模擬器」、「DNN能讓特徵充分交叉」的神話影響,業界對特徵工程的研究,遠不如模型結構那麼熱鬧。
本文希望傳遞這樣一種思想,即便在DNN時代,特徵工程仍然值得我們投入精力,深入研究。相比於粗獷地將原始特徵一古腦地扔進DIN/DIEN/SIM這些「強但重」的模型,在原始特徵上施以精細的刀工,挖掘出更加直白的信息餵到模型嘴邊上,
二來,離線挖掘出強有力的特徵,某種程度上能夠取代那些「強但重」的模型結構,節省預測耗時,適用於召回+粗排這些計算緊張的場景其實輸入側的故事,還遠沒有結束。
挖掘出重要特徵,如何接入DNN才能讓它們發揮使用?是不是和其他特徵一樣,餵到DNN的最底層,讓它們的信息慢慢逐層向上傳遞?關於這一點,請出門左轉,看我的另一篇文章《先入為主:將先驗知識注入推薦模型》。如我之前所述,推薦系統中,ID類特徵才是一等公民。而文中介紹的重要特徵,大多是實數型的。如何將這些重要的實數型特徵轉化為id類特徵再進行embedding,業界最近也有一些新的研究成果,突破了傳統的bucketize + embedding table的老套路,準備在下一篇文章中總結一下。欲知後事如何,請聽下回分解:-)歡迎乾貨投稿 \論文宣傳\合作交流推薦閱讀
由於公眾號試行亂序推送,您可能不再準時收到機器學習與推薦算法的推送。為了第一時間收到本號的乾貨內容, 請將本號設為星標,以及常點文末右下角的「在看」。