矩阵分解(MF)-推荐系统+python代码

    科技2022-07-13  114

    前言

    推荐系统中最为主流与经典的技术之一是协同过滤技术(Collaborative Filtering), 它是基于这样的假设:用户如果在过去对某些项目产生过兴趣,那么将来他很可能依然对其保持热忱。 目前推荐系统中用的最多的就是矩阵分解(Matrix Factorization)方法。 矩阵分解就是预测出评分矩阵中的缺失值,然后根据预测值以某种方式向用户推荐。 矩阵分解可以解决一些近邻模型无法解决的问题,近邻模型存在的问题: 1、物品之间存在相关性,信息量并不是随着向量维度增加而线性增加 2、矩阵元素稀疏,计算结果不稳定,增减一个向量维度,导致紧邻结果差异很大的情况出现。 这里以用户-项目评分为例来详解矩阵分解。

    一、矩阵分解

    1.案例

    用户-项目评分矩阵R(M×N) 打分矩阵R(n,m)是n行m列,n表示user个数,m表示item个数,‘-’表示用户没有打分

    2.思路

    矩阵分解就是把大矩阵分解成两个小矩阵, 即矩阵R可近似表示为矩阵P与矩阵Q的乘积: R n × m ≈ P n × k × Q k × m = R ^ n × m R_{n×m}\approx P_{n×k} × Q_{k×m} = \hat R_{n×m} Rn×mPn×k×Qk×m=R^n×m 矩阵P(n,k)表示n个user和K个特征之间的关系矩阵,将矩阵Q(m,k)表示m个item和K个特征之间的关系矩阵, 接下来就是要求的矩阵P和Q,即可的预测矩阵R

    3.推导步骤

    首先: r ^ i j = ∑ k = 1 K p i k q k j \hat r_{ij}=\sum^K_{k=1}p_{ik}q_{kj} r^ij=k=1Kpikqkj损失函数: e i j 2 = ( r i j − r ^ i j ) 2 = ( r i j − ∑ k = 1 k p i k q k j ) 2 e^2_{ij}=(r_{ij}-\hat r_{ij})^2=(r_{ij}-\sum^k_{k=1}p_{ik}q_{kj})^2 eij2=(rijr^ij)2=(rijk=1kpikqkj)2对损失函数加入正则项: 为了防止过拟合 加入正则L2范数的损失函数为: E i j 2 = ( r i j − r ^ i j ) 2 = ( r i j − ∑ k = 1 K p i k q k j ) 2 + β 2 ∑ k = 1 K ( p i k 2 + q k j 2 ) E^2_{ij}=(r_{ij}-\hat r_{ij})^2=(r_{ij}-\sum^K_{k=1}p_{ik}q_{kj})^2+\frac{\beta}{2}\sum^K_{k=1}(p^2_{ik}+q^2_{kj}) Eij2=(rijr^ij)2=(rijk=1Kpikqkj)2+2βk=1K(pik2+qkj2)使用梯度下降法更新p和q分量: 求解损失函数的负梯度:

    ∂ ∂ p i k E i j 2 = − 2 ( r i j − ∑ k = 1 K p i k q k j ) q k j + β p i k = − 2 e i j q k j + β p i k \frac{\partial}{\partial p_{ik}}E^2_{ij}=-2(r_{ij}-\sum^K_{k=1}p_{ik}q_{kj})q_{kj}+\beta p_{ik}=-2e_{ij}q_{kj}+\beta p_{ik} pikEij2=2(rijk=1Kpikqkj)qkj+βpik=2eijqkj+βpik

    ∂ ∂ p i k E i j 2 = − 2 ( r i j − ∑ k = 1 K p i k q k j ) q k j + β p i k = − 2 e i j q k j + β p k j \frac{\partial}{\partial p_{ik}}E^2_{ij}=-2(r_{ij}-\sum^K_{k=1}p_{ik}q_{kj})q_{kj}+\beta p_{ik}=-2e_{ij}q_{kj}+\beta p_{kj} pikEij2=2(rijk=1Kpikqkj)qkj+βpik=2eijqkj+βpkj

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

    p i k ′ = p i k − α ( ∂ ∂ p i k E i j 2 ) = p i k + α ( 2 e i j q k j − β p i k ) p^\prime_{ik}=p_{ik}-\alpha(\frac{\partial}{\partial p_{ik}}E^2_{ij})=p_{ik}+\alpha(2e_{ij}q_{kj}-\beta p_{ik}) pik=pikα(pikEij2)=pik+α(2eijqkjβpik)

    p k j ′ = p k j − α ( ∂ ∂ p k j E i j 2 ) = p k j + α ( 2 e i j q k j − β p k j ) p^\prime_{kj}=p_{kj}-\alpha(\frac{\partial}{\partial p_{kj}}E^2_{ij})=p_{kj}+\alpha(2e_{ij}q_{kj}-\beta p_{kj}) pkj=pkjα(pkjEij2)=pkj+α(2eijqkjβpkj)

    不停迭代,直到损失函数<=阈值;对结果的输出: 输出得到的预测矩阵 R ^ \hat R R^; 可对预测矩阵可视化,或转成表格也行; 可输出个学习效率图像。

    二、python代码实例

    from math import pow import numpy as np import matplotlib.pyplot as plt def MF(R,P,Q,K,steps=5000,alpha=0.0002,beta=0.02): #矩阵因子分解函数, #steps:迭代次数,alpha:步长 n,m = R.shape #R矩阵行,列 result = [] for step in range(steps): #进行迭代 for i in range(n): for j in range(m): eij = R[i][j] - np.dot(P[i,:],Q[:,j]) #.dot表矩阵乘 for k in range(K): #更新参数 if R[i][j] != 0: #限制评分不为0,即预测评分 P[i][k] += alpha*(2*eij*Q[k][j] - beta*P[i][k]) Q[k][j] += alpha*(2*eij*P[i][k] - beta*Q[k][j]) eR = np.dot(P,Q) e = 0 #误差 #求损失函数: for i in range(n): for j in range(m): if R[i][j] != 0: e += pow(R[i][j]-np.dot(P[i,:],Q[:,j]),2) #加入正则化 for k in range(K): e += (beta/2)*(pow(P[i][k],2) + pow(Q[k][j],2)) result.append(e) if e <0.001: break return P,Q,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 = np.array(R) N,M = R.shape #获取原矩阵的行列 K = 3 #k值可根据需求改变 P = np.random.rand(N,K) Q = np.random.rand(K,M) nP,nQ,result = MF(R, P, Q, K) print(R) R_MF = np.dot(nP,nQ) print(R_MF) plt.plot(range(len(result)),result) #学习效率 plt.show()

    输出:

    [[5 3 0 1] [4 0 0 1] [1 1 0 5] [1 0 0 4] [0 1 5 4]] [[4.98721783 2.95625121 3.57560418 1.00024006] [3.9709488 2.24242023 3.11559738 0.99765797] [1.04748953 0.88624237 5.33748545 4.96558301] [0.97508183 0.74294582 4.37299881 3.97589923] [1.84618381 1.13400265 4.94201315 4.01873275]]

    Processed: 0.009, SQL: 8