推荐系统中的用户兴趣建模(一):经典之作DIN及query attention框架

1.用户兴趣建模

随着深度学习的普及,Embedding&MLP的范式成为了排序模型经典范式,成为各个业务模型Base model的首选。
在这里插入图片描述
在排序模型中通常的特征类型有user profile 、item feature、context feature,user behavior feature, 其中第一类特征是静态的,很粗粒度的刻画了不同用户,第二、三类特征对于不同用户是相同的,只有第四类特征即用户兴趣,是实时更新的且”千人千面“,所以通常说的个性化也是通过这个特征来进行表达的,即用户兴趣是用户个性化的刻画。
用户历史行为序列是重要的 user 侧特征,但由于其因人而异,而模型结构则较难处理变长序列,故常用做法是把用户行为序列对应的 embedding list 通过pooling 操作压缩成固定维向量,作为用户历史兴趣表征,机智一点的做法可以通过给序列中不同的行为进行加权后求pooling(例如根据time decay)。但不管如何,这种pooling求和的方式显得过于粗糙,会造成有效信息的损失,这也是后续一系列用户行为建模方法的改进动机:如何更有效的学习到用户兴趣的embedding表达。

2.(KDD’ 18)DIN:对用户序列做一个attention加权

论文地址

前面提到过pooling求和的一种改进方式是加权求和,比如根据行为发生距离现在的时间decay,越近越获得更大的权重,这是一种直觉上的想法,但是现实的序列往往是这样的(摘自王喆的知乎)
在这里插入图片描述
这大概率是一个女生的行为序列,现在要打分的target item是那件粉红色的大衣,女生喜欢买衣服包包,也喜欢化妆品,甚至还为自己男朋友挑选过球衣球鞋,那么你在买大衣的时候,真的要把给男朋友买球鞋的偏好考虑进来么?具体到本文的例子中,在预测大衣的CTR这件事情上,用户浏览过杯子,跟用户浏览过另一件大衣这两个行为的重要程度是一样的吗?显然,我们更应该关注的是和target item相关的行为,这就要求我们需要一个对应用户行为序列大小的权重序列,以表示原行为序列中对要打分的target item的“”关联程度“,如何获得这个权重序列?简单,用attention就行了。这就是DIN的核心思想,简单而又巧妙。
在这里插入图片描述
具体模型长这样,其实相比于经典的Embedding&MLP范式,唯一的区别在于对用户行为序列进行了一个重加权,加权是通过一个子attention模块(上图右边的小方框)来获取权重的,之后再做sum pooling,和其他特征concat到一起送到后面的FC中。
在这里插入图片描述
以上就是DIN这篇paper中最核心的部分,当然论文还介绍了一些其他的算法方面的内容,只不过和用户兴趣建模无关,这里不再赘述,感兴趣可以参考原论文:

  • 用GAUC这个离线metric替代AUC
  • 用Dice方法替代经典的PReLU激活函数
  • 介绍一种Adaptive的正则化方法

3.query attention框架以及一些小trick

其实看过DIN源码的应该都知道,DIN中的target attention其实是几种attention的结合(加法attention,乘attention),使得信息损失最小,以下是这部分源码的注释:

def attention(queries, keys, keys_length):
  '''
    queries:     shape: [B, H], 即i_emb
    keys:        shape: [B, T, H], 即h_emb
    keys_length: shape: [B], 即self.sl
    B:batch size; T: 用户序列的长度;H:embedding size
  '''
  queries_hidden_units = queries.get_shape().as_list()[-1]                     
  # shape: [H]
  queries = tf.tile(queries, [1, tf.shape(keys)[1]])                            
  # [B,H] -> T*[B,H]
  queries = tf.reshape(queries, [-1, tf.shape(keys)[1], queries_hidden_units])  
  # T*[B,H] ->[B, T, H]
  din_all = tf.concat([queries, keys, queries-keys, queries*keys], axis=-1)     
  # attention操作,输出维度为[B, T, 4*H]
  d_layer_1_all = tf.layers.dense(din_all, 80, activation=tf.nn.sigmoid, \
                              name='f1_att', reuse=tf.AUTO_REUSE) # [B, T, 80]
  d_layer_2_all = tf.layers.dense(d_layer_1_all, 40, activation=tf.nn.sigmoid, \
                              name='f2_att', reuse=tf.AUTO_REUSE) # [B, T, 40]
  d_layer_3_all = tf.layers.dense(d_layer_2_all, 1, activation=None, \
                              name='f3_att', reuse=tf.AUTO_REUSE) # [B, T, 1]
  d_layer_3_all = tf.reshape(d_layer_3_all, [-1, 1, tf.shape(keys)[1]]) #[B, 1, T]
  outputs = d_layer_3_all # attention的输出, [B, 1, T]

  # Mask
  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) 
  outputs = tf.where(key_masks, outputs, paddings)  


  # Scale
  outputs = outputs / (keys.get_shape().as_list()[-1] ** 0.5)

  # Activation
  outputs = tf.nn.softmax(outputs) 
  # Weighted sum
  outputs = tf.matmul(outputs, keys)  
  
  return outputs

作为一个通用的query attention框架,在实际用的过程中,其实也可以将query由target item换成其他需要关注的东西,比如前面说的decay,一般来说效果是要好于直接拿decay做加权的。

另外原论文中提到的dice激活函数替代prelu一般在比较深的层也能取得较大的提升,可以多多尝试。

参考:
1.https://zhuanlan.zhihu.com/p/151403447
2.https://zhuanlan.zhihu.com/p/51623339

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值