din算法 代码_深度兴趣网络(DIN,Deep Interest Network)
1. DIN介紹
Deep Interest Network是基于BaseModel演化而來(lái)
1.1 流程:
整個(gè)流程可以描述為:1.檢查用戶歷史行為數(shù)據(jù) 2.使用matching module產(chǎn)生候選ads。3.通過(guò)ranking module做point-wise的排序,即得到每個(gè)候選ads的點(diǎn)擊概率,并根據(jù)概率排序得到推薦列表。4.記錄下用戶在當(dāng)前展示廣告下的反應(yīng)(點(diǎn)擊與否),作為label。
1.2 特征設(shè)計(jì)
本文將所涉及到的特征分為四個(gè)部分:用戶特征、用戶行為特征、廣告特征、上下文特征,具體如下:
然后,通過(guò)DNN學(xué)習(xí)數(shù)據(jù)之間的交叉特征。
1.3 模型結(jié)構(gòu)
首先,介紹一般的DNN模型結(jié)構(gòu),大致分為以下幾個(gè)部分:
?Embedding Layer: 原始數(shù)據(jù)是高維且稀疏的0-1矩陣,emdedding層用于將原始高維數(shù)據(jù)壓縮成低維矩陣;?Pooling Layer : 由于不同的用戶有不同個(gè)數(shù)的行為數(shù)據(jù),導(dǎo)致embedding矩陣的向量大小不一致,而全連接層只能處理固定維度的數(shù)據(jù),因此,利用Pooling Layer得到一個(gè)固定長(zhǎng)度的向量。
?Concat Layer:?經(jīng)過(guò)embedding layer和pooling layer后,原始稀疏特征被轉(zhuǎn)換成多個(gè)固定長(zhǎng)度的用戶興趣的抽象表示向量,然后利用concat layer聚合抽象表示向量,輸出該用戶興趣的唯一抽象表示向量;?MLP:將concat layer輸出的抽象表示向量作為MLP的輸入,自動(dòng)學(xué)習(xí)數(shù)據(jù)之間的交叉特征;?Loss:損失函數(shù)一般采用Loglos
傳統(tǒng)DNN模型在Embedding Layer -> Pooling Layer得到用戶興趣表示的時(shí)候,沒(méi)有考慮用戶與廣告之間的關(guān)系,即不同廣告之間的權(quán)重是一致的。之前也分析過(guò),這樣是有問(wèn)題的。因此,DIN利用attention機(jī)制,在得到用戶興趣表示時(shí)賦予不同的歷史行為不同的權(quán)重,即通過(guò)Embedding Layer -> Pooling Layer+attention實(shí)現(xiàn)局部激活。從最終反向訓(xùn)練的角度來(lái)看,就是根據(jù)當(dāng)前的候選廣告,來(lái)反向的激活用戶歷史的興趣愛(ài)好,賦予不同歷史行為不同的權(quán)重。
DIN認(rèn)為用戶的興趣不是一個(gè)點(diǎn),而是一個(gè)多峰的函數(shù)。一個(gè)峰就表示一個(gè)興趣,峰值的大小表示興趣強(qiáng)度。那么針對(duì)不同的候選廣告,用戶的興趣強(qiáng)度是不同的,也就是說(shuō)隨著候選廣告的變化,用戶的興趣強(qiáng)度不斷在變化。?
attention的公式如下:
其中,ei表示用戶U歷史行為embedding向量,wj表示ej的權(quán)重,Vu表示用戶所有行為embedding向量的加權(quán)和,表示用戶的興趣。候選廣告影響著每個(gè)behavior id的權(quán)重,也就是Local Activation。權(quán)重表示的是:每一個(gè)behavior id針對(duì)當(dāng)前的候選廣告Va,對(duì)總的用戶興趣表示的Embedding Vector的貢獻(xiàn)大小。在實(shí)際實(shí)現(xiàn)中,權(quán)重用激活函數(shù)Dice的輸出來(lái)表示,輸入是Vi和Va。
1.4 自適應(yīng)激活函數(shù)Dice
DIN提出了一種數(shù)據(jù)動(dòng)態(tài)自適應(yīng)激活函數(shù)Dice,認(rèn)為分割點(diǎn)不是固定為0的,而是隨著數(shù)據(jù)不同而動(dòng)態(tài)變化的。Dice公式如下:
ps的計(jì)算分為兩步:
?對(duì)進(jìn)行正態(tài)分布?xì)w一化處理,使得數(shù)據(jù)集中在正態(tài)分布均值處;?利用sigmoid函數(shù)歸一化,使得輸出在0~1之間。
f(s)的作用可以理解為一種平滑操作,alpha是一個(gè)超參數(shù),推薦值為0.99。
1.5 高效正則器
由于DNN模型往往十分復(fù)雜,而且參數(shù)較多。利用L1,L2等正則手段往往加重了模型過(guò)擬合,DIN提出了一種高效的正則方法:
由于數(shù)據(jù)中有些feature id出現(xiàn)次數(shù)較少,這在訓(xùn)練過(guò)程中將引入很多噪聲,從而加重了過(guò)擬合風(fēng)險(xiǎn)。DIN構(gòu)造的正則器會(huì)針對(duì)id出現(xiàn)的頻率不同,動(dòng)態(tài)調(diào)整其參數(shù)的正則化力度。即,出現(xiàn)頻率較高的id,給與較小的正則化力度,反之,則加大力度。
1.6?總結(jié)
DIN通過(guò)引入attention機(jī)制,針對(duì)不同的廣告構(gòu)造不同的用戶抽象表示,從而實(shí)現(xiàn)了在數(shù)據(jù)維度一定的情況下,更精準(zhǔn)地捕捉用戶當(dāng)前的興趣。此外,DIN模型也適用于其他有豐富行為數(shù)據(jù)的場(chǎng)景,比如,電商中的個(gè)性化推薦,以及當(dāng)前比較火熱的feed流推薦等。
2.算法實(shí)踐
2.1數(shù)據(jù)處理
基礎(chǔ)數(shù)據(jù)
論文中用的是Amazon Product Data數(shù)據(jù),包含兩個(gè)文件:reviews_Electronics_5.json, meta_Electronics.json.
文件格式鏈接中有說(shuō)明,其中reviews主要是用戶買了相關(guān)商品產(chǎn)生的上下文信息,包括商品id, 時(shí)間,評(píng)論等。meta文件是關(guān)于商品本身的信息,包括商品id, 名稱,類別,買了還買等信息。
數(shù)據(jù)處理
這里主要涉及到兩個(gè)腳本,他們的功能分別是提取原始數(shù)據(jù)、處理數(shù)據(jù)。
1_convert_pd.py
(1)將reviews_Electronics_5.json轉(zhuǎn)換成dataframe,列分別為reviewID ,asin, reviewerName等,
(2)將meta_Electronics.json轉(zhuǎn)成dataframe,并且只保留在reviewes文件中出現(xiàn)過(guò)的商品,去重。
(3)轉(zhuǎn)換完的文件保存成pkl格式。
2_remap_id.py
(1)將reviews_df只保留reviewerID, asin, unixReviewTime三列;
(2)將meta_df保留asin, categories列,并且類別列只保留三級(jí)類目;(至此,用到的數(shù)據(jù)只設(shè)計(jì)5列,(reviewerID, asin, unixReviewTime),(asin, categories));
(3)用asin,categories,reviewerID分別生產(chǎn)三個(gè)map(asin_map, cate_map, revi_map),key為對(duì)應(yīng)的原始信息,value為按key排序后的index(從0開(kāi)始順序排序),然后將原數(shù)據(jù)的對(duì)應(yīng)列原始數(shù)據(jù)轉(zhuǎn)換成key對(duì)應(yīng)的index;各個(gè)map的示意圖如下:
(4)將meta_df按asin對(duì)應(yīng)的index進(jìn)行排序,如圖:
(5)將reiviews_df中的asin轉(zhuǎn)換成asin_map中asin對(duì)應(yīng)的value值,并且按照reviewerID和時(shí)間排序。如圖:
(6)生成cate_list, 就是把meta_df的'categories'列取出來(lái)。
生成訓(xùn)練集和測(cè)試集
(1)將reviews_df按reviewerID進(jìn)行聚合,聚合的結(jié)果就是用戶的點(diǎn)擊序列。
(2)將hist的asin列作為每個(gè)reviewerID(也就是用戶)的正樣本列表(pos_list),注意這里的asin存的已經(jīng)不是原始的item_id了,而是通過(guò)asin_map轉(zhuǎn)換過(guò)來(lái)的index。負(fù)樣本列表(neg_list)為在item_count范圍內(nèi)產(chǎn)生不在pos_list中的隨機(jī)數(shù)列表。
訓(xùn)練集
因?yàn)楫?dāng)前點(diǎn)擊的商品只和該用戶之前的點(diǎn)擊記錄有關(guān),因此我們要獲取該用戶之前的歷史數(shù)據(jù)。
?正樣本:(reviewerID, hist, pos_item, 1)。?負(fù)樣本:(reviewerID, hist, neg_item, 0)。注意負(fù)樣本的邏輯是在item_count范圍內(nèi)不再pos_list中的隨機(jī)數(shù)列表。
測(cè)試集
對(duì)于每個(gè)pos_list和neg_list的最后一個(gè)item,用做生成測(cè)試集,測(cè)試集的格式為(reviewerID, hist, (pos_item, neg_item))
舉例
比如對(duì)于reviewerID=0的用戶,她的pos_list是[13179, 17993, 28326, 29247, 62275],那么生成的訓(xùn)練集和測(cè)試集分別是:
2.2 代碼解析
作者在寫DIN代碼model部分時(shí),是將train、eval和test放到一起來(lái)寫,看起來(lái)不太易懂,因此這里我將這三個(gè)部分分開(kāi),更好的分析模型運(yùn)作的過(guò)程。
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],真實(shí)記錄數(shù)量self.lr = tf.placeholder(tf.float64, []) #學(xué)習(xí)率hidden_units = 128#初始化幾個(gè)embedding矩陣。注意這里對(duì)cate也進(jìn)行embedding化,item的最終embedding=id_emb+cate_embuser_emb_w = tf.get_variable("user_emb_w", [user_count, hidden_units]) #user的emb,模型中沒(méi)用到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) #取出正樣本對(duì)應(yīng)的catei_emb = tf.concat(values=[ tf.nn.embedding_lookup(item_emb_w, self.i), tf.nn.embedding_lookup(cate_emb_w, ic),], axis=1)#我的理解是充當(dāng)item固有屬性了,bias的意思,是一個(gè)隨機(jī)數(shù),可訓(xùn)練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網(wǎng)絡(luò)中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代碼
一般情況下,我們做驗(yàn)證集的評(píng)價(jià),都是復(fù)用train的過(guò)程,只不過(guò)最后不進(jìn)行反向bp,但是這里將正負(fù)樣本一起送進(jìn)去,分別得到postive_score和negative_score,同時(shí)記錄兩者及差值,方便后續(xù)計(jì)算gauc。
在訓(xùn)練時(shí)候,每隔固定的step最好進(jìn)行eval,保存一個(gè)分?jǐn)?shù)最好的模型。
self.u = tf.placeholder(tf.int32, [None, ]) # [B],用戶idself.i = tf.placeholder(tf.int32, [None, ]) # [B],正樣本self.j = tf.placeholder(tf.int32, [None, ]) # [B],負(fù)樣本self.hist_i = tf.placeholder(tf.int32, [None, None]) # [B, T],用戶瀏覽記錄self.sl = tf.placeholder(tf.int32, [None, ]) # [B],真實(shí)記錄數(shù)量self.lr = tf.placeholder(tf.float64, []) #學(xué)習(xí)率hidden_units = 128#初始化幾個(gè)embedding矩陣。注意這里對(duì)cate也進(jìn)行embedding化,item的最終embedding=id_emb+cate_embuser_emb_w = tf.get_variable("user_emb_w", [user_count, hidden_units]) #user的emb,模型中沒(méi)用到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) #取出正樣本對(duì)應(yīng)的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) #我的理解是充當(dāng)item固有屬性了,bias的意思,是一個(gè)隨機(jī)數(shù),可訓(xùn)練#獲取負(fù)樣本的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網(wǎng)絡(luò)中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])# 處理負(fù)樣本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])# 驗(yàn)證結(jié)果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的過(guò)程是輸入一些用戶hist商品,一些待排序的item集合,然后得到每個(gè)item的打分。在model的部分,為了方便,只取了前predict_ads_num個(gè)廣告計(jì)算分?jǐn)?shù)。
在實(shí)際推薦過(guò)程中,送入模型的是一個(gè)待排序的廣告id的list,然后對(duì)這個(gè)list中的所有廣告計(jì)算分?jǐn)?shù),也就是排序的過(guò)程。
self.hist_i = tf.placeholder(tf.int32, [None, None]) # [B, T],用戶瀏覽記錄self.sl = tf.placeholder(tf.int32, [None, ]) # [B],真實(shí)記錄數(shù)量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])總結(jié)
以上是生活随笔為你收集整理的din算法 代码_深度兴趣网络(DIN,Deep Interest Network)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: html中如何显示图片
- 下一篇: 华为ipd产品开发流程_IPD(集成产品