王荣胜

基于矩阵分解的推荐算法

描述

我们都熟知在一些软件中常常有评分系统,但并不是所有的用户user都会对项目item进行评分,因此评分系统所收集到的用户评分信息必然是不完整的矩阵。那如何跟据这个不完整矩阵中已有的评分来预测未知评分呢。使用矩阵分解的思想很好地解决了这一问题。

假如我们现在有一个用户-项目的评分矩阵R(n,m)是nm列的矩阵,n表示user个数,m行表示item个数

i1 i2 i3 i4
u1 5 3 - 1
u2 4 - - 1
u3 1 1 - 5
u4 1 - - 4
u5 - 1 5 4

其中,u1⋯u5表示的是5个不同的用户,I1⋯i4表示的是4个不同的商品,这样便构成了用户-商品矩阵,在该矩阵中,有用户对每一件商品的打分,其中“-”表示的是用户未对该商品进行打分。

在推荐系统中有一类问题是对未打分的商品进行评分的预测。

基于矩阵分解的推荐算法

矩阵分解的一般形式

矩阵分解是指将一个矩阵分解成两个或者多个矩阵的乘积。对于上述的用户-商品矩阵(评分矩阵),记为 Rm×nR_{m\times n} 。可以将其分解成两个或者多个矩阵的乘积,假设分解成两个矩阵 Pm×kP_{m\times k}Qk×nQ_{k\times n} ,我们要使得矩阵Pm×kP_{m\times k}Qk×nQ_{k\times n}的乘积能够还原原始的矩阵Rm×nR_{m\times n}

Rm×nPm×k×Qk×n=R^m×nR_{m\times n}\approx P_{m\times k}\times Q_{k\times n}=\hat{R}_{m\times n}

其中,矩阵 Pm×kP_{m\times k} 表示的是m个用户与k个主题之间的关系,而矩阵Qk×nQ_{k\times n}表示的是kk个主题与nn个商品之间的关系。

利用矩阵分解进行预测

在上述的矩阵分解的过程中,将原始的评分矩阵Rm×nR_{m\times n}分解成两个矩阵Pm×kP_{m\times k}Qk×nQ_{k\times n}的乘积:

Rm×nPm×k×Qk×n=R^m×nR_{m\times n}\approx P_{m\times k}\times Q_{k\times n}=\hat{R}_{m\times n}

那么接下来的问题是如何求解矩阵Pm×kP_{m\times k}Qk×nQ_{k\times n}的每一个元素,可以将这个问题转化成机器学习中的回归问题进行求解。

损失函数

可以使用原始的评分矩阵Rm×nR_{m\times n}与重新构建的评分矩阵R^m×n\hat{R}_{m\times n}之间的误差的平方作为损失函数,即:

ei,j2=(ri,jr^i,j)2=(ri,jk=1Kpi,kqk,j)2e_{i,j}^2=\left ( r_{i,j}-\hat{r}_{i,j} \right )^2=\left (r_{i,j}-\sum_{k=1}^{K}p_{i,k}q_{k,j} \right )^2

最终,需要求解所有的非“-”项的损失之和的最小值:

min  loss=ri,jei,j2min\; loss= \sum_{r_{i,j}\neq -}e_{i,j}^2

损失函数的求解

对于上述的平方损失函数,可以通过梯度下降法求解,梯度下降法的核心步骤是

  • 求解损失函数的负梯度:

pi,kei,j2=2(ri,jk=1Kpi,kqk,j)qk,j=2ei,jqk,j\frac{\partial }{\partial p_{i,k}}e_{i,j}^2=-2\left ( r_{i,j}-\sum_{k=1}^{K}p_{i,k}q_{k,j} \right )q_{k,j}=-2e_{i,j}q_{k,j}

qk,jei,j2=2(ri,jk=1Kpi,kqk,j)pi,k=2ei,jpi,k\frac{\partial }{\partial q_{k,j}}e_{i,j}^2=-2\left ( r_{i,j}-\sum_{k=1}^{K}p_{i,k}q_{k,j} \right )p_{i,k}=-2e_{i,j}p_{i,k}

  • 根据负梯度的方向更新变量:

pi,k=pi,kαpi,kei,j2=pi,k+2αei,jqk,j{p_{i,k}}'=p_{i,k}-\alpha \frac{\partial }{\partial p_{i,k}}e_{i,j}^2=p_{i,k}+2\alpha e_{i,j}q_{k,j}

qk,j=qk,jαqk,jei,j2=qk,j+2αei,jpi,k{q_{k,j}}'=q_{k,j}-\alpha \frac{\partial }{\partial q_{k,j}}e_{i,j}^2=q_{k,j}+2\alpha e_{i,j}p_{i,k}

通过迭代,直到算法最终收敛。

加入正则项的损失函数即求解方法

通常在求解的过程中,为了能够有较好的泛化能力,会在损失函数中加入正则项,以对参数进行约束,加入L2L_2正则的损失函数为:

Ei,j2=(ri,jk=1Kpi,kqk,j)2+β2k=1K(pi,k2+qk,j2)E_{i,j}^2=\left (r_{i,j}-\sum_{k=1}^{K}p_{i,k}q_{k,j} \right )^2+\frac{\beta }{2}\sum_{k=1}^{K}\left ( p_{i,k}^2+q_{k,j}^2 \right )

利用梯度下降法的求解过程为:

  • 求解损失函数的负梯度:

pi,kEi,j2=2(ri,jk=1Kpi,kqk,j)qk,j+βpi,k=2ei,jqk,j+βpi,k\frac{\partial }{\partial p_{i,k}}E_{i,j}^2=-2\left ( r_{i,j}-\sum_{k=1}^{K}p_{i,k}q_{k,j} \right )q_{k,j}+\beta p_{i,k}=-2e_{i,j}q_{k,j}+\beta p_{i,k}

qk,jEi,j2=2(ri,jk=1Kpi,kqk,j)pi,k+βqk,j=2ei,jpi,k+βqk,j\frac{\partial }{\partial q_{k,j}}E_{i,j}^2=-2\left ( r_{i,j}-\sum_{k=1}^{K}p_{i,k}q_{k,j} \right )p_{i,k}+\beta q_{k,j}=-2e_{i,j}p_{i,k}+\beta q_{k,j}

  • 根据负梯度的方向更新变量:

pi,k=pi,kα(pi,kei,j2+βpi,k)=pi,k+α(2ei,jqk,jβpi,k){p_{i,k}}'=p_{i,k}-\alpha \left ( \frac{\partial }{\partial p_{i,k}}e_{i,j}^2+\beta p_{i,k} \right )=p_{i,k}+\alpha \left ( 2e_{i,j}q_{k,j}-\beta p_{i,k} \right )

qk,j=qk,jα(qk,jei,j2+βqk,j)=qk,j+α(2ei,jpi,kβqk,j){q_{k,j}}'=q_{k,j}-\alpha \left ( \frac{\partial }{\partial q_{k,j}}e_{i,j}^2+\beta q_{k,j} \right )=q_{k,j}+\alpha \left ( 2e_{i,j}p_{i,k}-\beta q_{k,j} \right )

通过迭代,直到算法最终收敛。

预测

利用上述的过程,我们可以得到矩阵Pm×kP_{m\times k}Qk×nQ_{k\times n},这样便可以为用户ii对商品jj进行打分:

k=1Kpi,kqk,j\sum_{k=1}^{K}p_{i,k}q_{k,j}

代码实现

import numpy as np
from math import pow
import matplotlib.pyplot as plt
%matplotlib inline

def mf(R,P,Q,K):
    alpha = 0.0002
    beta = 0.02
    times = 5000
    eplison = 0.001
    result = []
    #将矩阵Q进行转置
    Q = Q.T
    #将生成的矩阵相乘
    for time in range(times):
        #求R尖的值
        for i in range(len(R)):
            for j in range(len(R[i])):
                eij = R[i][j] - np.dot(P[i,:],Q[:,j])
                for k in range(K):
                    P[i][k] = P[i][k] + 2*alpha*eij*Q[k][j]
                    Q[k][j] = Q[k][j] + 2*alpha*eij*P[i][k]
        eR = np.dot(P,Q)
        #求eij的平方
        eij_2 = 0
        for i in range(len(R)):
            for j in range(len(R[i])):
                for k in range(K):
                    eij_2 = eij_2 + pow((R[i][j] - np.dot(P[i,:],Q[:,j])),2) + 0.5*beta*(pow(P[i][k],2))+pow(Q[k][j],2)
        result.append(eij_2)
        if eij_2 < eplison:
            break
    return P,Q.T,result 



def main():
    R = [
        [5,3,0,1],
        [4,0,0,1],
        [1,1,0,5],
        [1,0,0,4],
        [0,1,5,4]
    ]
    #将R列表转化为矩阵
    R = np.array(R)
    #获取该矩阵的大小
    N = len(R)
    M = len(R[0])

    print(N)
    print(M)
    ''' 
    如何预测缺失的评分呢?对于缺失的评分,可以转化为基于机器学习的回归问题,也就是连续值的预测,
    对于矩阵分解有如下式子,R是类似图1的评分矩阵,假设N*M维(N表示行数,M表示列数),可以分解为P跟Q矩阵,
    其中P矩阵维度N*K,P矩阵维度K*M。
    对于P,Q矩阵的解释,直观上,P矩阵是N个用户对K个主题的关系,Q矩阵是K个主题跟M个物品的关系,至于K个
    主题具体是什么,在算法里面K是一个参数,需要调节的,通常10~100之间。
    '''
    K = 2
    P = np.random.rand(N,K)
    Q = np.random.rand(M,K)
    #调用矩阵分解的函数
    mf_P,mf_Q,result = mf(R,P,Q,K)
    print("原始矩阵:",R)
    mf_R = np.dot(mf_P,mf_Q.T)
    print("经过MF后:",mf_R)
    #做出图像
    for t in range(5000):
        plt.scatter(t,result[t])
    plt.show()
main()
小白
本文作者:王荣胜 |「邮箱 」| 「QQ」 | 「QQ群
文章出处:https://sqdxwz.top/
版权声明:本文章采用「知识共享署名-相同方式共享 4.0 国际许可协议」许可。
Powered by 王荣胜 | | 载入天数...载入时分秒...
Google     Server provider     jsDelivr    
正在加载今日诗词....