推薦系統——MF及其python實現

前言

目前推薦系統中用的最多的就是矩陣分解方法,在Netflix Prize推薦系統大賽中取得突出效果。以用戶-項目評分矩陣爲例,矩陣分解就是預測出評分矩陣中的缺失值,然後根據預測值以某種方式向用戶推薦。今天以“用戶-項目評分矩陣R(M×N)”說明矩陣分解方式的原理以及python實現。

一、矩陣分解

1.案例引入

有如下R(5,4)的打分矩陣:(“-”表示用戶沒有打分)

其中打分矩陣R(n,m)是n行和m列,n表示user個數,m行表示item個數

那麼,如何根據目前的矩陣R(5,4)如何對未打分的商品進行評分的預測(如何得到分值爲0的用戶的打分值)?

——矩陣分解的思想可以解決這個問題,其實這種思想可以看作是有監督的機器學習問題(迴歸問題)。

矩陣分解的過程中,,矩陣R可以近似表示爲矩陣P與矩陣Q的乘積:

矩陣P(n,k)表示n個user和k個特徵之間的關係矩陣,這k個特徵是一箇中間變量,矩陣Q(k,m)的轉置是矩陣Q(m,k),矩陣Q(m,k)表示m個item和K個特徵之間的關係矩陣,這裏的k值是自己控制的,可以使用交叉驗證的方法獲得最佳的k值。爲了得到近似的R(n,m),必須求出矩陣P和Q,如何求它們呢?

2.推導步驟

  1. 首先令:
    式子1
  2. 對於式子1的左邊項,表示的是r^ 第i行,第j列的元素值,對於如何衡量,我們分解的好壞呢,式子2,給出了衡量標準,也就是損失函數,平方項損失,最後的目標,就是每一個元素(非缺失值)的e(i,j)的總和最小值
    式子2
  3. 使用梯度下降法獲得修正的p和q分量:
  • 求解損失函數的負梯度
  • 根據負梯度的方向更新變量
  1. 不停迭代直到算法最終收斂(直到sum(e^2) <=閾值,即梯度下降結束條件:f(x)的真實值和預測值小於自己設定的閾值)

  2. 爲了防止過擬合,增加正則化項

3.加入正則項的損失函數求解

  1. 通常在求解的過程中,爲了能夠有較好的泛化能力,會在損失函數中加入正則項,以對參數進行約束,加入正則L2範數的損失函數爲:

    對正則化不清楚的,公式可化爲:
  2. 使用梯度下降法獲得修正的p和q分量:
    -求解損失函數的負梯度
  • 根據負梯度的方向更新變量

4.預測

預測利用上述的過程,我們可以得到矩陣和,這樣便可以爲用戶 i 對商品 j 進行打分:

二、python代碼實現

以下是根據上文的評分例子做的一個矩陣分解算法,並且附有代碼詳解。

from math import *
import numpy
import matplotlib.pyplot as plt

def matrix_factorization(R,P,Q,K,steps=5000,alpha=0.0002,beta=0.02): #矩陣因子分解函數,steps:梯度下降次數;alpha:步長;beta:β。
    Q=Q.T                 # .T操作表示矩陣的轉置
    result=[]
    for step in range(steps): #梯度下降
        for i in range(len(R)):
            for j in range(len(R[i])):
                    eij=R[i][j]-numpy.dot(P[i,:],Q[:,j])       # .DOT表示矩陣相乘
                    for k in range(K):
                      if R[i][j]>0:        #限制評分大於零
                        P[i][k]=P[i][k]+alpha*(2*eij*Q[k][j]-beta*P[i][k])   #增加正則化,並對損失函數求導,然後更新變量P
                        Q[k][j]=Q[k][j]+alpha*(2*eij*P[i][k]-beta*Q[k][j])   #增加正則化,並對損失函數求導,然後更新變量Q
        eR=numpy.dot(P,Q)  
        e=0
        for i in range(len(R)):
            for j in range(len(R[i])):
              if R[i][j]>0:
                    e=e+pow(R[i][j]-numpy.dot(P[i,:],Q[:,j]),2)      #損失函數求和
                    for k in range(K):
                        e=e+(beta/2)*(pow(P[i][k],2)+pow(Q[k][j],2)) #加入正則化後的損失函數求和
        result.append(e)
        if e<0.001:           #判斷是否收斂,0.001爲閾值
            break
    return P,Q.T,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=numpy.array(R)
    N=len(R)    #原矩陣R的行數
    M=len(R[0]) #原矩陣R的列數
    K=3    #K值可根據需求改變
    P=numpy.random.rand(N,K) #隨機生成一個 N行 K列的矩陣
    Q=numpy.random.rand(M,K) #隨機生成一個 M行 K列的矩陣
    nP,nQ,result=matrix_factorization(R,P,Q,K)
    print(R)         #輸出原矩陣
    R_MF=numpy.dot(nP,nQ.T)
    print(R_MF)      #輸出新矩陣
    #畫圖
    plt.plot(range(len(result)),result)
    plt.xlabel("time")
    plt.ylabel("loss")
    plt.show()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章