今天我們剖析的也是推薦領域的經典論文,叫做Wide & Deep Learning for Recommender Systems。它發表於2016年,作者是Google App Store的推薦團隊。這年剛好是深度學習興起的時間。這篇文章討論的就是如何利用深度學習模型來進行推薦系統的CTR預測,可以說是在推薦系統領域一次深度學習的成功嘗試。
著名的推薦模型Wide & deep就是出自這篇論文,這個模型因為實現簡單,效果不俗而在各大公司廣泛應用。因此它同樣也可以認為是推薦領域的必讀文章之一。
摘要
在大規模特徵的場景當中,我們通常(2016年之前)是使用將非線性特徵應用線上性模型上的做法來實現的,使用這種方式,我們的輸入會是一個非常稀疏的向量。雖然我們要實現這樣的非線性特徵,通過一些特徵轉化以及特徵交叉的方法是可以實現的,但是這會需要消耗大量的人力物力。
這個問題其實我們之前在介紹FM模型的時候也曾經提到過,對於FM模型來說,其實解決的也是同樣的問題。只是解決的方法不同,FM模型的方法是引入一個n x k的引數矩陣V來計算所有特徵兩兩交叉的權重,來降低引數的數量以及提升預測和訓練的效率。而在本篇paper當中,討論的是使用神經網路來解決這個問題。
解決問題的核心在於embedding,embedding直譯過來是嵌入,但是這樣並不容易理解。一般來說我們可以理解成某些特徵的向量表示。比如Word2Vec當中,我們做的就是把一個單詞用一個向量來表示。這些向量就稱為word embedding。embedding有一個特點就是長度是固定的,但是值一般是通過神經網路來學習得到的。
我們可以利用同樣訓練embedding的方式來在神經網路當中訓練一些特徵的embedding,這樣我們需要的特徵工程的工作量就大大地減少。但是僅僅使用embedding也是不行的,在一些場景當中可能會引起過擬合,所以我們需要把線性特徵以及稀疏特徵結合起來,這樣就可以讓模型既不會陷入過擬合,又可以有足夠的能力可以學到更好的效果。
簡介
正如我們之前文章所分享的一樣,推薦系統也可以看成是搜尋的排序系統。它的輸入是一個使用者資訊以及使用者瀏覽的上下文資訊,返回的結果是一個排好序的序列。
正因為如此,對於推薦系統來說,也會面臨一個和搜尋排序系統一個類似的挑戰——記憶性和泛化性的權衡。記憶性可以簡單地理解成對商品或者是特徵之間成對出現的一種學習,由於使用者的歷史行為特徵是非常強的特徵,記憶性因此可以帶來更好的效果。但是與之同時也會有問題產生,最典型的問題就是模型的泛化能力不夠。
對於泛化能力來說,它的主要來源是特徵之間的相關性以及傳遞性。有可能特徵A和B直接和label相關,也可能特徵A與特徵B相關,特徵B與label相關,這種就稱為傳遞性。利用特徵之間的傳遞性, 我們就可以探索一些歷史資料當中很少出現的特徵組合,從而獲得很強的泛化能力。
在大規模的線上推薦以及排序系統當中,比如像是LR這樣的線性模型被廣泛應用,因為這些模型非常簡單、擴充性好、效能很強,並且可解釋性也很好。這些模型經常用one-hot這樣的二進位制資料來訓練,舉個例子,比如如果使用者安裝了netflix,那麼user_installed_app=netflix這個特徵就是1,否則就是0。因此呢,一些二階特徵的可解釋性就很強。
比如使用者如果還瀏覽過了Pandora,那麼user_installed_app=netflix,impression_app=pandora這個聯合特徵就是1,聯合特徵的權重其實就是這兩者的相關性。但是這樣的特徵需要大量的人工操作,並且由於樣本的稀疏性,對於一些沒有在訓練資料當中出現過的組合,模型就無法學習到它們的權重了。
但是這個問題可以被基於embedding的模型解決,比如之前介紹過的FM模型,或者是深度神經網路。它可以通過訓練出低維度下的embedding,用embedding向量去計算得到交叉特徵的權重。然而如果特徵非常稀疏的話,我們也很難保證生成的embedding的效果。比如使用者的偏好比較明顯,或者是商品比較小眾,在這樣的情況下會使得大部分的query-item的pair對沒有行為,然而由embedding算出來的權重可能大於0,因此而導致過擬合,使得推薦結果不準。對於這種特殊的情況,線性模型的擬合、泛化能力反而更好。
在這篇paper當中,我們將會介紹Wide & Deep模型,它在一個模型當中相容了記憶性以及泛化性。它可以同時訓練線性模型以及神經網路兩個部分,從而達到更好的效果。
論文的主要內容有以下幾點:
Wide & Deep模型,包含前饋神經網路embedding部分以及以及線性模型特徵轉換,在廣義推薦系統當中的應用 Wide & Deep模型在Google Play場景下的實現與評估,Google Play是一個擁有超過10億日活和100w App的移動App商店
推薦系統概述
這是一張經典的推薦系統的架構圖:
當使用者訪問app store的時候會生成一個請求,這個請求當中會包含使用者以及上下文的特徵。推薦系統會返回一系列的app,這些app都是模型篩選出來使用者可能會點選或者是購買的app。當使用者看到這些資訊之後,會產生一些行為,比如瀏覽(沒有行為)、點選、購買,產生行為之後,這些資料會被記錄在Logs當中,成為訓練資料。
我們看下上面部分,也就是從DataBase到Retrieval的部分。由於Database當中的資料量過大,足足有上百萬。所以我們想要在規定時間內(10毫秒)給所有的app都呼叫模型打一個分,然後進行排序是不可能的。所以我們需要對請求進行Retrieval,也就是召回。Retrieval系統會對使用者的請求進行召回,召回的方法有很多,可以利用機器學習模型,也可以進行規則。一般來說都是先基於規則快速篩選,再進行機器學習模型過濾。
進行篩選和檢索結束之後,最後再呼叫Wide & Deep模型進行CTR預估,根據預測出來的CTR對這些APP進行排序。在這篇paper當中我們同樣忽略其他技術細節,只關注與Wide & Deep模型的實現。
Wide & Deep原理
首先我們來看下業內的常用的模型的結構圖:
這張圖源於論文,從左到右分別展示了Wide模型,Wide & Deep模型以及Deep模型。從圖上我們也看得出來所謂的Wide模型呢其實就是線性模型,Deep模型是深度神經網路模型。下面結合這張圖對這兩個部分做一個詳細一點的介紹。
Wide部分
Wide部分其實就是一個泛化的形如的線性模型,就如上圖左邊部分所展示的一樣。y是我們要預測的結果,x是特徵,它是一個d維的向量。這裡的d是特徵的數量。同樣w也是一個d維的權重向量,b呢則是偏移量。這些我們在之前線性迴歸的模型當中曾經都介紹過,大家應該也都不陌生。
特徵包含兩個部分,一種是原始資料直接拿過來的資料,另外一種是我們經過特徵轉化之後得到的特徵。最重要的一種特徵轉化方式就是交叉組合,交叉組合可以定義成如下形式:
這裡的是一個bool型的變數,表示的是第i個特徵的第k種轉化函式的結果。由於使用的是乘積的形式,只有所有項都為真,最終的結果才是1,否則是0。比如"AND(gender=female,language=en)"這就是一個交叉特徵,只有當使用者的性別為女,並且使用的語言為英文同時成立,這個特徵的結果才會是1。通過這種方式我們可以捕捉到特徵之間的互動,以及為線性模型加入非線性的特徵。
Deep部分
Deep部分是一個前饋神經網路,也就是上圖當中的右側部分。
我們觀察一下這張圖會發現很多細節,比如它的輸入是一個sparse的feature,可以簡單理解成multihot的陣列。這個輸入會在神經網路的第一層轉化成一個低維度的embedding,然後神經網路訓練的是這個embedding。這個模組主要是被設計用來處理一些類別特徵,比如說item的類目,使用者的性別等等。
和傳統意義上的one-hot方法相比,embedding的方式用一個向量來表示一個離散型的變數,它的表達能力更強,並且這個向量的值是讓模型自己學習的,因此泛化能力也大大提升。這也是深度神經網路當中常見的做法。
Wide & Deep合併
Wide部分和Deep部分都有了之後,通過加權的方式合併在一起。這也就是上圖當中的中間部分。
最上層輸出之前其實是一個sigmoid層或者是一個linear層,就是一個簡單的線性累加。英文叫做joint,paper當中還列舉了joint和ensemble的區別,對於ensemble模型來說,它的每一個部分是獨立訓練的。而joint模型當中的不同部分是聯合訓練的。ensemble模型當中的每一個部分的引數是互不影響的,但是對於joint模型而言,它當中的引數是同時訓練的。
這樣帶來的結果是,由於訓練對於每個部分是分開的,所以每一個子模型的引數空間都很大,這樣才能獲得比較好的效果。而joint訓練的方式則沒有這個問題,我們把線性部分和深度學習的部分分開,可以互補它們之間的缺陷,從而達到更好的效果,並且也不用人為地擴大訓練引數的數量。
系統實現
app推薦的資料流包含了三個部分:資料生產、模型訓練以及模型服務。用一張圖來展示大概是這樣的:
資料生產
在資料生產的階段,我們使用app在使用者面前曝光一段時間作為一個樣本,如果這個app被使用者點選安裝,那麼這個樣本被標記為1,否則標記為0。這也是絕大多數推薦場景下的做法。
在這個階段,系統會去查表,把一些字串類別的特徵轉化成int型的id。比如娛樂類的對應1,攝影類的對應2,比如收費的對應0,免費的對應1等等。同時會把數字型別的特徵做標準化處理,縮放到[0, 1]的範圍內。
模型訓練
paper當中提供了一張模型的結構圖:
從上圖當中我們可以看到,左邊是一些連續性的特徵,比如年齡,安裝的app數量等等,右邊是一些離散型的特徵,比如裝置資訊,安裝過的app等等。這些離散型的特徵都會被轉化成embedding,之後和右邊的連續性特徵一起進入神經網路進行學習。paper當中使用的是32維的embedding。
模型每次訓練會使用超過500 billion的樣本數量進行訓練,每次蒐集到了新的訓練資料都會訓練模型。但是如果每一次訓練我們都從頭開始的話,顯然會非常緩慢,並且會浪費大量的計算資源。因此paper當中選擇了一種增量更新的模式,也就是說在模型更新的時候,會載入舊模型的引數,再使用最新的資料進行更新訓練。在新模型更新上線之前,會先驗證模型的效果,確認效果沒有問題之後再進行更新。
模型服務
當模型被訓練好被載入進來之後,對於每一個請求,伺服器都會從recall系統當中獲取一系列候選的app,以及使用者的特徵。接著呼叫模型對每一個app進行打分,獲取了分數之後,伺服器會對候選的app按照分數從高到低進行排序。
為了保證伺服器的響應能力,能夠在10ms時間內返回結果,paper採取了多執行緒併發執行的方法。老實講,我覺得這份資料有點虛。因為現在的模型沒有不使用併發執行的,但即使是併發執行,使用深度學習進行預測也很難保證效率能夠到達這種程度。也許是還採用了其他的一些優化,但是paper裡沒有全寫出來。
模型結果
為了驗證Wide & Deep模型的效果,paper在真實的場景當中從兩個角度進行了大量的測試。包括app的獲取量以及服務的表現。
App 獲取量
線上上環境進行了為期3周的A/B測試,1個桶作為對照桶,使用之前版本的線性模型。1個桶使用Wide & Deep模型,另外一個桶只使用Deep模型,去除了linear的部分。這三個桶各自佔據了1%的流量,最後得到的結果如下:
Wide & Deep模型不僅AUC更高,並且線上APP的獲取量也提升了3.9%。
服務效能
對於推薦系統來說,服務端的效能一直是一個很大的問題,因為既需要承載大量的流量,也需要保證延遲非常短。而使用機器學習或者是深度學習模型來進行CTR的預測,本身的複雜度是非常高的。根據paper當中的說法,高峰時期,他們的伺服器會承載1千萬的qps。
如果使用單執行緒來處理一個batch的資料需要31毫秒,為了提升速度,他們開發了多執行緒打分的機制,並且將一個batch拆分成了幾個部分進行併發計算。通過這樣的方式,將客戶端的延遲降低到了14毫秒。
程式碼實現
光說不練假把式,Wide & Deep在推薦領域一度表現不俗,並且模型的實現也不復雜。我曾經使用Pytorch實現過一個簡易版本,貼出來拋磚引玉給大家做一個參考。
import torch
from torch import nn
class WideAndDeep(nn.Module):
def __init__(self, dense_dim=13, site_category_dim=24, app_category_dim=32):
super(WideAndDeep, self).__init__()
# 線性部分
self.logistic = nn.Linear(19, 1, bias=True)
# embedding部分
self.site_emb = nn.Embedding(site_category_dim, 6)
self.app_emb = nn.Embedding(app_category_dim, 6)
# 融合部分
self.fusion_layer = nn.Linear(12, 6)
def forward(self, x):
site = self.site_emb(x[:, -2].long())
app = self.app_emb(x[:, -1].long())
emb = self.fusion_layer(torch.cat((site, app), dim=1))
return torch.sigmoid(self.logistic(torch.cat((emb, x[:, :-2]), dim=1)))
由於我當時的應用場景比較簡單,所以網路結構只有三層,但是原理是一樣的,如果要應用在複雜的場景當中,只需要增加特徵以及網路層次即可。
今天的文章就到這裡,衷心祝願大家每天都有所收穫。如果還喜歡今天的內容的話,請來一個三連支援吧~(點贊、關注、轉發)
{{uploading-image-891321.png(uploading...)}}