欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

用Python實現(xiàn)協(xié)同過濾的教程

 更新時間:2015年04月08日 15:33:49   投稿:goldensun  
這篇文章主要介紹了用Python實現(xiàn)協(xié)同過濾的教程,主要用于從大數(shù)據(jù)中抽取用戶信息偏好等等,需要的朋友可以參考下

協(xié)同過濾

在 用戶 —— 物品(user - item)的數(shù)據(jù)關(guān)系下很容易收集到一些偏好信息(preference),比如評分。利用這些分散的偏好信息,基于其背后可能存在的關(guān)聯(lián)性,來為用戶推薦物品的方法,便是協(xié)同過濾,或稱協(xié)作型過濾(collaborative filtering)。

這種過濾算法的有效性基礎(chǔ)在于:

    用戶的偏好具有相似性,即用戶是可分類的。這種分類的特征越明顯,推薦的準(zhǔn)確率就越高
    物品之間是存在關(guān)系的,即偏好某一物品的任何人,都很可能也同時偏好另一件物品

不同環(huán)境下這兩種理論的有效性也不同,應(yīng)用時需做相應(yīng)調(diào)整。如豆瓣上的文藝作品,用戶對其的偏好程度與用戶自身的品位關(guān)聯(lián)性較強;而對于電子商務(wù)網(wǎng)站來說,商品之間的內(nèi)在聯(lián)系對用戶的購買行為影響更為顯著。當(dāng)用在推薦上,這兩種方向也被稱為基于用戶的和基于物品的。本文內(nèi)容為基于用戶的。
影評推薦實例

本文主要內(nèi)容為基于用戶偏好的相似性進(jìn)行物品推薦,使用的數(shù)據(jù)集為 GroupLens Research 采集的一組從 20 世紀(jì) 90 年代末到 21 世紀(jì)初由 MovieLens 用戶提供的電影評分?jǐn)?shù)據(jù)。數(shù)據(jù)中包含了約 6000 名用戶對約 4000 部電影的 100萬條評分,五分制。數(shù)據(jù)包可以從網(wǎng)上下載到,里面包含了三個數(shù)據(jù)表——users、movies、ratings。因為本文的主題是基于用戶偏好的,所以只使用 ratings 這一個文件。另兩個文件里分別包含用戶和電影的元信息。

本文使用的數(shù)據(jù)分析包為 pandas,環(huán)境為 IPython,因此其實還默認(rèn)攜帶了 Numpy 和 matplotlib。下面代碼中的提示符看起來不是 IPython 環(huán)境是因為 Idle 的格式發(fā)在博客上更好看一些。
數(shù)據(jù)規(guī)整

首先將評分?jǐn)?shù)據(jù)從 ratings.dat 中讀出到一個 DataFrame 里:

>>> import pandas as pd
>>> from pandas import Series,DataFrame
>>> rnames = ['user_id','movie_id','rating','timestamp']
>>> ratings = pd.read_table(r'ratings.dat',sep='::',header=None,names=rnames)
>>> ratings[:3]
 user_id movie_id rating timestamp
0  1  1193  5 978300760
1  1  661  3 978302109
2  1  914  3 978301968
 
[3 rows x 4 columns]

ratings 表中對我們有用的僅是 user_id、movie_id 和 rating 這三列,因此我們將這三列取出,放到一個以 user 為行,movie 為列,rating 為值的表 data 里面。(其實將 user 與 movie 的行列關(guān)系對調(diào)是更加科學(xué)的方法,但因為重跑一遍太麻煩了,這里就沒改。)
 

>>> data = ratings.pivot(index='user_id',columns='movie_id',values='rating')
>>> data[:5]
movie_id 1 2 3 4 5 6 
user_id                  
1   5 NaN NaN NaN NaN NaN ...
2  NaN NaN NaN NaN NaN NaN ...
3  NaN NaN NaN NaN NaN NaN ...
4  NaN NaN NaN NaN NaN NaN ...
5  NaN NaN NaN NaN NaN 2 ...

可以看到這個表相當(dāng)?shù)孟∈?,填充率大約只有 5%,接下來要實現(xiàn)推薦的第一步是計算 user 之間的相關(guān)系數(shù),DataFrame 對象有一個很親切的 .corr(method='pearson', min_periods=1) 方法,可以對所有列互相計算相關(guān)系數(shù)。method 默認(rèn)為皮爾遜相關(guān)系數(shù),這個 ok,我們就用這個。問題僅在于那個 min_periods 參數(shù),這個參數(shù)的作用是設(shè)定計算相關(guān)系數(shù)時的最小樣本量,低于此值的一對列將不進(jìn)行運算。這個值的取舍關(guān)系到相關(guān)系數(shù)計算的準(zhǔn)確性,因此有必要先來確定一下這個參數(shù)。

    相關(guān)系數(shù)是用于評價兩個變量間線性關(guān)系的一個值,取值范圍為 [-1, 1],-1代表負(fù)相關(guān),0 代表不相關(guān),1 代表正相關(guān)。其中 0~0.1 一般被認(rèn)為是弱相關(guān),0.1~0.4 為相關(guān),0.4~1 為強相關(guān)。

min_periods 參數(shù)測定

測定這樣一個參數(shù)的基本方法為統(tǒng)計在 min_periods 取不同值時,相關(guān)系數(shù)的標(biāo)準(zhǔn)差大小,越小越好;但同時又要考慮到,我們的樣本空間十分稀疏,min_periods 定得太高會導(dǎo)致出來的結(jié)果集太小,所以只能選定一個折中的值。

這里我們測定評分系統(tǒng)標(biāo)準(zhǔn)差的方法為:在 data 中挑選一對重疊評分最多的用戶,用他們之間的相關(guān)系數(shù)的標(biāo)準(zhǔn)差去對整體標(biāo)準(zhǔn)差做點估計。在此前提下對這一對用戶在不同樣本量下的相關(guān)系數(shù)進(jìn)行統(tǒng)計,觀察其標(biāo)準(zhǔn)差變化。

首先,要找出重疊評分最多的一對用戶。我們新建一個以 user 為行列的方陣 foo,然后挨個填充不同用戶間重疊評分的個數(shù):
 

>>> foo = DataFrame(np.empty((len(data.index),len(data.index)),dtype=int),index=data.index,columns=data.index)
>>> for i in foo.index:
  for j in foo.columns:
   foo.ix[i,j] = data.ix[i][data.ix[j].notnull()].dropna().count()

這段代碼特別費時間,因為最后一行語句要執(zhí)行 4000*4000 = 1600萬遍;(其中有一半是重復(fù)運算,因為 foo 這個方陣是對稱的)還有一個原因是 Python 的 GIL,使得其只能使用一個 CPU 線程。我在它執(zhí)行了一個小時后,忍不住去測試了一下總時間,發(fā)現(xiàn)要三個多小時后就果斷 Ctrl + C 了,在算了一小半的 foo 中,我找到的最大值所對應(yīng)的行列分別為 424 和 4169,這兩位用戶之間的重疊評分?jǐn)?shù)為 998:


 

>>> for i in foo.index:
  foo.ix[i,i]=0#先把對角線的值設(shè)為 0
 
>>> ser = Series(np.zeros(len(foo.index)))
>>> for i in foo.index:
  ser[i]=foo[i].max()#計算每行中的最大值
 
>>> ser.idxmax()#返回 ser 的最大值所在的行號
4169
 
>>> ser[4169]#取得最大值
998
 
>>> foo[foo==998][4169].dropna()#取得另一個 user_id
424  4169
Name: user_id, dtype: float64

我們把 424 和 4169 的評分?jǐn)?shù)據(jù)單獨拿出來,放到一個名為 test 的表里,另外計算了一下這兩個用戶之間的相關(guān)系數(shù)為 0.456,還算不錯,另外通過柱狀圖了解一下他倆的評分分布情況:

>>> data.ix[4169].corr(data.ix[424])
0.45663851303413217
>>> test = data.reindex([424,4169],columns=data.ix[4169][data.ix[424].notnull()].dropna().index)
>>> test
movie_id 2  6  10 11 12 17 ...
424    4  4  4  4  1  5 ...
4169    3  4  4  4  2  5 ...
 
>>> test.ix[424].value_counts(sort=False).plot(kind='bar')
>>> test.ix[4169].value_counts(sort=False).plot(kind='bar')

201548154049025.png (371×261)

201548154118207.png (370×261)

對這倆用戶的相關(guān)系數(shù)統(tǒng)計,我們分別隨機抽取 20、50、100、200、500 和 998 個樣本值,各抽 20 次。并統(tǒng)計結(jié)果:

 >>> periods_test = DataFrame(np.zeros((20,7)),columns=[10,20,50,100,200,500,998])
>>> for i in periods_test.index:
  for j in periods_test.columns:
   sample = test.reindex(columns=np.random.permutation(test.columns)[:j])
   periods_test.ix[i,j] = sample.iloc[0].corr(sample.iloc[1])
 
 
>>> periods_test[:5]
  10  20  50  100  200  500  998
0 -0.306719 0.709073 0.504374 0.376921 0.477140 0.426938 0.456639
1 0.386658 0.607569 0.434761 0.471930 0.437222 0.430765 0.456639
2 0.507415 0.585808 0.440619 0.634782 0.490574 0.436799 0.456639
3 0.628112 0.628281 0.452331 0.380073 0.472045 0.444222 0.456639
4 0.792533 0.641503 0.444989 0.499253 0.426420 0.441292 0.456639
 
[5 rows x 7 columns]
>>> periods_test.describe()
    10   20   50   100  200  500 #998略
count 20.000000 20.000000 20.000000 20.000000 20.000000 20.000000 
mean 0.346810 0.464726 0.458866 0.450155 0.467559 0.452448 
std  0.398553 0.181743 0.103820 0.093663 0.036439 0.029758 
min -0.444302 0.087370 0.192391 0.242112 0.412291 0.399875 
25%  0.174531 0.320941 0.434744 0.375643 0.439228 0.435290 
50%  0.487157 0.525217 0.476653 0.468850 0.472562 0.443772 
75%  0.638685 0.616643 0.519827 0.500825 0.487389 0.465787 
max  0.850963 0.709073 0.592040 0.634782 0.546001 0.513486 
 
[8 rows x 7 columns]

從 std 這一行來看,理想的 min_periods 參數(shù)值應(yīng)當(dāng)為 200 左右??赡苡腥藭X得 200 太大了,這個推薦算法對新用戶簡直沒意義。但是得說,隨便算出個有超大誤差的相關(guān)系數(shù),然后拿去做不靠譜的推薦,又有什么意義呢。
算法檢驗

為了確認(rèn)在 min_periods=200 下本推薦算法的靠譜程度,最好還是先做個檢驗。具體方法為:在評價數(shù)大于 200 的用戶中隨機抽取 1000 位用戶,每人隨機提取一個評價另存到一個數(shù)組里,并在數(shù)據(jù)表中刪除這個評價。然后基于閹割過的數(shù)據(jù)表計算被提取出的 1000 個評分的期望值,最后與真實評價數(shù)組進(jìn)行相關(guān)性比較,看結(jié)果如何。
 

>>> check_size = 1000
>>> check = {}
>>> check_data = data.copy()#復(fù)制一份 data 用于檢驗,以免篡改原數(shù)據(jù)
>>> check_data = check_data.ix[check_data.count(axis=1)>200]#濾除評價數(shù)小于200的用戶
>>> for user in np.random.permutation(check_data.index):
  movie = np.random.permutation(check_data.ix[user].dropna().index)[0]
  check[(user,movie)] = check_data.ix[user,movie]
  check_data.ix[user,movie] = np.nan
  check_size -= 1
  if not check_size:
   break
 
 
>>> corr = check_data.T.corr(min_periods=200)
>>> corr_clean = corr.dropna(how='all')
>>> corr_clean = corr_clean.dropna(axis=1,how='all')#刪除全空的行和列
>>> check_ser = Series(check)#這里是被提取出來的 1000 個真實評分
>>> check_ser[:5]
(15, 593)  4
(23, 555)  3
(33, 3363) 4
(36, 2355) 5
(53, 3605) 4
dtype: float64

接下來要基于 corr_clean 給 check_ser 中的 1000 個 用戶-影片 對計算評分期望。計算方法為:對與用戶相關(guān)系數(shù)大于 0.1 的其他用戶評分進(jìn)行加權(quán)平均,權(quán)值為相關(guān)系數(shù):
 

>>> result = Series(np.nan,index=check_ser.index)
>>> for user,movie in result.index:#這個循環(huán)看著很亂,實際內(nèi)容就是加權(quán)平均而已
  prediction = []
  if user in corr_clean.index:
   corr_set = corr_clean[user][corr_clean[user]>0.1].dropna()#僅限大于 0.1 的用戶
  else:continue
  for other in corr_set.index:
   if not np.isnan(data.ix[other,movie]) and other != user:#注意bool(np.nan)==True
    prediction.append((data.ix[other,movie],corr_set[other]))
  if prediction:
   result[(user,movie)] = sum([value*weight for value,weight in prediction])/sum([pair[1] for pair in prediction])
 
 
>>> result.dropna(inplace=True)
>>> len(result)#隨機抽取的 1000 個用戶中也有被 min_periods=200 刷掉的
862
>>> result[:5]
(23, 555)  3.967617
(33, 3363) 4.073205
(36, 2355) 3.903497
(53, 3605) 2.948003
(62, 1488) 2.606582
dtype: float64
>>> result.corr(check_ser.reindex(result.index))
0.436227437429696
>>> (result-check_ser.reindex(result.index)).abs().describe()#推薦期望與實際評價之差的絕對值
count 862.000000
mean  0.785337
std  0.605865
min  0.000000
25%  0.290384
50%  0.686033
75%  1.132256
max  3.629720
dtype: float64

862 的樣本量能達(dá)到 0.436 的相關(guān)系數(shù),應(yīng)該說結(jié)果還不錯。如果一開始沒有濾掉評價數(shù)小于 200 的用戶的話,那么首先在計算 corr 時會明顯感覺時間變長,其次 result 中的樣本量會很小,大約 200+ 個。但因為樣本量變小的緣故,相關(guān)系數(shù)可以提升到 0.5~0.6 。

另外從期望與實際評價的差的絕對值的統(tǒng)計量上看,數(shù)據(jù)也比較理想。
實現(xiàn)推薦

在上面的檢驗,尤其是平均加權(quán)的部分做完后,推薦的實現(xiàn)就沒有什么新東西了。

首先在原始未閹割的 data 數(shù)據(jù)上重做一份 corr 表:
 

>>> corr = data.T.corr(min_periods=200)
>>> corr_clean = corr.dropna(how='all')
>>> corr_clean = corr_clean.dropna(axis=1,how='all')

我們在 corr_clean 中隨機挑選一位用戶為他做一個推薦列表:
 

>>> lucky = np.random.permutation(corr_clean.index)[0]
>>> gift = data.ix[lucky]
>>> gift = gift[gift.isnull()]#現(xiàn)在 gift 是一個全空的序列

最后的任務(wù)就是填充這個 gift:
 

>>> corr_lucky = corr_clean[lucky].drop(lucky)#lucky 與其他用戶的相關(guān)系數(shù) Series,不包含 lucky 自身
>>> corr_lucky = corr_lucky[corr_lucky>0.1].dropna()#篩選相關(guān)系數(shù)大于 0.1 的用戶
>>> for movie in gift.index:#遍歷所有 lucky 沒看過的電影
  prediction = []
  for other in corr_lucky.index:#遍歷所有與 lucky 相關(guān)系數(shù)大于 0.1 的用戶
   if not np.isnan(data.ix[other,movie]):
    prediction.append((data.ix[other,movie],corr_clean[lucky][other]))
  if prediction:
   gift[movie] = sum([value*weight for value,weight in prediction])/sum([pair[1] for pair in prediction])
 
 
>>> gift.dropna().order(ascending=False)#將 gift 的非空元素按降序排列
movie_id
3245  5.000000
2930  5.000000
2830  5.000000
2569  5.000000
1795  5.000000
981   5.000000
696   5.000000
682   5.000000
666   5.000000
572   5.000000
1420  5.000000
3338  4.845331
669   4.660464
214   4.655798
3410  4.624088
...
2833  1
2777  1
2039  1
1773  1
1720  1
1692  1
1538  1
1430  1
1311  1
1164  1
843   1
660   1
634   1
591   1
56   1
Name: 3945, Length: 2991, dtype: float64

 
補充

上面給出的示例都是些原型代碼,有很多可優(yōu)化的空間。比如 data 的行列轉(zhuǎn)換;比如測定 min_periods 時的方陣 foo 只需計算一半;比如有些 for 循環(huán)和相應(yīng)運算可以用數(shù)組對象方法來實現(xiàn)(方法版比用戶自己編寫的版本速度快很多);甚至肯定還有一些 bug。另外這個數(shù)據(jù)集的體積還不算太大,如果再增長一個數(shù)量級,那么就有必要針對計算密集的部分(如 corr)做進(jìn)一步優(yōu)化了,可以使用多進(jìn)程,或者 Cython/C 代碼。(或者換更好的硬件)

雖然協(xié)同過濾是一種比較省事的推薦方法,但在某些場合下并不如利用元信息推薦好用。協(xié)同過濾會遇到的兩個常見問題是

  1.     稀疏性問題——因用戶做出評價過少,導(dǎo)致算出的相關(guān)系數(shù)不準(zhǔn)確
  2.     冷啟動問題——因物品獲得評價過少,導(dǎo)致無“權(quán)”進(jìn)入推薦列表中

都是樣本量太少導(dǎo)致的。(上例中也使用了至少 200 的有效重疊評價數(shù))因此在對于新用戶和新物品進(jìn)行推薦時,使用一些更一般性的方法效果可能會更好。比如給新用戶推薦更多平均得分超高的電影;把新電影推薦給喜歡類似電影(如具有相同導(dǎo)演或演員)的人。后面這種做法需要維護(hù)一個物品分類表,這個表既可以是基于物品元信息劃分的,也可是通過聚類得到的。

相關(guān)文章

  • pandas 實現(xiàn)字典轉(zhuǎn)換成DataFrame的方法

    pandas 實現(xiàn)字典轉(zhuǎn)換成DataFrame的方法

    今天小編就為大家分享一篇pandas 實現(xiàn)字典轉(zhuǎn)換成DataFrame的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-07-07
  • 基于Pytorch實現(xiàn)分類器的示例詳解

    基于Pytorch實現(xiàn)分類器的示例詳解

    這篇文章主要為大家詳細(xì)介紹了如何基于Pytorch實現(xiàn)兩個分類器:?softmax分類器和感知機分類器,文中的示例代碼講解詳細(xì),需要的可以參考一下
    2023-04-04
  • Python快速優(yōu)雅的批量修改Word文檔樣式

    Python快速優(yōu)雅的批量修改Word文檔樣式

    本文主要將涉及os,glob,docx模塊的綜合應(yīng)用,幫助大家快速批量修改Word文檔樣式實現(xiàn)辦公自動化,感興趣的朋友可以了解下
    2021-05-05
  • Python實現(xiàn)操作Redis所有類型的方法詳解

    Python實現(xiàn)操作Redis所有類型的方法詳解

    Redis作為一款高性能的NoSQL數(shù)據(jù)庫,越來越受到了廣大開發(fā)者的喜愛。本篇博客將介紹如何使用Python操作Redis的所有類型,以及一些高級用法,感興趣的可以了解一下
    2023-04-04
  • 詳解Python中的Numpy、SciPy、MatPlotLib安裝與配置

    詳解Python中的Numpy、SciPy、MatPlotLib安裝與配置

    這篇文章主要介紹了詳解Python中的Numpy、SciPy、MatPlotLib安裝與配置,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • Python代碼列表求并集,交集,差集

    Python代碼列表求并集,交集,差集

    這篇文章主要介紹了Python代碼列表求并集,交集,差集,下面文章講詳細(xì)的介紹如何利用python代碼實現(xiàn)并集,交集,差集的相關(guān)資料展開內(nèi)容,需要的朋友可以參考一下
    2021-11-11
  • Pytorch 解決自定義子Module .cuda() tensor失敗的問題

    Pytorch 解決自定義子Module .cuda() tensor失敗的問題

    這篇文章主要介紹了Pytorch 解決自定義子Module .cuda() tensor失敗的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-06-06
  • Python3實現(xiàn)的簡單三級菜單功能示例

    Python3實現(xiàn)的簡單三級菜單功能示例

    這篇文章主要介紹了Python3實現(xiàn)的簡單三級菜單功能,涉及Python用戶交互以及針對json格式數(shù)據(jù)的遍歷、讀取、判斷等相關(guān)操作技巧,需要的朋友可以參考下
    2019-03-03
  • Python控制Firefox方法總結(jié)

    Python控制Firefox方法總結(jié)

    在本文里我們給大家分享了關(guān)于如何用Python控制Firefox的知識點總結(jié),有此需要的朋友們可以參閱下。
    2019-06-06
  • 基于OpenCV python3實現(xiàn)證件照換背景的方法

    基于OpenCV python3實現(xiàn)證件照換背景的方法

    這篇文章主要介紹了基于OpenCV python3實現(xiàn)證件照換背景的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03

最新評論