1. 前言
上文介绍了TensorFlow官方版的Attention API,本文讲解两个点击预估模型中用到的Attention方法。
FiBiNet 模型中的 SENET Attention 方法;
DIN 模型中的 Activation Unit Attention 方法;
2. SENET Attention 方式
FiBiNET全称 FiBiNET: Combining Feature Importance and Bilinear feature Interaction for Click-Through Rate Prediction,是新浪微博提出的一种基于深度学习的广告推荐/点击率预测算法。可以认为FiBiNET是在Wide & Deep模型的基础上对它的Wide部分进行了一些创新的改进,或者直接视为FNN的一个变体。主要的创新点在于:
在传统的Embedding Stage加入了一个SENET层对Embedding特征升级为新的一种Embedding,得到与特征重要性(Feature Importance)相关的信息;
不单独使用传统的Inner Product或Hadamard Product方法,而是选择了结合二者的一种新的Bilinear Interaction方法来获得特征之间的联系;
模型的整体架构图如图1所示:
图1 FiBiNET模型结构图
从图1中可以看到,相比于我们熟悉的基于深度学习的CTR预估模型,主要增加了SENET Layer和创新升级了Bilinear-Interaction Layer两个结构。该章节只讲解SENET Layer部分。
2.1 SENET 理论介绍
SENET全称Squeeze-and-Excitation Network,在CV中用的比较多,可以对特征间的依赖关系进行一定的提取。SENET一共分为三个部分:Squeeze、Excitation 和 Re-Weight,按照顺序执行后从原始的Embedding特征向量 得到加权后的 。如图2所示。
图2 SENET模型结构图
(1)Squeeze
这一步主要是将每个特征组的Embedding向量进行汇总统计,文中使用均值池化(也可以使用最大池化,但文中表示平均池化效果要好于最大池化)对Embedding向量 进行压缩为 ,其中 表示第 个特征的全局信息, 是标量。 的具体计算方式如下:
(2)Excitation
这一步基于特征组的压缩统计量 来学习特征组的重要性权重,文章使用两层的神经网络来学习。第一层为一个维度缩减层,第二层为维度提升层。公式表示为:
其中:
是一个向量,形式上同 ;
为激活函数;
(3)Re-Weight
最后一步是把 和 按照类似于Hadamard product的方法对其中的 个元素进行element-wise的相乘得到SENET的最终产出 ,如果把 视为一个权重向量,那么这一步也可以被叫做加权或rescale。新的Embedding 向量通过如下的方式计算得到:
2.2 代码实践
定义senet函数,此函数未使用激活函数。
def senet(inputs):
Z = tf.reduce_mean(inputs, axis=-1, )
w1 = np.array([[1., 1.], [2., 2.], [1., 1.]])
dot1 = tf.tensordot(Z, w1, axes=(-1, 0))
w2 = np.array([[1., 1., 2], [2., 2., 6]])
dot2 = tf.tensordot(dot1, w2, axes=(-1, 0))
return dot2
测试:
inputs = np.array([[[1., 1., 1., 1.], [2., 2., 2., 2.], [1., 1., 1., 2.]], \
[[1., 1., 1., 1.], [2., 2., 2., 2.], [1., 2., 1., 2.]]])
print("inputs_shape: {} \n".format(inputs.shape))
senet_result = senet(inputs)
sess = tf.InteractiveSession()
senet_result_sess = sess.run(senet_result)
print("senet_result_sess: \n", senet_result_sess)
"""
结果:
inputs_shape: (2, 3, 4)
senet_result_sess:
[[18.75 18.75 50. ]
[19.5 19.5 52. ]]
"""
3. Activation Unit Attention
DIN,Deep Interest Network for Click-Through Rate Prediction,深度兴趣网络。论文的关键在于对用户历史行为的embedding向量的改进。模型结构如下:
图3 DIN模型结构图
改进的关键在于DIN提出了一个局部激活单元,用来产生用户历史行为特征的权重,从而根据候选商品进行自适应调整不同历史行为特征对最终结果的影响程度。传统方法中不管候选商品是什么,经过Sum Pooling后得到的用户历史行为特征都是一样的。但是从现实角度来看,对于不同的候选商品,用户的不同历史行为商品的影响程度是不一样的,而传统深度模型并没有体现出这一点。而DIN中的Local Activation Unit则会根据历史行为商品和候选商品算出一个权重作为该历史行为商品对点击率的影响程度,此时经过 Sum Pooling后得到的用户历史行为特征则会根据不同候选商品发生变化,模型的多样化表征能力也就更强了。
3.1 Activation Unit结构
(1)输入层:
user 产生行为(例如点击、或购买)的 item embedding,作为key;
候选 item embedding,作为query;
(2)Out Product层:
计算矩阵之间的 element-wise 乘法;
(3)Concat层:
将 query, key, query-key, query*key (Out Product层,element-wise 乘法) 的结果进行拼接;
(4)Dense层:
全连接层,并以PRelu或Dice作为激活函数;
(5)Linear层(输出层):
全连接层,输出单元为1,即得到(query, key)相应的权重值;
3.2 Activation Unit 理论
其中:
是用户 历史行为Item的embedding,长度为 ;
是候选Item的embedding;
是前向网络,矩阵以 进行计算,输出结果为权重值;
3.3 Activation Unit 代码讲解
(1)定义query, keys
query = tf.convert_to_tensor(np.asarray([[[1., 1., 1., 3.]]]), dtype=tf.double)
key_list = tf.convert_to_tensor(np.asarray([[[1., 1., 2., 4.],
[4., 1., 1., 3.],
[1., 1., 2., 1.]]]), dtype=tf.double)
queries = K.repeat_elements(query, key_list.get_shape()[1], 1)
print("queries: \n", queries)
print()
print("key_list: \n", key_list)
结果:
query为1组向量,模拟1个候选Item的Embedding;
keys为3组向量,模拟某用户历史购买的3个Item的Embedding;
(2)拼接 query, key, query-key, query*key
att_input = tf.concat(
[queries, key_list, queries-key_list, queries*key_list], axis=-1)
print("att_input: \n", att_input)
结果:
(3)定义第一层 Dense 的weights, bias,及运算
dense_16to4_weights = tf.convert_to_tensor([[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 2., 1],
[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 2., 1],
[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 2., 1]], dtype=tf.double)
dense_4_bias = tf.convert_to_tensor([1., 2., 3., 10.], dtype=tf.double)
dense_16to4 = tf.nn.bias_add(
tf.tensordot(att_input, dense_16to4_weights, axes=(-1, 0)),
dense_4_bias)
print("dense_16to4: \n\n", dense_16to4)
结果:
将原16维的向量压缩到4维;
(4)定义最后一层 Dense 的weights, bias,及运算
dense_4to1_weights = tf.convert_to_tensor([1., 2., 3., 10.], dtype=tf.double)
dense_1_bias = tf.constant([0.2], dtype=tf.double)
dense_4to1 = tf.tensordot(dense_16to4, dense_4to1_weights, axes=(-1, 0)) + dense_1_bias
print("dense_4to1: \n\n", dense_4to1)
结果:
[601.2, 576.2, 439.2] 分别为 “某用户历史购买的3个Item的Embedding” 的权重值;
还需要对最终的结果做 Softmax 归一化处理,原文中:That is, normalization with softmax on the output of a(·) is abandoned.
dense_4to1_softmax = tf.nn.softmax(dense_4to1)
attention_result = tf.matmul(dense_4to1_softmax, key_list)
print("dense_4to1_softmax: \n\n", dense_4to1_softmax)
print()
print("attention_result: \n\n", attention_result)
也就是说,某用户最后的兴趣Embeddign分布如 attention_result 所示。
欢迎入群交流学习。