轻量级前端框架助力开发者提升项目效率与性能
1120
2022-10-12
DMIN 模型介绍与源码分析
DMIN (Deep Multi-Interest Network) 模型介绍与源码分析
文章目录
DMIN (Deep Multi-Interest Network) 模型介绍与源码分析
零.前言 (与正文无关, 请忽略)
广而告之
一. 文章信息二. 核心观点三. 核心观点解读四. 源码分析
Behavior Refiner LayerMulti-Interest Extractor Layer
五. 总结
零.前言 (与正文无关, 请忽略)
对自己之前分析过的文章做一个简单的总结:
机器学习基础:LR /LibFM特征交叉:DCN /PNN /DeepMCP /xDeepFM /FiBiNet /AFM用户行为建模:DSIN /DMR /DMIN /DIN多任务建模:MMOEGraph 建模:GraphSage /GAT
广而告之
一. 文章信息
论文标题: Deep Multi-Interest Network for Click-through Rate Prediction论文地址:2020论文作者: Xiao Zhibo, Luwei Yang作者单位: Alibaba
二. 核心观点
文章认为用户在同一时间会存在多种兴趣, 但潜在的主要兴趣最终会通过用户行为表现出来. 为了建模用户潜在的主要兴趣, 文章提出 DMIN 网络, 其中 Behavior Refiner Layer 采用 multi-head self-attention 获取更好的用户历史行为表达, 而 Multi-Interest Extractor Layer 也采用 Multi-Head Self-Attention 来建模用户多种潜在的主要兴趣, 计算权重系数时引入了 Position Encoding, 各个子空间(或者说各个 Head)对应的 embedding 即表示用户的 multi interest.
三. 核心观点解读
DMIN 网络结构设计如下图:
四. 源码分析
DMIN 模型定义在 文件中, 类名为 Model_DNN_Multi_Head.
Behavior Refiner Layer
采用 Multi-Head Self-Attention 完成对用户兴趣的初步提取:
maxlen = 20other_embedding_size = 2## 生成 Position Embeddingself.position_his = tf.range(maxlen)self.position_embeddings_var = tf.get_variable("position_embeddings_var", [maxlen, other_embedding_size])self.position_his_eb = tf.nn.embedding_lookup(self.position_embeddings_var, self.position_his) # T,Eself.position_his_eb = tf.tile(self.position_his_eb, [tf.shape(self.item_his_eb)[0], 1]) # B*T,Eself.position_his_eb = tf.reshape(self.position_his_eb, [tf.shape(self.item_his_eb)[0], -1, self.position_his_eb.get_shape().as_list()[1]]) # B,T,Ewith tf.name_scope("multi_head_attention"): multihead_attention_outputs = self_multi_head_attn(self.item_his_eb, num_units=EMBEDDING_DIM*2, num_heads=4,dropout_rate=0,is_training=True) print('multihead_attention_outputs.get_shape()',multihead_attention_outputs.get_shape()) multihead_attention_outputs1 = tf.compat.v1.layers.dense(multihead_attention_outputs,EMBEDDING_DIM*4,activation=tf.nn.relu) multihead_attention_outputs1 = tf.compat.v1.layers.dense(multihead_attention_outputs1,EMBEDDING_DIM*2) multihead_attention_outputs = multihead_attention_outputs1 +
self_multi_head_attn 函数定义如下:
def self_multi_head_attn(inputs, num_units, num_heads, dropout_rate, name="", is_training=True, is_layer_norm=True): """ Args: inputs(query): A 3d tensor with shape of [B, T, 2*E] inputs(keys): A 3d tensor with shape of [B, T, 2*E] num_units: 2*E num_heads: 4 """ ## 生成 Query, Key 以及 Value Q_K_V = tf.layers.dense(inputs, 3*num_units) ## [B, T, 6*E] Q, K, V = tf.split(Q_K_V, 3, -1) ## [Tensor(B, T, 2*E), Tensor(B, T, 2*E), Tensor(B, T, 2*E)] ## 生成 Multi-Head Q_ = tf.concat(tf.split(Q, num_heads, axis=2), axis=0) # (h*B, T, 2*E/h) K_ = tf.concat(tf.split(K, num_heads, axis=2), axis=0) # (h*B, T, 2*E/h) V_ = tf.concat(tf.split(V, num_heads, axis=2), axis=0) # (h*B, T, 2*E/h) ## 生成子空间中的 Attention 权重系数 outputs = tf.matmul(Q_, tf.transpose(K_, [0, 2, 1])) # [h*B, T, T] align= outputs / (36 ** 0.5) ## 代码中 E 默认是 18, 2*E 就是 36, Query/Key 的 emb 大小为 2*E diag_val = tf.ones_like(align[0, :, :]) ## [T, T] """ tril 为下三角矩阵: [ [1, 0, 0, ...], [1, 1, 0, ...], [1, 1, 1, ...] ... ] 使用下三角函数的目的是, 每一次只计算前 i 个行为的加权求和结果. 比如对第 T 个行为, 它只和第 T-1, T-2, ..., 0 个行为来计算 Attention 的结果. """ tril = tf.linalg.LinearOperatorLowerTriangular(diag_val).to_dense() # [T, T] key_masks = tf.tile(tf.expand_dims(tril, 0), [tf.shape(align)[0], 1, 1]) ## [h*B, T, T] padding = tf.ones_like(key_masks) * (-2 ** 32 + 1) ## [h*B, T, T] outputs = tf.where(tf.equal(key_masks, 0), padding, align) # [h*B, T, T] outputs = tf.nn.softmax(outputs) outputs = tf.layers.dropout(outputs, dropout_rate, training=is_training) ## 对用户行为进行加权求和, 做 Self-Attention outputs = tf.matmul(outputs, V_) ## [h*B, T, 2*E/h] ## 子空间的 emb 重新进行 concat outputs = tf.concat(tf.split(outputs, num_heads, axis=0), axis=2) ## [B, T, 2*E] # output linear outputs = tf.layers.dense(outputs, num_units) ## [B, T, 2*E] # drop_out before residual and layernorm outputs = tf.layers.dropout(outputs, dropout_rate, training=is_training) # Residual connection outputs += inputs ## [B, T, 2*E] # Normalize if is_layer_norm: outputs = layer_norm(outputs,name=name) ## [B, T, 2*E] return
最后得到 self_multi_head_attn 输出的大小为 [B, T, 2*E], 再将结果经过两个全连接层, 并做 Residual Connection, 得到大小为 [B, T, 2*E] 的 multihead_attention_outputs, 表示初步提取的用户兴趣.
同时还使用辅助 Loss 来提供更多的监督信息, 辅助 loss 代码如下:
aux_loss_1 = self.auxiliary_loss(multihead_attention_outputs[:, :-1, :], self.item_his_eb[:, 1:, :], self.noclk_item_his_eb[:, 1:, :], self.mask[:, 1:], stag="gru")self.aux_loss =
其中 auxiliary_loss 定义在: 代码如下:
def auxiliary_loss(self, h_states, click_seq, noclick_seq, mask, stag = None): """ 调用 auxiliary_loss 的过程中, 传入参数如下: + h_states: 传入 multihead_attention_outputs[:, :-1, :], 大小为 [B, T - 1, 2*E] + click_seq: 传入 item_his_eb[:, 1:, :], 大小为 [B, T - 1, 2*E], 为用户历史行为序列, 作为正样本 + noclick_seq: 传入 noclk_item_his_eb[:, 1:, :] auxiliary_net 为 3 层 DNN, 输出节点个数为 2, 输出结果会经过 softmax """ mask = tf.cast(mask, tf.float32) click_input_ = tf.concat([h_states, click_seq], -1) ## [B, T - 1, 4*E] noclick_input_ = tf.concat([h_states, noclick_seq], -1) click_prop_ = self.auxiliary_net(click_input_, stag = stag)[:, :, 0] noclick_prop_ = self.auxiliary_net(noclick_input_, stag = stag)[:, :, 0] ## 注意不要忘了乘上 mask click_loss_ = - tf.reshape(tf.log(click_prop_), [-1, tf.shape(click_seq)[1]]) * mask ## [B, T - 1] noclick_loss_ = - tf.reshape(tf.log(1.0 - noclick_prop_), [-1, tf.shape(noclick_seq)[1]]) * mask loss_ = tf.reduce_mean(click_loss_ + noclick_loss_) return
Multi-Interest Extractor Layer
inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, self.item_eb * self.item_his_eb_sum], 1)with tf.name_scope("multi_head_attention"): """ 对于 self_multi_head_attn_v2 函数, 其参数中: + multihead_attention_outputs: 来自上一层 Behavior Refiner Layer 的输出, 大小为 [B, T, 2*E] + num_units: 显式传入 36, 代码中 2*E 就等于 36 (E 默认为 18), 为了表示方便, 后面注释将 num_units 表示为 2*E 函数的输出结果 multihead_attention_outputss 是一个 list, 长度为 num_heads (即 4 个元素), 元素大小为 [B, T, 2*E] """ multihead_attention_outputss = self_multi_head_attn_v2(multihead_attention_outputs, num_units=36, num_heads=4,dropout_rate=0,is_training=True) ## 对 multihead_attention_outputss 中的每一个元素, 均经过两层 DNN, 元素大小仍为 [B, T, 2*E] ## 注意到此时对于每个行为, 它在 num_heads (4 个) 个 Head 中均有自己的特征表示 for i, multihead_attention_outputs_v2 in enumerate(multihead_attention_outputss): multihead_attention_outputs3 = tf.compat.v1.layers.dense(multihead_attention_outputs_v2, EMBEDDING_DIM*4,activation=tf.nn.relu) multihead_attention_outputs3 = tf.compat.v1.layers.dense(multihead_attention_outputs3, EMBEDDING_DIM*2) multihead_attention_outputs_v2 = multihead_attention_outputs3 + multihead_attention_outputs_v2 with tf.name_scope('Attention_layer'+str(i)): #这里使用position embedding来算attention, attention_output 的结果大小为 [B, 1, 2*E] attention_output, attention_score, attention_scores_no_softmax = din_attention_new(self.item_eb, multihead_attention_outputs_v2, self.position_his_eb, ATTENTION_SIZE, self.mask, stag=str(i)) att_fea = tf.reduce_sum(attention_output, 1) ## [B, 2*E] inp = tf.concat([inp, att_fea],1)
self_multi_head_attn_v2 函数不过多介绍了, 它里面大部分逻辑和 self_multi_head_attn 一样, 只是在最后输出时, 将不同 Head 的结果作为 list 给返回. 模型最后将用户的多兴趣与 inp 进行拼接, 再将 inp 输入到 MLP 中得到预估值.
五. 总结
可以和 DMR (Deep Match to Rank) 网络介绍与源码浅析 一起看, 或者同时看看 DIEN 的源码 (先立个 Flag, 之前立过, 如今再立一次), 加深印象.
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~