FM算法

开篇寄语

继上一篇LR总结之后,对于机器学习的理解算是提升了一些,接下来就按照Ranking模型的发展路线,依次展开,这篇讨论下基础的embedding模型FM(Factorization Machine)

1、LR模型的缺陷

关于sigmoid函数,我们不再做讨论,我们只看函数中跟参数相关的线性部分,即 θ T X \theta^TX θTX,为方便对于线性模型,我们暂且用 ω \omega ω而不是 θ \theta θ来表示参数,那么LR模型的线性部分即为:
y = ω 0 + ∑ i = 1 n ω i x i y = \omega_0 + \sum_{i=1}^{n} \omega_i x_i y=ω0+i=1nωixi

可以看到,不同特征之间是没有交叉的,为此,我们需要增加交叉的部分,形式如下:
y = ω 0 + ∑ i = 1 n ω i x i + ∑ i = 1 n − 1 ∑ j = i + 1 n ω i j x i x j y = \omega_0 + \sum_{i=1}^{n} \omega_i x_i + \sum_{i=1}^{n-1}\sum_{j=i+1}^{n} \omega_{ij}x_ix_j y=ω0+i=1nωixi+i=1n1j=i+1nωijxixj

这个就是FM算法需要解决的问题,特征交叉问题

2、FM求解

1,化简

从上面的式子可以很容易看出,组合部分的特征相关参数共有 n ( n − 1 ) 2 n(n−1)2 n(n1)2个。但是如第二部分所分析,在数据很稀疏的情况下,满足 x i x_i xi, x j x_j xj都不为0的情况非常少,这样将导致 ω i j ω_{ij} ωij无法通过训练得出。

为了求出 ω i j ω_{ij} ωij,我们对每一个特征分量 x i x_i xi引入辅助向量 V i = ( v i 1 , v i 2 , ⋯   , v i k ) V_i=(v_{i1},v_{i2},\cdots,v_{ik}) Vi=(vi1,vi2,,vik)。然后,利用 v i v j T v_iv^T_j vivjT ω i j ω_{ij} ωij进行求解
在这里插入图片描述
那么 ω i j ω_{ij} ωij组成的矩阵可以表示为:
在这里插入图片描述
上面的表达形式,就对应了一种矩阵的分解。对 k k k值的限定,就反应了FM模型的表达能力

求解< v i v_i vi, v j v_j vj>的时间复杂度是 O ( n 2 ) O(n^2) O(n2),可以通过类似于 ( ( a + b + c ) 2 − a 2 − b 2 − c 2 ((a+b+c)^2-a^2-b^2-c^2 ((a+b+c)2a2b2c2,求出交叉项,这样的话,求解的时间复杂度是 O ( n ) O(n) O(n),化简如下:
y = ∑ i = 1 n − 1 ∑ j = j + 1 n &lt; v i , v j &gt; x i x j = 1 2 ∑ i = 1 n ∑ j = 1 n &lt; v i , v j &gt; x i x j − 1 2 ∑ i = 1 n &lt; v i , v i &gt; x i x i = 1 2 ( ∑ i = 1 n ∑ j = 1 n ∑ f = 1 k v i , f v j , f x i x j − ∑ i = 1 n ∑ f = 1 k v i , f v i , f x i x i ) = 1 2 ∑ f = 1 k ( ( ∑ i = 1 n v i , f x i ) ( ∑ j = 1 n v j , f x j ) − ∑ i = 1 n v i , f 2 x i 2 ) = 1 2 ∑ f = 1 k ( ( ∑ i = 1 n v i , f x i ) 2 − ∑ i = 1 n v i , f 2 x i 2 ) \begin{aligned} y &amp;= \sum_{i=1}^{n-1}\sum_{j=j+1}^{n}&lt;v_i,v_j&gt;x_ix_j \\ &amp;=\frac{1}{2}\sum_{i=1}^{n}\sum_{j=1}^{n}&lt;v_i,v_j&gt;x_ix_j - \frac{1}{2}\sum_{i=1}^{n}&lt;v_i,v_i&gt;x_ix_i \\ &amp;= \frac{1}{2}(\sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{f=1}^{k}v_{i,f}v_{j,f}x_ix_j - \sum_{i=1}^{n}\sum_{f=1}^{k}v_{i,f}v_{i,f}x_ix_i) \\ &amp;= \frac{1}{2}\sum_{f=1}^{k}((\sum_{i=1}^{n}v_{i,f}x_i)(\sum_{j=1}^{n}v_{j,f}x_j)-\sum_{i=1}^{n}v_{i,f}^2x_i^2) \\ &amp;= \frac{1}{2}\sum_{f=1}^{k}((\sum_{i=1}^{n}v_{i,f}x_i)^2-\sum_{i=1}^{n}v_{i,f}^2x_i^2) \end{aligned} y=i=1n1j=j+1n<vi,vj>xixj=21i=1nj=1n<vi,vj>xixj21i=1n<vi,vi>xixi=21(i=1nj=1nf=1kvi,fvj,fxixji=1nf=1kvi,fvi,fxixi)=21f=1k((i=1nvi,fxi)(j=1nvj,fxj)i=1nvi,f2xi2)=21f=1k((i=1nvi,fxi)2i=1nvi,f2xi2)

至此,化简完毕,这个化简的公式在预测过程中是非常有用的

2,求导

首先,FM可以用于二分类,也可以用于多分类,根据学习函数决定,假设是Sigmoid函数,就是二分类,其求导包含两部分,Sigmoid( f ( z ) f(z) f(z))偏导和FM( f ( x ) f(x) f(x))偏导,即
∂ f ( z ) ∂ f ( v ) = ∂ f ( z ) ∂ f ( x ) ∂ f ( x ) ∂ f ( v ) \frac{\partial f(z)}{\partial f(v)} = \frac{\partial f(z)}{\partial f(x)} \frac{\partial f(x)}{\partial f(v)} f(v)f(z)=f(x)f(z)f(v)f(x)

其中,前半部分是Sigmoid函数通用的,跟LR算法一致,即
∂ f ( z ) ∂ f ( x ) = f ( z ) − y \begin{aligned} \frac{\partial f(z)}{\partial f(x)} = f(z) - y \end{aligned} f(x)f(z)=f(z)y

其中y即label,我们只需要求后半部分即可
根据我们化简之后的结果,求偏导得:
∂ f ( x ) ∂ f ( v ) = ∂ ( 1 2 ∑ f = 1 k ( ( ∑ i = 1 n v i , f x i ) 2 − ∑ i = 1 n v i , f 2 x i 2 ) ) / ∂ v i , f = x i ∑ j = 1 n v j , f x j − v i , f x i 2 \begin{aligned} \frac{\partial f(x)}{\partial f(v)} &amp;=\partial (\frac{1}{2}\sum_{f=1}^{k}((\sum_{i=1}^{n}v_{i,f}x_i)^2-\sum_{i=1}^{n}v_{i,f}^2x_i^2) )/\partial v_{i,f} \\ &amp;= x_i\sum_{j=1}^{n}v_{j,f}x_j - v_{i,f}x_i^2 \end{aligned} f(v)f(x)=(21f=1k((i=1nvi,fxi)2i=1nvi,f2xi2))/vi,f=xij=1nvj,fxjvi,fxi2

其中,第一部分的 i i i换成了 j j j是因为这部分加和跟 i i i无关,所以换一个符号以区分
(PS:以这个思路求解LR的导数更加方便了)
至此,FM求导完成

引用
[1]:https://blog.csdn.net/bitcarmanlee/article/details/52143909

FM(因子分解机)是一种经典的推荐算法,它可以用于处理稀疏数据并且具有较好的预测性能。下面是使用PyTorch实现FM算法的基本步骤: 1. 导入需要的库: ```python import torch import torch.nn as nn import torch.optim as optim ``` 2. 定义FM模型 ```python class FM(nn.Module): def __init__(self, input_dim, k): super(FM, self).__init__() self.k = k self.linear = nn.Linear(input_dim, 1) self.v = nn.Parameter(torch.randn(input_dim, k)) def forward(self, x): linear_part = self.linear(x) inter_part1 = torch.matmul(x, self.v) inter_part2 = torch.matmul(torch.pow(x, 2), torch.pow(self.v, 2)) inter_part = 0.5 * torch.sum(torch.sub(inter_part1, inter_part2), 1, keepdim=True) output = linear_part + inter_part return output ``` 3. 定义训练函数 ```python def train(model, dataloader, optimizer, criterion): model.train() train_loss = 0 for batch_idx, (data, target) in enumerate(dataloader): optimizer.zero_grad() output = model(data) loss = criterion(output, target) train_loss += loss.item() loss.backward() optimizer.step() return train_loss / len(dataloader.dataset) ``` 4. 定义测试函数 ```python def test(model, dataloader, criterion): model.eval() test_loss = 0 with torch.no_grad(): for data, target in dataloader: output = model(data) test_loss += criterion(output, target).item() return test_loss / len(dataloader.dataset) ``` 5. 加载数据集和设置超参数 ```python from torch.utils.data import DataLoader, Dataset class CustomDataset(Dataset): def __init__(self, x, y): self.x = x self.y = y def __getitem__(self, index): return self.x[index], self.y[index] def __len__(self): return len(self.x) X_train, y_train = ... X_test, y_test = ... train_dataset = CustomDataset(X_train, y_train) test_dataset = CustomDataset(X_test, y_test) batch_size = 64 train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True) test_dataloader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False) input_dim = X_train.shape[1] k = 10 lr = 0.01 num_epochs = 50 ``` 6. 训练模型 ```python model = FM(input_dim, k) optimizer = optim.SGD(model.parameters(), lr=lr) criterion = nn.MSELoss() for epoch in range(num_epochs): train_loss = train(model, train_dataloader, optimizer, criterion) test_loss = test(model, test_dataloader, criterion) print(f'Epoch {epoch+1}, Train Loss: {train_loss:.4f}, Test Loss: {test_loss:.4f}') ``` 这样就可以使用PyTorch实现FM算法了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值