基於深度學習的推薦(四):阿里DIN,使用Attention獲得用戶的動態representation

公衆號

關注公衆號:推薦算法工程師,輸入"進羣",加入交流羣,和小夥伴們一起討論機器學習,深度學習,推薦算法.

前言

Deep Interest Network(DIN) 是蓋坤大神領導的阿里媽媽的精準定向檢索及基礎算法團隊提出的,發表在18年的ACM SIGKDD會議上。這篇文章討論的是電子商業中的CTR預估問題.重點是對用戶的歷史行爲數據進行分析和挖掘.

論文指出,很多CTR預估的模型,比如Deep Cross Network,Wide&Deep Learning等,採用的都是一種相似的模型架構,首先使用Embedding&MLP 從高維稀疏輸入中提取低維稠密vectors,然後使用Sum/Average Pooling等技巧獲得定長vectors.然後使用MLP進行預測.但是這樣做存在一些問題.

首先是將原始的高維稀疏向量壓縮爲低維稠密向量後,定長向量sum pooling會導致信息損失;另外,低維向量的表達能力有限,而用戶的興趣多種多樣(不是幾十種這種量級的多種多樣),爲了提高定長vector的表達能力需要進行維度擴展,然而這容易帶來維度災難等問題,網站物品越來越多的時候總不能一直提高定長vector的維度吧.

然後作者注意到,用戶是否會點擊推薦給他的物品,僅取決於歷史行爲的一部分,並稱之爲local activation.因此,計算推薦物品的點擊率時,只有部分物品代表的興趣分佈真正在起作用.DIN正是通過使用attention機制,對不同推薦的物品,獲得用戶不同的特徵表示,從而進行更加精確的CTR預估.

論文鏈接:https://arxiv.org/pdf/1706.06978.pdf

1.模型分析

1.1 Baseline

首先說一下,論文中使用Goods表示用戶歷史中的廣告/商品,使用Ad代表候選廣告/商品。
Screenshot_2019-10-03_20-13-49.png

首先看下對照組設置的模型,其中user profile表示的意思如下:
Screenshot_2019-10-03_20-16-37.png

顯而易見,首先對各類數據進行稀疏編碼,然後使用MLP獲得稠密向量,把各類特徵串聯起來,後面再來個MLP。
這裏User Behavior使用multi-hot編碼,因爲用戶不太可能只對其中一個廣告感興趣。要注意的是User Profile和Goods id以及Context Features的稠密向量並不一定是同一緯度。用戶的特徵向量怎麼來的呢?將某個用戶歷史中點擊過的物品的稠密向量進行Sum Pooling,就是直接求和,通過它們來代表當前用戶。

1.2 DIN

Screenshot_2019-10-03_20-14-07.png

上圖就是DIN模型,就是比Baseline多了一個Attention機制,下面我們重點講講這個attention的細節。

顯而易見,所謂attention,就是給用戶歷史記錄中不同廣告賦予不同的權重,DIN中這個權重是一個和候選Ad相關的函數,也就是上圖右上角的Activation Unit:將Inputs from user和Inputs from Candidate Ad這兩部分特徵做交互,每個Goods都需要和Candidate Ad通過這個Activation Unit來獲得該Goods的權重。

然後和baseline一樣,使用Sum Pooling獲得定長vector作爲用戶的representation,其中權重函數a就是上圖中的Activation Unit:
Screenshot_2019-10-03_20-58-52.png

但是同樣是定長的user vector,候選廣告不同時,DIN中user vector是不同的,而baseline中是相同的,顯然DIN獲得的用戶representation更具有靈活性,也更加“精確”。

DIN中的Attention部分, 簡單地說,就是和Goods和Candidate Ad有關的權重函數,賦予不同Goods不同權重,從而獲得更好的用戶representation。

2. 訓練技巧

2.1 Dice激活函數

PRelu是一種常用的激活函數:
Screenshot_2019-10-03_21-14-48.png

但作者認爲,不應該所有的突變點都選爲0,而是應該依賴數據分佈。因此,對PRelu進行改進,提出Dice激活函數,其中s是激活函數輸入中的其中一維,即對每維進行去均值求方差操作:
Screenshot_2019-10-03_21-16-34.png

[1]中認爲,去均值化操作使得突變點爲s的均值,實現了data dependent的想法;而使用sigmoid可以得到0-1間的概率值,從而權衡s和αs。

2.2 Mini-batch 正則化

我們知道,電商中的商品數據符合長尾分佈,只有少量商品多次出現,大量商品只出現幾次或不出現。因此User Behaviors很稀疏是肯定的,由於輸入是高維繫數特徵,而模型又很複雜,參數太多,相當於複雜模型而數據不足,這種情況很容易過擬合。

而對大體量的擁有上億參數和非常稀疏的訓練網絡來說,直接應用L1,L2這種傳統正則化方法是不使用的。比如L2正則化,在一個mini-batch中,只有非零特徵對應的參數纔會被更新,然而所有的參數都需要計算L2正則。

因此作者提出進行Mini-batch Aware正則化,只計算每個mini-batch中非零特徵的相關參數:
Screenshot_2019-10-03_22-03-17.png
上述公式可以轉化爲:
Screenshot_2019-10-03_21-51-34.png

此時出現次數越多的非零特徵對應的乘法權重越大。而上述公式可近似爲:
Screenshot_2019-10-03_21-53-30.png
此時amj取值0或1,這種進一步的近似類似從sum pooling到max pooling的轉化,論文並沒有給出原因和證明,我想是爲了簡化訓練過程吧。

3.實戰

3.1 數據集

論文中使用了淘寶和亞馬遜的數據集,數據集都太大了。可以下載一個亞馬遜的小數據集進行實驗:

echo "begin download data"
mkdir raw_data && cd raw_data
wget -c http://snap.stanford.edu/data/amazon/productGraph/categoryFiles/reviews_Electronics_5.json.gz
gzip -d reviews_Electronics_5.json.gz
wget -c http://snap.stanford.edu/data/amazon/productGraph/categoryFiles/meta_Electronics.json.gz
gzip -d meta_Electronics.json.gz
echo "download data successful"

試驗中使用了這個小數據集的迷你版本:reviews_Electronics_5.json和meta_Electronics.json,來自參考代碼。其中reviews_Electronics_5.json:
Screenshot_2019-10-03_22-29-40.png
meta_Electronics.json:
Screenshot_2019-10-03_22-29-53.png
由於兩個數據可能來自不同的部門,因此爲了獲得完整的reviewerID,asin,和categories等條目,需要取兩者的公共部分。詳見代碼。

3.2 代碼分析

代碼使用的是jupyter notebook風格,各變量的形狀我都標註在後面了。attention和Dice部分對照論文一看就懂。大概講一下整個架構,也就是Model函數,分爲四部分:首先是candidate ad的embedding:

# get item embedding vectors for input 
i_c = tf.gather(cate_list, self.item)  # obtain category of every item
item_emb = tf.concat(values=[
    tf.nn.embedding_lookup(item_emb_w, self.item), # [B, H/2]
    tf.nn.embedding_lookup(cate_emb_w, i_c)        # [B, H/2]
], axis=1) # [B, H]

然後是user behaviors的embedding:

# get hist embedding vectors for input
h_c = tf.gather(cate_list, self.hist)
hist_emb = tf.concat(values=[
    tf.nn.embedding_lookup(item_emb_w, self.hist), # [B, T, H/2]
    tf.nn.embedding_lookup(cate_emb_w, h_c)        # [B, T, H/2]
], axis=2) # [B, T, H]

上述實現我覺得有些問題。self.hist中除用戶歷史商品外其他都是0,也就是說幾乎所有的用戶都擁有第0個商品的embedding,由於在之前的DataInput函數預處理時恰好將包含0商品的記錄全刪掉了,此時0商品的embedding相當於對所有用戶都添加了一個噪聲,再加上attention機制,影響應該不大,但仍然有些問題。

由behaviors中各goods的embedding獲得user embedding:

# get user embedding vectors based on user hist vectors and item(to predict) vectors
att_hist_emb = attention(item_emb, hist_emb, self.sl)       # [B, 1, H]
att_hist_emb = tf.layers.batch_normalization(inputs=att_hist_emb)  # [B, 1, H]
att_hist_emb = tf.reshape(att_hist_emb, [-1, hidden_units]) # [B, H]
att_hist_emb = tf.layers.dense(att_hist_emb, hidden_units)  # [B, H]
user_emb = att_hist_emb

然後是後面的MLP預測部分:

base_i = tf.concat([user_emb, item_emb], axis=-1) # [B, 2*H]
base_i = tf.layers.batch_normalization(base_i, name='base_i', reuse=tf.AUTO_REUSE) # [B, 2*H]
d_layer_1_i = tf.layers.dense(base_i, 80, activation=tf.nn.sigmoid, name='f1', reuse=tf.AUTO_REUSE) # [B, 80]
d_layer_1_i = dice(d_layer_1_i, name='dice1')
d_layer_2_i = tf.layers.dense(d_layer_1_i, 40, activation=tf.nn.sigmoid, name='f2', reuse=tf.AUTO_REUSE) # [B, 40]
d_layer_2_i = dice(d_layer_2_i, name='dice2')
d_layer_3_i = tf.layers.dense(d_layer_2_i, 1, activation=None, name='f3', reuse=tf.AUTO_REUSE) # [B, 1]
# 特徵平鋪
d_layer_3_i = tf.reshape(d_layer_3_i, [-1]) # [B,]
i_b = tf.gather(item_emb_b, self.item) # obtain bias of every item, [B,]
self.pre = i_b+d_layer_3_i # [B]

論文開源代碼:https://github.com/zhougr1993/DeepInterestNetwork

參考代碼:https://github.com/Crawler-y/Deep-Interest-Network

完整代碼和數據:https://github.com/wyl6/Recommender-Systems-Samples/tree/master/RecSys%20And%20Deep%20Learning/Attention/DIN

參考

[1] https://juejin.im/post/5b5591156fb9a04fe91a7a52

[2] https://www.jianshu.com/p/a356a135a0d2

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章