为什么模型中的user_emb矩阵没有使用?
DIN model中为何没有拼接user vector # 64
其他的模型里u_emb也是被hist替换掉了。我对小数据集没有特别强的感觉和经验,生产的时候也很难用上user id的,user id单独还好维度不是很大,但是一旦样本里user id和 目标item id都存在的时候,整个样本的特征表达和区分能力就非常强,比较容易过拟合。不过是听说过有的场景用user id成功的经历,我们的数据体量也非常大了,结论就是要么过拟合要么无效,没有做过多的研究。
开源给出的code只是在公开数据集实现的demo,这些数据集没有很丰富的特征。
生产的DIN模型我们是以item为最小的粒度,把item的所有相关的feature 如你说的 品牌 brand、类目 category,还有产品词 product word,店铺 shop,以及一些别的item为key能关联到的feature。把它们的embedding 和item的embedding concat 起来当做是最终item的embedding,这个concat操作也可以变成sum pooling操作,不过效果都是差不多的,concat在我们的场景会好一些。对于用户行为序列的item和预估候选的item都做同样的处理,最终输入模型。
由于Amazon数据集和Movielens数据集的功能维度都不高(约10万),因此所有深度模型(包括我们提出的DIN)都不会遇到严重的过拟合问题。 但是,当涉及包含较高维度稀疏特征的在线广告系统中的Alibaba数据集时,过度拟合将是一个很大的挑战。 例如,当训练具有细粒度特征的深层模型(例如,表1中尺寸为6亿个goods_ids的特征)时,不加正则化会在第一个epochs之后会发生严重的过度拟合,这会导致模型性能迅速下降。 因此,检验几种常用正则化的性能:
- Dropout:在每一个样本中随机丢弃50%的特征id;
- Filter:按样本中的出现频率过滤访问的goods_id,仅保留最频繁的那些。剩下的前2000万个goods_id;
- Regularization in DiFacto:与频繁特征相关的参数不太会被过度正则化;
- MBA:Mini-Batch Aware regularization method;
Dropout可快速防止过拟合,但会降低收敛速度。 DiFacto中的正则化会以较高的频率对goods_id设置更大的惩罚,其效果要比Filter差。 MBA效果最好。
DIN核心——local activation
att 一般都要进行缩放的,why?
对一个query进行attention
"""
输入有三个
候选广告queries,用户历史行为keys,以及Batch中每个行为的长度。
这里为什么要输入一个keys_length呢,因为每个用户发生过的历史行为是不一样多的,但是输入的keys维度是固定的(都是历史行为最大的长度),
因此我们需要这个长度来计算一个mask,告诉模型哪些行为是没用的,哪些是用来计算用户兴趣分布的。
经过以下几个步骤得到用户的兴趣分布:
将queries变为和keys同样的形状B * T * H
(B指batch的大小,T指用户历史行为的最大长度,H指embedding的维度)
通过三层神经网络得到queries和keys中每个key的权重,并经过softmax进行标准化(不是说不用归一化更能表达用户对query的兴趣分布吗?)
通过weighted sum得到最终用户的历史行为分布
"""
def attention(queries, keys, keys_length):
'''
queries: [B, H] (即i_emb)
keys: [B, T, H] (即h_emb)
keys_length: [B] 即self.sl
B: batch size;
T: 用户序列的长度;
H: embedding size;
'''
# shape: [H]
queries_hidden_units = queries.get_shape().as_list()[-1]
# [B,H] -> T*[B,H]
queries = tf.tile(queries, [1, tf.shape(keys)[1]])
# T*[B,H] ->[B, T, H]
queries = tf.reshape(queries, [-1, tf.shape(keys)[1], queries_hidden_units])
# 有差值,有外积 # B*T*4H
# attention操作,输出维度为[B, T, 4*H]
din_all = tf.concat([queries, keys, queries - keys, queries * keys], axis=-1)
# 三层全链接
# [B, T, 80]
d_layer_1_all = tf.layers.dense(din_all, 80, activation=tf.nn.sigmoid, name='f1_att', reuse=tf.compat.v1.AUTO_REUSE)
# [B, T, 40]
d_layer_2_all = tf.layers.dense(d_layer_1_all, 40, activation=tf.nn.sigmoid, name='f2_att',
reuse=tf.compat.v1.AUTO_REUSE)
# [B, T, 1]
d_layer_3_all = tf.layers.dense(d_layer_2_all, 1, activation=None, name='f3_att',
reuse=tf.compat.v1.AUTO_REUSE) # B*T*1
# [B, 1, T]
d_layer_3_all = tf.reshape(d_layer_3_all, [-1, 1, tf.shape(keys)[1]])
# attention的输出, [B, 1, T]
outputs = d_layer_3_all # B*1*T
# Mask
# [B, T]
key_masks = tf.sequence_mask(keys_length, tf.shape(keys)[1]) # [B, T]
# [B, 1, T]
key_masks = tf.expand_dims(key_masks, 1) # [B, 1, T]
# padding的mask后补一个很小的负数,这样softmax之后就会接近0.
paddings = tf.ones_like(outputs) * (-2 ** 32 + 1) # 在补足的地方附上一个很小的值,而不是0
# [B, 1, T] padding操作,将每个样本序列中空缺的商品都赋值为(-2 ** 32 + 1)
outputs = tf.where(key_masks, outputs, paddings) # [B, 1, T]
# Scale, 这里scale不是很明白
# att 一般都要进行缩放的,why?
outputs = outputs / (keys.get_shape().as_list()[-1] ** 0.5)
# Activation
# [B, 1, T] #这里的output是attention计算出来的权重,即论文公式(3)里的w,
outputs = tf.nn.softmax(outputs) # [B, 1, T]
# Weighted sum
# B * 1 * H 三维矩阵相乘,相乘发生在后两维,即 B * (( 1 * T ) * ( T * H ))
# outputs为最终用户的历史行为分布
# [B, 1, H]
outputs = tf.matmul(outputs, keys)
return outputs
对一批query进行attention
def attention_multi_items(queries, keys, keys_length):
'''
queries: [B, N, H] N is the number of ads
keys: [B, T, H]
keys_length: [B]
'''
queries_hidden_units = queries.get_shape().as_list()[-1]
queries_nums = queries.get_shape().as_list()[1]
queries = tf.tile(queries, [1, 1, tf.shape(keys)[1]])
queries = tf.reshape(queries, [-1, queries_nums, tf.shape(keys)[1], queries_hidden_units]) # shape : [B, N, T, H]
max_len = tf.shape(keys)[1]
keys = tf.tile(keys, [1, queries_nums, 1])
keys = tf.reshape(keys, [-1, queries_nums, max_len, queries_hidden_units]) # shape : [B, N, 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.compat.v1.AUTO_REUSE)
d_layer_2_all = tf.layers.dense(d_layer_1_all, 40, activation=tf.nn.sigmoid, name='f2_att',
reuse=tf.compat.v1.AUTO_REUSE)
d_layer_3_all = tf.layers.dense(d_layer_2_all, 1, activation=None, name='f3_att', reuse=tf.compat.v1.AUTO_REUSE)
d_layer_3_all = tf.reshape(d_layer_3_all, [-1, queries_nums, 1, max_len])
outputs = d_layer_3_all
# Mask
key_masks = tf.sequence_mask(keys_length, max_len) # [B, T]
key_masks = tf.tile(key_masks, [1, queries_nums])
key_masks = tf.reshape(key_masks, [-1, queries_nums, 1, max_len]) # shape : [B, N, 1, T]
paddings = tf.ones_like(outputs) * (-2 ** 32 + 1)
outputs = tf.where(key_masks, outputs, paddings) # [B, N, 1, T]
# Scale
outputs = outputs / (keys.get_shape().as_list()[-1] ** 0.5)
# Activation
outputs = tf.nn.softmax(outputs) # [B, N, 1, T]
outputs = tf.reshape(outputs, [-1, 1, max_len])
keys = tf.reshape(keys, [-1, max_len, queries_hidden_units])
# print outputs.get_shape().as_list()
# print keys.get_sahpe().as_list()
# Weighted sum
outputs = tf.matmul(outputs, keys)
outputs = tf.reshape(outputs, [-1, queries_nums, queries_hidden_units]) # [B, N, 1, H]
print(outputs.get_shape().as_list())
return outputs
model tf1.15恢复
inference.py
./save_path/
-| checkpoint
-| ckpt.data-00000-of-00001
-| ckpt.index
-| ckpt.meta
model = Model(user_count, item_count, cate_count, cate_list, predict_batch_size, predict_ads_num) # model定义要有
model.restore(sess, 'save_path/ckpt') # 恢复model参数
"""
saver = tf.train.import_meta_graph('save_path/ckpt.meta')
model = saver.restore(sess, tf.train.latest_checkpoint("save_path/"))
"""