前面文章SVD原理及推導已經把SVD的過程講的很清楚了,本文介紹如何將SVD應用於推薦系統中的評分預測問題。其實也就是復現Koren在NetFlix大賽中的使用到的SVD算法以及其擴展出的RSVD、SVD++。git
記得剛接觸SVD是在大二,那會兒跟師兄在作項目的時候就用到這個東西,而後到大三下學期恰好百度舉辦了一個電影推薦算法大賽,躍躍欲試地參加了,當時就用的SVD並且只會用這個,後來以爲效果還不錯,接着就又找來了Koren的論文,看了一下把SVD++也實現了,把二者結果融合獲得很多的提高。下面是最終比賽結果:github
其實這個最終結果是和第11名組隊後融合了他的結果,因此成績是同樣的。可是單純地使用SVD、SVD++融合獲得的結果最好的也能到0.606左右,這個結果已經很牛逼了,記得當時0.6111在榜上維持了兩個多星期的No.1。當時的遺憾就是沒能實現更多的SVD擴展算法,致使融合的結果無法再提高,代碼也因爲時間倉促寫的很臃腫,前段時間有空把算法重寫了一下並實現了更多的擴展算法,已經共享到github上了,有興趣的能夠看看:https://github.com/jingchenUSTC/SVDRecommenderSystem算法
下面開始介紹SVD算法,假設存在如下user和item的數據矩陣:數據結構
這是一個極其稀疏的矩陣,這裏把這個評分矩陣記爲R,其中的元素表示user對item的打分,「?」表示未知的,也就是要你去預測的,如今問題來了:如何去預測未知的評分值呢?上一篇文章用SVD證實了對任意一個矩陣A,都有它的滿秩分解:函數
那麼剛纔的評分矩陣R也存在這樣一個分解,因此能夠用兩個矩陣P和Q的乘積來表示評分矩陣R:post
上圖中的U表示用戶數,I表示商品數。而後就是利用R中的已知評分訓練P和Q使得P和Q相乘的結果最好地擬合已知的評分,那麼未知的評分也就能夠用P的某一行乘上Q的某一列獲得了:學習
這是預測用戶u對商品i的評分,它等於P矩陣的第u行乘上Q矩陣的第i列。這個是最基本的SVD算法,那麼如何經過已知評分訓練獲得P和Q的具體數值呢?測試
假設已知的評分爲:優化
則真實值與預測值的偏差爲:網站
繼而能夠計算出總的偏差平方和:
只要經過訓練把SSE降到最小那麼P、Q就能最好地擬合R了。那又如何使SSE降到最小呢?下面介紹一個經常使用的局部優化算法。
爲了說明梯度降低法,我找了一張PPT:
也就是說若是要最小化目標函數,必須往其負梯度方向搜索。這就是梯度降低法,注意它是一個局部優化算法,也就是說有可能落到局部最優解而不是全局最優解。
利用梯度降低法能夠求得SSE在Puk變量(也就是P矩陣的第u行第k列的值)處的梯度:
利用求導鏈式法則,e^2先對e求導再乘以e對Puk的求導:
因爲
因此
上式中括號裏的那一坨式子若是展開來看的話,其與Puk有關的項只有PukQki,其餘的無關項對Puk的求導均等於0
因此求導結果爲:
因此
爲了讓式子更簡潔,令
這樣作對結果沒有影響,只是爲了把求導結果前的2去掉,更好看點。獲得
如今獲得了目標函數在Puk處的梯度了,那麼按照梯度降低法,將Puk往負梯度方向變化:
令更新的步長(也就是學習速率)爲
則Puk的更新式爲
一樣的方式可獲得Qik的更新式爲
獲得了更新的式子,如今開始來討論這個更新要怎麼進行。有兩種選擇:一、計算完全部已知評分的預測偏差後再對P、Q進行更新。二、每計算完一個eui後當即對Pu和qi進行更新。這兩種方式都有名稱,分別叫:一、批梯度降低。二、隨機梯度降低。二者的區別就是批梯度降低在下一輪迭代才能使用本次迭代的更新值,隨機梯度降低本次迭代中當前樣本使用的值可能就是上一個樣本更新的值。因爲隨機性能夠帶來不少好處,好比有利於避免局部最優解,因此如今大多傾向於使用隨機梯度降低進行更新。
上面就是基本的SVD算法,可是,問題來了,上面的訓練是針對已知評分數據的,過度地擬合這部分數據有可能致使模型的測試效果不好,在測試集上面表現很糟糕。這就是過擬合問題,關於過擬合與欠擬合能夠看一下這張圖
第一個是欠擬合,第二個恰好,第三個過擬合。那麼如何避免過擬合呢?那就是在目標函數中加入正則化參數(加入懲罰項),對於目標函數來講,P矩陣和Q矩陣中的全部值都是變量,這些變量在不知道哪一個變量會帶來過擬合的狀況下,對全部變量都進行懲罰:
這時候目標函數對Puk的導數就發生變化了,如今就來求加入懲罰項後的導數。
括號裏第一項對Puk的求導前面已經求過了,第二項對Puk的求導很容易求得,第三項與Puk無關,導數爲0,因此
同理可得SSE對qik的導數爲
將這兩個變量往負梯度方向變化,則更新式爲
這就是正則化後的SVD,也叫RSVD。
加入偏置的SVD、RSVD
關於SVD算法的變種太多了,叫法也不統一,在預測式子上加點參數又會出來一個名稱。因爲用戶對商品的打分不只取決於用戶和商品間的某種關係,還取決於用戶和商品獨有的性質,Koren將SVD的預測公式改爲這樣
第一項爲總的平均分,bu爲用戶u的屬性值,bi爲商品i的屬性值,加入的這兩個變量在SSE式子中一樣須要懲罰,那麼SSE就變成了下面這樣:
由上式能夠看出SSE對Puk和qik的導數都沒有變化,但此時多了bu和bi變量,一樣要求出其更新式。首先求SSE對bu的導數,只有第一項和第四項和bu有關,第一項對bu的求導和以前的求導相似,用鏈式法則便可求得,第四項直接求導便可,最後可得偏導數爲
同理可得對bi的導數爲
因此往其負梯度方向變化獲得其更新式爲
這就是修改後的SVD(RSVD)。
全稱叫Asymmetric-SVD,即非對稱SVD,其預測式子爲
R(u)表示用戶u評過度的商品集合,N(u)表示用戶u瀏覽過但沒有評過度的商品集合,Xj和Yj是商品的屬性。這個模型頗有意思,看預測式子,用戶矩陣P已經被去掉了,取而代之的是利用用戶評過度的商品和用戶瀏覽過還沒有評分的商品屬性來表示用戶屬性,這有必定的合理性,由於用戶的行爲記錄自己就能反應用戶的喜愛。並且,這個模型能夠帶來一個很大的好處,一個商場或者網站的用戶數成千上萬甚至過億,存儲用戶屬性的二維矩陣會佔用巨大的存儲空間,而商品數卻沒有那麼多,因此這個模型的好處顯而易見。可是它有個缺點,就是迭代時間太長了,這是能夠預見的,以時間換空間嘛。
一樣的,須要計算其各個參數的偏導數,求出更新式,顯然,bu和bi的更新式和剛纔求出的同樣
其中的向量z等於下面這坨
如今要求qik、x和y的更新式,在這裏若是把z當作Pu則根據前面求得的更新式可得qik的更新式:
求Xj的導數須要有點耐心,SSE的第二項對Xj的導數很容易獲得,如今來求第一項對Xj的導數:
上式求和符中的i,j都是屬於R(u)的,能夠看到Zm只有當m==k時才與Xjk有關,因此
由於
因此
因此獲得SSE對Xjk的導數爲
同理可得SSE對Yjk的導數爲
因此獲得Xjk和Yjk的更新方程
這就叫ASVD。。。。。。
最後這個模型也是Koren文章中提到的,SVDPlusPlus(SVD++),它的預測式子爲
這裏的N(u)表示用戶u行爲記錄(包括瀏覽的和評過度的商品集合)。看了ASVD的更新式推導過程再來看這個應該很簡單,Puk和qik的更新式子不變,Yjk的更新式子和ASVD的更新式同樣:
這些就是Koren在NetFlix大賽中用到的SVD算法,最後,還有一個須要提的:
將前面預測公式中的u和i調換位置獲得其對偶算法,對於RSVD而言,u和i的位置是等價的、對稱的,因此其對偶算法和其自己沒有區別,但對於ASVD和SVD++則不一樣,有時候對偶算法獲得的結果更加精確,而且,若是將對偶算法和原始算法的預測結果融合在一塊兒的話,效果的提高會讓你吃驚!
對偶的ASVD預測公式:
這裏R(i)表示評論過商品i的用戶集合,N(i)表示瀏覽過商品i但沒有評論的用戶集合。因爲用戶數量龐大,因此對偶的ASVD會佔用很大空間,這裏須要作取捨了。
對偶的SVD++預測公式:
這裏N(i)表示對商品i有過行爲(瀏覽或評分)的用戶集合。
實現這一對偶的操做其實很簡單,只要讀取數據的時候把用戶id和商品id對調位置便可,也就是將R矩陣轉置後再訓練。
暫時實現的算法就這些,代碼都在github上了,有興趣的能夠看看,地址:https://github.com/jingchenUSTC/SVDRecommenderSystem