原作者:鄧旭東
原文發於作者個人微信公眾號:大鄧和他的Python(微訊號:
DaDengAndHisPython),點選檢視原文,掘金已獲得轉載授權。再次感謝作者。
昨天我從Youtube上把PyCon2018和PyData2018兩個會議對自己比較有用的視訊下載下來,昨天分享的《使用pandas做更好的資料科學》來自PyData2018。受到該演講者內容啟發寫了本文。
Daniel Pyrathon - A practical guide to Singular Value Decomposition in Python PyCon2018
奇異值分解Python實操
複製程式碼
一、預備知識
1.1 協同過濾
日常生活中,像亞馬遜、淘寶、京東、今日頭條等各大網際網路公司會無時不刻的收集我們的網路使用者行為資料,並根據積累的歷史行為資料對我們推送推薦內容或者推薦商品。這就是我們不曾感受到存在的推薦演算法所起到的作用,這之中比較常見的實現方式是協同過濾(Collaberative Filtering)。資料設計到使用者、產品及產品評價三種資訊,資料類似於下圖
1.2 相似的人更容易做相似的事
協同過濾的核心想法是相似的人往往會做相似的事情。比如,A 和 B 是兩個崇尚科技的人(相似資訊源於大量的觀影資料),而 B 喜歡 看科幻片 ,那麼我們猜測 A 也喜歡 科幻片。
1.3 問題提出
上面我們展示的使用者電影視覺化圖,實際上就是推薦演算法中經常用到的使用者-評價矩陣,
-
那麼我們如何對矩陣進行計算,才能獲取相似性資訊?
-
有了相似性資訊我們又如何去利用相似性資訊去做產品推薦?
-
我們知道兩個向量通過餘弦相似計算就可以得出兩個向量的近似程度,那麼這些向量我們又該如何從使用者-評價矩陣提取呢?
1.4 奇異值分解SVD
這就用到奇異值分解(Singular Value Decompositon),簡稱SVD。具體怎麼提取不是我們本文的重點,Python都幫我們實現了,我們只需要稍微瞭解下SVD,就直接上手用。
比如我們現在有了使用者-評價矩陣
給定一個矩陣,我們都可以分解得到兩種矩陣,一種是使用者資訊矩陣,一種是評價資訊(產品)矩陣。這兩種矩陣在本例中使用了n_features = 2,即對於使用者向量或者產品評價向量長度均為2,實際上也可以為其他數字(比如3,4。。)
那麼User1對於藍色電影的喜歡程度是可以通過向量計算得出3.52
1.5 使用者相似性
如下圖,在二維座標中我們可以看出不同使用者間的相似度。
二、專案實戰
我們將使用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
,且各個欄位之間以
間隔。部分資料如下
[`196 242 3 881250949
`,
`186 302 3 891717742
`,
`22 377 1 878887116
`,
`244 51 2 880606923
`,
`166 346 1 886397596
`]
複製程式碼
2.2 切割資料
在surprise庫中我們可以建立讀取器Reader的格式。在本例中,我們使用
將每行資料分隔後分配給
user item rating timestamp
定義好Reader格式後,我們使用Dataset物件對資料進行讀取操作。
from surprise import Reader, Dataset
#定義資料格式
reader = Reader(line_format=`user item rating timestamp`, sep=` `)
#使用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=` `)
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。還是相對靠譜的。