用Spark學(xué)習(xí)矩陣分解推薦算法
在矩陣分解在協(xié)同過濾推薦算法中的應(yīng)用中,我們對矩陣分解在推薦算法中的應(yīng)用原理做了總結(jié),這里我們就從實踐的角度來用Spark學(xué)習(xí)矩陣分解推薦算法。
1. Spark推薦算法概述
在Spark MLlib中,推薦算法這塊只實現(xiàn)了基于矩陣分解的協(xié)同過濾推薦算法。而基于的算法是FunkSVD算法,即將m個用戶和n個物品對應(yīng)的評分矩陣M分解為兩個低維的矩陣:
其中k為分解成低維的維數(shù),一般遠(yuǎn)比m和n小。如果大家對FunkSVD算法不熟悉,可以復(fù)習(xí)對應(yīng)的原理篇。
2. Spark推薦算法類庫介紹
在Spark MLlib中,實現(xiàn)的FunkSVD算法支持Python,Java,Scala和R的接口。由于前面的實踐篇我們都是基于Python,本文的后面的介紹和使用也會使用MLlib的Python接口。
Spark MLlib推薦算法python對應(yīng)的接口都在pyspark.mllib.recommendation包中,這個包有三個類,Rating, MatrixFactorizationModel和ALS。雖然里面有三個類,但是算法只是FunkSVD算法。下面介紹這三個類的用途。
Rating類比較簡單,僅僅只是為了封裝用戶,物品與評分這3個值。也就是說,Rating類里面只有用戶,物品與評分三元組, 并沒有什么函數(shù)接口。
ALS負(fù)責(zé)訓(xùn)練我們的FunkSVD模型。之所以這兒用交替最小二乘法ALS表示,是因為Spark在FunkSVD的矩陣分解的目標(biāo)函數(shù)優(yōu)化時,使用的是ALS。ALS函數(shù)有兩個函數(shù),一個是train,這個函數(shù)直接使用我們的評分矩陣來訓(xùn)練數(shù)據(jù),而另一個函數(shù)trainImplicit則稍微復(fù)雜一點,它使用隱式反饋數(shù)據(jù)來訓(xùn)練模型,和train函數(shù)相比,它多了一個指定隱式反饋信心閾值的參數(shù),比如我們可以將評分矩陣轉(zhuǎn)化為反饋數(shù)據(jù)矩陣,將對應(yīng)的評分值根據(jù)一定的反饋原則轉(zhuǎn)化為信心權(quán)重值。由于隱式反饋原則一般要根據(jù)具體的問題和數(shù)據(jù)來定,本文后面只討論普通的評分矩陣分解。
MatrixFactorizationModel類是我們用ALS類訓(xùn)練出來的模型,這個模型可以幫助我們做預(yù)測。常用的預(yù)測有某一用戶和某一物品對應(yīng)的評分,某用戶最喜歡的N個物品,某物品可能會被最喜歡的N個用戶,所有用戶各自最喜歡的N物品,以及所有物品被最喜歡的N個用戶。
對于這些類的用法我們再后面會有例子講解。
3. Spark推薦算法重要類參數(shù)
這里我們再對ALS訓(xùn)練模型時的重要參數(shù)做一個總結(jié)。
1) ratings : 評分矩陣對應(yīng)的RDD。需要我們輸入。如果是隱式反饋,則是評分矩陣對應(yīng)的隱式反饋矩陣。
2) rank : 矩陣分解時對應(yīng)的低維的維數(shù)。即PTm×kQk×nPm×kTQk×n中的維度k。這個值會影響矩陣分解的性能,越大則算法運(yùn)行的時間和占用的內(nèi)存可能會越多。通常需要進(jìn)行調(diào)參,一般可以取10-200之間的數(shù)。
3) iterations :在矩陣分解用交替最小二乘法求解時,進(jìn)行迭代的***次數(shù)。這個值取決于評分矩陣的維度,以及評分矩陣的系數(shù)程度。一般來說,不需要太大,比如5-20次即可。默認(rèn)值是5。
4) lambda: 在 python接口中使用的是lambda_,原因是lambda是Python的保留字。這個值即為FunkSVD分解時對應(yīng)的正則化系數(shù)。主要用于控制模型的擬合程度,增強(qiáng)模型泛化能力。取值越大,則正則化懲罰越強(qiáng)。大型推薦系統(tǒng)一般需要調(diào)參得到合適的值。
5) alpha : 這個參數(shù)僅僅在使用隱式反饋trainImplicit時有用。指定了隱式反饋信心閾值,這個值越大則越認(rèn)為用戶和他沒有評分的物品之間沒有關(guān)聯(lián)。一般需要調(diào)參得到合適值。
從上面的描述可以看出,使用ALS算法還是蠻簡單的,需要注意調(diào)參的參數(shù)主要的是矩陣分解的維數(shù)rank, 正則化超參數(shù)lambda。如果是隱式反饋,還需要調(diào)參隱式反饋信心閾值alpha 。
4. Spark推薦算法實例
下面我們用一個具體的例子來講述Spark矩陣分解推薦算法的使用。
這里我們使用MovieLens 100K的數(shù)據(jù),數(shù)據(jù)下載鏈接在這。
將數(shù)據(jù)解壓后,我們只使用其中的u.data文件中的評分?jǐn)?shù)據(jù)。這個數(shù)據(jù)集每行有4列,分別對應(yīng)用戶ID,物品ID,評分和時間戳。由于我的機(jī)器比較破,在下面的例子中,我只使用了前100條數(shù)據(jù)。因此如果你使用了所有的數(shù)據(jù),后面的預(yù)測結(jié)果會與我的不同。
首先需要要確保你安裝好了Hadoop和Spark(版本不小于1.6),并設(shè)置好了環(huán)境變量。一般我們都是在ipython notebook(jupyter notebook)中學(xué)習(xí),所以***把基于notebook的Spark環(huán)境搭好。當(dāng)然不搭notebook的Spark環(huán)境也沒有關(guān)系,只是每次需要在運(yùn)行前設(shè)置環(huán)境變量。
如果你沒有搭notebook的Spark環(huán)境,則需要先跑下面這段代碼。當(dāng)然,如果你已經(jīng)搭好了,則下面這段代碼不用跑了。
在跑算法之前,建議輸出Spark Context如下,如果可以正常打印內(nèi)存地址,則說明Spark的運(yùn)行環(huán)境搞定了。
- print sc
比如我的輸出是:
首先我們將u.data文件讀入內(nèi)存,并嘗試輸出***行的數(shù)據(jù)來檢驗是否成功讀入,注意復(fù)制代碼的時候,數(shù)據(jù)的目錄要用你自己的u.data的目錄。代碼如下:
輸出如下:
- u’196\t242\t3\t881250949′
可以看到數(shù)據(jù)是用\t分開的,我們需要將每行的字符串劃開,成為數(shù)組,并只取前三列,不要時間戳那一列。代碼如下:
輸出如下:
- [u’196′, u’242′, u’3′]
此時雖然我們已經(jīng)得到了評分矩陣數(shù)組對應(yīng)的RDD,但是這些數(shù)據(jù)都還是字符串,Spark需要的是若干Rating類對應(yīng)的數(shù)組。因此我們現(xiàn)在將RDD的數(shù)據(jù)類型做轉(zhuǎn)化,代碼如下:
輸出如下:
- Rating(user=196, product=242, rating=3.0)
可見我們的數(shù)據(jù)已經(jīng)是基于Rating類的RDD了,現(xiàn)在我們終于可以把整理好的數(shù)據(jù)拿來訓(xùn)練了,代碼如下, 我們將矩陣分解的維度設(shè)置為20,***迭代次數(shù)設(shè)置為5,而正則化系數(shù)設(shè)置為0.02。在實際應(yīng)用中,我們需要通過交叉驗證來選擇合適的矩陣分解維度與正則化系數(shù)。這里我們由于是實例,就簡化了。
將模型訓(xùn)練完畢后,我們終于可以來做推薦系統(tǒng)的預(yù)測了。
首先做一個最簡單的預(yù)測,比如預(yù)測用戶38對物品20的評分。代碼如下:
- print model.predict(38,20)
輸出如下:
- 0.311633491603
可見評分并不高。
現(xiàn)在我們來預(yù)測了用戶38最喜歡的10個物品,代碼如下:
- print model.recommendProducts(38,10)
輸出如下:
- [ Rating(user=38, product=95, rating=4.995227969811873),
- Rating(user=38, product=304, rating=2.5159673379104484),
- Rating(user=38, product=1014, rating=2.165428673820349),
- Rating(user=38, product=322, rating=1.7002266119079879),
- Rating(user=38, product=111, rating=1.2057528774266673),
- Rating(user=38, product=196, rating=1.0612630766055788),
- Rating(user=38, product=23, rating=1.0590775012913558),
- Rating(user=38, product=327, rating=1.0335651317559753),
- Rating(user=38, product=98, rating=0.9677333686628911),
- Rating(user=38, product=181, rating=0.8536682271006641)]
可以看出用戶38可能喜歡的對應(yīng)評分從高到低的10個物品。
接著我們來預(yù)測下物品20可能最值得推薦的10個用戶,代碼如下:
- print model.recommendUsers(20,10)
輸出如下:
- [ Rating(user=115, product=20, rating=2.9892138653406635),
- Rating(user=25, product=20, rating=1.7558472892444517),
- Rating(user=7, product=20, rating=1.523935609195585),
- Rating(user=286, product=20, rating=1.3746309116764184),
- Rating(user=222, product=20, rating=1.313891405211581),
- Rating(user=135, product=20, rating=1.254412853860262),
- Rating(user=186, product=20, rating=1.2194811581542384),
- Rating(user=72, product=20, rating=1.1651855319930426),
- Rating(user=241, product=20, rating=1.0863391992741023),
- Rating(user=160, product=20, rating=1.072353288848142)]
現(xiàn)在我們來看看每個用戶最值得推薦的三個物品,代碼如下:
- print model.recommendProductsForUsers(3).collect()
由于輸出非常長,這里就不將輸出copy過來了。
而每個物品最值得被推薦的三個用戶,代碼如下:
- print model.recommendUsersForProducts(3).collect()
同樣由于輸出非常長,這里就不將輸出copy過來了。
希望上面的例子對大家使用Spark矩陣分解推薦算法有幫助。