CTR深度学习模型之 DIEN(Deep Interest Evolution Network) 的理解与示例
本文介紹阿里提出的興趣進化網絡(Deep Interest Evolution Network,以下簡稱DIEN。
前一篇文章介紹的 DIN 模型如下圖所示:
DIN 直接把用戶的某個行為作為用戶的興趣,在實際的電商場景中這并不完全準確。
用戶的興趣受到環境因素與自身因素的影響,會不斷的變化。例如用戶會在一段時間內關注手機相關的商品,另一段時間內又會關注書籍相關的商品,而手機與書籍之間的關系就比較小了。本文的介紹的 DIEN 模型就是要捕捉用戶興趣的演變過程。DIEN 仍然采用了 attention 機制,捕捉與候選廣告相關的興趣發展路徑。
模型概覽
DIEN 模型結構如下圖所示:
與 DIN 相比,DIEN 將 User behaviors 的部分加以調整,變成了上圖中綠色區域的Behavior Layer、黃色區域的Interest Extractor Layer、粉色區域的Interest Evolving Layer三個部分。
Behavior Layer
Behavior Layer 主要就是對用戶行為數據進行 embedding,在此不做過多講解。
Interest Extractor Layer
Interest Extractor Layer ,也就是興趣提取層。用戶行為是用戶興趣的載體,興趣也會隨著用戶采取某個行動之后改變。因此要對興趣建模,首先要對行為之間的依賴關系進行建模。考慮到效率與性能,文中使用 GRU 對行為之間的依賴關系進行建模。GRU 的公式如下:
ut=σ(Wuit+Uuht?1+bu),rt=σ(Writ+Urht?1+br),h~t=tanh(Whit+rt°Uhht?1+bh),ht=(1?ut)°ht?1+ut°h~t,u_t = \sigma(W^ui_t+U^uh_{t-1}+b^u), \\ r_t = \sigma(W^ri_t+U^rh_{t-1}+b^r), \\ \tilde{h}_t = tan h(W^hi_t+r_t \circ U^h h_{t-1} + b^h), \\ h_t = (1-u_t) \circ h_{t-1} + u_t \circ \tilde{h}_t, ut?=σ(Wuit?+Uuht?1?+bu),rt?=σ(Writ?+Urht?1?+br),h~t?=tanh(Whit?+rt?°Uhht?1?+bh),ht?=(1?ut?)°ht?1?+ut?°h~t?,
由于 GRU 的特性,候選廣告商品是否被點擊的這個 label 在反向梯度傳播的過程中僅僅能夠對 GRU 的最終狀態形成有效的監督作用,而對于中間的行為序列隱含狀態缺少有效的監督信息。因此,為了使得中間的隱含能夠有效的學習興趣信息,使用下一個時刻的行為來監督當前隱含狀態的學習。為此,選擇真實的下一個商品作為正樣本,選擇其他商品作為負樣本,進而計算一個二分類的輔助損失函數:
Laux=?1N(∑i=1N∑tlog?σ(hti,ebi[t+1])+log?(1?σ(hti,e^bi[t+1])))\begin{aligned} L_{a u x}=-& \frac{1}{N}\left(\sum_{i=1}^{N} \sum_{t} \log \sigma\left(\mathbf{h}_{t}^{i}, \mathbf{e}_^{i}[t+1]\right)\right.\\ &\left.+\log \left(1-\sigma\left(\mathbf{h}_{t}^{i}, \hat{\mathbf{e}}_^{i}[t+1]\right)\right)\right) \end{aligned} Laux?=??N1?(i=1∑N?t∑?logσ(hti?,ebi?[t+1])+log(1?σ(hti?,e^bi?[t+1])))?
其中 htih_t^ihti? 表示第 t 個隱含狀態, ebie_b^iebi? 表示有真實點擊行為的正樣本,e^bi\hat e_b^ie^bi? 表示沒有點擊行為的負樣本,其中 σ\sigmaσ 表示將向量內積的結果輸入到 sigmoid 函數:
σ(a,b)=11+exp(?a?b)\sigma(a, b) = \frac{1}{1+exp^{(-a \cdot b)}} σ(a,b)=1+exp(?a?b)1?
而全局的損失函數則變為:
L=Ltarget+αLauxL = L_{target} + \alpha L_{aux} L=Ltarget?+αLaux?
其中, α 是平衡最終損失與興趣表示的超參數。引入損失函數的好處有:
總的來說,興趣提取層的作用是挖掘行為序列中商品之間的聯系,對用戶的興趣進行提取和表達。但是,每個 GRU 的隱含狀態還無法與候選廣告商品產生某種聯系,這部分工作是交給Interest Evolving Layer 來完成的。
Interest Evolving Layer
Interest Evolving Layer,如上圖中紅色區域所示,結合了注意力機制的局部激活能力以及GRU的序列學習能力來實現對與候選廣告商品相關的興趣演化過程建模。
具體而言,需要對用戶興趣與候選商品計算 attention:
at=exp(htWea)∑j=1Texp(hjWea)a_t = \frac{exp(h_tWe_a)}{\sum_{j=1}^Texp(h_jWe_a)} at?=∑j=1T?exp(hj?Wea?)exp(ht?Wea?)?
式子中的 αt\alpha_tαt? 表示相似度系數(注意力得分),eae_aea? 表示候選商品的 embedding 向量。在得到相似度系數之后,可以將這個系數嵌入第二個GRU中,至于如何結合到 GRU 中,論文給了三種方式:
1,GRU with attentional input(AIGRU)
這種方法直接將第二個 GRU 的輸入與相似度系數相乘,即:
it′=ht×ati'_t = h_t \times a_t it′?=ht?×at?
但是這種方法效果不好,因為即使用系數 0 來表示候選商品與該興趣完全無關,這個隱含狀態仍然會影響到之后的興趣演變過程的學習。
2,Attention based GRU(AGRU)
在問答領域,AGRU 可以有效的提取 query 中的關鍵信息。具體的,AGRU 利用相似度系數來代替 GRU 的 update gate,通過相似度系數來改變隱藏層的輸出:
ht′=(1?at)×ht?1′+at×h~t′h'_t = (1-a_t)\times h'_{t-1}+a_t \times \tilde{h}_t' ht′?=(1?at?)×ht?1′?+at?×h~t′?
雖然 AGRU 可以控制隱藏層的輸出,但是它使用了一個標量來代替原來公式中的向量,忽視了不同維度之間的差異。
3,GRU with attention update gate(AUGRU)
DIEN 的論文中結合了上面的兩種方法,提出了 AUGRU:
u~t′=at×ut′ht′=(1?u~t′)°ht?1′+u~t′°h~t′\tilde{u}_t' = a_t \times u_t' \\ h_t' = (1-\tilde{u}_t') \circ h_{t-1}' + \tilde{u}_t' \circ \tilde{h}_t' u~t′?=at?×ut′?ht′?=(1?u~t′?)°ht?1′?+u~t′?°h~t′?
文中將連續 50 天點擊的廣告作為樣本,用戶點擊目標商品之前 14 天的點擊行為作為歷史行為序列。其中前 49 天的樣本作為訓練集,第 50 天的樣本作為測試集。實驗表明,AUGRU 效果最佳。
模型示例
例子是在 tensorflow2 的環境中使用了 deepctr 實現的 DIN 模型,deepctr 安裝方式如下:
pip install deepctr[gpu]在其 github 倉庫中提供了一個 demo,其代碼以及關鍵部分的注釋如下:
import numpy as npfrom deepctr.models import DIEN from deepctr.feature_column import SparseFeat, VarLenSparseFeat, DenseFeat,get_feature_namesdef get_xy_fd(use_neg=False):# 對基礎特征進行 embeddingfeature_columns = [SparseFeat('user',vocabulary_size=3,embedding_dim=10),SparseFeat('gender', vocabulary_size=2,embedding_dim=4), SparseFeat('item_id', vocabulary_size=3,embedding_dim=8), SparseFeat('cate_id', vocabulary_size=2,embedding_dim=4),DenseFeat('pay_score', 1)]# 指定歷史行為序列對應的特征behavior_feature_list = ["item_id", "cate_id"]# 構造 ['item_id', 'cate_id'] 這兩個屬性歷史序列數據的數據結構: hist_item_id, hist_cate_id# 由于歷史行為是不定長數據序列,需要用 VarLenSparseFeat 封裝起來,并指定序列的最大長度為 4 # 注意,對于長度不足4的部分會用0來填充,因此 vocabulary_size 應該在原來的基礎上 + 1# 詳細內容參考:https://deepctr-doc.readthedocs.io/en/latest/Examples.html#multi-value-input-movielensfeature_columns += [VarLenSparseFeat(SparseFeat('hist_item_id', vocabulary_size=3 + 1, embedding_dim=8, embedding_name='item_id'),maxlen=4, length_name="seq_length"),VarLenSparseFeat(SparseFeat('hist_cate_id', 2 + 1, embedding_dim=4, embedding_name='cate_id'), maxlen=4,length_name="seq_length")]# 基礎特征數據uid = np.array([0, 1, 2])ugender = np.array([0, 1, 0])iid = np.array([1, 2, 3])cate_id = np.array([1, 2, 2]) score = np.array([0.1, 0.2, 0.3])# 構造歷史行為序列數據# 構造長度為 4 的 item_id 序列,不足的部分用0填充hist_iid = np.array([[1, 2, 3, 0], [3, 2, 1, 0], [1, 2, 0, 0]])# 構造長度為 4 的 cate_id 序列,不足的部分用0填充hist_cate_id = np.array([[1, 2, 2, 0], [2, 2, 1, 0], [1, 2, 0, 0]])# 行為序列長度behavior_length = np.array([3, 3, 2])# 構造實際的輸入數據feature_dict = {'user': uid, 'gender': ugender, 'item_id': iid, 'cate_id': cate_id,'hist_item_id': hist_iid, 'hist_cate_id': hist_cate_id,'pay_score': score, "seq_length": behavior_length}# 使用負采樣if use_neg:feature_dict['neg_hist_item_id'] = np.array([[1, 2, 3, 0], [1, 2, 3, 0], [1, 2, 0, 0]])feature_dict['neg_hist_cate_id'] = np.array([[1, 2, 2, 0], [1, 2, 2, 0], [1, 2, 0, 0]])feature_columns += [VarLenSparseFeat(SparseFeat('neg_hist_item_id', vocabulary_size=3 + 1, embedding_dim=8, embedding_name='item_id'),maxlen=4, length_name="seq_length"),VarLenSparseFeat(SparseFeat('neg_hist_cate_id', 2 + 1, embedding_dim=4, embedding_name='cate_id'),maxlen=4, length_name="seq_length")]x = {name:feature_dict[name] for name in get_feature_names(feature_columns)}y = np.array([1, 0, 1])return x, y, feature_columns, behavior_feature_listif __name__ == "__main__":if tf.__version__ >= '2.0.0':tf.compat.v1.disable_eager_execution()USE_NEG = Truex, y, feature_columns, behavior_feature_list = get_xy_fd(use_neg=USE_NEG)model = DIEN(feature_columns, behavior_feature_list,dnn_hidden_units=[4, 4, 4], dnn_dropout=0.6, gru_type="AUGRU", use_negsampling=USE_NEG)model.compile('adam', 'binary_crossentropy',metrics=['binary_crossentropy'])history = model.fit(x, y, verbose=1, epochs=10, validation_split=0.33)DIEN 模型至少需要傳入兩個參數,一個是 dnn_feature_columns , 用于對所有輸入數據進行 embedding;另一個是 history_feature_list,用于指定歷史行為序列特征的名字,例如 [“item_id”, “cate_id”]。
要特別注意的地方是:
1,特征 f 的歷史行為序列名為 hist_f 。例如要使用 ‘item_id’, ‘cate_id’ 這兩個特征的歷史行為序列數據,那么在構造輸入數據時,其命名應該加上前綴“hist_” ,即 ‘hist_item_id’, ‘hist_cate_id’。
2,使用負樣本的情況下,負樣本序列數據則需要加上前綴“neg_hist_” ,即 ‘neg_hist_item_id’, ‘neg_hist_cate_id’。
3,必須給定 seq_length ,即每個序列的長度作為輸入數據。
帶來的啟發
1,將監督學習引入 GRU 的訓練,有助于中間的隱含狀態學習到更多信息。
2,以 AUGRU 提供了一種有效將 attention 機制得到的相似度得分融入 GRU 的方式。
3,輔助 Loss 的設計雖然增加了訓練時間,但是有助于梯度的反向傳播,并且不會對預測任務增加計算量。
參考文章:
詳解阿里之Deep Interest Evolution Network(AAAI 2019)
從DIN到DIEN看阿里CTR算法的進化脈絡
Deep Interest and Evolution Network for CTR
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的CTR深度学习模型之 DIEN(Deep Interest Evolution Network) 的理解与示例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CTR深度学习模型之 DIN(Deep
- 下一篇: 梳理百年深度学习发展史-七月在线机器学习