目录
一、前言
在开始之前我们先引入例题,User表示用户,Data表示不同物品所获得的打分分数,请补全该表格
在打分表格中,用户并不见得会对所有项目都进行打分(0表示未打分),那么如何预测并补全打分表格呢,我们引入矩阵分解的概念。
为了变量名的统一,此处我采用吴恩达机器学习中的符号表示,用m表示用户数量的多少,具体表示为m行;n表示待打分物品数量的多少,具体表示为n列。在本例中 。
二、矩阵分解是什么?
首先我们需要知道,在矩阵乘法的过程中, ,也就意味着我们的打分表格,可以被分解为两个表格甚至多个表格,这些被分解的表格受到某些特征的共同作用,这种特征可以有多个,我们定义为K个。本例中我们取 。矩阵表示m个User和K个特征之间的关系矩阵,矩阵表示n个Item和K个特征之间的关系矩阵。
所以我们将问题进行了转移,把最终预测矩阵 转换为了两个关系矩阵的乘积,即,而损失函数的定义即可转换为(为了后续操作的方便,在此对损失函数的表示形式我们采取平方的方式)
而在对损失函数累加的过程中,原来未打过分的函数值我们无需考虑,我们只需对已打分的项进行误差判断,这一点在逻辑上是成立的。
三、函数过拟合情况下的正则化处理
首先我们来看一张图片,这张图片我截自吴恩达机器学习中对过拟合问题的描述。
在对已知数据进行拟合的过程中,如若已知数据点过少,并且我们初步建立的函数模型过于复杂,很容易出现这样的情况:我们所建立的函数模型完美的经过了所有的数据点,可并不是我们想要的结果,很显然图片上的第三个拟合出来的曲线完全不具有泛用性。所以在参数样本过少的情况下,我们引入正则化这一概念,来对我们拟合的变量进行惩罚。
在我们不知道哪些参数是重要的,哪些参数不重要的前提下,我们对所有的参数都进行一个惩罚,具体表现在损失函数中:
我们对损失函数加上了一个正数( 为正,的作用是方便后续的求导),用 乘以拟合参数的平方和,而系数 的取值我们可以随意定义。假设我们给 赋了一个极大的初值,为了保证损失函数最小,那么拟合参数就必须尽可能的小,从而达到惩罚目的。所以,通过正则项的作用,我们可以保证最终拟合出来的参数不会因为已知数据点过少而产生过拟合的结果。
四、梯度下降求解和
- 首先我们对已经加上正则项的损失函数分别求 和 的偏导:
- 然后根据梯度的方向对变量 和 进行更新:
- 进行迭代,直到损失函数 小于提前规定的阈值。
五、手写推算展示
六、源代码展示
这里作者基于notebook对结果进行展示。
1.导入python库
import numpy as np
from math import pow #主要用于求平方
import matplotlib as plt
import pandas as pd #导入pandas库的作用主要是用于从csv文件中读取数据
2.定义梯度下降函数
def matrix_factorization(R,P,Q,K,steps=5000,alpha=0.0002,Lambda=0.02):
result=[] #将每次迭代的损失函数值存到数组中,便于最后画图分析
for step in range(steps): #最大循环次数
for i in range(len(R)):
for j in range(len(R[i])): #通过两层遍历的嵌套,完成对原矩阵每个元素的的遍历
if R[i][j]>0: #若此数据为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]-Lambda*P[i][k])
Q[k][j]=Q[k][j]+alpha*(2*eij*P[i][k]-Lambda*Q[k][j])
eR=numpy.dot(P,Q) #完成梯度下降算法后,求出当前的预测矩阵,为下面求损失函数做准备
e=0 #e即为损失函数
for i in range(len(R)):
for j in range(len(R[i])):
if R[i][j]>0:
e=e+pow(R[i][j]-eR[i][j],2) #只对已评分的元素求损失函数
result.append(e)
if e<0.001: #若损失函数小于阈值,则跳出for循环,否则继续进行上述过程
break
return P,Q,result
3.定义主函数
if __name__ == '__main__':
R=pd.read_csv("df.csv") #用pandas库读取数据表格,若无csv表格可用代码手动输入
#R=[
# [5,3,0,1,0],
# [4,0,0,1,2],
# [1,1,0,5,0],
# [1,0,0,4,5],
# [0,1,5,4,3],
# [3,0,2,1,0]
# ]
R=numpy.array(R) #将读取出来的数据元素转化为矩阵
M=len(R)
N=len(R[0])
K=2
P=numpy.random.rand(M,K) #随机生成一个 M行 K列的矩阵
Q=numpy.random.rand(K,N) #随机生成一个 K行 N列的矩阵
nP,nQ,result=matrix_factorization(R,P,Q,K)
print("原始的评分矩阵R为:\n",R)
R_MF=numpy.dot(nP,nQ)
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()