Slope One進(jìn)行評(píng)分預(yù)測(cè)

Slope One是一種基于物品的協(xié)同過濾算法襟企,在2005年的paper《Slope One Predictors for Online Rating-Based Collaborative Filtering》被提出虐先,用于預(yù)測(cè)用戶對(duì)某一給定的物品的評(píng)分。

依然使用上一篇中提到的自己編造的少量評(píng)分?jǐn)?shù)據(jù)來描述該算法的運(yùn)作機(jī)制。

首先依然是加載數(shù)據(jù)和生成用戶物品關(guān)系矩陣如下。

import pandas as pd
import numpy as np


data_url = 'https://gist.githubusercontent.com/guerbai/3f4964350678c84d359e3536a08f6d3a/raw/f62f26d9ac24d434b1a0be3b5aec57c8a08e7741/user_book_ratings.txt'
df = pd.read_csv(data_url, sep = ',', header = None, names = ['user_id', 'book_id', 'rating'])
user_count = df['user_id'].unique().shape[0]
item_count = df['book_id'].unique().shape[0]
user_id_index_series = pd.Series(range(user_count), index=['user_001', 'user_002', 'user_003', 'user_004', 'user_005', 'user_006'])
item_id_index_series = pd.Series(range(item_count), index=['book_001', 'book_002', 'book_003', 'book_004', 'book_005', 'book_006'])

def construct_user_item_matrix(df):
    user_item_matrix = np.zeros((user_count, item_count), dtype=np.int8)
    for row in df.itertuples():
        user_id = row[1]
        book_id = row[2]
        rating = row[3]
        user_item_matrix[user_id_index_series[user_id], item_id_index_series[book_id]] = rating
    return user_item_matrix

user_item_matrix = construct_user_item_matrix(df)
print (user_item_matrix)
[[4 3 0 0 5 0]
 [5 0 4 0 4 0]
 [4 0 5 3 4 0]
 [0 3 0 0 0 5]
 [0 4 0 0 0 4]
 [0 0 2 4 0 5]]

構(gòu)造物品評(píng)分差異矩陣

接下來生成兩個(gè)shape為(item_count, item_count)的矩陣differential_matrixweight_matrix
前者記錄物品兩兩之間的被評(píng)分差異情況武翎,后者記錄對(duì)某兩個(gè)物品共同評(píng)分的人數(shù),用于之后的計(jì)算做加權(quán)溶锭。

以上面user_item_matrix舉例來講宝恶,index為2與4的item的共同評(píng)分人數(shù)為2(index為1與2的用戶),則計(jì)算這兩者的評(píng)分差異為:
((4-4)+(5-4))/2 = 0.5趴捅,故在differential_matrix[2][4]的位置填上0.5垫毙,同時(shí)在weight_matrix[2][4]的位置填上2。
同時(shí)拱绑,反過來综芥,differential_matrix[4][2]的值為-0.5,而weight_matrix[4][2]的位置依然為2猎拨,這種相對(duì)應(yīng)的位置不需要重復(fù)計(jì)算了膀藐。

下面的函數(shù)接受一個(gè)用戶物品關(guān)系矩陣,按照上述方法計(jì)算出這兩個(gè)矩陣红省。

def compute_differential(ratings):
    item_count = ratings.shape[1]
    differential_matrix = np.zeros((item_count, item_count))
    weight_matrix = np.zeros((item_count, item_count))
    for i in range(item_count):
        for j in range(i+1, item_count):
            differential = 0
            i_rating_user_indexes = ratings[:, i].nonzero()[0]
            j_rating_user_indexes = ratings[:, j].nonzero()[0]
            rating_i_j_user = set(i_rating_user_indexes).intersection(set(j_rating_user_indexes))
            user_count = len(rating_i_j_user)
            if user_count == 0:
                continue
            for user_index in rating_i_j_user:
                differential += ratings[user_index][i] - ratings[user_index][j]
            weight_matrix[i][j] = user_count
            weight_matrix[j][i] = user_count
            differential_matrix[i][j] = round(differential/user_count, 2)
            differential_matrix[j][i] = -differential_matrix[i][j]
    return differential_matrix, weight_matrix

differential_matrix, weight_matrix = compute_differential(user_item_matrix)

print ('differential_matrix')
print (differential_matrix)
print ('-----')
print ('weight_matrix')
print (weight_matrix)
differential_matrix
[[ 0.   1.   0.   1.   0.   0. ]
 [-1.   0.   0.   0.  -2.  -1. ]
 [-0.   0.   0.   0.   0.5 -3. ]
 [-1.   0.  -0.   0.  -1.  -1. ]
 [-0.   2.  -0.5  1.   0.   0. ]
 [ 0.   1.   3.   1.   0.   0. ]]
-----
weight_matrix
[[ 0.  1.  2.  1.  3.  0.]
 [ 1.  0.  0.  0.  1.  2.]
 [ 2.  0.  0.  2.  2.  1.]
 [ 1.  0.  2.  0.  1.  1.]
 [ 3.  1.  2.  1.  0.  0.]
 [ 0.  2.  1.  1.  0.  0.]]

進(jìn)行評(píng)分預(yù)測(cè)

得到上述兩個(gè)矩陣后可以根據(jù)用戶的歷史評(píng)分额各,為其進(jìn)行未發(fā)生過評(píng)分關(guān)聯(lián)的某物品的評(píng)分預(yù)測(cè)。

比如要為index為1的用戶user_002預(yù)測(cè)其對(duì)index為3的物品item_004的評(píng)分类腮,計(jì)算過程如下:
先取出該用戶看過的所有書臊泰,index分別為[0, 2, 4];
以index為0的物品item_001開始,查differential_matrix[3][0]值為-1蚜枢,表示item_004平均上比item_001低1分缸逃,以該用戶對(duì)item_001的評(píng)分為5為基準(zhǔn),5+(-1)=4厂抽,則利用item_001可對(duì)item_004做出的評(píng)分判斷為4分需频,查weight_matrix表知道同時(shí)評(píng)分過這兩個(gè)物品的用戶只有一個(gè),置信度不夠高筷凤,使用4*1=4昭殉,這便是加權(quán)的含義苞七;
但這還沒完,再根據(jù)index為2挪丢、4的item分別做上一步蹂风,并將得到的值加和為15,作為分子乾蓬,分母為每次計(jì)算的人數(shù)之和惠啄,即加權(quán)平均,為4任内;
最后得此次預(yù)測(cè)評(píng)分為15/4=3.75撵渡。

下面的函數(shù)接受五個(gè)參數(shù),分別為三個(gè)矩陣死嗦,用戶id趋距,物品id,結(jié)果為預(yù)測(cè)值越除。

def predict(ratings, differential_matrix, weight_matrix, user_index, item_index):
    if ratings[user_index][item_index] != 0: return ratings[user_index][item_index]
    fenzi = 0
    fenmu = 0
    for rated_item_index in ratings[user_index].nonzero()[0]:
        fenzi += weight_matrix[item_index][rated_item_index] * \
            (differential_matrix[item_index][rated_item_index] + ratings[user_index][rated_item_index])
        fenmu += weight_matrix[rated_item_index][item_index]
    return round(fenzi/fenmu, 2)
predict(user_book_matrix, book_differential, weight_matrix, 1, 3)
3.75

新的評(píng)分?jǐn)?shù)據(jù)

當(dāng)某用戶對(duì)某個(gè)其之間未評(píng)分過的物品進(jìn)行一次新的評(píng)分時(shí)节腐,需要更新三個(gè)矩陣的值。令人欣喜的是廊敌,Slope One的計(jì)算過程使得這種更新非常迅速铜跑,時(shí)間復(fù)雜度僅為O(x)门怪,其中x為該用戶之前評(píng)過分的所有物品的數(shù)量骡澈。

理所當(dāng)然要在user_item_matrix填入評(píng)分值,此外掷空,對(duì)此index為i的物品肋殴,需要與那x個(gè)物品依次組合在weight_matrix中將值增加1。同理differential_matrix也只需要累計(jì)上新的差值即可坦弟。
一個(gè)用戶評(píng)價(jià)過的物品數(shù)目是很有限的护锤,這種更新模型的方法可謂飛快。

def update_matrices(user_index, item_index, rating):
    rated_item_indexes = user_item_matrix[user_index].nonzero()[0]
    user_item_matrix[user_index][item_index] = rating
    for rated_item_index in rated_item_indexes:
        old_weight = weight_matrix[rated_item_index][item_index]
        weight_matrix[rated_item_index][item_index] += 1
        weight_matrix[item_index][rated_item_index] += 1
        differential_matrix[rated_item_index][item_index] = (differential_matrix[rated_item_index][item_index] \
            * old_weight + (user_item_matrix[user_index][rated_item_index] - rating)) / (old_weight + 1)
        differential_matrix[item_index][rated_item_index] = (differential_matrix[item_index][rated_item_index] \
            * old_weight + (rating - user_item_matrix[user_index][rated_item_index])) / (old_weight + 1)

評(píng)價(jià)

簡單易懂:參見代碼酿傍;
存儲(chǔ):存儲(chǔ)上除了user_item_matrix烙懦,還需要存下differential_matrixweight_matrix,為節(jié)省空間赤炒,可以只存后兩者的對(duì)角線的右上部分即可氯析;
預(yù)測(cè)時(shí)間復(fù)雜度:用戶評(píng)價(jià)過的物品數(shù)為x,由predict代碼莺褒,則做一次預(yù)測(cè)的時(shí)間復(fù)雜度為O(x)掩缓;
更新時(shí)間復(fù)雜度:當(dāng)用戶新進(jìn)行一次評(píng)分時(shí),由update_matrices代碼遵岩,時(shí)間復(fù)雜度為O(x);
新用戶友好:當(dāng)用戶僅進(jìn)行少量評(píng)分時(shí)你辣,即可為其進(jìn)行較高質(zhì)量的推薦。

參考

《Slope One Predictors for Online Rating-Based Collaborative Filtering》
Slope One wiki

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市舍哄,隨后出現(xiàn)的幾起案子宴凉,更是在濱河造成了極大的恐慌,老刑警劉巖表悬,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件跪解,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡签孔,警方通過查閱死者的電腦和手機(jī)叉讥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饥追,“玉大人图仓,你說我怎么就攤上這事〉疲” “怎么了救崔?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長捏顺。 經(jīng)常有香客問我六孵,道長,這世上最難降的妖魔是什么幅骄? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任劫窒,我火速辦了婚禮,結(jié)果婚禮上拆座,老公的妹妹穿的比我還像新娘主巍。我一直安慰自己,他們只是感情好挪凑,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布孕索。 她就那樣靜靜地躺著,像睡著了一般躏碳。 火紅的嫁衣襯著肌膚如雪搞旭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天菇绵,我揣著相機(jī)與錄音肄渗,去河邊找鬼。 笑死脸甘,一個(gè)胖子當(dāng)著我的面吹牛恳啥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播丹诀,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼钝的,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼翁垂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起硝桩,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤沿猜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后碗脊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體啼肩,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年衙伶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了祈坠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡矢劲,死狀恐怖赦拘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情芬沉,我是刑警寧澤躺同,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站丸逸,受9級(jí)特大地震影響蹋艺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜黄刚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一捎谨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧隘击,春花似錦侍芝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽棵红。三九已至凶赁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逆甜,已是汗流浹背虱肄。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留交煞,地道東北人咏窿。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像素征,于是被迫代替她去往敵國和親集嵌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子萝挤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • 本文結(jié)構(gòu)安排 Item-Based Collaboration Filtering Slope One Matri...
    澤澤馥澤澤閱讀 801評(píng)論 0 1
  • 太長不讀版:由推薦系統(tǒng)帶來的推薦服務(wù)基本上已經(jīng)滲透到我們生活的方方面面,本文作為淺談推薦系統(tǒng)的基礎(chǔ)篇根欧,主要從下面幾...
    stayrascal閱讀 31,563評(píng)論 5 60
  • 項(xiàng)目管理術(shù)語英漢對(duì)照表2018-7-20 A Abstract Resource 抽象資源 Abstraction...
    007明_陽閱讀 6,149評(píng)論 0 51
  • 畢業(yè)這個(gè)詞聽起來也不是特別的重要凤粗,但這個(gè)詞的背后有暗暗的傷感酥泛。大家一想到忍不住要離開了心里的眼淚就流個(gè)不停...
    青春_4683閱讀 248評(píng)論 0 1
  • 晚來天欲暗風(fēng)來,冬風(fēng)呲呲鉆衣裳嫌拣。 到床上窩暖入睡柔袁,清靜午夜無人語。 忽夢(mèng)一山與一屋异逐,日入屋來人悠閑瘦馍。 手捧茶來手翻...
    楊翎閱讀 275評(píng)論 0 0