矩阵分解常作为推荐系统中一个比较常用的方法,在传统的svd分解中,矩阵分解是把一个
R m × n = M m × m Σ U n × n T R_{m×n}=M_{m\times m}\Sigma U_{n\times n}^{T} Rm×n=Mm×mΣUn×nT
对于M矩阵来说是 R R T RR^T RRT ,而对于N是 R T R R^TR RTR 的特征向量。
但是R为稀疏矩阵(因为在用户的矩阵中部分用户对于部分物品是没有评分的,也就是矩阵元素为0),这也就意味着 R T R R^TR RTR 是不存在的,同样R乘以R的转置也不存在。所以这里就需要新的方法来处理这个问题。当然我们可以使用每行的平均值来填充空的元素,这一看起来比较合理的方法。不过这样会导致结果会有很大的偏见,所以不建议使用。
后来simon Funk就提出了一种分解方法,Funk mf,也就是我们日常所使用的mf。及利用中间变量来降维从而使得矩阵可分
R m × n = P m × k Q n × k T R_{m \times n}=P_{m \times k}Q_{n \times k}^T Rm×n=Pm×kQn×kT
这里的P和Q矩阵分别是用户u-中间矩阵,和物品矩阵,上面的模型也被称为隐含语义模型,或者叫做隐式反馈。
那么就得出预测矩阵
R ˉ = P × Q \bar{R}=P\times Q Rˉ=P×Q
有了预测矩阵,那么就可以想到梯度下降来求解最优参数了,于是:
S S E = ( R − R ˉ ) 2 = ( R − p × q ) 2 SSE=(R-\bar{R})^2=(R-p\times q)^2 SSE=(R−Rˉ)2=(R−p×q)2
对sse求导得:
∂ S S E ∂ P u = − 2 Q i ( R − P u × Q i ) \frac{\partial SSE}{\partial{P_u}}=-2Q_i(R-P_u\times Qi) ∂Pu∂SSE=−2Qi(R−Pu×Qi)
∂ S S E ∂ Q i = − 2 P u ( R − P u × Q i ) \frac{\partial{SSE}}{\partial{Q_i}}=-2P_u(R-P_u\times Qi) ∂Qi∂SSE=−2Pu(R−Pu×Qi)
但是,这里还有个问题,过拟合问题,所以为了防止过拟合问题,这里添加了L2正则项来避免这个问题。
sse式子就变为:
S S E = ( R − ∑ k = 1 K P u k Q k i ) 2 + β / 2 ​ ∑ k = 1 K ​ ( ∣ ∣ P ∣ ∣ 2 + ∣ ∣ Q ∣ ∣ 2 ) SSE=(R-\sum_{k=1}^K P_{uk} Q_{ki})^2+\beta/2\!\sum_{k=1}^K\!(||P||^2+||Q||^2) SSE=(R−k=1∑KPukQki)2+β/2k=1∑K(∣∣P∣∣2+∣∣Q∣∣2)
求导得:
∂ S S E ∂ P u = α ( 2 e u i Q i − β P u ) \frac{\partial{SSE}}{\partial P_u}=\alpha(2e_{ui}Q_{i}-\beta P_{u}) ∂Pu∂SSE=α(2euiQi−βPu)
∂ S S E ∂ Q i = α ( 2 e u i P u − β Q i ) \frac{\partial{SSE}}{\partial Q_{i}}=\alpha(2e_{ui}P_{u}-\beta Q_{i}) ∂Qi∂SSE=α(2euiPu−βQi)
其中eui 表示的是对SSE开根
打完收工;
下面放上python代码
#矩阵分解在打分预估系统中得到了成熟的发展和应用
# from pylab import *
import matplotlib.pyplot as plt
from math import pow
import numpy
def matrix_factorization(R,P,Q,K,steps=5000,alpha=0.0002,beta=0.02):
Q=Q.T # .T操作表示矩阵的转置
result=[]
for step in range(steps):
for i in range(len(R)):
for j in range(len(R[i])):
if R[i][j]>0:
eij=R[i][j]-numpy.dot(P[i,:],Q[:,j]) # .dot(P,Q) 表示矩阵内积
for k in range(K):
P[i][k]=P[i][k]+alpha*(2*eij*Q[k][j]-beta*P[i][k])
Q[k][j]=Q[k][j]+alpha*(2*eij*P[i][k]-beta*Q[k][j])
eR=numpy.dot(P,Q)
e=0
for i in range(len(R)):
for j in range(len(R[i])):
if R[i][j]>0:
e=e+pow(R[i][j]-numpy.dot(P[i,:],Q[:,j]),2)
for k in range(K):
e=e+(beta/2)*(pow(P[i][k],2)+pow(Q[k][j],2))
result.append(e)
if e<0.001:
break
return P,Q.T,result
if __name__ == '__main__':
R=[
[5,3,0,1],
[4,0,0,1],
[1,1,0,5],
[1,0,0,4],
[0,1,5,4]
]
R=numpy.array(R)
N=len(R)
M=len(R[0])
K=2
P=numpy.random.rand(N,K) #随机生成一个 N行 K列的矩阵
Q=numpy.random.rand(M,K) #随机生成一个 M行 K列的矩阵
nP,nQ,result=matrix_factorization(R,P,Q,K)
print("原始的评分矩阵R为:\n",R)
R_MF=numpy.dot(nP,nQ.T)
print("经过MF算法填充0处评分值后的评分矩阵R_MF为:\n",R_MF)
#-------------损失函数的收敛曲线图---------------
n=len(result)
x=range(n)
plt.plot(x,result,color='r',linewidth=3)
plt.title("Convergence curve")
plt.xlabel("generation")
plt.ylabel("loss")
plt.show()
但是,说一千道一万,我们只是预测了用户的偏好程度,也就是基于概率来求出了用户可能的评分,并不是预测行为。那么如果用户在对某一个物品的评分不是你所预测的分数(好比你预测用户会打5分,结果用户只打了1分)。这种情况应该怎么办喃?