DIN(Deep Interest Network):核心思想+源碼閱讀注釋

論文地址: https://arxiv.org/abs/1706.06978
DIN是近年來較為有代表性的一篇廣告推薦CTR預(yù)估的文章辞州,其中使用的attention機(jī)制也為使用序列特征提供了新的思路讯嫂。本文分析了DIN的核心思想。鑒于DIN源代碼的變量命名過于隨意,這里也提供了部分源代碼的注釋诚些,僅供參考飞傀。

論文分析

  • 核心思想:用戶的興趣是多元化的(diversity),并且對(duì)于特定的廣告诬烹,用戶不同的興趣會(huì)產(chǎn)生不同的影響(local activation)砸烦。

  • 舉個(gè)DIN論文上的例子:

    一位年輕的寶媽,在淘寶上點(diǎn)擊了一個(gè)新款包包的廣告(說明她對(duì)這一廣告很感興趣)绞吁。我們需要研究是什么因素造成了她會(huì)點(diǎn)擊幢痘,以便今后給她投放類似的廣告。一般來說家破,用戶行為特征(user bahavior features)(如用戶近期瀏覽/點(diǎn)擊/購買過的商品等)具有決定性的因素颜说。用戶行為特征一般是指某個(gè)用戶對(duì)于多個(gè)商品的行為序列购岗。不同的用戶,其用戶行為序列的長度和內(nèi)容都有很大差異(這就是diversity)门粪。對(duì)于這位寶媽喊积,假設(shè)其用戶序列中包含了她最近瀏覽的新款托特包,皮革手袋玄妈,以及鍋碗瓢盆等乾吻。很明顯,和鍋碗瓢盆相比拟蜻,前兩個(gè)商品對(duì)于她點(diǎn)擊這一新款包包廣告這一行為更具有決定性因素(這就是local activation)绎签。那么對(duì)于其他的廣告,如廚房清潔劑廣告酝锅,則其用戶行為序列中瀏覽的鍋碗瓢盆部分會(huì)成為預(yù)測該寶媽是否會(huì)點(diǎn)擊該廣告(即CTR預(yù)測)的決定性因素诡必。換句話說,預(yù)測某用戶對(duì)于某廣告的CTR時(shí)屈张,不能對(duì)用戶行為序列中所有商品都一視同仁擒权,而是要考慮目標(biāo)廣告的具體內(nèi)容與用戶行為序列的結(jié)合。

  • 現(xiàn)存的Embedding+MLP模型的問題

    現(xiàn)有的CTR預(yù)測模型阁谆,如FNN碳抄,Wide&Deep,Deep Crossing场绿,PNN等剖效,其主要結(jié)構(gòu)都是使用FM等方式實(shí)現(xiàn)Embedding,將大規(guī)模稀疏的Web數(shù)據(jù)轉(zhuǎn)化為稠密的vector焰盗。由于輸入特征中的用戶行為序列一般為multi-hot編碼璧尸,因此不同用戶數(shù)據(jù)embedding后的vector長度數(shù)不同的。一般會(huì)將這些vector通過一個(gè)pooling層(sum pooling 或mean pooling)得到長度固定的vector熬拒,并輸入到后續(xù)的MLP中訓(xùn)練爷光。這一過程的問題是,所有用戶的特征都用一個(gè)定長的vector來表示澎粟,并沒有考慮到用戶序列特征和目標(biāo)廣告之間的關(guān)系蛀序。DIN論文中使用的base model就是這樣一種模型,如圖1所示:

圖1. base model
  • 那么如何將兩者結(jié)合起來呢活烙?論文設(shè)計(jì)的DIN模型徐裸,可以自適應(yīng)地在計(jì)算用戶興趣向量時(shí)考慮到用戶歷史行為與候選廣告之間的關(guān)系。(原文:Instead pf expressing all user's diverse interests with the ssame vector, DIN adaptively calculates the representation vector of user interests by taking into consideration the relevance of historical behaviors w.r.t. candidate ad. ) 這種用戶興趣向量啸盏,對(duì)于不同的候選廣告(candidate ad)來說是不同的重贺。

    至于如何在網(wǎng)絡(luò)結(jié)構(gòu)中實(shí)現(xiàn),DIN引入了一種local activation unit,來計(jì)算用戶行為特征和候選廣告之間的關(guān)系气笙,如圖2所示:


    圖2. DIN model

對(duì)于候選廣告次企, 根據(jù)local activation unit計(jì)算出的用戶興趣向量為:
\boldsymbol v_U(A)=f(\boldsymbol {v_A,e_1,e_2,...,e_H} )=\sum_{j=1}^Ha\boldsymbol{(e_j,v_A)e_j}=\sum_{j=1}^H\boldsymbol{w_je_j} \tag 1

其中\boldsymbol {\{e_1,e_2,...,e_H\}}為代表用戶\boldsymbol U的行為序列的embedding向量,長度為H健民,\boldsymbol v_A為廣告\boldsymbol A的embedding 向量抒巢。在這種計(jì)算方式下,最終的用戶\boldsymbol U的興趣向量會(huì)根據(jù)不同的廣告\boldsymbol A而變化秉犹,這里a(\cdot)表示一個(gè)feed-forward network蛉谜,其輸出作為local activation的權(quán)值,與用戶向量相乘崇堵,如圖2中的標(biāo)注所示型诚。

Local activation借鑒了NMT(Neural Machine Translation)中的attention機(jī)制。不同的是鸳劳,傳統(tǒng)attention層會(huì)做歸一化處理狰贯,因此式(1)中的權(quán)值需要滿足\sum_iw_i=1。但是在DIN中則沒有此限制赏廓,\sum_iw_i的值被視為對(duì)用戶興趣強(qiáng)度值的近似涵紊,這樣對(duì)于不同的廣告A,的取值范圍不同幔摸,更能夠體現(xiàn)出local activation的意義摸柄。

部分源碼解讀

開源代碼地址(重點(diǎn)看/din/model.py):https://github.com/zhougr1993/DeepInterestNetwork/blob/master/din/model.py
DIN的開放源代碼中的變量命名非常的難以理解(i, j, h, y等等不知道什么意思)。這里經(jīng)過我的實(shí)驗(yàn)和推(xia)理(cai)既忆,總結(jié)了部分代碼的注釋驱负。個(gè)人理解,僅供參考患雇。

  • 首先是/din/model.py中的開始部分
import tensorflow as tf
from Dice import dice
class Model(object):
  def __init__(self, user_count, item_count, cate_count, cate_list,\
                               predict_batch_size, predict_ads_num):
    self.u = tf.placeholder(tf.int32, [None,]) 
    # shape: [B],  user id跃脊。 (B:batch size)
    self.i = tf.placeholder(tf.int32, [None,]) 
    # shape: [B]  i: 正樣本的item
    self.j = tf.placeholder(tf.int32, [None,]) 
    # shape: [B]  j: 負(fù)樣本的item
    self.y = tf.placeholder(tf.float32, [None,]) 
    # shape: [B], y: label
    self.hist_i = tf.placeholder(tf.int32, [None, None]) 
    # shape: [B, T] #用戶行為特征(User Behavior)中的item序列。T為序列長度
    self.sl = tf.placeholder(tf.int32, [None,]) 
    # shape: [B]; sl:sequence length苛吱,User Behavior中序列的真實(shí)序列長度(酪术?)
    self.lr = tf.placeholder(tf.float64, [])
    # learning rate
    hidden_units = 128
    user_emb_w = tf.get_variable("user_emb_w", [user_count, hidden_units])       
    # shape: [U, H], user_id的embedding weight. U是user_id的hash bucket size

    item_emb_w = tf.get_variable("item_emb_w", [item_count, hidden_units // 2])  #[I, H//2]
     # shape: [I, H//2], item_id的embedding weight. I是item_id的hash bucket size

    item_b = tf.get_variable("item_b", [item_count],
                             initializer=tf.constant_initializer(0.0))           
    # shape: [I], bias
    cate_emb_w = tf.get_variable("cate_emb_w", [cate_count, hidden_units // 2])  
    # shape: [C, H//2], cate_id的embedding weight. 

    cate_list = tf.convert_to_tensor(cate_list, dtype=tf.int64)   
    # shape: [C, H//2]

    ic = tf.gather(cate_list, self.i) 
    # 從cate_list中取出正樣本的cate
    i_emb = tf.concat(values = [   
        tf.nn.embedding_lookup(item_emb_w, self.i),
        tf.nn.embedding_lookup(cate_emb_w, ic),
        ], axis=1)
    # 正樣本的embedding,正樣本包括item和cate

    i_b = tf.gather(item_b, self.i)

    jc = tf.gather(cate_list, self.j) 
    # 從cate_list中取出負(fù)樣本的cate
    j_emb = tf.concat([             
        tf.nn.embedding_lookup(item_emb_w, self.j),
        tf.nn.embedding_lookup(cate_emb_w, jc),
        ], axis=1)
    # 負(fù)樣本的embedding翠储,負(fù)樣本包括item和cate

    j_b = tf.gather(item_b, self.j) #偏置b
    hc = tf.gather(cate_list, self.hist_i) 
    # 用戶行為序列(User Behavior)中的cate序列

    h_emb = tf.concat([tf.nn.embedding_lookup(item_emb_w, self.hist_i),
        tf.nn.embedding_lookup(cate_emb_w, hc),
        ], axis=2)
    #用戶行為序列(User Behavior)的embedding绘雁,包括item序列和cate序列
    hist_i = attention(i_emb, h_emb, self.sl) #attention操作
    #-- attention end ---
  • 接著跳到/din/model.py中的Line199看attention部分
def attention(queries, keys, keys_length):
  '''
    queries:     shape: [B, H], 即i_emb
    keys:        shape: [B, T, H], 即h_emb
    keys_length: shape: [B], 即self.sl
    B:batch size; T: 用戶序列的長度;H:embedding size
  '''
  queries_hidden_units = queries.get_shape().as_list()[-1]                     
  # shape: [H]
  queries = tf.tile(queries, [1, tf.shape(keys)[1]])                            
  # [B,H] -> T*[B,H]
  queries = tf.reshape(queries, [-1, tf.shape(keys)[1], queries_hidden_units])  
  # T*[B,H] ->[B, T, H]
  din_all = tf.concat([queries, keys, queries-keys, queries*keys], axis=-1)     
  # attention操作彰亥,輸出維度為[B, T, 4*H]
  d_layer_1_all = tf.layers.dense(din_all, 80, activation=tf.nn.sigmoid, \
                              name='f1_att', reuse=tf.AUTO_REUSE) # [B, T, 80]
  d_layer_2_all = tf.layers.dense(d_layer_1_all, 40, activation=tf.nn.sigmoid, \
                              name='f2_att', reuse=tf.AUTO_REUSE) # [B, T, 40]
  d_layer_3_all = tf.layers.dense(d_layer_2_all, 1, activation=None, \
                              name='f3_att', reuse=tf.AUTO_REUSE) # [B, T, 1]
  d_layer_3_all = tf.reshape(d_layer_3_all, [-1, 1, tf.shape(keys)[1]]) #[B, 1, T]
  outputs = d_layer_3_all # attention的輸出, [B, 1, T]

  # Mask
  key_masks = tf.sequence_mask(keys_length, tf.shape(keys)[1])   # [B, T]
  key_masks = tf.expand_dims(key_masks, 1) # [B, 1, T]
  paddings = tf.ones_like(outputs) * (-2 ** 32 + 1) 
  # padding的mask后補(bǔ)一個(gè)很小的負(fù)數(shù),這樣softmax之后就會(huì)接近0.
  outputs = tf.where(key_masks, outputs, paddings)  
  # [B, 1, T] padding操作衰齐,將每個(gè)樣本序列中空缺的商品都賦值為(-2 ** 32 + 1)

  # Scale
  outputs = outputs / (keys.get_shape().as_list()[-1] ** 0.5)

  # Activation
  outputs = tf.nn.softmax(outputs) 
  # [B, 1, T] #這里的output是attention計(jì)算出來的權(quán)重任斋,即論文公式(3)里的w,

  # Weighted sum
  outputs = tf.matmul(outputs, keys)  
  # [B, 1, H]

  return outputs

如果有疑問或指正,歡迎在評(píng)論區(qū)提出7峡帷瘟檩!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市澈蟆,隨后出現(xiàn)的幾起案子墨辛,更是在濱河造成了極大的恐慌,老刑警劉巖趴俘,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件睹簇,死亡現(xiàn)場離奇詭異,居然都是意外死亡寥闪,警方通過查閱死者的電腦和手機(jī)太惠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來疲憋,“玉大人凿渊,你說我怎么就攤上這事「苛” “怎么了埃脏?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長秋忙。 經(jīng)常有香客問我彩掐,道長,這世上最難降的妖魔是什么翰绊? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任佩谷,我火速辦了婚禮,結(jié)果婚禮上监嗜,老公的妹妹穿的比我還像新娘谐檀。我一直安慰自己,他們只是感情好裁奇,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布桐猬。 她就那樣靜靜地躺著,像睡著了一般刽肠。 火紅的嫁衣襯著肌膚如雪溃肪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天音五,我揣著相機(jī)與錄音惫撰,去河邊找鬼。 笑死躺涝,一個(gè)胖子當(dāng)著我的面吹牛厨钻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼夯膀,長吁一口氣:“原來是場噩夢啊……” “哼诗充!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起诱建,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤蝴蜓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后俺猿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茎匠,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年辜荠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了汽抚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡伯病,死狀恐怖造烁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情午笛,我是刑警寧澤惭蟋,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站药磺,受9級(jí)特大地震影響告组,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜癌佩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一木缝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧围辙,春花似錦我碟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至掸冤,卻和暖如春厘托,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背稿湿。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來泰國打工铅匹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人饺藤。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓包斑,卻偏偏與公主長得像考杉,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子舰始,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355