背景介绍
先介绍一下POLY2模型,POLY2是为了解决特征交叉问题而设计的模型,初期,算法工程师需要手动组合特征再通过各种手段分析特征。为了解决这种效率低下的问题,设计了特征的暴力组合。
POLY2的数学形式如下
ϕ P O L Y 2 ( w , x ) = b + ∑ i = 1 n w i x i + ∑ i = 1 n ∑ j = i + 1 n w i , j x i x j \phi POLY2(w,x)=b+\sum\limits_{i=1}^nw_ix_i+\sum\limits_{i=1}^n\sum\limits_{j=i+1}^nw_{i,j}x_ix_j ϕPOLY2(w,x)=b+i=1∑nwixi+i=1∑nj=i+1∑nwi,jxixj
前面 b + ∑ i = 1 n w i x i b+\sum\limits_{i=1}^nw_ix_i b+i=1∑nwixi是线性回归,后面部分是二阶特征交叉,暴力组合二阶特征,就是二阶特征相乘然后乘上一个系数。空间复杂度 O ( n 2 ) O(n^2) O(n2),一共 n ( n − 1 ) / 2 n(n-1)/2 n(n−1)/2个。
当特征维度较高时,权重参数量很大导致训练压力巨大。而且只有当 x i , x j x_i,x_j xi,xj都不为0时,才能反向传播训练 w i , j w_{i,j} wi,j。
FM针对稀疏one-hot编码,引入了稠密隐向量,减少了训练参数。
FM算法思想
对于样本 X X X每一个特征 ( x 1 , x 2 , x 3 . . . x n ) (x_1,x_2,x_3...x_n) (x1,x2,x3...xn),都有对应的隐向量 ( v 1 , v 2 , v 3 . . . v n ) (v_1,v_2,v_3...v_n) (v1,v2,v3...vn),其中隐向量维度为 k k k,其中 k ≪ n k\ll n k≪n,即隐向量矩阵大小 ( n , k ) (n,k) (n,k),参数总量为 n ∗ k n*k n∗k,使用两个隐向量点积 v i ⋅ v 2 v_i\cdot v_2 vi⋅v2得到的权值来代替POLY2算法中的枚举权重 w i , j w_{i,j} wi,j
ϕ F M ( w , x ) = b + ∑ i = 1 n w i x i + ∑ i = 1 n ∑ j = i + 1 n ⟨ v i , v j ⟩ x i x j \phi FM(w,x)=b+\sum\limits_{i=1}^nw_ix_i+\sum\limits_{i=1}^n\sum\limits_{j=i+1}^n\langle v_i,v_j\rangle x_ix_j ϕFM(w,x)=b+i=1∑nwixi+i=1∑nj=i+1∑n⟨vi,vj⟩xixj
FM公式化简
将计算复杂度从
O
(
n
2
k
)
O(n^2k)
O(n2k)降为
O
(
n
k
)
O(nk)
O(nk)
pytorch代码实现
class FactorizationMachine(nn.Module):
def __init__(self, n, k):
"""
:param n: feature_dim
:param k: embedding_dim
"""
super().__init__()
self.n = n
self.k = k
self.b = nn.Parameter(torch.zeros(1), requires_grad=True)
self.W = nn.Parameter(torch.randn(n, k))
self.linear = nn.Linear(n, 1)
nn.init.xavier_uniform(self.W)
def forward(self, x):
"""
:param x: shape [batch,feature_num]
:return:
"""
x1 = self.linear(x)
left = torch.mm(x, self.W) * torch.mm(x, self.W) #*是元素乘
right = torch.mm(x * x, self.W * self.W)
x2 = 0.5 * torch.sum(left - right, dim=-1,keepdim=True)
return self.b + x2 + x1
参考书目:
[1].深度学习推荐系统 王喆
[2].https://blog.csdn.net/qq_38237214/article/details/121338159