在推荐系统中,隐语义模型主要用来让模型自己去发掘物品之间潜在的特征。
代码链接: github.
一、关于隐语义模型(LFM)
1. 用隐语义模型来进行协同过滤的目标
- 揭示隐藏的特征,这些特征能够解释为什么给出对应的预测评分
- 这类特征可能是无法直接用语言解释描述的,事实上我们并不需要知道,类似"玄学”
2. 通过矩阵分解进行降维分析
- 协同过滤算法非常依赖历史数据,而一般的推荐系统中,偏好数据又往往是稀疏的;这就需要对原始数据做降维处理
- 分解之后的矩阵,就代表了用户和物品的隐藏特征
3. 隐语义模型实例
- 基于概率的隐语义分析( pLSA )
- 隐式迪利克雷分布模型( LDA )
- 矩阵因子分解模型(基于奇异值分解的模型, SVD )
二、LFM降维方法——矩阵因子分解
- 假设用户物品评分矩阵为 R,现在有m个用户,n个物品
- 我们想要发现 k个隐类,我们的任务就是找到两个矩阵P和Q,使这两个矩阵的乘积
近似等于R,即将用户物品评分矩阵R分解成为两个低维矩阵相乘:
用户对电影的评分矩阵R分解为用户特征矩阵P跟电影特征矩阵Q(f1,f2,f3,f4为相应的特征):
反过来,用户特征矩阵P乘以电影特征矩阵Q得到用户的评分矩阵,其中用户评分矩阵R中的元素就是矩阵P与矩阵Q中相应特征向量的点积,反映的是用户特征与物品特征的契合程度,故R中数字越大反映用户对物品喜好程度越大。
三、LFM的进一步理解
- 我们可以认为,用户之所以给电影打出这样的分数,是有内在原因的,我们可以挖掘出影响用户打分的隐藏因素,进而根据未评分电影与这些隐藏因素的关联度,决定此未评分电影的预测评分
- 应该有一些隐藏的因素,影响用户的打分,比如电影:演员、题材、年代.甚至不一定是人直接可以理解的隐藏因子
- 找到隐藏因子,可以对user和item进行关联(找到是由于什么使得user喜欢/不喜欢此item,什么会决定user喜欢/不喜欢此item),就可以推测用户是否会喜欢某一部未看过的电影
- 对于用户看过的电影,会有相应的打分,但一个用户不可能看过所有电影,对于用户没有看过的电影是没有评分的,因此用户评分矩阵大部分项都是空的,是一个稀疏矩阵
- 如果我们能够根据用户给已有电影的打分推测出用户会给没有看过的电影的打分,那么 就可以根据预测结果给用户推荐他可能打高分的电影
四、矩阵因子分解
- 我们现在来做一个一般性的分析
- 一个 m x n 的打分矩阵R可以用两个小矩阵Pmxk和Qkxn的乘积 R’ 来近似:
公式:
- 得到Pmxk和Qkxn的乘积 R’ 不再是稀疏的,之前R’中没有的项也可以由P、Q的乘积算出,这就得到了一个预测评分矩阵
注:R’ 代表上面公式中的矩阵乘积Rui,这么做是因为本人不会表示这个字符。。。233 - 得到Pmxk和Qkxn的乘积 R’ 不再是稀疏的,之前R中没有的项也可以由P、Q的乘积算出,这就得到了一个预测评分矩阵
- 如果得到的预测评分矩阵 R’ 与原评分矩阵R在已知评分位置上的值都近似,那么我们认 为它们在预测位置上的值也是近似的
五、模型的求解——损失函数
- 现在的问题是,怎样得到这样的分解方式 R’=PxQ 呢?
- 矩阵分解得到的预测评分矩阵 R’ ,与原评分矩阵R在已知的评分项.上可能会有误差,我们的目标是找到一个最好的分解方式,让分解之后的预测评分矩阵总误差最小
损失函数
- 我们选择平方损失函数,并且加入正则化项,以防过拟合:
其中是正则化项,λ 一般通过交叉验证得到的
六、模型的求解算法——ALS(求得精确结果,但不适用,可忽略不看)
- 现在,矩阵因子分解的问题已经转化成了一个标准的优化问题,需要求解P、Q,使目标损失函数取最小值
- 最小化过程的求解,一般采用随机梯度下降算法或者交替最小二乘法来实现
1. 交替最小二乘法( Alternating Least Squares , ALS )
- ALS的思想是,由于两个矩阵P和Q都未知,且通过矩阵乘法耦合在一起,为了使它们解耦,可以先固定Q,把P当作变量,通过损失函数最小化求出P,这就是-个经典的最小二乘问题;再反过来固定求得的P,把Q当作变量,求解出Q:如此交替执行,直到误差 满足阈值条件,或者到达迭代上限
2. ALS算法
- ALS算法具体过程如下:
I. 为Q指定- -个初值Qo,可以是随机生成或者全局平均值
II. 固定当前Qo值,求解P。
I. 固定当前Po值,求解Q1
IV. 固定当前Q1值,求解P1
V. … (重复以。上过程)
VI. 直到损失函数的值C收敛,迭代结束
3. 求解过程
- 以固定Q,求解P为例
- 由于每一个用户u都是相互独立的,当Q固定时,用户特征向量Pu应该取得的值与 其它用户特征向量无关;所以每一个Pu都可以单独求解
- 优化目标
可转化为
-
令
-
那么我们的目标变成了:求每一个用户特征向量Pu,使得Lu(Pu)取得最小值
-
Lu对Pu求偏导:
- 令
,可以得到:
-
由Pu,Qi是向量,则:
-
所以有
-
Qi是物品 i 的特征向量,所以等式可以写为:
- 可以看到,物品特征向量构成的矩阵,其实就是Q:
- 所以可以得到:
- 解得:
- 这样一一解出每一个Pu,就可以得到这一步的用户特征矩阵P了
- 同样,下一次固定P时,可以解得
七、梯度下降算法(实际使用)
- 由上可得,损失函数为:
- 对于每一个Pu求偏导:
- 梯度下降迭代:
- 同理,有:
八、Python代码实例
为了便于理解,代码是基于Jupyter编辑器实现,每一个步骤有对应的输出结果。
代码链接: github.
- 引入依赖文件
import numpy as np
import pandas as pd
- 数据准备,自定义评分矩阵R
#评分矩阵R
R = np.array([[4,0,2,0,1],
[0,0,2,3,1],
[4,1,2,0,1],
[4,1,2,5,1],
[3,0,5,0,2],
[1,0,3,0,4]])
- 算法实现
'''
@输入参数
R:M*N的评分矩阵
K:隐特征向量维度
max_iter:最大迭代次数
alpha:步长
lamda:正则化系数
@输出
分解之后的P、Q
P:初始化用户特征矩阵M*k
Q:初始化物品特征矩阵N*K
'''
#给定超参数
K= 5
max_iter = 5000
alpha = 0.0002
lamda = 0.004
#核心算法
def LFM_grad_desc(R,K,max_iter,alpha=0.0002,lamda = 0.002):
#基本维度参数定义
M = len(R)
N = len(R[0])
#P、Q初始值,随机生成
P = np.random.rand(M,K)
Q = np.random.rand(N,K)
Q = Q.T
#开始迭代
for step in range(max_iter):
#对所有的用户u、物品i做遍历,对应的特征向量Pu,Qi梯度下降
for u in range(M):
for i in range(N):
#对于每一个大于0的评分,求出预测的评分误差
if R[u][i] > 0:
eui = np.dot(P[u,:],Q[:,i]) - R[u][i]
#带入公式,按照梯度下降算法更新当前的Pu与Qi
for k in range(K):
P[u][k] = P[u][k] - alpha * (2 * eui * Q[k][i] + 2 * lamda * P[u][k])
Q[k][i] = Q[k][i] - alpha * (2 * eui * P[u][k] + 2 * lamda * Q[k][i])
#u、i遍历完成,所有的特征向量更新完成,可以得到P、Q,可以计算预测评分矩阵
predR = np.dot(P,Q)
#计算当前损失函数
cost = 0
for u in range(M):
for i in range(N):
if R[u][i] > 0:
cost += (np.dot(P[u,:],Q[:,i]) - R[u][i]) ** 2
#加上正则化项
for k in range(K):
cost += lamda * (P[u][k] ** 2 + Q[k][i] ** 2)
if cost < 0.001:
break
return P,Q.T,cost
- 测试
P,Q,cost = LFM_grad_desc(R,K,max_iter,alpha,lamda)
'''
print(P)
print(Q)
print(cost)
print(R)
'''
predR = P.dot(Q.T)
#预测矩阵
predR
输出结果:
array([[3.94148951, 1.22116106, 2.07332208, 4.72376533, 0.91906299],
[2.67232719, 0.74839699, 1.98552529, 3.03276144, 0.97156313],
[3.9330541 , 0.94175409, 2.10780256, 4.09245595, 0.88109636],
[4.09412304, 1.05343721, 1.87535923, 4.92277698, 1.17325781],
[3.04067184, 1.03100579, 4.90618167, 3.34662411, 2.08753076],
[0.96925078, 0.10826894, 3.05169384, 3.81235739, 3.92397575]])
从输出的预测矩阵就可以看出,原先矩阵中为 0 的位置现在已经有了相应的预测值。