推荐系统基础03:矩阵分解与FM
推荐系统基础03:矩阵分解与FM
- 1. 隐语义模型与矩阵分解
- 2. 隐语义模型(LFM)
- 隐语义
- 隐特征矩阵
- 3.矩阵分解模型
- 4. 矩阵分解算法的求解
- Basic SVD
- FunkSVD(又称RSVD,正则化SVD)
- Baseline estimates
- Asymmetric-SVD
- SVD++
- 6. 编程实现
- 7. FM模型
- 逻辑回归模型及其缺点
- 二阶交叉项的考虑及改进
- 8. FM公式的理解
- 9. FM模型的应用
- 10.总结
- 参考资料
本篇为Datawhale组队学习笔记,datawhale推荐系统基础
1. 隐语义模型与矩阵分解
为了使得协同过滤更好处理稀疏矩阵问题, 增强泛化能力, 从协同过滤中衍生出矩阵分解模型(Matrix Factorization,MF)或者叫隐语义模型。
在协同过滤共现矩阵的基础上, 使用更稠密的隐向量表示用户和物品, 挖掘用户和物品的隐含兴趣和隐含特征, 在一定程度上弥补协同过滤模型处理稀疏矩阵能力不足的问题。
2. 隐语义模型(LFM)
隐语义
隐语义及隐含语义核心思想是通过隐含特征(latent factor)联系用户兴趣和物品(item), 基于用户的行为找出潜在的主题和分类, 然后对item进行自动聚类,划分到不同类别/主题(用户的兴趣)。
下面是项亮老师《推荐系统实践》里面的一个例子:
如果我们知道了用户A和用户B两个用户在豆瓣的读书列表, 从他们的阅读列表可以看出,用户A的兴趣涉及侦探小说、科普图书以及一些计算机技术书, 而用户B的兴趣比较集中在数学和机器学习方面。 那么如何给A和B推荐图书呢?
- 对于UserCF,首先需要找到和他们看了同样书的其他用户(兴趣相似的用户),然后给他们推荐那些用户喜欢的其他书。
- 对于ItemCF,需要给他们推荐和他们已经看的书相似的书,比如作者B看了很多关于数据挖掘的书,可以给他推荐机器学习或者模式识别方面的书。
而如果是隐语义模型的话, 它会先通过一些角度把用户兴趣和这些书归一下类, 当来了用户之后, 首先得到他的兴趣分类, 然后从这个分类中挑选他可能喜欢的书籍。
先将用户兴趣和物品进行分类,再从对应分类中挑选,协同过滤是考虑相似,没有进行分类。分类的依据就是隐含特征,比如书籍的话它的内容, 作者, 年份, 主题等都可以算隐含特征.
下面是王喆老师《深度学习推荐系统》的一个原理图。
隐特征矩阵
我们下面拿一个音乐评分的例子来具体看一下隐特征矩阵的含义。
1.潜在因子—— 用户矩阵Q 这个矩阵表示不同用户对于不同元素的偏好程度, 1代表很喜欢, 0代表不喜欢, 比如下面这样:
2.潜在因子——音乐(物品)矩阵P 表示每种音乐含有各种元素的成分, 比如下表中, 音乐A是一个偏小清新的音乐, 含有小清新的Latent Factor的成分是0.9, 重口味的成分是0.1, 优雅成分0.2…
利用上面的这两个矩阵, 我们就能得出张三对音乐A的喜欢程度:
张三对小清新的偏好 * 音乐A含有小清新的成分 + 张三对重口味的偏好 * 音乐A含有重口味的成分 + 张三对优雅的偏好 * 音乐A含有优雅的成分…,
每个用户对每首歌其实都可以得到这样的分数, 最后就得到了我们的评分矩阵:
通过隐含特征就可以把用户的兴趣和音乐的进行一个分类, 其实就是找到了每个用户每个音乐的一个隐向量表达形式,向量每一位对应一个进行分类的隐含特征。这个隐向量就可以反映出用户的兴趣和物品的风格。
之前协同过滤中ItemCF是用用户作为向量来表示物品,然后计算物品之间的相似度。而代表用户的向量UserCF是用户对物品的行为记录作为向量计算用户间的相似度。
而这里是把协同过滤算法进行了一种延伸, 把用户的相似性和物品的相似性通过了一个叫做隐向量的方式进行表达。
3.矩阵分解模型
真实的情况下我们其实是没有上面那两个矩阵的, 音乐那么多, 用户那么多, 我们没有办法去找一些隐特征去表示出这些东西, 另外一个问题就是即使能表示也不一定准, 对于每个用户或者每个物品的风格,我们每个人都有不同的看法。 所以事实上, 我们有的只有用户的评分矩阵, 也就是最后的结果, 并且一般这种矩阵长这样:
这种矩阵非常的稀疏,如果直接基于用户相似性或者物品相似性去填充这个矩阵是不太容易的, 并且很容易出现长尾问题, 所以矩阵分解就可以比较容易的解决这个问题。
矩阵分解模型其实就是在想办法基于这个评分矩阵去找到上面例子中的那两个矩阵, 也就是用户兴趣和物品的隐向量表达, 然后就把这个评分矩阵分解成Q和P两个矩阵乘积的形式, 这时候就可以基于这两个矩阵去预测某个用户对某个物品的评分了。 然后基于这个评分去进行推荐。这就是矩阵分解算法的原理。
在矩阵分解的算法框架下, 我们就可以通过分解协同过滤的共现矩阵来得到用户和物品的隐向量, 就是上面的用户矩阵Q和物品矩阵P。
这里的隐含特征就不知道具体含义了,需要模型去学。特征越多,表达信息越强。
由用户矩阵和物品矩阵相乘可以得最终评分矩阵。
4. 矩阵分解算法的求解
谈到矩阵分解, 最常用的方法是特征值分解(EVD)或者奇异值分解(SVD),但是这两种方式在这里不适用。
首先是EVD, 它要求分解的矩阵是方阵, 显然用户-物品矩阵不满足这个要求, 而传统的SVD分解, 会要求原始矩阵是稠密的, 而我们这里的这种矩阵一般情况下是非常稀疏的, 如果想用奇异值分解, 就必须对缺失的元素进行填充, 而一旦补全, 空间复杂度就会非常高, 且补的不一定对。 然后就是SVD分解计算复杂度非常高, 而我们的用户-物品矩阵非常大, 所以基本上无法使用。
机器学习部分到了
Basic SVD
Basic-SVD的思想很简单: 把求解上面两个矩阵的参数问题转换成一个最优化问题, 可以通过训练集里面的观察值利用最小化来学习用户矩阵和物品矩阵。
我们上面已经知道了, 如果有了用户矩阵和物品矩阵的话, 我们就知道了如果想计算用户uuu对物品iii的评分, 只需要 Preference(u,i)=rui=puTqi=∑f=1Fpu,kqk,i\operatorname{Preference}(u, i)=r_{u i}=p_{u}^{T} q_{i}=\sum_{f=1}^{F} p_{u, k} q_{k,i} Preference(u,i)=rui=puTqi=f=1∑Fpu,kqk,i 而现在, 我们有真实的ru,ir_{u,i}ru,i, 但是没有puTqip_{u}^{T} q_{i}puTqi, 那么我们可以初始化一个啊, 随机初始化一个用户矩阵UUU和一个物品矩阵VVV, 然后不就有puTqip_{u}^{T} q_{i}puTqi了? 当然你说, 随机初始化的肯定不准啊, 但是, 有了puTqip_{u}^{T} q_{i}puTqi之后, 我们就可以计算一个猜测的r^ui\hat{r}_{u i}r^ui, 即 r^ui=puTqi\hat{r}_{u i}=p_{u}^{T} q_{i} r^ui=puTqi
这时候, 肯定是不准, 那么这个猜测的和真实值之间就会有一个误差: eui=rui−r^uie_{u i}=r_{u i}-\hat{r}_{u i} eui=rui−r^ui
有了误差, 我们就可以计算出总的误差平方和: SSE=∑u,ieui2=∑u,i(rui−∑k=1Kpu,kqk,i)2\operatorname{SSE}=\sum_{u, i} e_{u i}^{2}=\sum_{u, i}\left(r_{u i}-\sum_{k=1}^{K} p_{u,k} q_{k, i}\right)^{2} SSE=u,i∑eui2=u,i∑(rui−k=1∑Kpu,kqk,i)2 有了损失, 我们就可以想办法进行训练, 把SSE降到最小, 那么我们的两个矩阵参数就可以算出来。所以就把这个问题转成了最优化的的问题, 而我们的目标函数就是:
minq,p∑(u,i)∈K(rui−puTqi)2\min _{\boldsymbol{q}^{}, \boldsymbol{p}^{}} \sum_{(u, i) \in K}\left(\boldsymbol{r}_{\mathrm{ui}}-p_{u}^{T} q_{i}\right)^{2} q,pmin(u,i)∈K∑(rui−puTqi)2
这里的KKK表示所有用户评分样本的集合。
有了目标函数, 那么我们就可以使用梯度下降算法来降低损失。 那么我们需要对目标函数求偏导, 得到梯度。 我们的目标函数如果是上面的SSE, 我们下面来推导一下最后的导数:
SSE=∑u,ieui2=∑u,i(rui−∑k=1Kpu,kqk,i)2\operatorname{SSE}=\sum_{u, i} e_{u i}^{2}=\sum_{u, i}\left(r_{u i}-\sum_{k=1}^{K} p_{u,k} q_{k,i}\right)^{2} SSE=u,i∑eui2=u,i∑(rui−k=1∑Kpu,kqk,i)2
首先我们求SSE在pu,kp_{u,k}pu,k(也就是Q矩阵的第uuu行kkk列)的梯度:
∂∂pu,kSSE=∂∂pu,k(eui2)=2eui∂∂pu,keui=2eui∂∂pu,k(rui−∑k=1Kpu,kqk,i)=−2euiqk,i\frac{\partial}{\partial p_{u,k}} S S E=\frac{\partial}{\partial p_{u,k}}\left(e_{u i}^{2}\right) =2e_{u i} \frac{\partial}{\partial p_{u,k}} e_{u i}=2e_{u i} \frac{\partial}{\partial p_{u,k}}\left(r_{u i}-\sum_{k=1}^{K} p_{u,k} q_{k,i}\right)=-2e_{u i} q_{k,i} ∂pu,k∂SSE=∂pu,k∂(eui2)=2eui∂pu,k∂eui=2eui∂pu,k∂(rui−k=1∑Kpu,kqk,i)=−2euiqk,i
然后求SSE在qk,iq_{k,i}qk,i处(也就是V矩阵的第kkk行iii列)的梯度:
∂∂qk,iSSE=∂∂pk,i(eui2)=2eui∂∂pk,ieui=2eui∂∂pk,i(rui−∑k=1Kpu,kqk,i)=−2euipu,k\frac{\partial}{\partial q_{k,i}} S S E=\frac{\partial}{\partial p_{k,i}}\left(e_{u i}^{2}\right) =2e_{u i} \frac{\partial}{\partial p_{k,i}} e_{u i}=2e_{u i} \frac{\partial}{\partial p_{k,i}}\left(r_{u i}-\sum_{k=1}^{K} p_{u,k} q_{k,i}\right)=-2e_{u i} p_{u,k} ∂qk,i∂SSE=∂pk,i∂(eui2)=2eui∂pk,i∂eui=2eui∂pk,i∂(rui−k=1∑Kpu,kqk,i)=−2euipu,k 为了让公式更为简单, 把前面的2给他越掉, 即可以令SSE等于: SSE=12∑u,ieui2=12∑u,i(rui−∑k=1Kpukqki)2\operatorname{SSE}=\frac{1}{2} \sum_{u, i} e_{u i}^{2}=\frac{1}{2} \sum_{u, i}\left(r_{u i}-\sum_{k=1}^{K} p_{u k} q_{k i}\right)^{2} SSE=21u,i∑eui2=21u,i∑(rui−k=1∑Kpukqki)2
这时候, 梯度就没有前面的系数了, 有了梯度, 接下来我们就可以用梯度下降算法更新梯度了: pu,k=pu,k−η(−euiqk,i)=pu,k+ηeuiqk,ip_{u, k}=p_{u,k}-\eta (-e_{ui}q_{k,i})=p_{u,k}+\eta e_{ui}q_{k,i} pu,k=pu,k−η(−euiqk,i)=pu,k+ηeuiqk,i qk,i=qk,i−η(−euipu,k)=qk,i+ηeuipu,kq_{k, i}=q_{k, i}-\eta (-e_{ui}p_{u,k})=q_{k, i}+\eta e_{ui}p_{u,k} qk,i=qk,i−η(−euipu,k)=qk,i+ηeuipu,k
这里的η\etaη是学习率, 控制步长用的。
FunkSVD(又称RSVD,正则化SVD)
上面这个有个问题就是当参数很多的时候, 就是两个矩阵很大的时候, 往往容易陷入过拟合的困境, 这时候, 就需要在目标函数上面加上正则化的损失(加入惩罚项), 就变成了FunkSVD。
Baseline estimates
但在实际中, 单纯的r^ui=puTqi\hat{r}_{u i}=p_{u}^{T} q_{i}r^ui=puTqi也是不够的, 还要考虑其他的一些因素, 比如一个评分系统, 有些固有的属性和用户物品无关, 而用户也有些属性和物品无关, 物品也有些属性和用户无关。
Baseline estimates是一种考虑用户评分倾向和物品评分倾向的方法, 在原来的基础上加上偏置项, 来消除用户和物品打分的偏差, 即预测公式如下: r^ui=μ+bu+bi+puT⋅qi\hat{r}_{u i}=\mu+b_{u}+b_{i}+p_{u}^{T} \cdot q_{i} r^ui=μ+bu+bi+puT⋅qi 这个预测公式加入了3项偏置μ,bu,bi\mu,b_u,b_iμ,bu,bi, 作用如下:
μ\muμ: 训练集中所有记录的评分的全局平均数。 在不同网站中, 因为网站定位和销售物品不同, 网站的整体评分分布也会显示差异。 比如有的网站中用户就喜欢打高分, 有的网站中用户就喜欢打低分。 而全局平均数可以表示网站本身对用户评分的影响。
bub_ubu: 用户偏差系数, 可以使用用户uuu给出的所有评分的均值, 也可以当做训练参数。 这一项表示了用户的评分习惯中和物品没有关系的那种因素。 比如有些用户比较苛刻, 对什么东西要求很高, 那么他评分就会偏低, 而有些用户比较宽容, 对什么东西都觉得不错, 那么评分就偏高
bib_ibi: 物品偏差系数, 可以使用物品iii收到的所有评分的均值, 也可以当做训练参数。 这一项表示了物品接受的评分中和用户没有关系的因素。 比如有些物品本身质量就很高, 因此获得的评分相对比较高, 有的物品本身质量很差, 因此获得的评分相对较低。
加了用户和物品的打分偏差之后, 矩阵分解得到的隐向量更能反映不同用户对不同物品的“真实”态度差异, 也就更容易捕捉评价数据中有价值的信息, 从而避免推荐结果有偏。
加入的这两个变量在SSE式子中同样需要惩罚,那么SSE就变成了下面这样:
SSE=12∑u,ieui2+12λ∑u∣pu∣2+12λ∑i∣qi∣2+12λ∑ubu2+12λ∑ubi2=12∑u,i(rui−μ−bu−bi−∑k=1Kpukqki)2+12λ∑u∣pu∣2+12λ∑i∣qi∣2+12λ∑ubu2+12λ∑ubi2\begin{array}{l} \operatorname{SSE}=\frac{1}{2} \sum_{u, i} e_{u i}^{2}+\frac{1}{2} \lambda \sum_{u}\left|\boldsymbol{p}_{u}\right|^{2}+\frac{1}{2} \lambda \sum_{i}\left|\boldsymbol{q}_{i}\right|^{2}+\frac{1}{2} \lambda \sum_{u} \boldsymbol{b}_{u}^{2}+\frac{1}{2} \lambda \sum_{u} \boldsymbol{b}_{i}^{2} \ =\frac{1}{2} \sum_{u, i}\left(\boldsymbol{r}_{u i}-\boldsymbol{\mu}-\boldsymbol{b}_{u}-\boldsymbol{b}_{i}-\sum_{k=1}^{K} \boldsymbol{p}_{u k} \boldsymbol{q}_{k i}\right)^{2}+\frac{1}{2} \lambda \sum_{u}\left|\boldsymbol{p}_{u}\right|^{2}+\frac{1}{2} \lambda \sum_{i}\left|\boldsymbol{q}_{i}\right|^{2}+\frac{\mathbf{1}}{2} \lambda \sum_{u} \boldsymbol{b}_{u}^{2}+\frac{1}{2} \lambda \sum_{u} \boldsymbol{b}_{i}^{2} \end{array} SSE=21∑u,ieui2+21λ∑u∣pu∣2+21λ∑i∣qi∣2+21λ∑ubu2+21λ∑ubi2=21∑u,i(rui−μ−bu−bi−∑k=1Kpukqki)2+21λ∑u∣pu∣2+21λ∑i∣qi∣2+21λ∑ubu2+21λ∑ubi2 此时如果把bub_ubu和bib_ibi当做训练参数的话, 那么它俩的梯度是:
∂∂buSSE=−eui+λbu\frac{\partial}{\partial b_{u}} S S E=-e_{u i}+\lambda b_{u} ∂bu∂SSE=−eui+λbu ∂∂biSSE=−eui+λbi\frac{\partial}{\partial b_{i}} S S E=-e_{u i}+\lambda b_{i} ∂bi∂SSE=−eui+λbi 更新公式为:
bu=bu+η(eui−λbu)\boldsymbol{b}_{u}=\boldsymbol{b}_{\boldsymbol{u}}+\boldsymbol{\eta}\left(\boldsymbol{e}_{u i}-\lambda \boldsymbol{b}_{\boldsymbol{u}}\right) bu=bu+η(eui−λbu)
bi=bi+η(eui−λbi)\boldsymbol{b}_{\boldsymbol{i}}=\boldsymbol{b}_{\boldsymbol{i}}+\boldsymbol{\eta}\left(\boldsymbol{e}_{\boldsymbol{u} i}-\lambda \boldsymbol{b}_{\boldsymbol{i}}\right) bi=bi+η(eui−λbi) 而对于pu,kp_{u,k}pu,k和pk,ip_{k,i}pk,i, 导数没有变化, 更新公式也没有变化。
后续改进还有:
Asymmetric-SVD
该方法引入了用户显式反馈和隐式反馈对预测的影响。在评分数据集中,用户的评分值代表了用户的显式反馈,而用户评分和未评分的操作代表了用户的隐式反馈。
去掉了P用户矩阵,取而代之的是利用用户评过分的商品和用户浏览过尚未评分的商品属性来表示用户属性,Xj和Yj是商品的属性。
R(u)为用户 u 的显式反馈(评分)集合,x为显式反馈物品属性。
N(u)为用户u的隐式反馈集合,y为隐式反馈物品的属性,u 对 j 的隐式偏好将通过 y 反应到对 i 的预测中,如果 j 可以预测 i,那么 y 将较大(可通过0或1表示)。
这种方法的用意是:用户的兴趣不仅和评分物品有关,而且和未评分物品有关。
SVD++
SVD++是对Asymmetric-SVD的改进,进一步简化了Asymmetric-SVD方法,更加直观、高效,预测评分表达为:
6. 编程实现
把之前的例子拿过来:
任务就是根据这个评分矩阵, 猜测Alice对物品5的打分。
SVD在解决这个问题上是这么做的:
- 首先, 初始化用户矩阵P和物品矩阵Q, P的维度是[users_num, F], Q的维度是[item_nums, F], 这个F是隐向量的维度。 也就是把通过隐向量的方式把用户的兴趣和F的特点关联了起来。 初始化这两个矩阵的方式很多, 但根据经验, 随机数需要和1/sqrt(F)成正比。 下面代码中会发现。
- 有了两个矩阵之后, 我就可以根据用户已经打分的数据去更新参数, 这就是训练模型的过程, 方法很简单, 就是遍历用户, 对于每个用户, 遍历它打分的物品, 然后两者相乘加上偏置就是预测的评分, 这时候与真实评分有个差距, 根据上面的梯度下降就可以进行参数的更新。
这样训练完之后, 我们就可以得到用户Alice和物品5的隐向量, 根据这个就可以预测Alice对物品5的打分。 下面的代码的逻辑就是上面这两步, 这里使用带有偏置项和正则项的那个SVD算法:
class SVD():def __init__(self, rating_data, F=5, alpha=0.1, lmbda=0.1, max_iter=100):self.F = F # 这个表示隐向量的维度self.P = dict() # 用户矩阵P 大小是[users_num, F]self.Q = dict() # 物品矩阵Q 大小是[item_nums, F]self.bu = dict() # 用户偏差系数self.bi = dict() # 物品偏差系数self.mu = 0.0 # 全局偏差系数self.alpha = alpha # 学习率self.lmbda = lmbda # 正则项系数self.max_iter = max_iter # 最大迭代次数self.rating_data = rating_data # 评分矩阵# 初始化矩阵P和Q, 方法很多, 一般用随机数填充, 但随机数大小有讲究, 根据经验, 随机数需要和1/sqrt(F)成正比cnt = 0 # 统计总的打分数, 初始化mu用for user, items in self.rating_data.items():#随机初始化self.P[user] = [random.random() / math.sqrt(self.F) for x in range(0, F)]self.bu[user] = 0 #初始化为0cnt += len(items) for item, rating in items.items():if item not in self.Q:self.Q[item] = [random.random() / math.sqrt(self.F) for x in range(0, F)]self.bi[item] = 0 #初始化为0self.mu /= cnt# 有了矩阵之后, 就可以进行训练, 这里使用随机梯度下降的方式训练参数P和Qdef train(self):for step in range(self.max_iter):for user, items in self.rating_data.items():for item, rui in items.items():rhat_ui = self.predict(user, item) # 得到预测评分# 计算误差e_ui = rui - rhat_uiself.bu[user] += self.alpha * (e_ui - self.lmbda * self.bu[user])self.bi[item] += self.alpha * (e_ui - self.lmbda * self.bi[item])# 随机梯度下降更新梯度for k in range(0, self.F):self.P[user][k] += self.alpha * (e_ui*self.Q[item][k] - self.lmbda * self.P[user][k])self.Q[item][k] += self.alpha * (e_ui*self.P[user][k] - self.lmbda * self.Q[item][k])self.alpha *= 0.1 # 每次迭代步长要逐步缩小# 预测user对item的评分, 这里没有使用向量的形式def predict(self, user, item):return sum(self.P[user][f] * self.Q[item][f] for f in range(0, self.F)) + self.bu[user] + self.bi[item] + self.mu
# 定义数据集, 也就是那个表格, 注意这里我们采用字典存放数据, 因为实际情况中数据是非常稀疏的, 很少有情况是现在这样
def loadData():rating_data={1: {'A': 5, 'B': 3, 'C': 4, 'D': 4},2: {'A': 3, 'B': 1, 'C': 2, 'D': 3, 'E': 3},3: {'A': 4, 'B': 3, 'C': 4, 'D': 3, 'E': 5},4: {'A': 3, 'B': 3, 'C': 1, 'D': 5, 'E': 4},5: {'A': 1, 'B': 5, 'C': 5, 'D': 2, 'E': 1}}return rating_data# 接下来就是训练和预测
rating_data = loadData()
basicsvd = SVD(rating_data, F=10)
basicsvd.train()
for item in ['E']:print(item, basicsvd.predict(1, item))## 结果:
E 3.252210242858994
7. FM模型
因子分解机模型,即Factorization Machines Model,简称FM。
矩阵分解算法依然是只用到了评分矩阵, 没有考虑到用户特征, 物品特征和上下文特征, 这使得矩阵分解丧失了利用很多有效信息的机会, 同时在缺乏用户历史行为的时候, 无法进行有效的推荐。为了解决这个问题, 逻辑回归模型及后续的因子分解机模型, 凭借其天然的融合不同特征的能力, 逐渐在推荐系统领域得到了更广泛的应用。
逻辑回归模型及其缺点
一般来说做推荐CTR预估(Click-Through-Rate,点击率)时最简单的思路就是将特征做线性组合(逻辑回归LR),传入sigmoid中得到一个概率值,本质上这就是一个线性模型,因为sigmoid是单调增函数不会改变里面的线性模型的CTR预测顺序,因此逻辑回归模型效果会比较差。也就是LR的缺点有:
- 是一个线性模型
- 每个特征对最终输出结果独立
每个特征并不都是独立的,需要把特征组合起来。FM假设特征两两相关。
二阶交叉项的考虑及改进
由于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∑j=i+1nwijxixjy = w_0+\sum_{i=1}^nw_ix_i+\sum_{i=1}^{n-1}\sum_{j=i+1}^nw_{ij}x_ix_j y=w0+i=1∑nwixi+i=1∑n−1j=i+1∑nwijxixj 但这个式子有一个问题,只有当xix_ixi与xjx_jxj均不为0时这个二阶交叉项才会生效,后面这个特征交叉项本质是和多项式核SVM等价的,为了解决这个问题,我们的FM登场了!
比如计算机和程序员这两个特征同时存在在一个样本才能组合起来。而FM可以使两个特征不同时存在也能组合起来。
由Cholesky分解,正定矩阵(实对称矩阵)可以化为一个下三角矩阵与其共轭转置矩阵的积的分解:
W=VVTW = V V^T W=VVT
FM模型使用了如下的优化函数:
y=w0+∑i=1nwixi+∑i=1n∑j=i+1n<vi,vj>xixjy = w_0+\sum_{i=1}^nw_ix_i+\sum_{i=1}^{n}\sum_{j=i+1}^n\lt v_i,v_j\gt x_ix_j y=w0+i=1∑nwixi+i=1∑nj=i+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!
8. 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可以在线性时间对新样本作出预测。
证明:
9. FM模型的应用
最直接的想法就是直接把FM得到的结果放进sigmoid中输出一个概率值,由此做CTR预估,事实上我们也可以做召回。
由于FM模型是利用两个特征的Embedding做内积得到二阶特征交叉的权重,那么我们可以将训练好的FM特征取出离线存好,之后用来做KNN向量检索。
工业应用的具体操作步骤:
- 离线训练好FM模型(学习目标可以是CTR)
- 将训练好的FM模型Embedding取出
- 将每个uid对应的Embedding做avg pooling(平均)形成该用户最终的Embedding,item也做同样的操作
- 将所有的Embedding向量放入Faiss等
- 线上uid发出请求,取出对应的user embedding,进行检索召回
10.总结
协同过滤中ItemCF是用用户作为向量来表示物品,然后计算物品之间的相似度。而代表用户的向量UserCF是用户对物品的行为记录作为向量计算用户间的相似度。矩阵分解是把协同过滤算法进行了一种延伸, 把用户的相似性和物品的相似性通过了一个叫做隐向量的方式进行表达。
为了防止过拟合,消除用户和物品打分偏差。BasicSVD进行了一系列优化成为了FunkSVD
矩阵分解优点:
泛化能力强: 一定程度上解决了稀疏问题
空间复杂度低: 由于用户和物品都用隐向量的形式存放, 少了用户和物品相似度矩阵, 空间复杂度由n2n^2n2降到了(n+m)∗f(n+m)*f(n+m)∗f
更好的扩展性和灵活性:矩阵分解的最终产物是用户和物品隐向量, 这个深度学习的embedding思想不谋而合, 因此矩阵分解的结果非常便于与其他特征进行组合和拼接, 并可以与深度学习无缝结合。FM模型后一般接激活函数打分或者分类,在lr的基础上利用了矩阵分解的思想。用法上要与矩阵分解,协同过滤区分理解。
参考资料
- 王喆 - 《深度学习推荐系统》
- 项亮 - 《推荐系统实战》
- 奇异值分解(SVD)的原理详解及推导
- Matrix factorization techniques for recommender systems论文
- 隐语义模型(LFM)和矩阵分解(MF)
- 推荐系统:矩阵分解(Matrix factorization)
- SVD在推荐系统中的应用详解以及算法推导
- FM因子分解机模型的原理、推导、代码和应用
推荐系统基础03:矩阵分解与FM相关推荐
- 推荐系统组队学习——矩阵分解和FM
文章目录 一.矩阵分解 1.隐语义模型与矩阵分解 2.矩阵分解算法的原理 3.Basic SVD 4.SVD++ 5.优化方法 1.交替最小二乘原理 (ALS) 2.加权交替最小二乘(Weighted ...
- 推荐系统基础之矩阵分解
1 隐语义模型与矩阵分解 隐语义模型最早在文本领域被提出,用于挖掘文本的隐含语义.在推荐系统中,隐语义模型的核心思想就是基于用户的行为挖掘用户和物品的潜在特征.那么,如何找出用户和物品的潜在特征呢?矩 ...
- 推荐系统之矩阵分解和FM
推荐系统之矩阵分解和FM 一.矩阵分解 1. 隐语义模型与矩阵分解 2. 隐语义模型 3. 矩阵分解算法的原理 4. 矩阵分解算法的求解 5. Basic SVD 6. 编程实现 7. 课后思考 8. ...
- 推荐系统知识梳理——矩阵分解
隐语义模型与矩阵分解 协同过滤算法的特点就是完全没有利用到物品本身或者是用户自身的属性, 仅仅利用了用户与物品的交互信息就可以实现推荐,是一个可解释性很强, 非常直观的模型, 但是也存在一些问题, 第 ...
- 推荐系统中的矩阵分解技术
推荐系统中的矩阵分解技术 本文翻译自Koren Y, Bell R, Volinsky C. Matrix Factorization Techniques for Recommender Syste ...
- 推荐系统的矩阵分解和FM模型
文章目录 一. 隐语义模型与矩阵分解 1 .矩阵分解理解 2. 隐语义模型 3. 矩阵分解算法的原理 4. 矩阵分解算法的求解 6. 编程实现 7.矩阵分解的优缺点分析 二.FM模型算法 1.FM模型 ...
- mf模型 svd++_算法小板报(六)——初探MF(矩阵分解)和FM模型
一.简介 矩阵分解(Matrix Factorization,MF)是推荐系统中非常经典的一个算法,虽然现今工业界直接使用的较少,但其背后蕴含的编码降维思想,得以广泛应用于推荐领域之中.本文则主要来梳 ...
- 从矩阵分解到FM的演进、FM如何用于召回和排序以及实现说明
本文主要包含以下内容: 1.背景 2.矩阵分解类算法 2.1 协同过滤 CF 2.2 矩阵分解 MF 2.3 逻辑回归 LR 2.4 因子分解机FM 2.5 组合模型 3.FM深入介绍 3.1 FM特 ...
- 自己动手写一个推荐系统,推荐系统小结,推荐系统:总体介绍、推荐算法、性能比较, 漫谈“推荐系统”, 浅谈矩阵分解在推荐系统中的应用...
自己动手写一个推荐系统 废话: 最近朋友在学习推荐系统相关,说是实现完整的推荐系统,于是我们三不之一会有一些讨论和推导,想想索性整理出来. 在文中主要以工程中做推荐系统的流程着手,穿插一些经验之谈,并 ...
最新文章
- java对象的访问定位_2、JVM-Java对象的创建、对象结构、对象访问定位-Go语言中文社区...
- vue + skyline 搭建 一个开发环境
- kmalloc、vmalloc、malloc的区别
- 工单系统的设计与实现(4)
- Android UI效果实现——Activity滑动退出效果
- Android开发经典笔试面试题汇总(持续更新中)
- 优秀Unix管理员的七个习惯
- C语言dev查看调试变量,C语言程序设计-c4_C语言上机_Dev调试.doc
- Skin++ 使用教程(VC软件外观美化工具)
- [C++/CLI编程宝典][7]基本概念
- 2020 豆瓣电影榜单出炉,直接在豆瓣上看电影吧
- 抽象代数学习笔记三《群:对称性变换与对称性群》
- 基于asp.net的企业固定资产管理系统
- Quick BI产品核心功能大图(三)电子表格:新手亦可表格自由
- 会议预约及信息发布服务器,会议预约系统
- DDD(Domain-Driven Design)《领域驱动设计》书籍
- 手游后劲不足,“体验”会是端游发展的一张王牌吗?
- excel高级筛选怎么用_Excel集|高级筛选使用方法(多条件筛选)
- 无线局域网中iPhone无法访问IIS
- 超级棒的一个DP问题详解(入门)