背景
FNN模型是2016年提出的一个FM和DNN的一个组合, 这个模型的底层是FM,上层是DNN,该模型提出的目的依然是特征之间的交叉和表达能力的增强, 作者在论文中说FM虽然考虑了二阶交叉,但是无法延伸到高阶交叉信息的学习,于是乎提出了底层FM二阶交叉学习之后,再接一个DNN的组合,当然,还提出了一些有效的预训练方式。 但是该模型会存在一些问题,于是在2017年DeepFM出现了,该模型依然是FM和DNN的组合,但不是串行,而是基于W&D的架构,换成了并行的方式,让FM充当于W部分(当然不只是单纯的替换),这样既考虑了低阶交叉,也考虑高阶交叉,且实现了端到端,避免了W&D中W的特征工程。但是该模型的FM依然是二阶交叉,无可避免的限制了表达能力。于是乎,2017年,NFM也被提出,它改进了FM的二阶隐向量内积部分,换成了一个表达能力更强的函数,且Deep部分也加入了特征交叉池化层。这就是这三个模型的一个发展逻辑。
FM和DNN在稀疏数据的优势非常明显且正好又互补, 且W&D开辟了组合模型的先河,关键是如何组合的问题? FNN给出了一种思路, DeepFM给出了一种思路, NFM这里同样是有着组合的味道,但是不是那么简单的拼接式组合了,而是设计了一种结构,把FM和DNN拼接了起来
NFM模型结构
公式
y ^ N F M ( x ) = w 0 + ∑ i = 1 n w i x i + f ( x ) \hat{y}_{N F M}(\mathbf{x})=w_{0}+\sum_{i=1}^{n} w_{i} x_{i}+f(\mathbf{x}) y^NFM(x)=w0+i=1∑nwixi+f(x)
FM的一个问题,就是只能到二阶交叉, 且是线性模型, 这是他本身的一个局限性, 而如果想突破这个局限性, 就需要从他的公式本身下点功夫, 于是乎,作者在这里改进的思路就是用一个表达能力更强的函数来替代原FM中二阶隐向量内积的部分。
Bi-Interaction Pooling layer
f
B
I
(
V
x
)
=
∑
i
=
1
n
∑
j
=
i
+
1
n
x
i
v
i
⊙
x
j
v
j
f_{B I}\left(\mathcal{V}_{x}\right)=\sum_{i=1}^{n} \sum_{j=i+1}^{n} x_{i} \mathbf{v}_{i} \odot x_{j} \mathbf{v}_{j}
fBI(Vx)=i=1∑nj=i+1∑nxivi⊙xjvj
⊙表示两个向量的元素积操作,即两个向量对应维度相乘得到的元素积向量。这一个交叉完了之后k个维度不求和,最后会得到一个向量,而FM那里内积的话最后得到一个数, 在进行两两Embedding元素积之后,对交叉特征向量取和, 得到该层的输出向量, 很显然, 输出是一个k维的向量。这里很大的一点改进就是加入特征池化层之后, 把二阶交互的信息合并, 且上面接了一个DNN网络, 这样就能够增强FM的表达能力了, 因为FM只能到二阶, 而这里的DNN可以进行多阶且非线性,只要FM把二阶的学习好了, DNN这块学习来会更加容易
结构
底层考虑了交叉,然后高层使用的DNN网络
思考题
NFM中的特征交叉与FM中的特征交叉有何异同,分别从原理和代码实现上进行对比分析
FM公式计算的为内积,且最终需要对K个维度进行求和
concated_embeds_value = inputs # B x n x k
square_of_sum = tf.square(tf.reduce_sum(concated_embeds_value, axis=1, keepdims=True)) # B x 1 x k
sum_of_square = tf.reduce_sum(concated_embeds_value * concated_embeds_value, axis=1, keepdims=True) # B x1 xk
cross_term = square_of_sum - sum_of_square # B x 1 x k
cross_term = 0.5 * tf.reduce_sum(cross_term, axis=2, keepdims=False) # B x 1
NFM计算的是元素积,不需要对K个维度进行求和
concated_embeds_value = inputs # B x n x k
square_of_sum = tf.square(tf.reduce_sum(concated_embeds_value, axis=1, keepdims=False)) # B x k
sum_of_square = tf.reduce_sum(concated_embeds_value * concated_embeds_value, axis=1, keepdims=False) # B x k
cross_term = 0.5 * (square_of_sum - sum_of_square) # B x k