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

特征工程到底是什麽?

2015-04-05知識

前言

記得我入演算法這一行的第一份工作面試的時候,最終的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)的形式完成
  • 短期歷史的統計,會直接存取線上緩存(redis)
  • 物料粒度
  • 可以是item id
  • 也可以是item上的內容,比如一二級分類、標簽、關鍵詞、...
  • 動作型別
  • 正向:點選、有效觀看、完整觀看、點贊、轉發、評論、...
  • 負向:忽略(曝光未點選)、短播、踩、...
  • 統計物件
  • 次數、時長、金額、...
  • 統計方法
  • 收整合列表、計算XTR、計算占比、...
  • 透過以上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小時,點選的文章列表
  • 使用者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】一文中提出了用預訓練來解決以上難題的思路

  • 訓練一個GNN模型
  • 圖的頂點就是樣本中出現過的categorical feature,樣本中常見共現特征之間建立邊,邊上的值就是這一對兒特征離線統計出的xtr
  • 按照link prediction的方式來訓練GNN
  • 線下訓練和線上預測時,將每個使用者特征與每個物料特征兩兩組合,每對兒特征餵入訓練好的GNN模型,返回預估xtr作為特征。
  • 這種作法,即節省了儲存+檢索海量特征組合的開銷,又因為GNN本身也是基於embedding的,對於罕見或少見特征對的擴充套件性也比較好。
  • 此篇文章的重點是「用預估代替統計+儲存」的思路,是否必須用GNN模型,我看倒也未必。如果擔心GNN線上預估耗時問題,我看用FM模型也不錯:

  • 訓練一個常規的FM模型預估xtr
  • 模型訓練好後,將每個categorical feature的embedding儲存起來
  • 線下訓練或線上預估時,將所有使用者特征與所有物料特征,兩兩組合。
  • 針對每一對兒特征,獲取各自的feature embedding,點積再sigmoid,就得到這一對兒特征上的預估xtr,供訓練或預測
  • 後記

    多多少少受「DNN萬能函式模擬器」、「DNN能讓特征充分交叉」的神話影響,業界對特征工程的研究,遠不如模型結構那麽熱鬧。

    本文希望傳遞這樣一種思想,即便在DNN時代,特征工程仍然值得我們投入精力,深入研究。相比於粗獷地將原始特征一古腦地扔進DIN/DIEN/SIM這些「強但重」的模型,在原始特征上施以精細的刀工,挖掘出更加直白的資訊餵到模型嘴邊上,

  • 一來,令模型對特征中的資訊「吸收」更好
  • 二來,離線挖掘出強有力的特征,某種程度上能夠取代那些「強但重」的模型結構,節省預測耗時,適用於召回+粗排這些計算緊張的場景
  • 其實輸入側的故事,還遠沒有結束。

  • 挖掘出重要特征,如何接入DNN才能讓它們發揮使用?是不是和其他特征一樣,餵到DNN的最底層,讓它們的資訊慢慢逐層向上傳遞?關於這一點,請出門左轉,看我的另一篇文章【 先入為主:將先驗知識註入推薦模型 】。
  • 如我之前所述,推薦系統中,ID類特征才是一等公民。而文中介紹的重要特征,大多是實數型的。如何將這些重要的實數型特征轉化為id類特征再進行embedding,業界最近也有一些新的研究成果,突破了傳統的bucketize + embedding table的老套路,準備在下一篇文章中總結一下。欲知後事如何,請聽下回分解:-)