矩陣分解在協同過濾推薦算法中的應用


一般在推薦系統中,數據往往是使用 用戶-物品 矩陣來表示的。用戶對其接觸過的物品進行評分,評分表示了用戶對於物品的喜愛程度,分數越高,表示用戶越喜歡這個物品。而這個矩陣往往是稀疏的,空白項是用戶還未接觸到的物品,推薦系統的任務則是選擇其中的部分物品推薦給用戶。

(markdown寫表格太麻煩了,直接上傳圖片吧)

對於這個 用戶-物品 矩陣,可以利用非空項的數據來預測空白項的數據,即預測用戶對於其未接觸到的物品的評分,並根據預測情況,將評分高的物品推薦給用戶。預測評分的方式有很多,本篇主要講述如何使用矩陣分解來進行這個預測。

1.奇異值分解SVD

想詳細理解SVD,推薦一篇博客 奇異值分解(SVD)原理與在降維中的應用
此時可以將這個 用戶-物品 對應的m×n矩陣 M 進行 SVD 分解,並通過選擇部分較大的一些奇異值來同時進行降維,也就是說矩陣M此時分解為:
(不知道如何在 markdown 中輸入公式,先用圖片代替)
其中,m 是用戶的維度,n 是物品的維度,k 是矩陣 M 的較大的 k 個奇異值,k 往往遠小於 m 和 n,這也是 SVD 可以用來降維的原因。
如果我們要預測第 i 個用戶對第 j 個物品的評分 mij ,則只需要計算 即可。通過這種方法,我們可以將評分表里面所有沒有評分的位置得到一個預測評分。通過找到最高的若干個評分對應的物品推薦給用戶。
在 Python 的 numpy 中,linalg已經實現了SVD,可以直接調用。

>>> import numpy as np
>>> A=np.mat([[1,2,3],[4,5,6]])  
>>> from numpy import linalg as la  
>>> U,sigma,VT=la.svd(A)  
>>> U  
matrix([[-0.3863177 , -0.92236578],  
        [-0.92236578,  0.3863177 ]])  
>>> sigma  
array([ 9.508032  ,  0.77286964])  
>>> VT  
matrix([[-0.42866713, -0.56630692, -0.7039467 ],  
        [ 0.80596391,  0.11238241, -0.58119908],  
        [ 0.40824829, -0.81649658,  0.40824829]])  

有一點需要注意,sigma 本來應該是一個 2*2 的對角方陣,但 linalg.svd() 只返回了一個行向量的 sigma。之所以這樣做,是因為當 M 是非常大的矩陣時,只返回奇異值可以節省很大的存儲空間。當然,如果我們要重構 M,就必須先將 sigma 轉化為矩陣。

可以看出這種方法簡單直接,似乎很有吸引力。但是有一個很大的問題我們忽略了,就是SVD分解要求矩陣是稠密的,也就是說矩陣的所有位置不能有空白。有空白時我們的M是沒法直接去SVD分解的。大家會說,如果這個矩陣是稠密的,那不就是說我們都已經找到所有用戶物品的評分了嘛,那還要SVD干嘛! 的確,這是一個問題,傳統SVD采用的方法是對評分矩陣中的缺失值進行簡單的補全,比如用全局平均值或者用用戶物品平均值補全,得到補全后的矩陣。接着可以用SVD分解並降維。

雖然有了上面的補全策略,我們的傳統SVD在推薦算法上還是較難使用。因為我們的用戶數和物品一般都是超級大,隨便就成千上萬了。這么大一個矩陣做SVD分解是非常耗時的。

2.改進的SVD

參考矩陣分解在協同過濾推薦算法中的應用-劉建平Pinard的博客,其中介紹了 3 種改進的 SVD 算法,分別是:FunkSVD,BiasSVD,SVD++。

FunkSVD

在Spark MLlib中,推薦算法只實現了基於矩陣分解的協同過濾推薦算法。其中,矩陣分解算法使用的是 FunkSVD 算法。不同於傳統SVD,FunkSVD 將 用戶-項目 矩陣 M 分解為兩個低維的矩陣,而不是三個矩陣:

相關代碼如下,這使用 movielens 數據集:

from pyspark import SparkContext
from pyspark import SparkConf

sc = SparkContext("local", "testing")
# 打開用戶評分文件,此文件是用戶對項目的評分,文件每一行前3項分別是用戶id,物品id,評分
>>> user_data = sc.textFile("D:/movielens/ml-100k/u.data")

# 查看第一行數據
>>> user_data.first()
'196\t242\t3\t881250949'

# 獲取相關數據,即前3列
>>> rates = user_data.map(lambda x: x.split("\t")[0:3])
>>> print (rates.first())
['196', '242', '3']

# 將數據封裝成 Rating 類
>>> from pyspark.mllib.recommendation import Rating
>>> rates_data = rates.map(lambda x: Rating(int(x[0]),int(x[1]),int(x[2]))) # 將字符串轉為整型
>>> print (rates_data.first())
Rating(user=196, product=242, rating=3.0)

# 訓練模型
>>> from  pyspark.mllib.recommendation import ALS
>>> from pyspark.mllib.recommendation import MatrixFactorizationModel
>>> sc.setCheckpointDir('checkpoint/')
>>> ALS.checkpointInterval = 2
>>> model = ALS.train(ratings=rates_data, rank=20, iterations=5, lambda_=0.02)
18/02/27 21:01:30 WARN BLAS: Failed to load implementation from: com.github.fommil.netlib.NativeSystemBLAS
18/02/27 21:01:30 WARN BLAS: Failed to load implementation from: com.github.fommil.netlib.NativeRefBLAS
18/02/27 21:01:31 WARN LAPACK: Failed to load implementation from: com.github.fommil.netlib.NativeSystemLAPACK
18/02/27 21:01:31 WARN LAPACK: Failed to load implementation from: com.github.fommil.netlib.NativeRefLAPACK

# 預測用戶38對物品20的評分
>>> print (model.predict(38,20))
2.8397113617489707

# 預測用戶38最喜歡的10個物品
>>> print (model.recommendProducts(38,10))
[Rating(user=38, product=574, rating=7.962267822839763), Rating(user=38, product=555, rating=6.729462263620687), Rating(user=38, product=143, rating=6.585357510430526), Rating(user=38, product=1278, rating=6.543555968120091), Rating(user=38, product=843, rating=6.47086091691414), Rating(user=38, product=1425, rating=6.452495383671975), Rating(user=38, product=905, rating=6.143432962038866), Rating(user=38, product=812, rating=6.092267908277885), Rating(user=38, product=682, rating=6.045279001502015), Rating(user=38, product=562, rating=5.956117491161081)]

3.最小交替二乘

Spark MLlib 提供了 最小交替二乘 的實現,即 ALS 算法,可以參見 Spark 實踐——基於 Spark MLlib 和 YFCC 100M 數據集的景點推薦系統Spark 實踐——音樂推薦和 Audioscrobbler 數據集


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM