din算法 代码_深度兴趣网络(DIN,Deep Interest Network)
1. DIN介紹
Deep Interest Network是基于BaseModel演化而來
1.1 流程:
整個流程可以描述為:1.檢查用戶歷史行為數據 2.使用matching module產生候選ads。3.通過ranking module做point-wise的排序,即得到每個候選ads的點擊概率,并根據概率排序得到推薦列表。4.記錄下用戶在當前展示廣告下的反應(點擊與否),作為label。
1.2 特征設計
本文將所涉及到的特征分為四個部分:用戶特征、用戶行為特征、廣告特征、上下文特征,具體如下:
然后,通過DNN學習數據之間的交叉特征。
1.3 模型結構
首先,介紹一般的DNN模型結構,大致分為以下幾個部分:
?Embedding Layer: 原始數據是高維且稀疏的0-1矩陣,emdedding層用于將原始高維數據壓縮成低維矩陣;?Pooling Layer : 由于不同的用戶有不同個數的行為數據,導致embedding矩陣的向量大小不一致,而全連接層只能處理固定維度的數據,因此,利用Pooling Layer得到一個固定長度的向量。
?Concat Layer:?經過embedding layer和pooling layer后,原始稀疏特征被轉換成多個固定長度的用戶興趣的抽象表示向量,然后利用concat layer聚合抽象表示向量,輸出該用戶興趣的唯一抽象表示向量;?MLP:將concat layer輸出的抽象表示向量作為MLP的輸入,自動學習數據之間的交叉特征;?Loss:損失函數一般采用Loglos
傳統DNN模型在Embedding Layer -> Pooling Layer得到用戶興趣表示的時候,沒有考慮用戶與廣告之間的關系,即不同廣告之間的權重是一致的。之前也分析過,這樣是有問題的。因此,DIN利用attention機制,在得到用戶興趣表示時賦予不同的歷史行為不同的權重,即通過Embedding Layer -> Pooling Layer+attention實現局部激活。從最終反向訓練的角度來看,就是根據當前的候選廣告,來反向的激活用戶歷史的興趣愛好,賦予不同歷史行為不同的權重。
DIN認為用戶的興趣不是一個點,而是一個多峰的函數。一個峰就表示一個興趣,峰值的大小表示興趣強度。那么針對不同的候選廣告,用戶的興趣強度是不同的,也就是說隨著候選廣告的變化,用戶的興趣強度不斷在變化。?
attention的公式如下:
其中,ei表示用戶U歷史行為embedding向量,wj表示ej的權重,Vu表示用戶所有行為embedding向量的加權和,表示用戶的興趣。候選廣告影響著每個behavior id的權重,也就是Local Activation。權重表示的是:每一個behavior id針對當前的候選廣告Va,對總的用戶興趣表示的Embedding Vector的貢獻大小。在實際實現中,權重用激活函數Dice的輸出來表示,輸入是Vi和Va。
1.4 自適應激活函數Dice
DIN提出了一種數據動態自適應激活函數Dice,認為分割點不是固定為0的,而是隨著數據不同而動態變化的。Dice公式如下:
ps的計算分為兩步:
?對進行正態分布歸一化處理,使得數據集中在正態分布均值處;?利用sigmoid函數歸一化,使得輸出在0~1之間。
f(s)的作用可以理解為一種平滑操作,alpha是一個超參數,推薦值為0.99。
1.5 高效正則器
由于DNN模型往往十分復雜,而且參數較多。利用L1,L2等正則手段往往加重了模型過擬合,DIN提出了一種高效的正則方法:
由于數據中有些feature id出現次數較少,這在訓練過程中將引入很多噪聲,從而加重了過擬合風險。DIN構造的正則器會針對id出現的頻率不同,動態調整其參數的正則化力度。即,出現頻率較高的id,給與較小的正則化力度,反之,則加大力度。
1.6?總結
DIN通過引入attention機制,針對不同的廣告構造不同的用戶抽象表示,從而實現了在數據維度一定的情況下,更精準地捕捉用戶當前的興趣。此外,DIN模型也適用于其他有豐富行為數據的場景,比如,電商中的個性化推薦,以及當前比較火熱的feed流推薦等。
2.算法實踐
2.1數據處理
基礎數據
論文中用的是Amazon Product Data數據,包含兩個文件:reviews_Electronics_5.json, meta_Electronics.json.
文件格式鏈接中有說明,其中reviews主要是用戶買了相關商品產生的上下文信息,包括商品id, 時間,評論等。meta文件是關于商品本身的信息,包括商品id, 名稱,類別,買了還買等信息。
數據處理
這里主要涉及到兩個腳本,他們的功能分別是提取原始數據、處理數據。
1_convert_pd.py
(1)將reviews_Electronics_5.json轉換成dataframe,列分別為reviewID ,asin, reviewerName等,
(2)將meta_Electronics.json轉成dataframe,并且只保留在reviewes文件中出現過的商品,去重。
(3)轉換完的文件保存成pkl格式。
2_remap_id.py
(1)將reviews_df只保留reviewerID, asin, unixReviewTime三列;
(2)將meta_df保留asin, categories列,并且類別列只保留三級類目;(至此,用到的數據只設計5列,(reviewerID, asin, unixReviewTime),(asin, categories));
(3)用asin,categories,reviewerID分別生產三個map(asin_map, cate_map, revi_map),key為對應的原始信息,value為按key排序后的index(從0開始順序排序),然后將原數據的對應列原始數據轉換成key對應的index;各個map的示意圖如下:
(4)將meta_df按asin對應的index進行排序,如圖:
(5)將reiviews_df中的asin轉換成asin_map中asin對應的value值,并且按照reviewerID和時間排序。如圖:
(6)生成cate_list, 就是把meta_df的'categories'列取出來。
生成訓練集和測試集
(1)將reviews_df按reviewerID進行聚合,聚合的結果就是用戶的點擊序列。
(2)將hist的asin列作為每個reviewerID(也就是用戶)的正樣本列表(pos_list),注意這里的asin存的已經不是原始的item_id了,而是通過asin_map轉換過來的index。負樣本列表(neg_list)為在item_count范圍內產生不在pos_list中的隨機數列表。
訓練集
因為當前點擊的商品只和該用戶之前的點擊記錄有關,因此我們要獲取該用戶之前的歷史數據。
?正樣本:(reviewerID, hist, pos_item, 1)。?負樣本:(reviewerID, hist, neg_item, 0)。注意負樣本的邏輯是在item_count范圍內不再pos_list中的隨機數列表。
測試集
對于每個pos_list和neg_list的最后一個item,用做生成測試集,測試集的格式為(reviewerID, hist, (pos_item, neg_item))
舉例
比如對于reviewerID=0的用戶,她的pos_list是[13179, 17993, 28326, 29247, 62275],那么生成的訓練集和測試集分別是:
2.2 代碼解析
作者在寫DIN代碼model部分時,是將train、eval和test放到一起來寫,看起來不太易懂,因此這里我將這三個部分分開,更好的分析模型運作的過程。
train代碼
self.u = tf.placeholder(tf.int32, [None, ]) # [B],用戶idself.i = tf.placeholder(tf.int32, [None, ]) # [B],正樣本self.y = tf.placeholder(tf.float32, [None, ]) # [B],labelself.hist_i = tf.placeholder(tf.int32, [None, None]) # [B, T],用戶瀏覽記錄self.sl = tf.placeholder(tf.int32, [None, ]) # [B],真實記錄數量self.lr = tf.placeholder(tf.float64, []) #學習率hidden_units = 128#初始化幾個embedding矩陣。注意這里對cate也進行embedding化,item的最終embedding=id_emb+cate_embuser_emb_w = tf.get_variable("user_emb_w", [user_count, hidden_units]) #user的emb,模型中沒用到item_emb_w = tf.get_variable("item_emb_w", [item_count, hidden_units // 2]) #item的embitem_b = tf.get_variable("item_b", [item_count], initializer=tf.constant_initializer(0.0)) #item的biascate_emb_w = tf.get_variable("cate_emb_w", [cate_count, hidden_units // 2]) #cate的embcate_list = tf.convert_to_tensor(cate_list, dtype=tf.int64)#獲取正樣本的embeddingic = tf.gather(cate_list, self.i) #取出正樣本對應的catei_emb = tf.concat(values=[ tf.nn.embedding_lookup(item_emb_w, self.i), tf.nn.embedding_lookup(cate_emb_w, ic),], axis=1)#我的理解是充當item固有屬性了,bias的意思,是一個隨機數,可訓練i_b = tf.gather(item_b, self.i)#獲取歷史行為的embeddinghc = tf.gather(cate_list, self.hist_i)h_emb = tf.concat([ tf.nn.embedding_lookup(item_emb_w, self.hist_i), tf.nn.embedding_lookup(cate_emb_w, hc),], axis=2)#將正樣本embedding和歷史行為embedding送到attention網絡中hist_i = attention(i_emb, h_emb, self.sl)# -- attention end ---hist_i = tf.layers.batch_normalization(inputs=hist_i)hist_i = tf.reshape(hist_i, [-1, hidden_units], name='hist_bn')hist_i = tf.layers.dense(hist_i, hidden_units, name='hist_fcn')u_emb_i = hist_i #u_emb_i就是attention的輸出向量print(u_emb_i.get_shape().as_list())print(i_emb.get_shape().as_list())# -- fcn begin -------din_i = tf.concat([u_emb_i, i_emb], axis=-1)din_i = tf.layers.batch_normalization(inputs=din_i, name='b1')d_layer_1_i = tf.layers.dense(din_i, 80, activation=tf.nn.sigmoid, name='f1')d_layer_1_i = dice(d_layer_1_i, name='dice_1')d_layer_2_i = tf.layers.dense(d_layer_1_i, 40, activation=tf.nn.sigmoid, name='f2')d_layer_2_i = dice(d_layer_2_i, name='dice_2')d_layer_3_i = tf.layers.dense(d_layer_2_i, 1, activation=None, name='f3')d_layer_3_i = tf.reshape(d_layer_3_i, [-1])self.logits = i_b + d_layer_3_iself.loss = tf.reduce_mean( tf.nn.sigmoid_cross_entropy_with_logits( logits=self.logits, labels=self.y) )def test(self, sess, uij): return sess.run(self.logits_sub, feed_dict={ self.u: uij[0], self.i: uij[1], self.j: uij[2], self.hist_i: uij[3], self.sl: uij[4], })eval代碼
一般情況下,我們做驗證集的評價,都是復用train的過程,只不過最后不進行反向bp,但是這里將正負樣本一起送進去,分別得到postive_score和negative_score,同時記錄兩者及差值,方便后續計算gauc。
在訓練時候,每隔固定的step最好進行eval,保存一個分數最好的模型。
self.u = tf.placeholder(tf.int32, [None, ]) # [B],用戶idself.i = tf.placeholder(tf.int32, [None, ]) # [B],正樣本self.j = tf.placeholder(tf.int32, [None, ]) # [B],負樣本self.hist_i = tf.placeholder(tf.int32, [None, None]) # [B, T],用戶瀏覽記錄self.sl = tf.placeholder(tf.int32, [None, ]) # [B],真實記錄數量self.lr = tf.placeholder(tf.float64, []) #學習率hidden_units = 128#初始化幾個embedding矩陣。注意這里對cate也進行embedding化,item的最終embedding=id_emb+cate_embuser_emb_w = tf.get_variable("user_emb_w", [user_count, hidden_units]) #user的emb,模型中沒用到item_emb_w = tf.get_variable("item_emb_w", [item_count, hidden_units // 2]) #item的embitem_b = tf.get_variable("item_b", [item_count], initializer=tf.constant_initializer(0.0)) #item的biascate_emb_w = tf.get_variable("cate_emb_w", [cate_count, hidden_units // 2]) #cate的embcate_list = tf.convert_to_tensor(cate_list, dtype=tf.int64)#獲取正樣本的embeddingic = tf.gather(cate_list, self.i) #取出正樣本對應的catei_emb = tf.concat(values=[ tf.nn.embedding_lookup(item_emb_w, self.i), tf.nn.embedding_lookup(cate_emb_w, ic),], axis=1)i_b = tf.gather(item_b, self.i) #我的理解是充當item固有屬性了,bias的意思,是一個隨機數,可訓練#獲取負樣本的embeddingjc = tf.gather(cate_list, self.j)j_emb = tf.concat([ tf.nn.embedding_lookup(item_emb_w, self.j), tf.nn.embedding_lookup(cate_emb_w, jc), ], axis=1)j_b = tf.gather(item_b, self.j) #同上面的邏輯#獲取歷史行為的embeddinghc = tf.gather(cate_list, self.hist_i)h_emb = tf.concat([ tf.nn.embedding_lookup(item_emb_w, self.hist_i), tf.nn.embedding_lookup(cate_emb_w, hc),], axis=2)# 處理正樣本#將正樣本embedding和歷史行為embedding送到attention網絡中hist_i = attention(i_emb, h_emb, self.sl)hist_i = tf.layers.batch_normalization(inputs=hist_i)hist_i = tf.reshape(hist_i, [-1, hidden_units], name='hist_bn')hist_i = tf.layers.dense(hist_i, hidden_units, name='hist_fcn')u_emb_i = hist_i #u_emb_i就是attention的輸出向量din_i = tf.concat([u_emb_i, i_emb], axis=-1)din_i = tf.layers.batch_normalization(inputs=din_i, name='b1')d_layer_1_i = tf.layers.dense(din_i, 80, activation=tf.nn.sigmoid, name='f1')d_layer_1_i = dice(d_layer_1_i, name='dice_1')d_layer_2_i = tf.layers.dense(d_layer_1_i, 40, activation=tf.nn.sigmoid, name='f2')d_layer_2_i = dice(d_layer_2_i, name='dice_2')d_layer_3_i = tf.layers.dense(d_layer_2_i, 1, activation=None, name='f3')d_layer_3_i = tf.reshape(d_layer_3_i, [-1])# 處理負樣本hist_j = attention(j_emb, h_emb, self.sl)hist_j = tf.layers.batch_normalization(inputs=hist_j)hist_j = tf.reshape(hist_j, [-1, hidden_units], name='hist_bn')hist_j = tf.layers.dense(hist_j, hidden_units, name='hist_fcn', reuse=True)u_emb_j = hist_jdin_j = tf.concat([u_emb_j, j_emb], axis=-1)din_j = tf.layers.batch_normalization(inputs=din_j, name='b1', reuse=True)d_layer_1_j = tf.layers.dense(din_j, 80, activation=tf.nn.sigmoid, name='f1', reuse=True)d_layer_1_j = dice(d_layer_1_j, name='dice_1')d_layer_2_j = tf.layers.dense(d_layer_1_j, 40, activation=tf.nn.sigmoid, name='f2', reuse=True)d_layer_2_j = dice(d_layer_2_j, name='dice_2')d_layer_3_j = tf.layers.dense(d_layer_2_j, 1, activation=None, name='f3', reuse=True)d_layer_3_j = tf.reshape(d_layer_3_j, [-1])# 驗證結果x = i_b - j_b + d_layer_3_i - d_layer_3_j # [B]self.mf_auc = tf.reduce_mean(tf.to_float(x < 0))self.score_i = tf.sigmoid(i_b + d_layer_3_i)self.score_j = tf.sigmoid(j_b + d_layer_3_j)self.score_i = tf.reshape(self.score_i, [-1, 1])self.score_j = tf.reshape(self.score_j, [-1, 1])self.p_and_n = tf.concat([self.score_i, self.score_j], axis=-1)def eval(self, sess, uij): u_auc, socre_p_and_n = sess.run([self.mf_auc, self.p_and_n], feed_dict={ self.u: uij[0], self.i: uij[1], self.j: uij[2], self.hist_i: uij[3], self.sl: uij[4], }) return u_auc, socre_p_and_ntest代碼
test的過程是輸入一些用戶hist商品,一些待排序的item集合,然后得到每個item的打分。在model的部分,為了方便,只取了前predict_ads_num個廣告計算分數。
在實際推薦過程中,送入模型的是一個待排序的廣告id的list,然后對這個list中的所有廣告計算分數,也就是排序的過程。
self.hist_i = tf.placeholder(tf.int32, [None, None]) # [B, T],用戶瀏覽記錄self.sl = tf.placeholder(tf.int32, [None, ]) # [B],真實記錄數量self.lr = tf.placeholder(tf.float64, [])hidden_units = 128item_emb_w = tf.get_variable("item_emb_w", [item_count, hidden_units // 2])item_b = tf.get_variable("item_b", [item_count],initializer=tf.constant_initializer(0.0))cate_emb_w = tf.get_variable("cate_emb_w", [cate_count, hidden_units // 2])cate_list = tf.convert_to_tensor(cate_list, dtype=tf.int64)hc = tf.gather(cate_list, self.hist_i)h_emb = tf.concat([ tf.nn.embedding_lookup(item_emb_w, self.hist_i), tf.nn.embedding_lookup(cate_emb_w, hc),], axis=2)# prediciton for selected items# logits for selected item:item_emb_all = tf.concat([ item_emb_w, tf.nn.embedding_lookup(cate_emb_w, cate_list)], axis=1)item_emb_sub = item_emb_all[:predict_ads_num, :]item_emb_sub = tf.expand_dims(item_emb_sub, 0)item_emb_sub = tf.tile(item_emb_sub, [predict_batch_size, 1, 1])hist_sub = attention_multi_items(item_emb_sub, h_emb, self.sl)hist_sub = tf.layers.batch_normalization(inputs=hist_sub, name='hist_bn', reuse=tf.AUTO_REUSE)hist_sub = tf.reshape(hist_sub, [-1, hidden_units])hist_sub = tf.layers.dense(hist_sub, hidden_units, name='hist_fcn', reuse=tf.AUTO_REUSE)u_emb_sub = hist_subitem_emb_sub = tf.reshape(item_emb_sub, [-1, hidden_units])din_sub = tf.concat([u_emb_sub, item_emb_sub], axis=-1)din_sub = tf.layers.batch_normalization(inputs=din_sub, name='b1', reuse=True)d_layer_1_sub = tf.layers.dense(din_sub, 80, activation=tf.nn.sigmoid, name='f1', reuse=True)d_layer_1_sub = dice(d_layer_1_sub, name='dice_1_sub')d_layer_2_sub = tf.layers.dense(d_layer_1_sub, 40, activation=tf.nn.sigmoid, name='f2', reuse=True)d_layer_2_sub = dice(d_layer_2_sub, name='dice_2_sub')d_layer_3_sub = tf.layers.dense(d_layer_2_sub, 1, activation=None, name='f3', reuse=True)d_layer_3_sub = tf.reshape(d_layer_3_sub, [-1, predict_ads_num])self.logits_sub = tf.sigmoid(item_b[:predict_ads_num] + d_layer_3_sub)self.logits_sub = tf.reshape(self.logits_sub, [-1, predict_ads_num, 1])總結
以上是生活随笔為你收集整理的din算法 代码_深度兴趣网络(DIN,Deep Interest Network)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html中如何显示图片
- 下一篇: (stm32f103学习总结)—ADC模