NFM 网络介绍与源码浅析

NFM 网络介绍与源码浅析

前言

OK, 周末继续肝!!! 昨晚完成了 FNN 网络介绍与源码浅析

广而告之

可以在微信中搜索 “珍妮的算法之路” 或者 “world4458” 关注我的微信公众号;另外可以看看知乎专栏 PoorMemory-机器学习, 以后文章也会发在知乎专栏中;

NFM (Neural Factorization Machines)

文章信息

核心观点

本文主要介绍了 Bi-Interaction Pooling Layer, 用于对二阶交叉特征进行建模. 和 FM 使用内积对二阶交叉特征建模的思路不同的是, Bi-Interaction Pooling Layer 是将两两特征进行 element-wise product, 生成了 n × ( n − 1 ) 2 \frac{n\times (n - 1)}{2} 2n×(n1) 个交叉特征, 然后再对这些交叉特征进行累加 (这应该就是名字中 Pooling 的含义), 当然在实际计算中, 是无需将 n × ( n − 1 ) 2 \frac{n\times (n - 1)}{2} 2n×(n1) 个交叉特征全部算出来的, 而是类似 FM 的思路, 对计算公式做了调整, 降低了计算复杂度. Bi-Interaction Pooling Layer 的输出结果之后会进一步输入到 MLP 中, 以学习更丰富高阶的特征.

另外, 为了利用到低阶特征, 作者设计的网络中也包含了线性部分 (网络结构图中没有画出来), 类似于 Wide & Deep 的结构.

还有, 我觉得网络的 Deep 部分最后的效果其实就是将 FM 的二阶交叉特征输入到 DNN 网络中~~

核心观点介绍

NFM 网络的 Deep 部分结构图如下所示 (注意这是 Deep 部分, 线性部分没有在图中体现):

其中 V x = { x 1 v 1 , … , x n v n } \mathcal{V}_{x} = \left\{x_{1} \mathbf{v}_{1}, \ldots, x_{n} \mathbf{v}_{n}\right\} Vx={x1v1,,xnvn} 表示输入特征 x \bm{x} x 对应的 embedding 向量. 之后特征的 embeddings 输入到 Bi-Interaction 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=1nj=i+1nxivixjvj

其中 ⊙ \odot 表示 element-wise product; 注意到 Bi-Interaction Layer 效果和 Pooling operation 是一样的, 将多个交叉特征累加, 转换为一个向量. 上式可以用线性时间计算出来, 只需要将公式改为:

f B I ( V x ) = 1 2 [ ( ∑ i = 1 n x i v i ) 2 − ∑ i = 1 n ( x i v i ) 2 ] f_{B I}\left(\mathcal{V}_{x}\right)=\frac{1}{2}\left[\left(\sum_{i=1}^{n} x_{i} \mathbf{v}_{i}\right)^{2}-\sum_{i=1}^{n}\left(x_{i} \mathbf{v}_{i}\right)^{2}\right] fBI(Vx)=21(i=1nxivi)2i=1n(xivi)2

其中 v 2 \mathbf{v}^2 v2 表示 v ⊙ v \mathbf{v}\odot\mathbf{v} vv. 是不是嗅到了熟悉的 FM 的味道 🤣 🤣 🤣

之后, Bi-Interaction Layer 的输出结果进一步输入到 MLP 中, 得到向量 z L \bm{z}_L zL, 为了在输出层得到预测 score, 还需要使用权重 h T \bm{h}^T hT z L \bm{z}_L zL 转化为数值: h T ⋅ z L \bm{h}^T\cdot\bm{z}_L hTzL.

另外, NFM 还用线性层对低阶特征进行了处理. 因此, NFM 的完整结构可以公式表示为:

y ^ N F M ( x ) = w 0 + ∑ i = 1 n w i x i + h T σ L ( W L ( … σ 1 ( W 1 f B I ( V x ) + b 1 ) … ) + b L ) \begin{aligned} \hat{y}_{N F M}(\mathbf{x}) &=w_{0}+\sum_{i=1}^{n} w_{i} x_{i} \\ &+\mathbf{h}^{T} \sigma_{L}\left(\mathbf{W}_{L}\left(\ldots \sigma_{1}\left(\mathbf{W}_{1} f_{B I}\left(\mathcal{V}_{x}\right)+\mathbf{b}_{1}\right) \ldots\right)+\mathbf{b}_{L}\right) \end{aligned} y^NFM(x)=w0+i=1nwixi+hTσL(WL(σ1(W1fBI(Vx)+b1))+bL)

整个模型的参数为: Θ = { w 0 , { w i , v i } , h , { W l , b l } } \Theta=\left\{w_{0},\left\{w_{i}, \mathbf{v}_{i}\right\}, \mathbf{h},\left\{\mathbf{W}_{l}, \mathbf{b}_{l}\right\}\right\} Θ={w0,{wi,vi},h,{Wl,bl}}, 和 FM 相比, NFM 多出的参数主要是 { W l , b l } \left\{\mathbf{W}_{l}, \mathbf{b}_{l}\right\} {Wl,bl}, 即 MLP 的参数, 主要用来对特征间更高阶的交叉进行学习.

下面看看代码:

来自 https://github.com/hexiangnan/neural_factorization_machine/blob/master/NeuralFM.py

其中 NFM 的核心实现如下:

# Model.
# _________ sum_square part _____________
# get the summed up embeddings of features.
nonzero_embeddings = tf.nn.embedding_lookup(self.weights['feature_embeddings'], self.train_features)
self.summed_features_emb = tf.reduce_sum(nonzero_embeddings, 1) # None * K
# get the element-multiplication
self.summed_features_emb_square = tf.square(self.summed_features_emb)  # None * K

# _________ square_sum part _____________
self.squared_features_emb = tf.square(nonzero_embeddings)
self.squared_sum_features_emb = tf.reduce_sum(self.squared_features_emb, 1)  # None * K

# ________ FM __________
self.FM = 0.5 * tf.sub(self.summed_features_emb_square, self.squared_sum_features_emb)  # None * K
if self.batch_norm:
    self.FM = self.batch_norm_layer(self.FM, train_phase=self.train_phase, scope_bn='bn_fm')
self.FM = tf.nn.dropout(self.FM, self.dropout_keep[-1]) # dropout at the bilinear interactin layer

# ________ Deep Layers __________
for i in range(0, len(self.layers)):
    self.FM = tf.add(tf.matmul(self.FM, self.weights['layer_%d' %i]), self.weights['bias_%d'%i]) # None * layer[i] * 1
    if self.batch_norm:
        self.FM = self.batch_norm_layer(self.FM, train_phase=self.train_phase, scope_bn='bn_%d' %i) # None * layer[i] * 1
    self.FM = self.activation_function(self.FM)
    self.FM = tf.nn.dropout(self.FM, self.dropout_keep[i]) # dropout at each Deep layer
self.FM = tf.matmul(self.FM, self.weights['prediction'])     # None * 1

# _________out _________
Bilinear = tf.reduce_sum(self.FM, 1, keep_dims=True)  # None * 1
self.Feature_bias = tf.reduce_sum(tf.nn.embedding_lookup(self.weights['feature_bias'], self.train_features) , 1)  # None * 1
Bias = self.weights['bias'] * tf.ones_like(self.train_labels)  # None * 1
self.out = tf.add_n([Bilinear, self.Feature_bias, Bias])  # None * 1

代码并不复杂, 其中:

nonzero_embeddings = tf.nn.embedding_lookup(self.weights['feature_embeddings'], self.train_features)
self.summed_features_emb = tf.reduce_sum(nonzero_embeddings, 1) # None * K
# get the element-multiplication
self.summed_features_emb_square = tf.square(self.summed_features_emb)  # None * K

# _________ square_sum part _____________
self.squared_features_emb = tf.square(nonzero_embeddings)
self.squared_sum_features_emb = tf.reduce_sum(self.squared_features_emb, 1)  # None * K

# ________ FM __________
self.FM = 0.5 * tf.sub(self.summed_features_emb_square, self.squared_sum_features_emb)  # None * K

和 FM 就是一模一样的~, 只不过 FM 最后还要对 embedding 这个维度 (就是注释中 K 这个维度) 进行累加.

总结

Good, 和 DeepFM 有区别也有联系, 后面再安排上 DeepFM, 现在去锻炼 🤣 🤣 🤣

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值