一、前言

在开始之前我们先引入例题,User表示用户,Data表示不同物品所获得的打分分数,请补全该表格

                                    

在打分表格中,用户并不见得会对所有项目都进行打分(0表示未打分),那么如何预测并补全打分表格呢,我们引入矩阵分解的概念。

为了变量名的统一,此处我采用吴恩达机器学习中的符号表示,用m表示用户数量的多少,具体表示为m行;n表示待打分物品数量的多少,具体表示为n列。在本例中m=5,n=5。

二、矩阵分解。

       首先我们需要知道,在矩阵乘法的过程中, P(m,n)=P(m,k)*Q(k,n)

也就意味着我们的打分表格,可以被分解为两个表格甚至多个表格,这些被分解的表格受到某些特征的共同作用,这种特征可以有多个,我们定义为K个。本例中我们取k=2 矩阵P(m,K)表示m个User和K个特征之间的关系矩阵,矩阵Q(K,n)表示n个Item和K个特征之间的关系矩阵。

         所以我们将问题进行了转移,把最终预测矩阵 \hat{R} 转换为了两个关系矩阵的乘积,即\hat{R}(m,n)=P(m,k)*Q(k,n),而损失函数的定义即可转换为(为了后续操作的方便,在此对损失函数的表示形式我们采取平方的方式) 

                                      

 三、梯度下降求解。

  • 首先我们对已经加上正则项的损失函数分别求 P_{ik} 和 Q_{kj} 的偏导:

                               \frac {\partial{J^2}}{\partial{P_{ik}}}=-2*J_{ij}*Q_{kj}+\lambda\sum_{k=1}^kP_{ik}

                               \frac {\partial{J^2}}{\partial{Q_{kj}}}=-2*J_{ij}*P_{ik}+\lambda\sum_{k=1}^kQ_{kj}

  • 然后根据梯度的方向对变量 P_{ik} 和 Q_{kj} 进行更新:

P_{ik}=P_{ik}-\alpha*\frac {\partial{J_{ij}^2}}{\partial{P_{ik}}}=P_{ij}+2*\alpha*J_{ij}*Q_{kj}-\lambda*P_{ik}

Q_{kj}=Q_{kj}-\alpha*\frac {\partial{J_{ij}^2}}{\partial{Q_{kj}}}=Q_{kj}+2*\alpha*J_{ij}*P_{ik}-\lambda*Q_{kj}

  • 进行迭代,直到损失函数 J_{ij}^2 小于提前规定的阈值。

四、 源代码。

import numpy as np
from math import pow        #主要用于求平方
import matplotlib as plt
import pandas as pd          #导入pandas库的作用主要是用于从csv文件中读取数据
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
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]
    # ]
 
    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()

 

更多推荐