推薦系統與協同過濾、奇異值分解

原做者:鄧旭東html

原文發於做者我的微信公衆號:大鄧和他的Python(微信號:python

DaDengAndHisPython),點擊查看原文,掘金已得到轉載受權。再次感謝做者。算法


昨天我從Youtube上把PyCon2018和PyData2018兩個會議對本身比較有用的視頻下載下來,昨天分享的《使用pandas作更好的數據科學》來自PyData2018。受到該演講者內容啓發寫了本文。
bash

視頻地址:v.qq.com/x/page/i067…
微信

Daniel Pyrathon - A practical guide to Singular Value Decomposition in Python PyCon2018
奇異值分解Python實操

複製代碼

1、預備知識

1.1 協同過濾

平常生活中,像亞馬遜、淘寶、京東、今日頭條等各大互聯網公司會無時不刻的收集咱們的網絡用戶行爲數據,並根據積累的歷史行爲數據對咱們推送推薦內容或者推薦商品。這就是咱們未曾感覺到存在的推薦算法所起到的做用,這之中比較常見的實現方式是協同過濾(Collaberative Filtering)。數據設計到用戶、產品及產品評價三種信息,數據相似於下圖網絡


1.2 類似的人更容易作類似的事

協同過濾的核心想法是類似的人每每會作類似的事情。好比,A 和 B 是兩個崇尚科技的人(類似信息源於大量的觀影數據),而 B 喜歡 看科幻片 ,那麼咱們猜想 A 也喜歡 科幻片。
機器學習



1.3 問題提出

上面咱們展現的用戶電影可視化圖,實際上就是推薦算法中常常用到的用戶-評價矩陣,ide

  • 那麼咱們如何對矩陣進行計算,才能獲取類似性信息?學習

  • 有了類似性信息咱們又如何去利用類似性信息去作產品推薦?測試

  • 咱們知道兩個向量經過餘弦類似計算就能夠得出兩個向量的近似程度,那麼這些向量咱們又該如何從用戶-評價矩陣提取呢?

1.4 奇異值分解SVD

這就用到奇異值分解(Singular Value Decompositon),簡稱SVD。具體怎麼提取不是咱們本文的重點,Python都幫咱們實現了,咱們只須要稍微瞭解下SVD,就直接上手用。

好比咱們如今有了用戶-評價矩陣


給定一個矩陣,咱們均可以分解獲得兩種矩陣,一種是用戶信息矩陣,一種是評價信息(產品)矩陣。這兩種矩陣在本例中使用了n_features = 2,即對於用戶向量或者產品評價向量長度均爲2,實際上也能夠爲其餘數字(好比3,4。。)


那麼User1對於藍色電影的喜歡程度是能夠經過向量計算得出3.52


1.5 用戶類似性

以下圖,在二維座標中咱們能夠看出不一樣用戶間的類似度。


2、項目實戰

咱們將使用Python的surprise庫,對MovieLens數據集構建一個簡單的協同過濾推薦系統。

安裝方法:

pip3 install scikit-surprise
複製代碼

若是你的anaconda自帶jupyter notebook。那麼你可能須要使用下面的安裝方法

conda install -c conda-forge scikit-surprise
複製代碼

從安裝名咱們發現其他scikit的特殊關係,因此熟悉scikit的同窗看本文會比較輕鬆。

2.1 準備數據

MovieLens數據集含有1000個用戶的100000個觀影評分記錄。其中咱們只須要使用該數據集中的u.data文件,該文件以行存儲,每一行包括userID itemID rating timestamp,且各個字段之間以\t間隔。部分數據以下

['196\t242\t3\t881250949\n', 
'186\t302\t3\t891717742\n', 
'22\t377\t1\t878887116\n', 
'244\t51\t2\t880606923\n', 
'166\t346\t1\t886397596\n']
複製代碼

2.2 切割數據

在surprise庫中咱們能夠建立讀取器Reader的格式。在本例中,咱們使用\t將每行數據分隔後分配給

user item rating timestamp

定義好Reader格式後,咱們使用Dataset對象對數據進行讀取操做。

from surprise import Reader, Dataset

#定義數據格式
reader = Reader(line_format='user item rating timestamp', sep='\t')

#使用reader格式從u.data文件中讀取數據
data = Dataset.load_from_file('u.data', reader=reader)
複製代碼

2.3 交叉檢驗

surprise提供了交叉驗證(crossvalidation)的接口,crossvalidation是啥?

咱們先看圖解釋下


一份數據平均的分紅5份,若是4份作訓練集,1份作測試集。那麼當咱們訓練模型的時候有1/5的數據咱們的模型是沒法學習的,這就浪費了20%。

可是咱們又不能拿把全部的數據通過一次訓練,再拿其中訓練過的數據去作預測。由於這樣會致使準確率a很是高,但放到實踐中這個模型的預測準確率其實是低於a的。

因此就有了crossvalidation交叉檢驗。咱們一份數據訓練5次,每次完整的數據分紅4份訓練1份測試。這樣就解決了上面遇到的問題。以下圖

#n_folds=5是指數據分紅5份,作5次訓練預測
data.split(n_folds=5)
複製代碼

2.4 最優化Optimization

訓練怎麼達到最優,那就要有Optimization,也就是要有一個可供參考的標準。

訓練的方式與其餘機器學習方法相似,要使得一種算法試圖優化其預測值儘量接近真實值。在協做過濾應用中,咱們的算法將嘗試預測某個用戶-電影組合的評級,並將該預測值真實值進行比較。 使用經典偏差測量如均方根偏差(Root mean squared error,RMSE)和平均絕對偏差(Mean absolute error,MAE)來測量預測值和真實值之間的差別。

在surprise庫中,咱們有普遍的算法可供選擇,併爲每種算法(SVD,NMF,KNN)提供多種參數選擇。 就咱們的例子而言,咱們將使用SVD算法。 優化目標measures採用RMSE', 'MAE

from surprise import SVD, evaluate

#至關於scikit的機器學習算法的初始化
svd = SVD()

#至關於scikit中的score,模型評估
evaluate(svd, data, measures=['RMSE', 'MAE'])
複製代碼

運行

Evaluating RMSE, MAE of algorithm SVD.
    ------------
    Fold 1
    RMSE: 0.9324
    MAE:  0.7346
    ------------
    Fold 2
    RMSE: 0.9422
    MAE:  0.7423
    ------------
    Fold 3
    RMSE: 0.9367
    MAE:  0.7398
    ------------
    Fold 4
    RMSE: 0.9310
    MAE:  0.7323
    ------------
    Fold 5
    RMSE: 0.9393
    MAE:  0.7422
    ------------
    ------------
    Mean RMSE: 0.9363
    Mean MAE : 0.7382
    ------------
    ------------
複製代碼

從上面運行結果看,optimizer選用RMSE後,5次訓練的平均準確率高達93.63%。

2.5 預測

最後咱們仍是很想看看訓練出模型,其預測能力到底結果怎麼樣?

此次咱們就作交叉驗證了,省事點直接所有丟給SVD去訓練

from surprise import SVD
from surprise import Reader, Dataset

#讀取數據
reader = Reader(line_format='user item rating timestamp', sep='\t')
data = Dataset.load_from_file('u.data', reader=reader)
data = data.build_full_trainset() 

#初始化svd模型,用data訓練模型
svd =SVD()
svd.fit(data)
複製代碼

上面的代碼

data = data.build_full_trainset()
複製代碼

這一行原本我沒有寫,可是當我註釋掉這一行。出現下面的錯誤,

DatasetAutoFolds' object has no attribute 'global_mean' on python surprise 複製代碼

最後在stackoverflow中找到解決辦法,須要將data轉化爲surprise可以用的trainset類。

https://stackoverflow.com/questions/49263964/datasetautofolds-object-has-no-attribute-global-mean-on-python-surprise
複製代碼

下面繼續咱們的預測,userid爲196,itemid爲302, 其真實評分爲4。

userid = str(196)
itemid = str(302)
actual_rating = 4
print(svd.predict(userid, 302, 4))
複製代碼

運行

user: 196  item: 302   r_ui = 4.00   est = 3.41   {'was_impossible': False}
複製代碼

預測值爲3.41, 真實值爲4。仍是相對靠譜的。

相關文章
相關標籤/搜索