美文网首页推荐算法
行为序列建模:MIMN系列3——DIN Attention单元和

行为序列建模:MIMN系列3——DIN Attention单元和

作者: xiaogp | 来源:发表于2023-07-03 11:47 被阅读0次

    关键词:MIMNDINDiceBatch normalization

    内容摘要

    该系列的前两期已经介绍了MIMN原理源码,以及消费Kafka部署的实战,链接如下:
    行为序列建模:MIMN系列1——原理初探和源码解析
    行为序列建模:MIMN系列2——消费Kafka实时预测代码实战
    本期回过头来看MIMN的一些细节,前文也提到,MIMN其实是几个技术的拼接,并没有发明新的技术,通过这几个技术的组合完成了一个CTR任务,文本主要简单介绍下其中的DIN AttentionDice激活函数


    研究内容概述

    本文涉及DIN Attention和Dice,出自《Deep Interest Network for Click-Through Rate Prediction》阿里这篇论文提及的深度兴趣网络DIN算法,MIMN借鉴了DIN里面的注意力机制和自适应分布激活函数

    • DIN Attention:基于目标物品计算历史行为序列的物品embedding的加权求和,对历史行为中相关的物品加权,而不是采用平均池化
    • Dice:优化传统的PRelu激活函数,使得训练过程能自适应不同分布的数据,类似Batch Normalization使得模型更加容易收敛

    DIN Attention

    在DIN网络结构中存在Activation Unit,如下图所示


    DIN模型结构

    历史序列中的每一个good 1-N都会进入Activation Unit计算得到每一个good的权重,该权重再和good的原始embedding进行加权进入SUM Pooling进行求和。
    这个Activation Unit是所有goods共享参数的,Activation Unit结构如下


    Activation Unit结构

    左边Input from User是历史序列每个good,右边Input from Ad是目标good/Ad,两个进行Out Product操作,从源码来看Out Product是element-wise操作,及两个向量对应位置进行相乘和相减操作,然后和序列good,目标good的embedding一起拼接,输入全连接层,最终输入给一个神经元Linear(1)得到一个权重值的输出。
    看下源码的实现会更加清晰

    # query是目标物品 [batch_size, emb_size]
    # facts是历史序列物品 [batch_size, seq_size, emb_size]
    queries = tf.tile(query, [1, tf.shape(facts)[1]])
    queries = tf.reshape(queries, tf.shape(facts))
    din_all = tf.concat([queries, facts, queries - facts, queries * facts], axis=-1)
    

    先通过复制tf.tile和tf.reshape将目标物品全部平铺开来和历史序列物品一一对应,然后进行对应位置相减queries - facts和相乘queries * facts操作拼接原始输入得到din的原始特征表达,最终din_all维度是[batch_size, seq_size, emb_size * 4]。
    然后进入全连接层得到最终每个good的权重值

    d_layer_1_all = tf.layers.dense(din_all, 80, activation=tf.nn.sigmoid, name='f1_att' + stag)
    d_layer_2_all = tf.layers.dense(d_layer_1_all, 40, activation=tf.nn.sigmoid, name='f2_att' + stag)
    d_layer_3_all = tf.layers.dense(d_layer_2_all, 1, activation=None, name='f3_att' + stag)
    d_layer_3_all = tf.reshape(d_layer_3_all, [-1, 1, tf.shape(facts)[1]])
    scores = d_layer_3_all
    

    下面对权重进行softmax,权重和为1

        if softmax_stag:
            scores = tf.nn.softmax(scores)  # [B, 1, T]
    

    然后进行加权组合

    # Weighted sum
        if mode == 'SUM':
            # TODO 加权求和
            # [256, 1, 4] * [256, 4, 32] => [256, 1, 32]
            output = tf.matmul(scores, facts)  # [B, 1, H]
            # output = tf.reshape(output, [-1, tf.shape(facts)[-1]])
        else:
            # [256, 1, 4] => [256, 4]
            scores = tf.reshape(scores, [-1, tf.shape(facts)[1]])
            # [256, 4, 32] * [256, 4, 1] => [256, 4, 32]
            output = facts * tf.expand_dims(scores, -1)
            # [256, 4, 32] => [256, 4, 32]
            output = tf.reshape(output, tf.shape(facts))
    

    如果不是Sum Pool则仅返回加权embedding结果,不相加聚合。
    最后回到应用层,din attention得到的加权求和的历史所有物品embedding结果合并成1个embedding拼接到总输入(用户特征,候选物品特征,历史行为物品特征,上下文特征)

    multi_channel_hist = din_attention(self.target_rule_batch, channel_memory_tensor, self.rnn_hidden_size, None, stag='pal')
    inp = tf.concat([read_out, tf.squeeze(multi_channel_hist)], 1)
    

    本质上就是候选物品和目标物品的向量,两两之间基于element-wise操作和全连接自己迭代学习一个权重。


    Dice激活函数

    Dice是在PRelu的基础上改进而来,是PRelu带有数据标准化平滑版本。
    PRelu公式如下,想比于Relu在s<=0时带有一个可学习的参数控制斜率

    PRelu公式

    在上一层有多少个神经元,就有多少个可学习的参数,每个神经元都有一个可学习的参数不共享,PRelu的代码如下

    def prelu(_x, scope='', default_name='prelu'):
        """parametric ReLU activation"""
        with tf.variable_scope(name_or_scope=scope, default_name=default_name, reuse=tf.AUTO_REUSE):
            _alpha = tf.get_variable("prelu_" + scope + "_" + default_name, shape=_x.get_shape()[-1],
                                     dtype=_x.dtype, initializer=tf.constant_initializer(0.1))
            return tf.maximum(0.0, _x) + _alpha * tf.minimum(0.0, _x)
    

    Dice将PRelu的指示函数改为了带有标准化的sigmoid


    Dice公式

    首先sigmoid的加入使得函数平滑而不是一刀切在0的左右都是一个斜率,然后引入类似BN的操作,在每个训练的Batch内对数据做标准化操作,使得可以自适应地根据数据分布调整整流点的位置而不是固定为0,如图

    Dice自适应调整整流点

    个人感觉Dice本质和BN是一样的,有了BN可以不需要额外在激活函数里面做类似Dice这样的操作了,注意和BN一样,训练和预测是两套策略,训练使用训练的mini-batch计算得到,预测使用之前累计的所有样本进行估计,网上有Dice如下

    def Dice(_x, axis=-1, epsilon=0.000000001, name='dice', training=True):
        alphas = tf.get_variable('alpha_'+name, _x.get_shape()[-1],
                initializer=tf.constant_initializer(0.0),
                dtype=tf.float32)
        inputs_normed = tf.layers.batch_normalization(
                inputs=_x, 
                axis=axis, 
                epsilon=epsilon, 
                center=False, 
                scale=False, 
                training=training)
        x_p = tf.sigmoid(inputs_normed)
        return alphas * (1.0 - x_p) * _x + x_p * _x
    

    参考深度学习中Batch Normalization和Dice激活函数,本质上就是BN+Sigmoid

    相关文章

      网友评论

        本文标题:行为序列建模:MIMN系列3——DIN Attention单元和

        本文链接:https://www.haomeiwen.com/subject/gxpfudtx.html