DMIN 模型介绍与源码分析

网友投稿 1120 2022-10-12

DMIN 模型介绍与源码分析

DMIN 模型介绍与源码分析

DMIN (Deep Multi-Interest Network) 模型介绍与源码分析

文章目录

​​DMIN (Deep Multi-Interest Network) 模型介绍与源码分析​​

​​零.前言 (与正文无关, 请忽略)​​

​​广而告之​​

​​一. 文章信息​​​​二. 核心观点​​​​三. 核心观点解读​​​​四. 源码分析​​

​​Behavior Refiner Layer​​​​Multi-Interest Extractor Layer​​

​​五. 总结​​

零.前言 (与正文无关, 请忽略)

对自己之前分析过的文章做一个简单的总结:

机器学习基础:​​LR​​​ /​​LibFM​​特征交叉:​​DCN​​​ /​​PNN​​​ /​​DeepMCP​​​ /​​xDeepFM​​​ /​​FiBiNet​​​ /​​AFM​​用户行为建模:​​DSIN​​​ /​​DMR​​​ /​​DMIN​​​ /​​DIN​​多任务建模:​​MMOE​​Graph 建模:​​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小时内删除侵权内容。

上一篇:开发小程序行业「小程序开发市场」
下一篇:springBoot配置国产达梦数据库的示例详解
相关文章

 发表评论

暂时没有评论,来抢沙发吧~