DIN模型介绍

简介

场景:根据用户历史行为序列给用户推荐物品
例如:根据用户最近买了鞋子,裙子,现在要预估一件女性大衣的CTR
或者音乐场景中,用户最近在听rap,给他推荐一些中国新说唱的歌曲的CTR

  1. 普通做法,对用户历史行为的item embedding做average pooling
  2. 但是实际中,用户行为中有些跟当前推荐物品有关,有些跟当前物品无关,比如女性偶尔给男朋友买了球鞋,偶尔听到一首热门歌曲,其实跟用户当前想要的物品关联度不高,而女性又一次买了高跟鞋,可能这次买口红的关联行酒更大
    这就是attention机制的意义。

Attention机制

注意力机制顾名思义,就是模型在预测的时候,对用户不同行为的注意力是不一样的,“相关”的行为历史看重一些,“不相关”的历史甚至可以忽略。那么这样的思想反应到模型中也是直观的
在这里插入图片描述
Vu是用户兴趣表达,Vi是历史物品embedding,Va是当前物品embedding
wi是每个历史物品和当前物品的相关性权重,可以由Vi,Va的相关性函数表示。g(Vi, Va)由Activation Unit表示
在这里插入图片描述

传统的Attention机制中,给定两个item embedding,比如u和v,通常是直接做点积uv或者uWv,其中W是一个|u|x|v|的权重矩阵,但这篇paper中阿里显然做了更进一步的改进,着重看上图右上角的activation unit,首先是把u和v以及u v的element wise差值向量合并起来作为输入,然后喂给全连接层,最后得出权重,这样的方法显然损失的信息更少

源码分析

输入数据处理:
lookup history embedding
lookup song embedding 和 song category embdding

hidden_units = 128

user_emb_w = tf.get_variable("user_emb_w", [user_count, hidden_units])
item_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)

ic = tf.gather(cate_list, self.i)
i_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)

jc = 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)

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)

hist_i =attention(i_emb, h_emb, self.sl)

attention 模块
输入:
queries: B x H 当前物品embedding
keys: B x T x H 用户历史行为物品序列embedding,T为序列长度
keys_length: 用户历史行为的实际长度 <= T

注意mask操作,tf.sequence_mask(keys_length, tf.shape(keys)[1]) 构造mask,让用户实际序列外的值很很小的值,softmax之后进行加权和后不受影响

def attention(queries, keys, keys_length):
  '''
    queries:     [B, H]
    keys:        [B, T, H]
    keys_length: [B]
  '''
  queries_hidden_units = queries.get_shape().as_list()[-1]
  queries = tf.tile(queries, [1, tf.shape(keys)[1]]) # 对当前物品复制T次 --》 B H*T
  queries = tf.reshape(queries, [-1, tf.shape(keys)[1], queries_hidden_units])  # B T H
  din_all = tf.concat([queries, keys, queries-keys, queries*keys], axis=-1)   # 计算相关性权重
  d_layer_1_all = tf.layers.dense(din_all, 80, activation=tf.nn.sigmoid, name='f1_att', reuse=tf.AUTO_REUSE)
  d_layer_2_all = tf.layers.dense(d_layer_1_all, 40, activation=tf.nn.sigmoid, name='f2_att', reuse=tf.AUTO_REUSE)
  d_layer_3_all = tf.layers.dense(d_layer_2_all, 1, activation=None, name='f3_att', reuse=tf.AUTO_REUSE)
  d_layer_3_all = tf.reshape(d_layer_3_all, [-1, 1, tf.shape(keys)[1]])  # B 1 T  
  outputs = d_layer_3_all 
  # Mask  防止padding进行更新和计算权重
  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)  ## 很小的值,为了softmax的权重很低
  outputs = tf.where(key_masks, outputs, paddings)  # [B, 1, T]
# 序列外补很小的值
  # Scale
  outputs = outputs / (keys.get_shape().as_list()[-1] ** 0.5)

  # Activation
  outputs = tf.nn.softmax(outputs)  # [B, 1, T]

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

  return outputs
发布了35 篇原创文章 · 获赞 61 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/cql342624757/article/details/103940661
DIN
今日推荐