本次分享一篇依旧是2017年由何向南教授团队发表的《Attentional Factorization Machines: Learning the Weight of Feature Interactions via Attention Networks》。本文的模型AFM主要聚焦于attention模型,文章末尾给出了该模型的复现代码。
本文约1.2k字,预计阅读10分钟。
AFM模型与NFM一样,也是何向南教授团队在2017年推出的模型,与NFM模型基本类似。但是NFM是通过一个sum pooling将所有的embedding向量进行聚合,它相当于每个向量具有相同的重要程度,如此会减弱其中具有重要信息的向量。因此AFM模型,通过增加一个Attention网络用来区分不同特征的交互的重要性,来提高整个模型的性能。Introduction不在赘述,直接对其模型进行剖析。
输入指的是稀疏特征,Embedding层与FM相同,都是将输入特征的稀疏表示映射到非零特征的密集型向量。
【注】:目前介绍过的模型大部分都是如此。
FM模型使用的是对每一对特征向量使用内积进行交互。受FM启发,作者提出了一个Pair-wise Interaction层,用来对每对Embedding向量进行各个元素对应相乘(element-wise product)交互【此处与NFM相同】。因此,若有 个特征向量的话,那就会产生 个交互向量。对其进行公式化,令非零特征向量 ,对应Embedding向量为 ,因此,该层的输出为:
其中 表示两个向量对应元素相乘。
作者提出,若是将所有得到的交互向量进行聚合(sum pooling)其输出再经过一个全连接层,得到最后的预测结果:
其中 ,若 ,那么结构就是一个FM模型(Embedding通过FM训练)。
【注】该方法很难实际应用。对于FM模型,我们知道它进行二阶特征交互只需要一个线性时间,而此处该层却是 , 为embedding向量的个数,这已经损失了原有FM模型的效率。而在实际复现中,也无法通过矩阵计算,只能使用for完成该层计算(不知道是否有其他更快的方法进行实现)。
本文最主要的创新点集中在该层。这个想法是不同的特征交互向量在将它们压缩为单个表示时做出不同的贡献。本文通过对交互向量执行加权求和,将注意力机制应用于特征交互:
其中 表示对 的注意力分数。但是,问题在于,对于从未在训练数据中共同出现的特征,无法估计其交互作用的注意力得分。为了解决泛化问题,模型使用多层感知器(MLP)进一步将注意力得分参数化,将其称为注意力网络。
注意力网络将其公式化为:
其中 是模型的参数, 表示了attention网络的隐藏层大小。attention分数 再通过softmax函数进行规范化得到 。
【注】此处本质上就是通过两层全连接层。且此处并没有查询向量,即在注意力网络中会给定一个任务相关的查询向量 ,例如对于之前提到DIN模型,目标物品与所有的历史记录的物品的相关度便是attention分数,而目标物品即是查询向量。
最终的预测结果为:
该模型实现较为简单;本文在原有的基础上增加了聚合的方式(sum pooling与average pooling)与Attention网络进行对比:
class AFM(keras.Model):
def __init__(self, feature_columns, mode, attention_hidden_unit=None):
super(AFM, self).__init__()
self.dense_feature_columns, self.sparse_feature_columns = feature_columns
self.mode = mode
self.embed_layers = {
'embed_' + str(i): Embedding(input_dim=feat['feat_num'], input_length=1,
output_dim=feat['embed_dim'], embeddings_initializer='random_uniform')
for i, feat in enumerate(self.sparse_feature_columns)
}
if self.mode == 'max':
self.max = GlobalMaxPooling1D()
elif self.mode == 'avg':
self.avg = GlobalAveragePooling1D()
else:
self.attention_dense = Dense(units=attention_hidden_unit, activation='relu')
self.attention_dense2 = Dense(units=1, activation=None)
self.dense = Dense(units=1, activation=None)
self.concat = Concatenate(axis=-1)
def call(self, inputs):
# Input Layer
dense_inputs, sparse_inputs = inputs
# Embedding Layer
embed = [self.embed_layers['embed_{}'.format(i)](sparse_inputs[:, i]) for i in range(sparse_inputs.shape[1])]
embed = tf.transpose(tf.convert_to_tensor(embed), [1, 0, 2])
# Pair-wise Interaction Layer
element_wise_product_list = []
for i in range(embed.shape[1]):
for j in range(i+1, embed.shape[1]):
element_wise_product_list.append(tf.multiply(embed[:, i], embed[:, j]))
element_wise_product = tf.transpose(tf.convert_to_tensor(element_wise_product_list), [1, 0, 2])
if self.mode == 'max':
# MaxPooling Layer
x = self.max(element_wise_product)
elif self.mode == 'avg':
# AvgPooling Layer
x = self.avg(element_wise_product)
else:
# Attention Layer
x = self.attention(element_wise_product)
# Concatenate Layer
x = self.concat([dense_inputs, x])
# Output Layer
outputs = tf.nn.sigmoid(self.dense(x))
return outputs
其中关于注意力网络:
def attention(self, keys):
a = self.attention_dense(keys)
a = self.attention_dense2(a)
a_score = tf.nn.softmax(a)
a_score = tf.transpose(a_score, [0, 2, 1])
outputs = tf.reshape(tf.matmul(a_score, keys), shape=(-1, keys.shape[2]))
return outputs
模型具体代码见:https://github.com/BlackSpaceGZY
AFM在NFM的基础上,对sum pooling进行改进,通过加入区分不同特征的重要程度的attention网络来提高模型的性能。不过最近通过与一位PDD大佬交流,目前即使做召回也基本不会用AFM、NFM,理由文章中也提到了,AFM太慢。大佬说,目前做召回用的比较多的便是PNN,原因是快。毕竟召回需要的是快速的缩小候选物品的范围,然后再通过复杂的精排模型进行筛选。
本人最近建立了一个开源项目:Recommender System with TF2.0
---使用TF2.0对经典的推荐论文进行复现。点击下方【阅读原文】链接或输入上述地址即可进入,欢迎大家star和fork。
往期精彩回顾
Github开源项目2.0---使用TF2.0对经典推荐论文进行复现【持续更新中...】
【论文导读】NFM---FM与DNN相结合,附TF2.0复现代码
【论文导读】融合FM的Wide&Deep---DeepFM模型
【论文导读】Deep Crossing模型---使用残差网络作为MLP的具体实现
【论文导读】深入理解PNN模型---加入Product层
【论文导读】Wide&Deep模型的进阶---Cross&Deep模型,附TF2.0复现代码
扫码关注更多精彩
点分享
点点赞
点在看