用戶行為序列推薦模型
- 2019 年 12 月 20 日
- 筆記

文章作者:汪劍 出門問問 算法工程師
編輯整理:Hoh Xil
內容來源:作者原創分享
文章出品:DataFunTalk
註:歡迎轉載,轉載請留言。
導讀:今天我們談談用戶行為序列上的推薦模型。首先我們對序列推薦問題做一個定義和描述,然後主要講述可以用在序列推薦任務中的 NN 模型,最後給出一點個人看法以及文中相關的參考文獻供參閱。
用戶行為大多數情況下都是存在時間上的先後關係的,在某一個時刻向用戶推薦哪些物品一般是根據當前時刻之前用戶的行為來做決策的,我們可以將序列推薦問題看做是在時間維度去學習一個模型策略來根據用戶過去的行為歷史來預測用戶將來感興趣的物品。
相對基於序列的推薦模型則是非序列化的推薦模型,如經典的矩陣分解模型和圖模型,如圖1。這兩種模型主要考慮通過節點之間的鄰接關係進行建模,時序通常是作為其中一個的隱式特徵或者約束加入模型中來進行學習的。而在序列模型中,時間先後順序是作為一個顯式的強制約束加入模型中的 ( t 時刻根據之前的行為進行學習,然後 t+1 時刻根據之前的行為進行學習 ),因此序列模型中可以防止 future information leakage,對於 next items recommendation 的任務是比較適合的模型。

圖1 序列推薦模型與非序列推薦模型
序列模型在我們的日常生活當中也存在着不少應用場景。比如金融交易中的股票漲跌預測以及自然語言處理中的語言模型。可以把一個用戶在過去一段時間內發生過行為的物品集合 S 定義為:

其中item表示物品信息,如物品 ID 以及相關屬性,action 表示用戶行為信息,如行為類型:瀏覽 ( impression ),點擊 ( click ),購買 ( buy ) 等,timestamp 表示行為發生的時間。context 為事件的上下文信息。將 S 中的元素根據 timestamp 從前到後排序後得到用戶的有序行為序列S'=

接下來我們主要討論的序列推薦則可以看做是在序列 S' 上學習的一個預測模型 P:

相對於矩陣分解和圖推薦,在序列上學習推薦模型最重要的一點是用戶某個時刻的行為只受該時刻之前的行為影響,不受用戶之後的行為影響,如圖2,t4 時刻我們預測用戶行為物品,預測的輸入只包括:

,
不包括 t5 時刻的行為。

圖2 用戶序列行為的相互影響
——數據準備——
在開始模型訓練之前,我們需要準備訓練和測試數據。序列推薦模型的輸入是用戶的行為序列數據,在多數情況下對訓練數據進行預處理對模型性能有一定幫助。我們可以先對數據中的序列信息進行一個簡單統計,進行可視化顯示以幫助我們對數據有一個總體認識,下表給出一些可以進行統計的數據範例:

針對以上的統計量,我們可以進行以下的數據過濾和處理工作:
❶ 無效用戶過濾,如 robot ,網站爬蟲等。這些用戶的行為條目數通常遠大於正常用戶的行為數,可以結合用戶行為數目的直方圖,通過一些基於統計的異常點檢測算法 ( 如檢測,MAD-基於絕對離差中位數,基於密度的檢測方法等 ) 找到這些用戶並過濾掉;
❷ 熱門用戶和熱門物品的處理,可以對熱門用戶和物品對應的數據進行降採樣,或者是進行降權,以減少熱門度對推薦結果的影響;長序列可以根據時間進行切分,減少長序列對模型收斂性的影響。
接着我們需要把預處理後的數據分成訓練集和測試集。如論文[14]中提到的,隨機的將數據集分成訓練和測試集合對於 next-item 的推薦會造成 future information leakage,一個可行方式是在用戶的行為序列上選取一個時間點作為訓練和測試集的分割點,用戶之前的行為加入訓練集,之後的行為放入測試集,如果我們只關注非重複物品的推薦,那麼同一用戶的行為物品需要在訓練和測試集之間進行去重。
數據準備好以後,下面開始模型工作。首先我們需要明確模型需要完成什麼任務,是召回還是排序,並且針對具體任務定義模型的優化指標。如果是召回,可以採用 precision_recall,F 值,如果是排序,可以採用 AUC,NDCG,MAP。在序列上做召回相當於在某個時刻去預測整個物品空間集合內用戶對各個物品發生行為反饋的概率,即一個多分類的問題,ground truth label 是該時刻用戶實際發生反饋的物品。在召回的物品集合中,接下來可以進行一個更為精細的排序,輸入是更為精細的用戶行為特徵信息以及相對較小的候選物品集合, ground truth label 是用戶對候選物品的反饋行為,然後在這個小的集合內對每一個物品進行打分,作為用戶對物品發生反饋的概率。具體構建召回和排序的流程和框架,讀者可以參看論文[14]。
——MLP——
下面即談下可以在序列推薦中使用的模型。首先第一個模型是 Multi-layer Perceptron ( 多層感知機 ),也是模型結構相對簡單但在工業界運用比較廣泛的深度模型。MLP 模型是包含多個全連接隱藏層的前向反饋模型, 輸入是之前用戶的最近 N 次行為的數據,輸出是一個固定長度的向量來表示用戶的歷史行為信息。如將圖2中的序列展開後生成的模型輸入為:[([e1], e2)),([e1, e2], e3),([e1,e2,e3], e4),([e1, e2, e3, e4], e5)],每個 tuple 的第一個元素是前 N 個時刻的行為信息,第二個元素為當前時刻的行為信息。模型先對輸入的這 N 個時刻的行為信息進行 embedding,然後通過 pooling 將這 N 個 embedding 進行聚合,生成低維度的向量,最後經過一層或者多層的全連接層生成輸出向量。

圖3 recall & ranking via MLP
圖3中右邊部分,輸出向量送給 softmax 層產生物品全集中每個物品的概率,t 時刻的物品 ID 作為 groud truth,loss 使用 sofmax cross entropy loss ( 如果物品的數目特別多,一般考慮做 sampled softmax )。物品向量即對應 softmax 權重矩陣中的列向量,然後可以用第三方平台 ( 如 fuss ) 建立向量索引。線上服務時用戶的歷史行為序列的模型向量作為 key, 通過 nearest neighbor search 完成召回。
上圖左邊部分對應在候選集合中進行排序。類比[18]中的 DSSM 結構,模型產生的輸出向量與目標物品 embedding 之間的相關性作為目標物品的排序分數,其中計算相關性可以採用以下方法:

圖4 combine embedding for prediction score
其中 history embedding ( 歷史行為向量 ) 表示為 H, target embedding ( 預測物品向量 ) 表示為 T,圖4中的幾個計算方式可以簡單表示為:
- cosine similarity:

- inner product:

- bi-linear product:

- fully-connected layer:

在實際排序過程中,除了事件中的物品 ID 可以作為特徵之外,物品本身的屬性以及事件上下文也可以用到模型中。可以如圖5,把這些特徵通過 embedding 層之後進行聚合,拼接成一個長向量,然後輸入到多層全連接網絡。

圖5 combine input embedding via concatenation
也可以每個特徵在序列維度單獨進行 embedding 和 pooling ( 各個特徵的 pooling 方式可以不一樣 ),然後在輸入全連接層之前進行聚合。

圖6 concatenate pooling output
圖6中的幾個模塊可以單獨說一下:
❶ Embedding 層
embedding 層的作用是:將序列中的事件信息映射為一個浮點數向量。每個事件的信息包括物品特徵信息以及上下文信息,其中物品的特徵包括離散型特徵 ( 物品的 ID 編號,物品的類別,物品的標籤等 ) 和連續型特徵 ( 物品的歷史熱度等 ),其中物品的 ID 和類別是每個物品唯一標識,屬於 univalent 離散特徵,而每個物品包含的標籤不止一個,為 multivalent 離散特徵。
離散特徵的向量化可以採用兩種方式:1. embedding lookup;2. onehot。embedding lookup 通過 ID 在一個大小為 M x d 的矩陣中查找到該特徵編號所對應的行,作為特徵的向量,其中 M 是該離散特徵的取值個數,d 是特徵向量的維度。onehot 則是定義一個0-1向量,其中向量長度是特徵所有取值的個數,而特徵的具體值對應向量中的下標,該下標對應的 one-hot 元素值為1,其他位置下標的值為0。如果是 multivalent 特徵,則將各個特徵取值對應的向量進行相加,得到特徵的向量。

圖7 embedding lookup and onehot
連續型的數值特徵可以直接使用,使用前一般會做歸一化,使得特徵歸一到(0,1)之間。物品的文字描述和圖片等屬於高維特徵,通常需要先降維到一個低維度的向量再使用。文字描述一般可以經過分詞去停用詞等預處理過程,然後將每個詞映射為詞向量。詞向量可使用預訓練的詞向量模型 ( 如 word2vec,glove ),也可以使用隨機初始化的詞向量。詞向量的序列然後經過 LSTM 等語言序列模型後,生成整個物品的文字描述向量。圖片的原始數據通過預處理後經過一個預訓練的模型 ( 如 vgg,inception ),取其某一層的輸出作為圖片向量 ( 如 vgg 的 fc6 )。實際中發現預訓練模型的最後幾層輸出維度比較高但是數據比較稀疏,可以加一個全連接層將高維的稀疏向量映射為一個低維向量,作為最終輸出的圖片向量。

圖8 產生圖片和文字向量
除了物品特徵信息之外,我們也可以引入事件上下文信息對用戶的時序行為進行建模,主要包括事件發生時間,地點以及事件行為類型。上下文信息的建模方式同離散特徵建模方式類似,通常上下文信息取值範圍不會太大,可以直接使用 one_hot 編碼。上下文信息中事件的時間是比較重要的信息:如前提到的,序列推薦相對於自然語言中的序列模型雖然沒有非常強的先後依賴關係,但是對於刻畫用戶的長期行為和短期行為以及用戶行為隨時間的變化趨勢,時間上下文還是非常重要的。我們通常不直接使用時間戳的絕對值,而是對時間進行分桶,如我們可以在事件發生的時間與目標預測的時間之間取一個相對間隔:

,其中 t 是當前目標預測時間戳,

是事件的時間戳,是時間間隔單位,然後對這個間隔進行分桶。分桶方式可以根據自身需求進行設計,如論文[19]中提到了一種以2的冪次方來進行分桶的方式:

時間間隔映射到上述範圍的結果作為分桶結果:

。
得到上下文向量後,如何與物品的特徵向量進行整合?簡單的方式是直接與特徵向量進行拼接,參見論文[14]。另外一種方法是建立上下文與物品特徵的 low-rank relationship:將上下文向量分別映射到物品各個特徵的向量空間內,然後與物品的特徵向量進行求和 ( + ),參見論文[12];或者進行點乘 ( * ),參見論文[16]。

圖9 上下文向量與特徵向量結合
我們為什麼要將上下文信息映射到各個物品特徵的向量空間內,而不是統一使用一個向量呢?因為每個特徵受上下文的影響程度不同,如有些特徵受時間的影響比較大,如價格,而有些特徵對時間沒有那麼敏感,比如物品的 ID 和文字描述等。將統一的上下文信息進行細粒度的劃分,能夠更好的刻畫用戶的興趣的變化趨勢。
❷ Pooling 層
通過 embedding 層得到序列向量後,pooling 層將序列向量聚合為定長的向量,一方面後繼全連接層的輸入需要固定長度的向量,另一方面通過 pooling 層我們可以獲取到用戶歷史行為序列的全局信息。pooling 層通常採用的方式有 sum pooling 或者是 average pooling。sum pooling 將序列中每個元素的向量進行累加,average pooling 則是將 sum pooling 得到的向量進行平均。簡單定義如下:


k 表示向量的第 k 維,d 為向量維度。
❸ FC 層
FC 層相對比較簡單:pooling 層的輸出向量經過多個全連接層,最終輸出用戶對候選物品的興趣的預估:P(y|x1,x2,…xn)。全連接層的輸出維度一般是逐漸變小,一方面起到降維的作用,並且通過全連接去學習不同特徵之間的高階關聯關係,全連接層採用非線性激活函數將所有特徵向量單元之間的關聯進行非線性化抽象,從而提升模型的描述能力。
❹ 注意力機制
上述 MLP 模型對物品序列的建模是獨立於預測的候選物品的,我們最終形成用戶行為序列向量的時候是一碗水端平的去考慮歷史行為影響的。這樣如論文[4]中提到的,我們沒有考慮到用戶興趣存在多樣性,其不同類型的歷史行為對用戶當前的決策影響程度也是不一樣的,比如當前用戶要買書,那麼在用戶的歷史購買行為中,我們應該去多關注該用戶之前都買什麼樣的書,而該用戶在其他方面的購買記錄就相對顯得沒有那麼重要。因此一個很自然的想法是給用戶的不同歷史行為賦予不同權重。圖10對上述的 MLP 模型進行了一定修改,在各個特徵的 pooling 層中引入注意力機制,從過去的序列向量平均變成序列向量的加權求和,序列向量的權重由候選物品與行為物品之間的相關度決定。


圖10 attention pooling

上圖為 attention 的示例和公式,

計算預測物品與歷史序列物品之間的相關度,

經過 softmax 歸一化後生成序列中每個物品對應的注意力權重 ats,序列中物品向量進行帶權重的求和得到整個序列的向量 ct。
注意力分數的計算除了 multiplicative 和 additive 兩種計算方式外,在論文[4]中採用了基於淺層網絡的方式,其中

和

通過

,

計算出候選物品與歷史物品的組合向量,生成的組合向量與原始向量進行拼接,先經過一個非線性激活函數的全連接層進行降維,最終經過一個線性層輸出注意力分數,這樣通過向量組合和淺層網絡生成注意力分數,可以盡量減少物品之間交互信息的損失。具體公式讀者可以參見源論文[4]。

圖11 attention score from fully-connected layers
——CNN——
從上面 MLP 模型我們可以看到,用戶歷史序列的向量是通過全局 pooling 的方式得到的,相當於對用戶的歷史行為做了一個全局描述。但是在用戶的歷史行為中會存在着一些局部的連續行為模式,比如用戶在過去幾天內連續買過嬰兒用品,那麼在推薦中我們可以根據這個信息向用戶推薦一些跟育兒相關的商品。在全局的 pooling 層我們有可能會丟失這些局部行為信息,因此可以建立模型來對用戶行為中的局部行為模式進行建模,而局部信息建模的一個理想候選模型則是卷積神經網絡 ( Convolution Neural Network )。
論文[5]講述了如何通過卷積神經網絡對用戶行為序列進行建模,其建模思想同論文[6]通過卷積神經網絡對句子序列進行建模完成句子分類的工作,即 TextCNN。圖12給出 TextCNN 如何通過提取句子序列信息完成句子分類:

圖12 TextCNN 示意圖
圖中輸入句子分詞後的序列是 "wait fro the video and do n't rent it",建模步驟從左至右:第一步先將每個詞映射為詞向量,生成一個 n x k 的二維矩陣,其中 n 是句子的長度,k 是詞向量的維度;第二步在 n x k 的矩陣上進行卷積。卷積核 [卷積高度,卷積寬度,輸入維度,輸出維度 ] 中卷積高度是一個超參數,由調用方指定每次卷積要覆蓋的連續詞的個數,卷積寬度需要跟詞向量的維度一致,這是因為每一個向量代表一個詞,在抽取特徵的過程中,詞做為文本的最小粒度,應該保證其信息的完整性。輸入維度為1,輸出維度由調用者指定。一般情況下,我們會使用多個卷積核,如上圖中的紅色和黃色部分,分別對應卷積高度等於2和卷積高度等於3的卷積核。多個卷積核在不同的局部範圍內運用相鄰連續詞的上下文進行建模,從而從不同的粒度去刻畫句子序列的信息 ( 一般情況下,卷積高度我們一般取2或者3,對應 bi-gram 和 tri-gram )。每個卷積核的輸出通過池化層進行降維,通常使用最大值池化 ( max-pooling ),最後各個池化層的輸出拼接在一起,送到全連接 softmax 層。
所以用卷積神經網絡對句子序列的建模主要包括:1. 卷積;2. 池化;3. 拼接;4. 全連接。同樣的方法也可以運用在用戶的行為序列建模中。我們可以復用 MLP 的模型結構,只是在 pooling 層對輸入的行為序列使用卷積和池化,其模型結構如圖13:

圖13 Convolution on user sequence
以下偽代碼是對上圖過程做一個簡單描述:
pooling_outputs = [] for kernel_size in kernels: #使用一維卷積,相當於[kernel_size, input_emb_size, 1, output_dimension]二維卷積 conv_out = tf.layers.conv1d(input_emb, kernel_size, output_dimension, strides=1, padding=』VALID』) #在序列長度維度進行最大值池化 #pool_out的shape變為[batch_size, output_dimension] pool_out = tf.reduce_max(conv_out, -2) pooling_outputs.append(pool_out) #所有池化生成的向量進行拼接 return tf.concat(pooling_outputs, -1)
CNN 和 MLP 也可以結合在一起使用,在 pooling 層採用全局的 pooling 提取用戶行為序列的全局信息,同時通過卷積提取序列的局部信息,然後將兩者拼接在一起作為全連接層的輸入,論文[5]中提到的結合 horizontal convolution 和 vertical convolution 的方法即是採用了這種結合全局和局部信息的思想。
以上的 CNN 序列建模的一個特點是採用多個高度的卷積核來提取序列不同局部範圍內的信息,每個高度的卷積只做一層,所以我們可以看做是 wide convolution。在其他領域中,我們也會採用多層的卷積神經網絡來對輸入的數據信息進行逐層的抽象。那麼在序列推薦中,我們也可以使用同樣的 deep convolution 來對歷史序列信息進行處理,在後面我們會談及相關的模型。
——RNN——
相對於 MLP 和 CNN 模型進行用戶序列建模,循環神經網絡 ( RNN ) 也許是相對更為直觀的序列網絡模型,可以直接去刻畫用戶興趣隨着時間的演化過程。循環網絡可以擴展到更長的序列,相對於前饋神經網絡,我們可以在不同的時間步上共享模型參數,同時循環神經網絡也可用於在線實時更新。
利用循環神經網絡對用戶序列建模可以參見圖14:

圖14 RNN序列建模
其中 R 是展開的循環神經網絡單元,可以是普通的 RNN 單元,也可以是 LSTM,GRU 等 advanced 的網絡單元。x[1],x[2],… ,x[t] 是用戶行為序列每個時間步上用戶產生行為物品的 embedding,作為循環神經網絡單元的輸入,h[1],h[2],… ,h[t] 是每個時間步上循環神經網絡單元的輸出狀態。在時間 t 用戶對物品發生行為的概率為:

其中 h[t-1] 是上一時間步循環神經網絡單元的輸出狀態,x[t] 是時間 t 用戶行為的上下文信息,y 是時間 t 的 label 信息 ( 召回:第 t 個時間步用戶發生行為物品的 ID;排序:第 t 個時間步用戶對各個候選物品的行為的 label ),注意:我們在時間 t 進行預測時,不能將時間 t 的 label 信息 y 用到了模型訓練中。
將循環神經網絡運用到用戶行為序列建模的一個工作來自論文[7]:將 GRU 模型 ( Gated Recurrent Unit ) 運用到用戶會話上下文推薦當中。Youtube 也上線了 RNN 模型[18]並且在模型中融合了推薦的上下文信息來完成視頻推薦。圖15摘自論文[7],描述序列中每個時間步如何應用 GRU 來預測用戶對物品發生行為的概率:

圖15 GRU 建模單元
序列中第 N 個時間步輸入用戶行為物品的 ID,ID 通過 embedding 層映射為向量,並進行歸一化。embedding 之後經過一個或者多個 GRU 單元最後接一個 softmax 層獲取每個物品的預估概率。為了防止梯度消失的問題,採用了殘差網絡結構,將上一個 GRU 單元的輸出與 embedding 層的輸出進行相加,作為下一個 GRU 單元的輸入。
論文[7]對建模過程中一些具體的問題進行了討論,如如何構建批次訓練數據,如何構造訓練損失函數等,感興趣讀者可以仔細閱讀該論文。
上面基於 GRU 的序列推薦模型是基於物品的 ID 構建模型輸入的,如果我們也希望將物品的特徵信息以及上下文信息運用到模型中呢?論文[8]談到了如何在模型中將物品的特徵信息與 ID 信息結合在一起使用,如圖16:

圖16 combine feature with ID in GRU
將上圖的幾種融合方式進行分解:
第一種也是最直接的方法是在輸入端進行融合:將物品的 ID 向量和特徵向量拼接為一個輸入向量給到一個 GRU 單元:

圖17 combine feature with ID in GRU inputs
第二種方法是在輸出端進行融合:ID 向量和特徵向量分別接不同的 GRU,然後將各個 GRU 的輸出向量拼接在一起作為最終輸出。相比於第一種方法, 這種方法對各個端的特徵獨立進行建模,在輸出端進行拼接後通過後繼的全連接層可以隱式加入特徵之間的交互。

圖18 combine feature with ID in GRU outputs
第三種方法中,ID 向量和特徵向量還是分別接不同的 GRU 單元,其相對於第二種方法的不同點在於顯式考慮了物品 ID 和特徵之間的交互。其建立交互的方式是將 ID 對應的 GRU 單元的輸出狀態與特徵向量對應的 GRU 單元的輸出狀態進行點乘,作為特徵部分的 GRU 的狀態輸出:feature_states = feature_states * id_states。可以理解為在模型中加入 ID 特徵與物品特徵的 low-rank relationship,從而提高模型的表達能力與泛化性。

圖19 combine feature with ID via dot_product in GRU
第四種方法,ID 端和特徵端的 GRU 輸出狀態通過一個共享的權重矩陣映射到一個輸出狀態向量:W * id_states + W * feature_states,相當於 ID 和特徵各自的 GRU 輸出狀態通過帶權重的求和方式進行融合,相比於前面三種方法各端的 GRU 輸出是拼接在一起輸入給後繼模型的,各個 GRU 的輸出向量不共享參數的,而第四種模型將在各端的模型輸出之間共享權重矩陣,這樣做的好處是減少模型的參數量,降低過擬合的風險。

圖20 combine feature with ID via shared weight matrix in GRU
除了 item 信息之外,訓練數據中的上下文信息如何集成到 RNN 模型中呢?論文[18]中提供了一種基於 dot product 的方式將上下文信息分別用在了 RNN 單元的輸入和輸出中,通過 dot product 去 model 用戶行為的上下文信息和行為物品信息之間的 low-rank relationship,如下圖:

圖21 integrate context into GRU
圖中模型需要預測 τ 步用戶會觀看哪部視頻,採用的是 softmax 分類的方式完成 candidate generation。循環神經網絡單元使用的是 GRU,可以看到,用到的特徵包括兩種:
1. 第 τ-1 步用戶觀看視頻的物品 feature 和上下文 feature c<τ-1>,兩者做 dot product 作為 GRU 層的輸入
2. 第 τ 步的上下文信息 c<τ>,與GRU的輸出狀態進行點積,作為softmax的輸入。文中使用到的上下文信息包括時間,設備,訪問頁面。上下文信息具體加入的公式是:

——Temporal CNN——
RNN 結構用於序列建模的優點是結構簡單,訓練和預測的資源佔用小,缺點是隨着序列變長容易出現梯度消失的問題,同時 RNN 的訓練不容易實現並行。前面我們談了如何在時間序列上運用 CNN 進行輸入序列建模,CNN 結構通過矩陣運算的並行性容易實現模型訓練的並行化,同時相對不容易出現序列變長後的梯度消失問題。因此我們希望能夠像 RNN 一樣在整個時間序列上通過 CNN 進行建模,這樣便產生了 TCN ( TemporalCNN ) [17]。TCN 採用多層的一維卷積架構在序列長度的維度進行卷積,通過 zero padding 保證每一層的長度相同。TCN 架構結合了 casual convolution ( 因果卷積 ),residual connection ( 殘差連接 ) 以及 dilated convolution ( 空洞卷積 ),其特點包括:
1. 採用因果卷積,保證訓練過程中未來的信息不會泄露到過去時間的建模中;
2. 將任意長度的序列如同 RNN 那樣映射為相同長度的輸出序列。
首先我們看看什麼是因果卷積,如下圖所示,在事件序列中每個時間步 t 的狀態輸出僅與前一層的 t 時刻以及 t 時刻之前的狀態進行卷積:

圖22 TCN causual convolution
在具體實現因果卷積時,我們可以採用在序列的開頭進行補齊的方法保證輸出序列的長度與輸入序列保持一致,每層輸入補齊的長度是:

如下圖所示:

圖23 TCN padding
代碼實現示例:
def conv_with_padding(input_sequence, kernel_size, dilation_rate, output_filters): padding_size = (kernel_size - 1) * dilation_rate #在序列的左邊補齊 padded_sequence = tf.pad(input_sequence, [[0,0], [padding_size,0], [0,0]]) conv_output = tf.layers.conv1d(padded_sequence, filters=output_filters, kernel_size=kernel_size, activation=None, padding=』VALID』, strides=1, dilated_rate=dilation_rate) return conv_output
通過多層的因果卷積網絡疊加,高層的感受野的大小與網絡層數呈線性關係增加。但是為了捕捉更長的歷史信息,我們可以在卷積網絡中加入 dilated convolution ( 空洞卷積 )。空洞卷積通過有間隔的採樣,通過較少的參數實現隨着層數增加而指數級增大的感受野 ( receptive field )。相比原來的正常卷積,dilated convolution 多了一個 hyper-parameter 稱之為 dilation rate ,對應 kernel 的採樣間隔數量,即傳統卷積核相鄰之間插入 dilation rate-1 個空洞數。當 rate=1 時,相當於傳統的卷積核。
當 dilation rate = d,kernel size = k 時,位置 i 的實際卷積窗口為:

其窗口覆蓋的範圍大小為。空洞卷積的具體計算公式如下:

其中 g 是卷積函數,

表示只對過去時間的狀態進行卷積。

圖24 TCN residual connection
隨着網絡層次的加深,多層卷積結構中也可能存在梯度消失的問題,所以在 TCN 中在層與層之間引入了殘差 ( residual connection )。如上圖所示,一個殘差結構塊中包括了兩個卷積結構,每個卷積結構採用因果卷積和空洞卷積,卷積後的輸出進行歸一化和非線性化。第二個卷積的輸出維度與輸入向量的維度有可能不同,因此加入了一個1×1的卷積將輸入和輸出的維度保持一致。
後來出現的 Trellis Network[11] 同 TCN 一樣屬於一種基於一維卷積的特殊時序網絡,同樣具有因果卷積 ( casual convolution ), 殘差結構 ( residual connection ) 的特性,其相對 TCN 的不同點在於:
1. 所有層之間實現權值共享;
2. 整個網絡的輸入序列作為每層輸入的一部分。
從下圖 ( 摘自論文[14] ) 可以看到 Trellis Network 的基本結構 ( 圖(a) ) 以及如何將基本結構擴展到整個序列 ( 圖(b) )。

圖25 Trellis Network Architecture
TrellisNet 與 RNN 和 CNN 具有緊密的聯繫,因此 Trellis Network 上也可以運用 CNN 和 RNN 的某些建模技巧,例如:CNN 的大卷積核,空洞卷積,RNN 的 gated unit。關於其詳細描述與證明請參見論文[11]。
——Self-Attention——
以上的 RNN 和 TCN 模型在建立序列模型時沒有顯式的去考慮各個時刻行為之間的相互關係,而各個時刻事件之間的相關性在預測任務中也是比較重要的信息,為了捕獲序列中歷史事件之間的相互關係,可以使用自注意力機制[12]。文中提出的 Transformer 結構在模型的 Encoder 端通過注意力機制顯式計算句子序列中每個單詞與其他單詞之間的關聯,利用層層疊加的自注意力層對每一個詞得到其上下文信息的表徵。Decoder 端也採用類似的機制,通過 attend 之前 Decode 端的輸出以及 Encoder 端的輸出產生下一步輸出的文字。下圖是 Transformer 中的基本 encoder-decoder 的結構:

圖26 Transformer 結構
那麼類似的,在用戶行為序列中我們也可以運用上自注意力機制。在模型訓練階段,通過多層的自注意力模塊學習序列中各個時刻事件的相互關係,最終輸出序列中每個時刻的狀態向量。在預測階段,可以只用序列中最後一個位置的狀態向量去表示整個序列的歷史信息,如下圖所示:

圖27 基於自注意力機制的序列模型示意
我們對模型中的幾個主要部分進行講解:
❶ Position Embedding
輸入序列中的每個物品經過 input embedding layer 生成每個物品的特徵向量,為了在自注意力模型中體現序列中事件發生的先後時序關係,通過 positional embedding 將序列中事件的先後順序映射到向量空間,然後疊加到物品向量中。位置向量的生成方式可以像 transformer 一樣用句中每個詞在句子中的位置去做 embedding,或者通過正弦和餘弦函數讓網絡能夠理解相對位置關係 ( 對於正弦和餘弦函數,pos + k 位置的 PE 可以表示成 pos 位置 PE 的一個線性變化 )。如果序列中每個事件都記錄了發生的時間,那麼我們可以使用事件發生的時間來做 positional embedding ( 先將事件的時間戳進行分桶,然後去做 embedding )。
❷ Self-attention Blocks
假設輸入序列經過 embedding 後的向量是 X,X 的維度分別是 [B, L, H],其中 B 表示批處理的大小,L 是序列的長度,H 是隱層大小。輸入序列向量經過一個或者多個自注意力模塊,最終輸出一個跟輸入序列長度一樣的輸出序列:[B, L, H]。通過多個自注意力模塊的疊加,逐漸讓網絡學習到序列中的各個事件之間更複雜的關係,迭代地構建輸入序列的整體信息。
每個自注意力模塊如上圖所示可以分成兩個部分:
① Multi-Head Attention
Multi-Head Attention 中學習事件之間的相互關係。多頭 ( Multi-Head ) 類似 CNN 中不同 size 的 kernel,每個 head 對應一個特定的狀態子空間,head 之間參數不共享。設置獨立的 head 有兩個好處:第一個好處是可訓練參數更多,各個 head 專註於不同方面的信息,模型更具擴展性;另一個好處是特定 head 捕獲輸入之間的特定關係,為 Attention 賦予了多個表示子空間,極大提升模型表現能力,這是傳統序列模型所不具備的。在實際計算中,我們先在隱層向量維度 ( H ) 切分出 head,然後在各個 head 空間內做 Attention。如下圖,我們需要計算第2個事件 attention 後的向量,可以按照圖示步驟來完成:

圖28 時刻2的 attention 示意
上圖的核心可以用以下公式來概括:

其中 Q, K, V 分別是輸入 X 分別做線性映射得到的向量,對應 Attention 中的 query, key, value。除以

是因為在 Q,K 的維度比較大的時候,容易進入 softmax 飽和區,進行縮放可以保持梯度的穩定性。
另外需要注意的是,q2 只與當前以及之前位置即 k1, v1 和 k2, v2 進行 attention。在實際實現中我們可以在

後加上一個 mask 矩陣 M,M 矩陣是一個下三角矩陣,維度是 [B, L, L],M[b, i, j] 表示序列 b 中第 i 個位置是否能夠與第 j 個位置進行 attention:

M[b, i, j] = 0,表示 i 可以與 j 做 attention,M[b, i, j] = -inf ( 一個很大的負數,eg . -1e9 ),表示 i 不能與 j 做 attention。設置一個很大的負數,

+ M 經過 softmax 後不能做 attention 的位置則變為0。
每個 head 輸出的 embedding 通過 concat 生成 Multi-Head Attention 的輸出。

② Position-wise Feed Forward
Multi-Head Attention 的輸出作為 Position-wise Feed-Forward Network 的輸入。
每個 FFN 包括兩個全連接層,第一個全連接層運用 ReLu 的激活函數,第二個全連接層是一個線性映射。

圖29 Position-wise Feed Forward
公式:

在該公式中,

在每個位置是共享的,各個位置的向量之間不相互作用。在 Multi-Head Attention 中各個 head 內部主要進行線性變換,head 和 head 之間相互獨立。在 FFN 層通過非線性全連接網絡在 head 信息中引入非線性性,並將各個 head 之間的信息進行關聯, 類似於在 Deepfm 中通過 MLP 進行高維度的特徵組合。
③ LayerNorm & Residual Connection
在 Multi-Head Attention 和 FFN 的輸出中都加入了殘差連接和 Layer Normalization。殘差連接中將模塊的輸入加到模型的輸出中,以防止隨着模塊層數加深出現的梯度消失問題。LN 對序列中每個位置的輸出狀態向量進行標準化,使得輸出數據的整體分佈更加穩定。自注意力模型中沒有使用 Batch Normalzation,因為 BN 受到 batch size,sequence length 等因素的影響,並不適合序列模型。LN 和殘差連接使用公式如下:

❸ Train
經過多層自注意力模塊後,得到序列的輸出向量 O:[B, L, H]。假設輸入序列向量為 E:[B, L, H],我們使用點積 ⊙ 計算 O 與 E 的相關度:

R[b][t] 表示第 b 個序列中第 t 個時間步行為物品 ( positive item ) 的向量與 t 時刻之前位置的輸出向量的相關度 ( 取 t-1 時刻來代表t時刻之前位置的信息 ),相當於是將 O 右移了一位,以保證我們用每個時刻之前的信息來預測在該時刻與行為物品的相關度,如圖27中我們使用步驟1,2,3的輸出狀態去預測步驟2,3,4。在t時刻我們可以隨機 sample negative items 然後採用 pairwise rank loss,增大 postivie item 的相關度並減小 negative item 的相關度。
❹ Prediction
預測時,我們取序列的最後一個元素的輸出向量 V,待預測的目標物品的向量為 W,使用點積 ⊙ 計算 V 與 W 的相關度:
predict_score =

自注意力模型本小節開始已經談了其優點,能夠在長序列上比較好的去建立各個時間之間的相關性,不過模型本身比較複雜,每層計算複雜度比較高,而且模型在數據量較小情況下不容易收斂,效果不一定比 GRU 這類簡單模型好,因此在實際應用中需要對數據和工程做比較多的調整和優化。
——強化學習——
這一節我們換個角度去看序列推薦問題。如果我們將用戶的行為序列看做是一個順序的決策過程,那麼我們可以用馬爾科夫決策過程 ( MDP ) 來表示用戶的行為序列。馬爾科夫決策過程包含以下幾個方面:
❶ 狀態空間 S
在t時刻用戶的狀態 St 定義為用戶在 t 時間之前的歷史行為,如用戶在 t 之前點擊過的 N 個物品。馬爾科夫決策過程中每個狀態具有馬爾科夫性,即

因此序列中用戶在各個時刻的狀態可以只根據前一時刻的狀態得到。
❷ 動作空間 A
動作定義為可以給用戶推薦的候選物品空間。agent 的一次推薦相當於在候選物品空間內選擇一個或者多個物品返回給用戶。
❸ 獎勵 R
agent 推薦物品給用戶之後,根據用戶對推薦列表的反饋 ( 忽略或者點擊 ) 來得到 ( 狀態-行為 ) 的即時獎勵 reward:


中 t+1 表示獎勵具有延遲性,即在一個時刻發生行為,在下一個時刻收到環境的反饋。
❹ 轉移概率 P
用戶在 t 時刻的狀態為用戶最近點擊的 N 個物品,定義為

。我們定義

為用戶在 t 時刻對物品 a 產生行為反饋後從狀態

轉移到狀態

的概率。agent 為用戶推薦了 K 個物品, 如果用戶忽略了推薦的全部物品,那麼下一個時刻的狀態

和當前的狀態保持一致。如果用戶點擊了其中一個推薦的物品,那麼下一個時刻的狀態

是在當前狀態

的基礎上,加入該點擊的推薦物品並且將原來的 N 個物品中點擊時間最久遠的那個物品去掉 ( 可以理解為一個先進先出的隊列 )。
❺ 折扣因子

實際情況下我們希望最大化的是長期回報而不僅是即時獎勵。因此t時刻的回報為從該時間起的一個總折扣獎勵,即

折扣因子一般取0到1之間的一個數。
❻ 策略
有了狀態和行為,我們會用策略 ( policy ) 來形式化的刻畫用戶的決策過程。策略描述了用戶在某個狀態 s 下採取某個行為 a 的概率:

可以理解為用戶在某個時間點的狀態 s 下對候選物品 a 產生行為的概率。
❼ 值函數
用值函數來刻畫行為的回報。值函數分為兩種,一種定義為從一個狀態 s 開始的回報期望值:

它衡量的是從某個狀態開始所能得到的累計回報的期望值。另外一種定義為從一個狀態 s 開始,採取某個行為 a 後的回報期望值

對應用戶在某個具體狀態下採取某個具體行為的累計回報的期望值。至於如何求解

和

,讀者可以參閱相關的強化學習的資料。
回到序列推薦問題上來,我們的目標是找到一個最優的決策過程 ( policy ) 以最大化整個序列的期望回報:

求解這個問題我們可以採用的方法包括:value-based 和 policy-based 方法。在實際應用中,由於物品空間一般比較大,採用 value-based 方法計算開銷比較大,模型不易收斂,更多情況下會採用 policy-based 方法,如 policy gradient。Policy gradient 中每次策略採取某個動作的概率通過模型 θ 計算:

目標是求解模型 θ 使得整個行為序列的回報最大化:

設

對 θ 求導:

其中關鍵點是求

,
設


則

其中

和

跟 θ 無關,那麼


其中

對應模型的輸出,

對應行為的回報期望。關於 policy gradient 的詳細推導請參見[20]。
接下來需要定義的則是模型 θ 以及獎勵 r。t 時刻的獎勵 r 是從該時刻開始到將來某個時刻的回報的期望值。在實際應用中,比較直接的是採用 t 時刻的即時回報,如用戶是否發生點擊行為來作為獎勵r,同時可以考慮 t 時刻之後一段時間內用戶的反饋行為,如用戶的停留時長等。模型 θ 對 t 時刻的用戶序列狀態進行建模,輸出該時刻的用戶狀態向量。論文[16]中 θ 採用的是 RNN 來對用戶行為序列進行建模,如下圖所示:

圖30 RNN cell for policy gradient
其中 s(t) 是第 t 步的輸入狀態,來自於第 t-1 步 RNN 單元的輸出狀態,同時在 t 步用戶對推薦物品 a 產生行為,其中 a 對應的 embedding 是 u(a, t),包括 t 時刻物品的 embedding 和 t 時刻的上下文 embedding。將 s(t) 和 u(a, t) 輸入第 t 步 RNN 單元的 update gate 和 input gate,產生輸出狀態 s(t+1):

根據用戶的當前狀態採用 sampled softmax 產生候選推薦物品,也就是策略

的輸出:

v(a) 是候選物品的向量,上面這個公式即 youtube DNN 做召回的思想:在實際使用中,用 RNN 的輸出狀態做 k-nearest neighbor search, 得到一個相對較小的候選集合,然後再在這個候選集合中計算每一個候選物品的概率。
文[15]中採用的是 off-policy 的 RL ( 相對於 on-policy,off-policy 從不同分佈的反饋數據中學習 ),需要考慮 bias correction。關於具體如何對 data bias 進行 correction,可以參看論文。
——總結——
綜上所述,把對以上幾個模型的一點個人認識歸納如下表,僅供參考:

至於在具體選型時候,應該選哪個模型用到產品上,個人覺得可以參考以下兩個建議:
1. 找適合業務需求的:根據自身的業務目標,當前的數據量,數據中信息豐富程度,工程架構上的支持,訓練成本,上線成本等來折中考慮。
2. 先選簡單的,簡單的模型大部分情況下都 work 的比較好。
讀者可以比較工業界和學術界發表的論文即可看出,工業界使用的模型大部分情況下都更簡單易擴展,更多注重數據和工程實踐。因此在選型時候,可以多參考現有的工業界的成果,快速搭建 baseline 系統,然後再逐漸改進。
——參考文獻——
[1] Deep Learning based Recommender System: A Survey and New Perspectives
[2] Deep learning sequential recommendation systems
[3] Image Matters: Visually modeling user behaviors using Advanced Model Server
[4] Deep Interest Network for Click-Through Rate Prediction
[5] Convolutional Sequence Embedding Recommendation Model
[6] Convolutional Neural Networks for Sentence Classification
[7] Session-based recommendations with recurrent neural networks
[8] Parallel Recurrent Neural Network Architectures for Feature-rich Session-based Recommendations
[9] Hierarchical question-image co-attention for visual question answering
[10] An Empirical Evaluation of Generic Convolutional and Recurrent Networks for Sequence Modeling
[11] Trellis Networks for Sequence Modeling
[12] Attention Is All You Need
[13] Layer Normalization
[14] Deep Neural Networks for YouTube Recommendations
[15] Top-K Off-Policy Correction for a REINFORCE Recommender System
[16] Latent Cross: Making Use of Context in Recurrent Recommender Systems
[17] Convolutional sequence modeling revisited
[18] Learning Deep Structured Semantic Models for Web Search using Clickthrough Data
[19] ATRank: An Attention-Based User Behavior Modeling Framework for Recommendation
[20] A blog summarizing policy gradient:
https://lilianweng.github.io/lil-log/2018/04/08/policy-gradient-algorithms.html
[21] Reinforcement Learning for Slate-based Recommender Systems: A Tractable Decomposition and Practical Methodology