推荐系统知识梳理——FM
FM的paper地址如下:https://www.csie.ntu.edu.tw/~b97053
1. FM模型的引入
1.1 逻辑回归模型及其缺点
FM模型其实是一种思路,具体的应用稍少。一般来说做推荐CTR预估时最简单的思路就是将特征做线性组合(逻辑回归LR),传入sigmoid中得到一个概率值,本质上这就是一个线性模型,因为sigmoid是单调增函数不会改变里面的线性模型的CTR预测顺序,因此逻辑回归模型效果会比较差。也就是LR的缺点有:
- 是一个线性模型
- 每个特征对最终输出结果独立,需要手动特征交叉(xi∗xjx_i*x_jxi∗xj),比较麻烦
1.2 二阶交叉项的考虑及改进
由于LR模型的上述缺陷(主要是手动做特征交叉比较麻烦),干脆就考虑所有的二阶交叉项,也就是将目标函数由原来的
y=w0+∑i=1nwixiy = w_0+\sum_{i=1}^nw_ix_i y=w0+i=1∑nwixi 变为
y=w0+∑i=1nwixi+∑i=1n−1∑i+1nwijxixjy = w_0+\sum_{i=1}^nw_ix_i+\sum_{i=1}^{n-1}\sum_{i+1}^nw_{ij}x_ix_j y=w0+i=1∑nwixi+i=1∑n−1i+1∑nwijxixj 但这个式子有一个问题,只有当xix_ixi与xjx_jxj均不为0时这个二阶交叉项才会生效,后面这个特征交叉项本质是和多项式核SVM等价的,为了解决这个问题,我们的FM登场了!
FM模型使用了如下的优化函数:
y=w0+∑i=1nwixi+∑i=1n∑i+1n<vi,vj>xixjy = w_0+\sum_{i=1}^nw_ix_i+\sum_{i=1}^{n}\sum_{i+1}^n\lt v_i,v_j\gt x_ix_j y=w0+i=1∑nwixi+i=1∑ni+1∑n<vi,vj>xixj 事实上做的唯一改动就是把wijw_{ij}wij替换成了<vi,vj>\lt v_i,v_j\gt<vi,vj>,大家应该就看出来了,这实际上就有深度学习的意味在里面了,实质上就是给每个xix_ixi计算一个embedding,然后将两个向量之间的embedding做内积得到之前所谓的wijw_{ij}wij好处就是这个模型泛化能力强 ,即使两个特征之前从未在训练集中同时出现,我们也不至于像之前一样训练不出wijw_{ij}wij,事实上只需要xix_ixi和其他的xkx_kxk同时出现过就可以计算出xix_ixi的embedding!
2. FM公式的理解
从公式来看,模型前半部分就是普通的LR线性组合,后半部分的交叉项:特征组合。首先,单从模型表达能力上来看,FM是要强于LR的,至少它不会比LR弱,当交叉项参数wijw_{ij}wij全为0的时候,整个模型就退化为普通的LR模型。
对于有nnn个特征的模型,特征组合的参数数量共有1+2+3+⋯+n−1=n(n−1)21+2+3+\cdots + n-1=\frac{n(n-1)}{2}1+2+3+⋯+n−1=2n(n−1)个,并且任意两个参数之间是独立的。所以说特征数量比较多的时候,特征组合之后,维度自然而然就高了。
定理:任意一个实对称矩阵(正定矩阵)WWW都存在一个矩阵VVV,使得 W=V.VTW=V.V^{T}W=V.VT成立。
类似地,所有二次项参数ωij\omega_{ij}ωij可以组成一个对称阵WWW(为了方便说明FM的由来,对角元素可以设置为正实数),那么这个矩阵就可以分解为W=VTVW=V^TVW=VTV,VVV 的第jjj列(vjv_{j}vj)便是第jjj维特征(xjx_{j}xj)的隐向量。
y^(X)=ω0+∑i=1nωixi+∑i=1n−1∑j=i+1n<vi,vj>xixj\hat{y}(X) = \omega_{0}+\sum_{i=1}^{n}{\omega_{i}x_{i}}+\sum_{i=1}^{n-1}{\sum_{j=i+1}^{n} \color{red}{<v_{i},v_{j}>x_{i}x_{j}}} y^(X)=ω0+i=1∑nωixi+i=1∑n−1j=i+1∑n<vi,vj>xixj
需要估计的参数有ω0∈R\omega_{0}∈ Rω0∈R,ωi∈R\omega_{i}∈ Rωi∈R,V∈RV∈ RV∈R,<⋅,⋅>< \cdot, \cdot><⋅,⋅>是长度为kkk的两个向量的点乘,公式如下:
<vi,vj>=∑f=1kvi,f⋅vj,f<v_{i},v_{j}> = \sum_{f=1}^{k}{v_{i,f}\cdot v_{j,f}} <vi,vj>=f=1∑kvi,f⋅vj,f
上面的公式中:
- ω0\omega_{0}ω0为全局偏置;
- ωi\omega_{i}ωi是模型第iii个变量的权重;
- ωij=<vi,vj>\omega_{ij} = < v_{i}, v_{j}>ωij=<vi,vj>特征iii和jjj的交叉权重;
- viv_{i}vi是第iii维特征的隐向量;
- <⋅,⋅><\cdot, \cdot><⋅,⋅>代表向量点积;
- k(k<<n)k(k<<n)k(k<<n)为隐向量的长度,包含 kkk 个描述特征的因子。
FM模型中二次项的参数数量减少为 knknkn个,远少于多项式模型的参数数量。另外,参数因子化使得 xhxix_{h}x_{i}xhxi 的参数和 xixjx_{i}x_{j}xixj 的参数不再是相互独立的,因此我们可以在样本稀疏的情况下相对合理地估计FM的二次项参数。
具体来说,xhxix_{h}x_{i}xhxi 和 xixjx_{i}x_{j}xixj的系数分别为 <vh,vi>\lt v_{h},v_{i}\gt<vh,vi> 和 <vi,vj>\lt v_{i},v_{j}\gt<vi,vj> ,它们之间有共同项 viv_{i}vi 。也就是说,所有包含“ xix_{i}xi 的非零组合特征”(存在某个 j≠ij \ne ij=i ,使得 xixj≠0x_{i}x_{j}\neq 0xixj=0 )的样本都可以用来学习隐向量viv_{i}vi,这很大程度上避免了数据稀疏性造成的影响。
而在多项式模型中,whiw_{hi}whi 和 wijw_{ij}wij 是相互独立的。
显而易见,FM的公式是一个通用的拟合方程,可以采用不同的损失函数用于解决regression、classification等问题,比如可以采用MSE(Mean Square Error)loss function来求解回归问题,也可以采用Hinge/Cross-Entropy loss来求解分类问题。
当然,在进行二元分类时,FM的输出需要使用sigmoid函数进行变换,该原理与LR是一样的。直观上看,FM的复杂度是 O(kn2)O(kn^2)O(kn2) 。但是FM的二次项可以化简,其复杂度可以优化到 O(kn)O(kn)O(kn) 。由此可见,FM可以在线性时间对新样本作出预测。
证明:
∑i=1n−1∑j=i+1n<vi,vj>xixj=12∑i=1n∑j=1n<vi,vj>xixj−12∑i=1n<vi,vi>xixi=12(∑i=1n∑j=1n∑f=1kvi,fvj,fxixj−∑i=1n∑f=1kvi,fvi,fxixi)=12∑f=1k[(∑i=1nvi,fxi)⋅(∑j=1nvj,fxj)−∑i=1nvi,f2xi2]=12∑f=1k[(∑i=1nvi,fxi)2−∑i=1nvi,f2xi2]\begin{aligned} \sum_{i=1}^{n-1} \sum_{j=i+1}^{n}<v_{i}, v_{j}>x_{i} x_{j} &=\frac{1}{2} \sum_{i=1}^{n} \sum_{j=1}^{n}<v_{i}, v_{j}>x_{i} x_{j}-\frac{1}{2} \sum_{i=1}^{n}<v_{i}, v_{i}>x_{i} x_{i} \\ &=\frac{1}{2}\left(\sum_{i=1}^{n} \sum_{j=1}^{n} \sum_{f=1}^{k} v_{i, f} v_{j, f} x_{i} x_{j}-\sum_{i=1}^{n} \sum_{f=1}^{k} v_{i, f} v_{i, f} x_{i} x_{i}\right) \\ &=\frac{1}{2} \sum_{f=1}^{k}\left[\left(\sum_{i=1}^{n} v_{i, f} x_{i}\right) \cdot\left(\sum_{j=1}^{n} v_{j, f} x_{j}\right)-\sum_{i=1}^{n} v_{i, f}^{2} x_{i}^{2}\right] \\ &=\frac{1}{2} \sum_{f=1}^{k}\left[\left(\sum_{i=1}^{n} v_{i, f} x_{i}\right)^{2}-\sum_{i=1}^{n} v_{i, f}^{2} x_{i}^{2}\right] \end{aligned} i=1∑n−1j=i+1∑n<vi,vj>xixj=21i=1∑nj=1∑n<vi,vj>xixj−21i=1∑n<vi,vi>xixi=21⎝⎛i=1∑nj=1∑nf=1∑kvi,fvj,fxixj−i=1∑nf=1∑kvi,fvi,fxixi⎠⎞=21f=1∑k[(i=1∑nvi,fxi)⋅(j=1∑nvj,fxj)−i=1∑nvi,f2xi2]=21f=1∑k⎣⎡(i=1∑nvi,fxi)2−i=1∑nvi,f2xi2⎦⎤
解释:
- vi,fv_{i,f}vi,f 是一个具体的值;
- 第1个等号:对称矩阵 WWW 对角线上半部分;
- 第2个等号:把向量内积 viv_{i}vi,vjv_{j}vj 展开成累加和的形式;
- 第3个等号:提出公共部分;
- 第4个等号: iii 和 jjj 相当于是一样的,表示成平方过程。
3. FM模型的应用
最直接的想法就是直接把FM得到的结果放进sigmoid中输出一个概率值,由此做CTR预估,事实上我们也可以做召回。
由于FM模型是利用两个特征的Embedding做内积得到二阶特征交叉的权重,那么我们可以将训练好的FM特征取出离线存好,之后用来做KNN向量检索。
工业应用的具体操作步骤:
- 离线训练好FM模型(学习目标可以是CTR)
- 将训练好的FM模型Embedding取出
- 将每个uid对应的Embedding做avg pooling(平均)形成该用户最终的Embedding,item也做同样的操作
- 将所有的Embedding向量放入Faiss等
- 线上uid发出请求,取出对应的user embedding,进行检索召回
4. 代码实践
4.1 调包实现
- 调包版
直接看Github官方仓库:https://github.com/coreylynch/pyFM,里面有介绍如何安装以及使用,下面搬运一遍
- 安装
方法一:直接pip install
pip install git+https://github.com/coreylynch/pyFM
方法二:手动安装
输入上面这行代码应能下载这个包并安装,如果安装失败可能是网络原因,这时可以考虑手动下载这个包然后手动python setup.py install安装,这时候通常会报错,去掉setup.py文件里面的libraries=[“m”]一行再重新安装即可
具体操作是:
- 在https://github.com/coreylynch/pyFM中手动下载包
- 将包解压,更改里面的setup.py文件,去掉setup.py文件里面的libraries=[“m”]一行
- cd到当前文件夹下python setup.py install
测试:
这部分主要作为简单上手让读者了解如何使用这个包~
- 第一步:导包
from pyfm import pylibfm
from sklearn.feature_extraction import DictVectorizer
import numpy as np
- 第二步:创建训练集并转换成one-hot编码的特征形式
train = [{"user": "1", "item": "5", "age": 19},{"user": "2", "item": "43", "age": 33},{"user": "3", "item": "20", "age": 55},{"user": "4", "item": "10", "age": 20},
]
v = DictVectorizer()
X = v.fit_transform(train)
print(X.toarray())
看看结果,对比一下维度是否符合预期:
[[19. 0. 0. 0. 1. 1. 0. 0. 0.][33. 0. 0. 1. 0. 0. 1. 0. 0.][55. 0. 1. 0. 0. 0. 0. 1. 0.][20. 1. 0. 0. 0. 0. 0. 0. 1.]]
- 第三步:创建标签
这里简单创建了一个全1的标签:
y = np.repeat(1.0,X.shape[0])
yarray([1., 1., 1., 1.])
- 第四步:训练并预测
就和调用sklearn的包是一样的用法:
fm = pylibfm.FM()
fm.fit(X,y)
fm.predict(v.transform({"user": "1", "item": "10", "age": 24}))
电影评分数据集实战
数据集在这里下载,数据集本地具体保存路径读者自行阅读代码找找: http://www.grouplens.org/system/files/ml-100k.zip
导包,并定义一个导入指定格式数据集的函数
import numpy as np
from sklearn.feature_extraction import DictVectorizer
from pyfm import pylibfm# Read in data
def loadData(filename,path="ml-100k/"):data = []y = []users=set()items=set()with open(path+filename) as f:for line in f:(user,movieid,rating,ts)=line.split('\t')data.append({ "user_id": str(user), "movie_id": str(movieid)})y.append(float(rating))users.add(user)items.add(movieid)return (data, np.array(y), users, items)
导入训练集和测试集,并转换格式
(train_data, y_train, train_users, train_items) = loadData("ua.base")
(test_data, y_test, test_users, test_items) = loadData("ua.test")
v = DictVectorizer()
X_train = v.fit_transform(train_data)
X_test = v.transform(test_data)
训练模型并测试
# Build and train a Factorization Machine
fm = pylibfm.FM(num_factors=10, num_iter=100, verbose=True, task="regression", initial_learning_rate=0.001, learning_rate_schedule="optimal")
fm.fit(X_train,y_train)
预测结果打印误差
preds = fm.predict(X_test)
from sklearn.metrics import mean_squared_error
print("FM MSE: %.4f" % mean_squared_error(y_test,preds))#FM MSE: 0.8873
分类任务实战
搞数据
import numpy as np
from sklearn.feature_extraction import DictVectorizer
from sklearn.cross_validation import train_test_split
from pyfm import pylibfmfrom sklearn.datasets import make_classificationX, y = make_classification(n_samples=1000,n_features=100, n_clusters_per_class=1)
data = [ {v: k for k, v in dict(zip(i, range(len(i)))).items()} for i in X]X_train, X_test, y_train, y_test = train_test_split(data, y, test_size=0.1, random_state=42)v = DictVectorizer()
X_train = v.fit_transform(X_train)
X_test = v.transform(X_test)
建模型
我们可以看到主要改变的参数是num_factors和tasks,读者可以想想为什么
fm = pylibfm.FM(num_factors=50, num_iter=10, verbose=True, task="classification", initial_learning_rate=0.0001, learning_rate_schedule="optimal")
fm.fit(X_train,y_train)
由于是分类任务,误差函数肯定不一样
from sklearn.metrics import log_loss
print("Validation log loss: %.4f" % log_loss(y_test,fm.predict(X_test)))#Validation log loss: 1.3678
4.2 从零实现
tf代码参考源代码文档中的FM.py
之后补一个pytorch的
- 课后思考
请大家思考一下FM存在的问题, 以及可以从哪些地方再进行改进?
局限性:当query-item矩阵是稀疏并且是high-rank的时候(比如user有特殊的爱好,或item比较小众),很难非常效率的学习出低维度的表示
主要集中于:
- 和深度学习结合
- 和Learning to Rank结合
- 分布式训练
- 更高阶特征交叉
具体参见文章:一文看懂 FM ( Factorization Machine ) 模型的各种变式
推荐系统知识梳理——FM相关推荐
- 推荐系统知识梳理——GBDTLR
前言:前几天由于突发事情耽搁,没有来的及整理,今天补上.本次是datawhale组织的学习活动,具体参见:RecommendationSystemFundamentals 1. GBDT+LR简介 前 ...
- 推荐系统知识梳理——协同过滤
注:本次为参加datawhale的打卡活动~详细资料在team-learning-rs 核心系列内容: 协同过滤算法: 包括基于用户的协同过滤(UserCF)和基于商品的协同过滤(ItemCF),这是 ...
- 推荐系统知识梳理——WideDeep
1. 点击率预估简介 点击率预估是用来解决什么问题? 点击率预估是对每次广告点击情况作出预测,可以输出点击或者不点击,也可以输出该次点击的概率,后者有时候也称为pClick. 点击率预估模型需要做什么 ...
- 推荐系统知识梳理——矩阵分解
隐语义模型与矩阵分解 协同过滤算法的特点就是完全没有利用到物品本身或者是用户自身的属性, 仅仅利用了用户与物品的交互信息就可以实现推荐,是一个可解释性很强, 非常直观的模型, 但是也存在一些问题, 第 ...
- RecSys‘22 推荐系统论文梳理
2022推荐系统论文梳理系列 推荐系统相关顶会整理 IJCAI'22 推荐系统论文梳理 ICML/ICLR'22 推荐系统论文梳理 WWW'22 推荐系统论文之序列推荐篇 WWW'22 推荐系统论文之 ...
- ACL/NAACL‘22 推荐系统论文梳理
2022推荐系统论文梳理系列 推荐系统相关顶会整理 IJCAI'22 推荐系统论文梳理 ICML/ICLR'22 推荐系统论文梳理 WWW'22 推荐系统论文之序列推荐篇 WWW'22 推荐系统论文之 ...
- WSDM‘22推荐系统论文梳理
2022推荐系统论文梳理系列 推荐系统相关顶会整理 IJCAI'22 推荐系统论文梳理 ICML/ICLR'22 推荐系统论文梳理 WWW'22 推荐系统论文之序列推荐篇 WWW'22 推荐系统论文之 ...
- WSDM‘23 推荐系统论文梳理
之前把2022年已公布的推荐系统相关顶会梳理一遍 ,历史推荐系统顶会论文梳理系列文章可以参考公众号或知乎,快捷合辑详见<2022推荐系统顶会论文梳理系列>. WSDM'23已公布录用结果, ...
- 大数据知识梳理(Hadoop、HDFS)(整理中。。。)
大数据知识梳理(Hadoop.HDFS)(更新中...) 第1讲 大数据概述 1.1 大数据时代 1.2 大数据概念和影响 1.3 大数据的应用 1.4 大数据的关键技术 1.5 大数据与云计算.物联 ...
最新文章
- 客户端动态调用WCF服务中的方法
- 手把手带你剖析 Springboot 启动原理!
- SQL Server-聚焦SNAPSHOT基于行版本隔离级别详解(三十)
- 关于idea中新建web项目 webapp文件夹没有小蓝点 ,启动服务,访问不到解决方案
- [LintCode] 字符串查找
- Clipboard.js:不用Flash实现剪贴板功能的轻量级JavaScript库
- Guava入门~Strings
- html 页面元素id不唯一,规定html元素的唯一的id属性
- 【C语言】使用指针得到数组的最后一个数
- 自定义日期工具类 java 1614698552
- 挖矿仍然有利可图吗?
- Linux之lastb命令
- 自定义 Behavior - 仿新浪微博发现页的实现
- 深入理解Thread.sleep()函数
- 架构设计(5)-架构愿景分析
- 手把手教你写网络爬虫:Web应用的漏洞检测实战篇!
- oracle 2019 ocp,2019 OCP简介
- 打开ps提示计算机中丢失,ps打开出现dll文件丢失怎么解决
- Manjaro 安装MySQL
- 2.2.1 Nginx高性能负载均衡器