利用矩阵分解实现推荐算法
相关案例介绍
矩阵中,描述了5个用户(U1,U2,U3,U4 ,U5)对4个物品(D1,D2,D3,D4)的评分(1-5分),- 表示没有评分,现在目的是把没有评分的 给预测出来,然后按预测的分数高低,给用户进行推荐。
矩阵R可以近似表示为P与Q的乘积:R(m,n)≈ P(m,K)*Q(K,n)
所以评估的矩阵可以由下式计算:
实现方法
1.计算重新构建的矩阵每个元素的值,列出表达式
2. 构建损失函数
用原始矩阵元素和新矩阵元素差的平方作为损失函数,因此每段元素差的平方表达式为:
最后的损失函数是所有已知项损失之和:
3.利用梯度下降获得分量
根据梯度下降公式:
向负梯度方向更新变量:
不停迭代直至到达终止条件
为了防止过拟合,需要加入正则化项
只需把损失值改为下式即可
梯度下降公式更新为:
代码流程解析
1.遍历原矩阵R中的所有非0元素,同时计算出每一项的损失值
2.根据梯度下降更新P, Q矩阵中的所有元素
3.根据损失函数计算总量即所有之和
4.判断是否达到临界条件,如第3步结果小于目标条件值,终止迭代过程
5.循环进行此过程直至达到目标次数
完整代码推导
1.构建原矩阵R,及P,Q矩阵,设置k的值
P = numpy.random.rand(N, K) 为生成一个任何元素不大于1的N行K列的矩阵
R = [
[5, 3, 0, 1],
[4, 0, 0, 1],
[1, 1, 0, 5],
[0, 1, 5, 4],
]
R = numpy.array(R)
N = len(R)
M = len(R[0])
K = 2
P = numpy.random.rand(N, K) # 得到随机分配的P和Q矩阵
Q = numpy.random.rand(K, M)
2.设置步长即a,b的值用于构造损失函数和进行梯度下降,即a代表α,b代表β
这里的a, b设置如下:
a = 0.0002
b = 0.02
3. 开始进行迭代,并在每次迭代中遍历整个R矩阵
for step in range(5000): # 迭代次数
for i in range(len(R)):
for j in range(len(R[i])):
求每组元素损失值的表达式为
eij = R[i][j]-numpy.dot(P[i, :], Q[:, j]
P,Q矩阵的元素进行更改
for k in range(K):
P[i][k] = P[i][k]+a*(2*eij*Q[k][j]-b*P[i][k])
Q[k][j] = Q[k][j]+a*(2*eij*P[i][k]-b*Q[k][j])
对应上述梯度下降公式
for step in range(5000): # 迭代次数
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]) # 真实值减去预测值
for k in range(K): # 对P和Q矩阵里的变量进行更新
P[i][k] = P[i][k]+a*(2*eij*Q[k][j]-b*P[i][k])
Q[k][j] = Q[k][j]+a*(2*eij*P[i][k]-b*Q[k][j])
4. 计算损失函数(新旧矩阵元素差的平方和)
前半部分
loss = loss+pow(R[i][j]-numpy.dot(P[i, :], Q[:, j]), 2)
后半部分
loss = loss+(b/2)*(pow(R[i][k], 2)+pow(Q[k][j], 2))
最后 判断是否达到终止条件:
if loss < 0.0001:
break
完整函数
result = [] # 一个空列表
a = 0.0002
b = 0.02
for step in range(5000): # 迭代次数
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]) # 真实值减去预测值
for k in range(K): # 对P和Q矩阵里的变量进行更新
P[i][k] = P[i][k]+a*(2*eij*Q[k][j]-b*P[i][k])
Q[k][j] = Q[k][j]+a*(2*eij*P[i][k]-b*Q[k][j])
loss = 0
for i in range(len(R)):
for j in range(len(R[i])):
if R[i][j] != 0: # 计算损失函数
loss = loss+pow(R[i][j]-numpy.dot(P[i, :], Q[:, j]), 2)
for k in range(K): # 贝塔部分
loss = loss+(b/2)*(pow(R[i][k], 2)+pow(Q[k][j], 2))
result.append(loss) # 损失函数值插入到列表中
if loss < 0.0001:
break
最后在代码末尾计算P,Q矩阵之积即可得到新矩阵
MR = numpy.dot(P1, Q1)
源代码
import matplotlib.pyplot as plt
from math import pow
import numpy #引用numpy库,用于对矩阵的处理
def matrix_factorization(R,P,Q,K):
result=[] #一个空列表
a=0.0002
b=0.02
for step in range(5000): #迭代次数
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]) #真实值减去预测值
for k in range(K): #对P和Q矩阵里的变量进行更新
P[i][k]=P[i][k]+a*(2*eij*Q[k][j]-b*P[i][k])
Q[k][j]=Q[k][j]+a*(2*eij*P[i][k]-b*Q[k][j])
loss=0
for i in range(len(R)):
for j in range(len(R[i])):
if R[i][j]!=0: #计算损失函数
loss=loss+pow(R[i][j]-numpy.dot(P[i,:],Q[:,j]),2)
for k in range(K): #贝塔部分
loss=loss+(b/2)*(pow(R[i][k],2)+pow(Q[k][j],2))
result.append(loss) #将损失函数值插入到列表中
if loss<0.001:
break
return P,Q,result
if __name__ == '__main__':
R=[
[5,3,0,1],
[4,0,0,1],
[1,1,0,5],
[0,1,5,4],
]
R=numpy.array(R)
N=len(R)
M=len(R[0])
K=2
P=numpy.random.rand(N,K) #得到随机分配的P和Q矩阵
Q=numpy.random.rand(K,M)
print("原始矩阵为:\n",R)
P1,Q1,LOSS= matrix_factorization(R,P,Q,K)
MR=numpy.dot(P1,Q1) #P1,Q1两个相乘
print("经过矩阵分解得到的矩阵:\n",MR)
#绘制损失函数图
n=len(LOSS)
x=range(n)
plt.plot(x,LOSS,color='r',linewidth=3)
plt.title("Convergence curve")
plt.xlabel("generation")
plt.ylabel("loss")
plt.show()