NFM(Neural Factorization Machines)
推荐系统小白–无所为而为
将FM模型纵向的融入DNN模型中的策略。
其主要的创新点在于引入了一个特征交叉池化层的结构
Bi-Interaction Pooling Layer:本模型最重要的一个结构。
在Embedding层和神经网络之间加入了特征交叉池化层是本网络的核心创新了,正是因为这个结构,实现了FM与DNN的无缝连接, 组成了一个大的网络,且能够正常的反向传播。假设
V
x
\mathcal{V}_{x}
Vx是所有特征embedding的集合, 那么在特征交叉池化层的操作:
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
⊙
\odot
⊙表示两个向量的元素积操作,即两个向量对应维度相乘得到的元素积向量(可不是点乘呀),其中第
k
k
k维的操作:
(
v
i
⊙
v
j
)
k
=
v
i
k
v
j
k
\left(v_{i} \odot v_{j}\right)_{k}=\boldsymbol{v}_{i k} \boldsymbol{v}_{j k}
(vi⊙vj)k=vikvjk
特征交叉池化层的具体位置如下图所示:
这里在学习过程中,由于对FM理解不深刻,在该模型中没有弄清楚FM模型在什么地方了。
Bi-Interaction Pooling Layer
在embedding层后加入这个特征交叉池化层是核心创新点
假设
V
x
\mathcal{V}_{x}
Vx是所有特征embedding的集合, 那么在特征交叉池化层的操作:
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
⊙
\odot
⊙表示两个向量的元素积操作,即两个向量对应维度相乘得到的元素积向量(可不是点乘呀),其中第
k
k
k维的操作:
(
v
i
⊙
v
j
)
k
=
v
i
k
v
j
k
\left(v_{i} \odot v_{j}\right)_{k}=\boldsymbol{v}_{i k} \boldsymbol{v}_{j k}
(vi⊙vj)k=vikvjk
上面是两个向量的元素积操作,向量交叉后不求和,最终得到的是k维向量。在这个步骤完成之后,组合了一下二阶交叉的信息,即对交叉特征向量求和,得到该层的输出向量,最后是一个k维的向量。
将这个向量进一步导入DNN之后,原本二阶交叉的向量变成了多阶非线性的特征交叉,这意味着只要FM把二阶的学号,DNN就可以更容易的学习高阶非线性特征。
具体的BiInteractionPooling层搭建的代码如下:
class BiInteractionPooling(Layer):
def __init__(self):
super(BiInteractionPooling, self).__init__()
def call(self, inputs):
# 优化后的公式为: 0.5 * (和的平方-平方的和) =>> B x 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
return cross_term
def compute_output_shape(self, input_shape):
return (None, input_shape[2])
# computer_output_shape计算输出向量的维度
思考题:NFM中的特征交叉与FM中的特征交叉有何异同,分别从原理和代码上进行对比分析?
原理上:
我觉得原理上最大的区别就在于这个build_embedding_layers上,这层选择的特征,选取了dnn部分的特征和linear部分的特征输入进BiInteraction Pooling Layer层中。
代码上我还没有实现过FM,需要等实现之后再进行补充。