一、矩陣分解
1.案例
我們都熟知在一些軟件中常常有評分系統,但並不是所有的用戶user人都會對項目item進行評分,因此評分系統所收集到的用戶評分信息必然是不完整的矩陣。那如何跟據這個不完整矩陣中已有的評分來預測未知評分呢。使用矩陣分解的思想很好地解決了這一問題。
假如我們現在有一個用戶-項目的評分矩陣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.首先令:

2。對於式子1的左邊項,表示的是r^ 第i行,第j列的元素值,對於如何衡量,我們分解的好壞呢,式子2,給出了衡量標准,也就是損失函數,平方項損失,最后的目標,就是每一個元素(非缺失值)的e(i,j)的總和最小值

3.使用梯度下降法獲得修正的p和q分量:
- 求解損失函數的負梯度

- 根據負梯度的方向更新變量:

4.不停迭代直到算法最終收斂(直到sum(e^2) <=閾值,即梯度下降結束條件:f(x)的真實值和預測值小於自己設定的閾值)
5.為了防止過擬合,增加正則化項
3.加入正則項的損失函數求解
1.通常在求解的過程中,為了能夠有較好的泛化能力,會在損失函數中加入正則項,以對參數進行約束,加入正則L2范數的損失函數為:

對正則化不清楚的,公式可化為:

2.使用梯度下降法獲得修正的p和q分量:
- 求解損失函數的負梯度:

- 根據負梯度的方向更新變量:

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

二、代碼實現
import numpy as np
import matplotlib.pyplot as plt
def matrix(R, P, Q, K, alpha, beta):
result=[]
steps = 1
while 1 :
#使用梯度下降的一步步的更新P,Q矩陣直至得到最終收斂值
steps = steps + 1
eR = np.dot(P,Q)
e=0
for i in range(len(R)):
for j in range(len(R[i])):
if R[i][j]>0:
# .dot(P,Q) 表示矩陣內積,即Pik和Qkj k由1到k的和eij為真實值和預測值的之間的誤差,
eij=R[i][j]-np.dot(P[i,:],Q[:,j])
#求誤差函數值,我們在下面更新p和q矩陣的時候我們使用的是化簡得到的最簡式,較為簡便,
#但下面我們仍久求誤差函數值這里e求的是每次迭代的誤差函數值,用於繪制誤差函數變化圖
e=e+pow(R[i][j] - np.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))
for k in range(K):
#在更新p,q時我們使用化簡得到了最簡公式
P[i][k]=P[i][k]+alpha*(2*eij*Q[k][j]-beta*P[i][k])
Q[k][j]=Q[k][j]+alpha*(2*eij*P[i][k]-beta*Q[k][j])
print('迭代輪次:', steps, ' e:', e)
result.append(e)#將每一輪更新的損失函數值添加到數組result末尾
#當損失函數小於一定值時,迭代結束
if eij<0.00001:
break
return P,Q,result
R=[
[5,3,1,1,4],
[4,0,0,1,4],
[1,0,0,5,5],
[1,3,0,5,0],
[0,1,5,4,1],
[1,2,3,5,4]
]
R=np.array(R)
alpha = 0.0001 #學習率
beta = 0.002
N = len(R) #表示行數
M = len(R[0]) #表示列數
K = 3 #3個因子
p = np.random.rand(N, K) #隨機生成一個 N行 K列的矩陣
q = np.random.rand(K, M) #隨機生成一個 M行 K列的矩陣
P, Q, result=matrix(R, p, q, K, alpha, beta)
print("矩陣Q為:\n",Q)
print("矩陣P為:\n",P)
print("矩陣R為:\n",R)
MF = np.dot(P,Q)
print("預測矩陣:\n",MF)
#下面代碼可以繪制損失函數的收斂曲線圖
n=len(result)
x=range(n)
plt.plot(x, result,color='b',linewidth=3)
plt.xlabel("generation")
plt.ylabel("loss")
plt.show()
