基于深度学习的推荐(四):阿里DIN,使用Attention获得用户的动态representation

释放双眼,带上耳机,听听看~!

文章目录

  • 公众号

    • 前言
    • 1.模型分析
  • 1.1 Baseline
    * 1.2 DIN

      1. 训练技巧
  • 2.1 Dice激活函数
    * 2.2 Mini-batch 正则化

    • 3.实战
  • 3.1 数据集
    * 3.2 代码分析

    • 参考

公众号

前言

Deep Interest Network(DIN) 是盖坤大神领导的阿里妈妈的精准定向检索及基础算法团队提出的,发表在18年的ACM SIGKDD会议上。这篇文章讨论的是电子商业中的CTR预估问题.重点是对用户的历史行为数据进行分析和挖掘.

论文指出,很多CTR预估的模型,比如Deep Cross Network,Wide&Deep Learning等,采用的都是一种相似的模型架构,首先使用Embedding&MLP 从高维稀疏输入中提取低维稠密vectors,然后使用Sum/Average Pooling等技巧获得定长vectors.然后使用MLP进行预测.但是这样做存在一些问题.

首先是将原始的高维稀疏向量压缩为低维稠密向量后,定长向量sum pooling会导致信息损失;另外,低维向量的表达能力有限,而用户的兴趣多种多样(不是几十种这种量级的多种多样),为了提高定长vector的表达能力需要进行维度扩展,然而这容易带来维度灾难等问题,网站物品越来越多的时候总不能一直提高定长vector的维度吧.

然后作者注意到,用户是否会点击推荐给他的物品,仅取决于历史行为的一部分,并称之为local activation.因此,计算推荐物品的点击率时,只有部分物品代表的兴趣分布真正在起作用.DIN正是通过使用attention机制,对不同推荐的物品,获得用户不同的特征表示,从而进行更加精确的CTR预估.

论文链接:https://arxiv.org/pdf/1706.06978.pdf

1.模型分析

1.1 Baseline

首先说一下,论文中使用Goods表示用户历史中的广告/商品,使用Ad代表候选广告/商品。

首先看下对照组设置的模型,其中user profile表示的意思如下:

显而易见,首先对各类数据进行稀疏编码,然后使用MLP获得稠密向量,把各类特征串联起来,后面再来个MLP。
这里User Behavior使用multi-hot编码,因为用户不太可能只对其中一个广告感兴趣。要注意的是User Profile和Goods id以及Context Features的稠密向量并不一定是同一纬度。用户的特征向量怎么来的呢?将某个用户历史中点击过的物品的稠密向量进行Sum Pooling,就是直接求和,通过它们来代表当前用户。

1.2 DIN

上图就是DIN模型,就是比Baseline多了一个Attention机制,下面我们重点讲讲这个attention的细节。

显而易见,所谓attention,就是给用户历史记录中不同广告赋予不同的权重,DIN中这个权重是一个和候选Ad相关的函数,也就是上图右上角的Activation Unit:将Inputs from user和Inputs from Candidate Ad这两部分特征做交互,每个Goods都需要和Candidate Ad通过这个Activation Unit来获得该Goods的权重。

然后和baseline一样,使用Sum Pooling获得定长vector作为用户的representation,其中权重函数a就是上图中的Activation Unit:

但是同样是定长的user vector,候选广告不同时,DIN中user vector是不同的,而baseline中是相同的,显然DIN获得的用户representation更具有灵活性,也更加“精确”。

DIN中的Attention部分, 简单地说,就是和Goods和Candidate Ad有关的权重函数,赋予不同Goods不同权重,从而获得更好的用户representation。

2. 训练技巧

2.1 Dice激活函数

PRelu是一种常用的激活函数:

但作者认为,不应该所有的突变点都选为0,而是应该依赖数据分布。因此,对PRelu进行改进,提出Dice激活函数,其中s是激活函数输入中的其中一维,即对每维进行去均值求方差操作:

[1]中认为,去均值化操作使得突变点为s的均值,实现了data dependent的想法;而使用sigmoid可以得到0-1间的概率值,从而权衡s和αs。

2.2 Mini-batch 正则化

我们知道,电商中的商品数据符合长尾分布,只有少量商品多次出现,大量商品只出现几次或不出现。因此User Behaviors很稀疏是肯定的,由于输入是高维系数特征,而模型又很复杂,参数太多,相当于复杂模型而数据不足,这种情况很容易过拟合。

而对大体量的拥有上亿参数和非常稀疏的训练网络来说,直接应用L1,L2这种传统正则化方法是不使用的。比如L2正则化,在一个mini-batch中,只有非零特征对应的参数才会被更新,然而所有的参数都需要计算L2正则。

因此作者提出进行Mini-batch Aware正则化,只计算每个mini-batch中非零特征的相关参数:
上述公式可以转化为:

此时出现次数越多的非零特征对应的乘法权重越大。而上述公式可近似为:
此时amj取值0或1,这种进一步的近似类似从sum pooling到max pooling的转化,论文并没有给出原因和证明,我想是为了简化训练过程吧。

3.实战

3.1 数据集

论文中使用了淘宝和亚马逊的数据集,数据集都太大了。可以下载一个亚马逊的小数据集进行实验:


1
2
3
4
5
6
7
8
9
1echo "begin download data"
2mkdir raw_data && cd raw_data
3wget -c http://snap.stanford.edu/data/amazon/productGraph/categoryFiles/reviews_Electronics_5.json.gz
4gzip -d reviews_Electronics_5.json.gz
5wget -c http://snap.stanford.edu/data/amazon/productGraph/categoryFiles/meta_Electronics.json.gz
6gzip -d meta_Electronics.json.gz
7echo "download data successful"
8
9

试验中使用了这个小数据集的迷你版本:reviews_Electronics_5.json和meta_Electronics.json,来自参考代码。其中reviews_Electronics_5.json:
meta_Electronics.json:
由于两个数据可能来自不同的部门,因此为了获得完整的reviewerID,asin,和categories等条目,需要取两者的公共部分。详见代码。

3.2 代码分析

代码使用的是jupyter notebook风格,各变量的形状我都标注在后面了。attention和Dice部分对照论文一看就懂。大概讲一下整个架构,也就是Model函数,分为四部分:首先是candidate ad的embedding:


1
2
3
4
5
6
7
8
1# get item embedding vectors for input
2i_c = tf.gather(cate_list, self.item)  # obtain category of every item
3item_emb = tf.concat(values=[
4    tf.nn.embedding_lookup(item_emb_w, self.item), # [B, H/2]
5    tf.nn.embedding_lookup(cate_emb_w, i_c)        # [B, H/2]
6], axis=1) # [B, H]
7
8

然后是user behaviors的embedding:


1
2
3
4
5
6
7
8
1# get hist embedding vectors for input
2h_c = tf.gather(cate_list, self.hist)
3hist_emb = tf.concat(values=[
4    tf.nn.embedding_lookup(item_emb_w, self.hist), # [B, T, H/2]
5    tf.nn.embedding_lookup(cate_emb_w, h_c)        # [B, T, H/2]
6], axis=2) # [B, T, H]
7
8

上述实现我觉得有些问题。self.hist中除用户历史商品外其他都是0,也就是说几乎所有的用户都拥有第0个商品的embedding,由于在之前的DataInput函数预处理时恰好将包含0商品的记录全删掉了,此时0商品的embedding相当于对所有用户都添加了一个噪声,再加上attention机制,影响应该不大,但仍然有些问题。

由behaviors中各goods的embedding获得user embedding:


1
2
3
4
5
6
7
8
1# get user embedding vectors based on user hist vectors and item(to predict) vectors
2att_hist_emb = attention(item_emb, hist_emb, self.sl)       # [B, 1, H]
3att_hist_emb = tf.layers.batch_normalization(inputs=att_hist_emb)  # [B, 1, H]
4att_hist_emb = tf.reshape(att_hist_emb, [-1, hidden_units]) # [B, H]
5att_hist_emb = tf.layers.dense(att_hist_emb, hidden_units)  # [B, H]
6user_emb = att_hist_emb
7
8

然后是后面的MLP预测部分:


1
2
3
4
5
6
7
8
9
10
11
12
13
1base_i = tf.concat([user_emb, item_emb], axis=-1) # [B, 2*H]
2base_i = tf.layers.batch_normalization(base_i, name='base_i', reuse=tf.AUTO_REUSE) # [B, 2*H]
3d_layer_1_i = tf.layers.dense(base_i, 80, activation=tf.nn.sigmoid, name='f1', reuse=tf.AUTO_REUSE) # [B, 80]
4d_layer_1_i = dice(d_layer_1_i, name='dice1')
5d_layer_2_i = tf.layers.dense(d_layer_1_i, 40, activation=tf.nn.sigmoid, name='f2', reuse=tf.AUTO_REUSE) # [B, 40]
6d_layer_2_i = dice(d_layer_2_i, name='dice2')
7d_layer_3_i = tf.layers.dense(d_layer_2_i, 1, activation=None, name='f3', reuse=tf.AUTO_REUSE) # [B, 1]
8# 特征平铺
9d_layer_3_i = tf.reshape(d_layer_3_i, [-1]) # [B,]
10i_b = tf.gather(item_emb_b, self.item) # obtain bias of every item, [B,]
11self.pre = i_b+d_layer_3_i # [B]
12
13

论文开源代码:https://github.com/zhougr1993/DeepInterestNetwork

参考代码:https://github.com/Crawler-y/Deep-Interest-Network

完整代码和数据:https://github.com/wyl6/Recommender-Systems-Samples/tree/master/RecSys%20And%20Deep%20Learning/Attention/DIN

给TA打赏
共{{data.count}}人
人已打赏
安全运维

MongoDB最简单的入门教程之二 使用nodejs访问MongoDB

2021-12-11 11:36:11

安全运维

Ubuntu上NFS的安装配置

2021-12-19 17:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索