FM算法介绍

参考文献
Factorization Machines

FM,全名Factorization Machines,因式分解机,听着就逼格满满,不明觉厉,可用于推荐系统,也可广泛地使用于各种分类,回归,排序的场合,下面我力图用最简单的语言来描述。原作者在 提出FM的时候,立了两个靶子,一个是SVM,一个是MF,对这两个一顿嘲讽。

先来看SVM,全名叫Support Vector Machine,支持向量机,名字更具逼格,其算法过程也跟它名字一样闪耀着逼格,吓哭了不少萌新,都可以算是机器学习算法里理解难度最高的几个算法了,但那又如何呢,原作者认为,还没我刚提出来的FM好用呢,我比你简单,还比你好用!你除了吓唬人,还会些什么?SVM的算法原理大家可以去博客上搜索看看,其实就是一个predictor,平时做做分类,回归啥的,算是一个可以适用于多场合的通用predictor,说是可以适用多场合,但有个场合,就是数据集是那种高度稀疏的场合(就是有一堆0,茫茫的0中偶尔有几个1或者什么的数字惊鸿一瞥),这种场合在推荐领域极为常见,而不巧,FM在这种场合下也运作的很好 。

再来看MF,乍一看跟FM极为容易看混,这也是很多人脑子开始发昏的根源,但其实人家全名叫Matrix factorization,就是矩阵分解,21世纪的社会主义接班人,谁还没学过线性代数呢,所以该算法的好处在于有线性代数整个学科的支持,有一堆现成的数学工具可用,能把问题用线性代数的语言定义好我就能把你解出来,一些变量间的纠结关系把你分解的明明白白,比如有一个评分矩阵,每个矩阵元素都是一个用户对一个商品的评分,那我矩阵分解就把这个评分矩阵分解成两个矩阵,一个用户矩阵,一个商品矩阵,互相界线分明,用这两个新的矩阵重新作运算,就可以算出原先本来不存在的某用户对某商品的评分,这就可以用来作预测了,因为这里我们重点说FM,所以这里如果没看明白可以去博客上搜搜那些些MF的文章,反正它也可以拿来作预测就对了。但上面也说了,只要你把问题用线性代数的语言定义好我就能解决,但有些问题实在不容易用线性代数的语言定义啊,所以注定了这类算法适用面较窄,MF,还有同家族的SVD++,PITF,FPMC(不用纠结是什么,其实我也不大懂,就解决某个具体问题的算法而已)都只能针对一类问题解决,而FM呢,作者放言,把所有问题打包了,我一个打十个,都能打!到最后我甚至放言,你们这些家族的算法通通只是我FM的特例,我稍微变换下形式就成了你!而你却不能变成我!

下面我们正式开始介绍FM,俗话说,没有场景的学习都是耍流氓,我们引入一个场景,也是所有介绍FM的文章都会介绍的一个场景,为什么大家都会介绍这个场景呢,因为这是原作者提出来的。
在这里插入图片描述
来看上面那张图,每一行都是一个记录,左边的x是特征,右边的y是评分,整个场景就是对应某用户对某部电影的评分,特征有很多种,对应蓝框代表就是对应哪个具体的用户,这里用的独热编码,只有对应的位置会标1,其余全标0,每个位置代表一个具体的类别,这里每个位置就是对应哪一个具体的用户。再往右边点的橙框,也是独热编码,每个位置对应一部具体的电影,TI是泰坦尼克,NH是诺丁山,SW是星球大战,ST是星际迷航。再往右边的黄框是对应某用户评价过的所有电影,每个具体的位置代表一部电影,评价过的电影都在对应位置标记一个数字,这些数字的总和为1。再往右边的绿框是个时间戳。再往右边的紫框对应用户上一部评价的电影,在相应的位置标1,如果这是第一次评价电影,没有上一部,这里就全标0。你看,这里就是上面说过的数据集里一大堆0的场景,SVM惭愧得低下了头。
既然x有了,y有了,那我反手甩出一个
在这里插入图片描述
回归拟合,算出各个wi,不就得到一个线性预测器,预测的时候往里一套不就可以了!可以收工回家了~~well,哪有那么简单,这样当然可以,但是效果肯定很差。
我们来看FM怎么想,FM正手甩出一个
在这里插入图片描述
仔细看与上面那个式子的区别,就后面多了一项,考虑了特征xi与特征xj间的交叉项,这就不是线性拟合了,模型拟合能力就变强了,然后前面的参数是一个很奇怪的<vi,vj>,那这是什么?为什么不是直接用wij然后用数据拟合出来?原因是这样的场景通常对应一个大型稀疏矩阵 ,xi,xj间的交叉项非常多,你想,一个用户,一部电影都占一项,这就导致了需要拟合的wij也是非常多,而其实对应的记录数据并不多(至少比起要拟合的参数),所以实在没有能力拟合那么多的参数, 那么<vi,vj>是什么,为什么可以解决记录数据不多无法拟合大量参数的问题?原因在于共享参数!再往下看
在这里插入图片描述
可以看到<vi,vj>就是两个向量vi,vj的点积,那这两个是什么向量?其脚标是i,j,不是跟xi,xj的脚标一样么?没错,他们是一一对应的,vi就是所谓特征xi的隐向量,vj是特征xj的隐向量,每个特征都对应一个隐向量,把每个特征对应的隐向量算出来后,以后再碰到到应特征的交叉项,交叉项前面的参数就可以直接使用对应特征的隐向量间的点积<vi,vj>。设有特征xa,xb,xc,xd,可有交叉项xaxb,xaxc,xaxd,xbxc,xbxd,xcxd,共有六个参数要拟合,而若有对应的隐向量va,vb,vc,vd,则只需要算出这4个隐向量,那六个参数就不必再拟合了,直接套式子算,这就是所谓的参数共享,特征越多,用该方法省下要算的参数就越多,在那种数据集大型稀疏的场合(特征都巨多,大型稀疏通常皆由独热编码导致,独热编码的每一位都算一个特征),由于这精打细算,FM便能运作良好。至于隐向量的维度,那是个超参数,通常不会取很大,限制模型的拟合能力,才能有更好的泛化能力。那么这隐向量怎么算?想必学过机器学习的人都懂得一个叫SGD的大杀器,要算隐向量,这里很容易可以构筑出一个优化问题,设想这是一个监督问题,目标就是那个评分y,直接梯度下降就能求出隐向量了
在这里插入图片描述
以上那个式子就是对应的梯度,数学功底好的人可以自己推导玩玩,不好就算了,有了梯度,然后愉快地动用梯度下降就能算出隐向量,甚至还顺便把前面两项的w0,wi都算了,这样就齐活了,参数都有了,直接上手预测就行了。到这里,FM的核心就讲完了。

再强调几点:
其一 算法的复杂度
看那式子交叉项那么多,用这来预测,会不会很慢,原作者大手一挥,不会,正手又甩出一个
在这里插入图片描述
看着很复杂,其实不用管,反正就是经过一阵巧妙的运算大大降低了算法的运算复杂度,大家都啧啧称赞。

其二 d-way Factorization Machine
在这里插入图片描述
前面那个不是两个特征项交叉么,那我推广下,多项特征项交叉,前面那个可以追溯命名为2-way Factorization Machine,那模型更复杂了,拟合能力更强了,但我们也知道,模型也不是拟合能力越强越好的,要是过拟合就不好了

其三 与SVM模型的比较
SVM出来挨打!前面我们说了,SVM不适合在数据集大型稀疏的场合下应用,原因是什么?其实本质上就是SVM并没有像FM那样通过参数共享的方法解决要用少量记录数据拟合大量参数的问题,只能让贤于FM,原作者在论文中对这方面用数学语言进行了更晦涩的描述,但意思就是这么个意思。

其四 与MF的比较
MF以及你的兄弟们都出来挨打!对于MF家族的嘲讽,作者采取的策略是你们会的我都会,我会的你们就未必了!我只要巧妙的略施小计变换下形式就能成为你。
对于MF,最开始说了,他做的就是把一个矩阵分解成两个矩阵,比如把一个用户对商品的评分矩阵分解成用户矩阵和商品矩阵,在FM看来,就跟个弱智差不多,FM说,那我把用户矩阵视为用户特征对应的隐向量的组合,商品矩阵视为商品特征对应的隐向量的组合,问题不就转化为求解商品特征和用户特征的隐向量了么,对比我们前面提到的那个场景来说,你就只用了两个类型的特征啊,就是用那个蓝框和橙框就完了,够弱智!
对于MF家族的其他变体算法来说,FM所做的就是多考虑了些特征,把问题转化为多求解了这些多考虑的特征的隐向量的问题,通过这样视角的转化,FM几乎把MF家族一网打尽,以一当百!

至此,FM算法介绍完毕,后面还有FFM与DeepFm,见如下链接

FFM算法
DeepFM

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
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、付费专栏及课程。

余额充值