Netflix Prize 矩陣分解(Matrix factorization)預測用戶評分

Python實現 Netflix Prize 矩陣分解(Matrix factorization)預測用戶評分

筆者使用Python實現用於 Netflix Prize的矩陣分解預測模型,
Github鏈接:https://github.com/SJTUzhou/NetflixPrizeMatrixFactorization
參考文獻:https://datajobs.com/data-science-repo/Recommender-Systems-[Netflix].pdf
以下詳細說明。

1. 背景

Netflix Prize 是奈飛公司於2006年開始舉行的一場比賽,旨在提高對用戶的影視劇評分的預測準確率。

Netflix 提供不同用戶對不同電影的評分用於參賽者的模型訓練,提交模型的評判標準基於對給定的用戶和電影的評分預測的均方根誤差(Root Mean Square Error)。
RMSE=i=1N(yi^yi)2NRMSE = \sqrt{\frac{\sum_{i=1}^{N}(\hat{y_i}-y_i)^2}{N}}
NN爲需要進行預測的數量,yiy_i爲用戶的實際評分(yi{1,2,3,4,5}y_i \in \{1,2,3,4,5\}),yi^\hat{y_i}爲預測評分(雖然Netflix允許提交小數預測,比如3.4,但是在官方進行RMSE計算時,會對這一預測進行四捨五入取整)。

直接採用電影評分平均分作爲預測的模型,在測試集上得到的 RMSERMSE 約爲1.0540
Netflix原來的預測模型Cinematch,在測試集上得到的 RMSERMSE 約爲0.9514;若想得到Netflix Prize的大獎,需要達到將0.9514這一準確率再提升10%的要求,即0.8567左右。在2009年,已有參賽者達到這一要求。

詳細簡介可以瀏覽維基百科:https://en.wikipedia.org/wiki/Netflix_Prize
最終官方排名網址:https://www.netflixprize.com/leaderboard.html

2. Netflix Prize數據集

Netflix Prize數據集下載地址:https://www.kaggle.com/netflix-inc/netflix-prize-data
數據集文件夾中有README,官方寫得很詳細,請仔細閱讀。
數據集中共有100,480,570條評分,這是480,189名用戶對17,770部電影的評分。

Netflix提供了 training data, probe data 和 qualifying data,其中training data用於訓練模型,probe data實際爲training data中的一部分,用於測試模型準確率,qualifying data用於對提交的算法進行準確率比較,因此只有大賽評審團知道其中的用戶評分(rating),在訓練時不需要qualifying data這個數據集。

數據集中的每一條數據都可以認爲是一個四元組,
即(movieID, userID, 用戶評分Rating, 評分時間Timestamp)。
值得注意的是movieID爲 1 ~ 17,770的整數,userID爲不連續的整數,且不從0開始。

筆者在實際訓練中採用交叉驗證(Cross Validation)的方式,對training data中的每一部電影的用戶評分進行隨機劃分,分別用於測試和訓練,因此沒有用到probe data,得到的結果與官網測試的結果不具有可比性。
關於什麼是交叉驗證:https://blog.csdn.net/lhx878619717/article/details/49079785

3. 矩陣分解 Matrix Factorization

在預測第 ii 個用戶對第 jj 部電影的評分過程中,矩陣分解的基本思路是把這個用戶(user_i)看作一個n維的列向量(uiRn\mathbf {u_i} \in R^n),同理,把這部電影(movie_j)也看作是一個n維的列向量(mjRn\mathbf {m_j} \in R^n),預測評分由 y^ij=mjTui\hat {y}_{ij} = \mathbf {m_j} ^T\mathbf {u_i} 給出。 這裏是矩陣相乘,所以 mjTui=uiTmj\mathbf {m_{j}} ^T\mathbf {u_{i}} = \mathbf {u_{i}} ^T\mathbf {m_{j}},相當於把這兩個向量對應元素相乘然後求和得到預測評分。

關於用戶向量 ui\mathbf {u_{i}},電影向量 mj\mathbf {m_{j}} 具體代表什麼,我們可以這樣理解。比如,這兩個向量的維度都是10,mj\mathbf {m_{j}} 的第一個元素可能代表着這部電影是否是動作電影,數值爲正意味着是,爲負意味着不是,大小代表動作的激烈程度;ui\mathbf {u_{i}} 的第一個元素可能代表着這個用戶是否喜歡動作電影,數值爲正意味着喜歡,爲負意味着不喜歡,大小代表喜好程度。這樣,一個喜歡動作電影的用戶在給動作電影評分時偏向於給出正面評價,對應元素相乘爲正;反之,不喜歡動作電影的用戶對這部動作電影評分可能偏低,對應元素相乘爲負。以此類推,向量維度爲10,特徵數量爲10,相當於從10個方面刻畫電影的對應程度和用戶對着10個方面的偏好程度。當然,以上只是一種解釋方法。

考慮到每部電影有自己的平均分,每個用戶也有自己的打分偏好,比如張三給看過的電影打分偏高,李四給看過的電影打分偏低,這些都是偏差量(bias),因此我們在預測的時候可以採取以下策略:
y^ij=mjTui+mjavg+uioffset\hat {y}_{ij} = \mathbf {m}_j ^T\mathbf {u}_i+m_j^{avg}+u_i^{offset} 其中 uioffset=uiavgyglobalavgu_i^{offset} = u_i^{avg}-y_{global}^{avg}uiavgu_i^{avg} 是用戶 ii 所打分的平均分,yglobalavgy_{global}^{avg} 是數據集中所有評分的總平均分。

因此,使用矩陣分解進行預測的模型,所需訓練的實際是用戶特徵矩陣 Un×Nuser\mathbf {U_{n \times N_{user}}}和電影特徵矩陣 Mn×Nmovie\mathbf {M_{n \times N_{movie}}},其中 nn 是特徵數量,NmovieN_{movie}NuserN_{user} 是數據集中的電影總數和用戶總數。

通過添加L2正則化係數λ\lambda,我們可以更好的優化模型,最優矩陣Un×Nuser\mathbf {U_{n \times N_{user}}}Mn×Nmovie\mathbf {M_{n \times N_{movie}}},可以通過將以下損失函數函數取最小值得到,即:
L=(i,j)K(yijmjTuimjavguioffset)2+λ(mj2+ui2+mjavg2+uioffset2)L = \sum_{(i,j) \in K} (y_{ij}-\mathbf {m}_j ^T\mathbf {u}_i-m_j^{avg}-u_i^{offset})^2 + \lambda (\| \mathbf {m}_j \| ^2 + \| \mathbf {u}_i \| ^2+{m_j^{avg}}^2+{u_i^{offset}}^2)

Mn×Nmovie,Un×Nuser=argmin(L)\mathbf {M_{n \times N_{movie}}}, \mathbf {U_{n \times N_{user}}} = argmin (L)

對於這兩個需要訓練的特徵矩陣,可以採用交替最小二乘法(Alternating least squares)或隨機梯度下降(Stochastic gradient descent)進行優化,以使模型獲得更小的均方根誤差(RMSE)。

這兩種方法的數學原理這裏不作贅述。
交替最小二乘法(Alternating least squares)的數學原理:https://flashgene.com/archives/55882.html
隨機梯度下降(Stochastic gradient descent)的數學原理:https://www.zhihu.com/question/264189719

4.具體實現(Python)

筆者Github鏈接:https://github.com/SJTUzhou/NetflixPrizeMatrixFactorization

4.1 數據預處理

因爲Netflix給出的數據是文本文件,數據格式如下所示:
Netflix數據格式
1:” 指的是電影ID爲1,接下來是不同用戶的評分,每一行裏分別是用戶ID,評分和評分時間。

在這裏我不考慮評分時間對評分帶來的影響,我需要的是一個列數爲3的矩陣作爲數據集,第1列是電影ID,第2列是用戶ID,第3列是評分;從而可以用numpy進行相關運算。實際上這個數據集,筆者按照電影ID順序排列。

movieID爲 1 ~ 17,770的整數,userID爲不連續的整數,且不從0開始。考慮到編程中的索引從0開始,所以筆者的實際訓練計算中 movie_index 爲 0 ~ 17,769 的整數而 user_index 是將實際userID從小到大依次映射到 0 ~ 480188 的整數上,這樣可以方便numpy運算。因此,在處理數據中保存一個user_ids.npy的numpy二進制文件。

處理完數據,我們進行數據集劃分。筆者按照一個比例,比如0.1,來劃分,即對一部電影的所有評分,隨機90%作爲訓練集,剩餘10%作爲測試集。

預處理數據及訓練集測試集劃分的相關代碼:prepare_data.py
如果採用交替最小二乘法(Alternating least squares)作爲優化算法,需要額外獲得按用戶ID順序重新排列的數據集,相關代碼:ALS_extra_data.py
保存的文件名及一些常量:const.py

4.2 使用隨機梯度下降(SGD)訓練模型並評估

尋找最優超參數:特徵維度 nn 和 L2 正則化係數 λ\lambda
n{5,10,50}n \in \{ 5,10,50 \}
λ{0,0.01,0.02,0.03,0.04,0.05,0.06,0.07,0.08,0.09,0.1,0.15,0.2,0.4}\lambda \in \{0,0.01,0.02,0.03,0.04,0.05,0.06,0.07,0.08,0.09,0.1,0.15,0.2,0.4 \}
相關代碼:matrix_stoc_grad_desc.py

4.3 使用交替最小二乘法(ALS)訓練模型並評估

尋找最優超參數:特徵維度 nn 和 L2 正則化係數 λ\lambda
n{5,10,20,50,100}n \in \{ 5,10,20,50,100 \}
λ{0,0.01,0.02,0.03,0.04,0.05,0.06,0.07,0.08,0.09,0.1,0.15,0.2,0.4}\lambda \in \{0,0.01,0.02,0.03,0.04,0.05,0.06,0.07,0.08,0.09,0.1,0.15,0.2,0.4 \}
相關代碼:ALS_matrix.py

4.4 繪製結果

相關代碼:graphique.py

不同超參數對RMSE的影響
在這裏插入圖片描述
部分訓練效果
在這裏插入圖片描述

5. 結語

  1. 使用隨機梯度下降算法(SGD)訓練時,沒有對數據集整體進行隨機打亂,可能導致效果偏差。
  2. 交替最小二乘法(ALS)在訓練速度和收斂速度上優於隨機梯度下降算法(SGD),主要是因爲在這個問題環境下,交替最小二乘法(ALS)可以並行計算。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章