基于用戶的協(xié)同過濾算法的理解與簡單實(shí)現(xiàn)

姓名:崔少杰 ? ? ? 學(xué)號(hào):16040510021

轉(zhuǎn)載自:http://www.reibang.com/p/79d24fa3664f=有修改

【嵌牛導(dǎo)讀】:基于用戶的協(xié)同過濾算法(User-CF)屬于基于領(lǐng)域的算法诽表⌒较Γ基于用戶的協(xié)同過濾算法主要包括兩個(gè)步驟。找到和目標(biāo)用戶興趣相似的用戶集合隔箍。找到這個(gè)集合中的用戶喜歡的寺酪,且目標(biāo)用戶沒有聽說過的物品推薦給目標(biāo)用戶坎背。

【嵌牛鼻子】:協(xié)同過濾算法、皮爾遜相關(guān)度

【嵌牛提問】:如何簡單實(shí)現(xiàn)基于用戶的協(xié)同過濾算法寄雀?

【嵌牛正文】:相似度算法

步驟一的關(guān)鍵就是計(jì)算兩個(gè)用戶之間的相似度得滤,以下列舉兩種比較常見的相似度計(jì)算方法。

給定用戶u和用戶v咙俩,令N(u)表示用戶u曾經(jīng)有過正反饋的物品集合耿戚,令N(v)為用戶v曾經(jīng)有過正反饋的物品集合。

Jaccard公式

Jaccard公式

余弦相似度

余弦相似度

另外還有一種基于向量的相似度算法阿趁,也是在代碼實(shí)現(xiàn)中將使用的相似度計(jì)算方法膜蛔。具體的關(guān)于Pearson相關(guān)系數(shù)的介紹會(huì)在下文中闡述。

Pearson相關(guān)系數(shù)

Pearson相關(guān)系數(shù)

關(guān)于這三種相似度之間的差別和選擇標(biāo)準(zhǔn)脖阵,自己也還沒有仔細(xì)去研究皂股。之后有時(shí)間的話會(huì)補(bǔ)充。

計(jì)算舉例

計(jì)算單獨(dú)的兩個(gè)用戶之間的相似度

下面將簡單介紹利用余弦相似度計(jì)算方法得到兩個(gè)用戶之間的相似度命黔。

計(jì)算舉例

例如呜呐,用戶A對(duì)物品{a,b,d}有過行為,用戶B對(duì)物品{a,c}有過行為悍募,利用余弦相似度公式計(jì)算用戶A和用戶B的興趣相似度為:

A和B之間的相似度

同理蘑辑,我們可以算出用戶A和用戶C、D的相似度:

A和C坠宴、D之間的相似度

計(jì)算用戶兩兩之間的相似度矩陣

對(duì)兩兩用戶都利用余弦相似度計(jì)算相似度洋魂。這種方法的時(shí)間復(fù)雜度是O(N^2)。耗時(shí)很大喜鼓。

因此我們需要建立一張物品到用戶之間的倒排表副砍,這樣就能排除對(duì)根本沒有任何聯(lián)系的用戶之間的相似度計(jì)算。

再根據(jù)倒排表計(jì)算共同評(píng)分過的物品的矩陣庄岖。

如下圖所示

計(jì)算共同評(píng)分過的物品的矩陣

矩陣中的每一個(gè)數(shù)值都是余弦相似度中的分子部分豁翎,然后將分子除以分母可以得到最終的用戶興趣相似度。

即可以將上圖中的共同評(píng)分過的物品的矩陣轉(zhuǎn)換為用戶之間的相似度矩陣隅忿,且只用計(jì)算非0的部分心剥。

例如,計(jì)算A與B的用戶相似度背桐。用戶A和B的矩陣中的值為1刘陶,即他們共同交集的物品為1。A總共評(píng)分過的物品為3牢撼,B總共評(píng)分過的物品為2匙隔。根據(jù)余弦相似度算法,得出相似度為

計(jì)算結(jié)果

篩選出K個(gè)與目標(biāo)用戶最相似的用戶

得到了用戶之間的相似度后熏版,算法會(huì)給用戶推薦和他興趣最相似的K個(gè)用戶喜歡的物品

以下公式度量了算法中用戶u對(duì)物品i的感興趣程度

用戶u對(duì)物品i的感興趣程度

其中纷责,S(u,K)包含和用戶u興趣最相近的K個(gè)用戶,N(i)是對(duì)物品i有過行為的用戶集合撼短,Wuv是用戶u和用戶v的興趣相似度再膳,rvi為1

舉例

對(duì)目標(biāo)用戶A進(jìn)行推薦。選取K=3曲横,用戶A對(duì)物品c喂柒,e沒有過行為不瓶,因此可以把這兩個(gè)物品推薦給用戶A。根據(jù)算法灾杰,用戶A對(duì)物品c蚊丐,e的興趣是

用戶A對(duì)物品c,e的興趣

基于豆瓣圖書評(píng)論數(shù)據(jù)的簡單實(shí)現(xiàn)

評(píng)論數(shù)據(jù)格式介紹

自己對(duì)評(píng)論數(shù)據(jù)作了一個(gè)簡單的清洗艳吠,數(shù)據(jù)格式如下:

圖書名+用戶名+評(píng)分信息+評(píng)分時(shí)間

中間以"\t"作為分隔符

部分圖書數(shù)據(jù)如下圖所示

部分圖書數(shù)據(jù)

使用Pearson相關(guān)系數(shù)計(jì)算用戶相似度

之前介紹的協(xié)同過濾算法并沒有引進(jìn)評(píng)分的概念麦备,引進(jìn)評(píng)分之后,就不能簡單地利用集合去計(jì)算了昭娩,而是應(yīng)該利用向量去計(jì)算凛篙。

Pearson相關(guān)系數(shù)將兩個(gè)用戶共同評(píng)分的n個(gè)項(xiàng)目看做一組向量,計(jì)算兩個(gè)用戶在這n個(gè)項(xiàng)目上評(píng)分的相關(guān)性栏渺,減去用戶平均評(píng)分是基于用戶評(píng)分尺度的考量呛梆,公式如下:

Pearson相關(guān)系數(shù)

其中Iuv是用戶u和v都評(píng)過分的項(xiàng)目的集合,ru是用戶u所有評(píng)分的平均分

計(jì)算用戶u對(duì)物品i評(píng)分的預(yù)測(cè)

得到用戶相似度后磕诊,接下來就是根據(jù)目標(biāo)用戶u的近鄰用戶得到u對(duì)于物品i的評(píng)分預(yù)測(cè)削彬,公式如下:

用戶u對(duì)物品i的評(píng)分預(yù)測(cè)

評(píng)分預(yù)測(cè)越高,目標(biāo)用戶u喜歡該物品的概率也就越高秀仲。

最終按照評(píng)分預(yù)測(cè)的高低可得到最適合推薦給用戶u的物品列表融痛,在這個(gè)實(shí)例里也就是圖書列表。

Python實(shí)現(xiàn)

導(dǎo)入數(shù)據(jù)

導(dǎo)入的原始數(shù)據(jù)為訓(xùn)練集(trainSet)神僵,利用該訓(xùn)練集生成圖書-用戶倒排表(bookUser)雁刷,再利用倒排表生成用戶-用戶共同評(píng)分過的圖書矩陣(u2u)

# encoding=utf-8frommathimportsqrtimportcodecsdefloadData():# 訓(xùn)練集trainSet = {}# 看過該本書的所有用戶集合bookUser = {}# 用戶-用戶共有書籍矩陣u2u = {}? ? TrainFile ='/Users/cjl/Desktop/comments.txt'# 加載訓(xùn)練集# trainset {"userName", {"bookName, rating"}}# bookUser {"bookName", ["username1", "username2", ...]} 即為物品-用戶倒排表forlineincodecs.open(TrainFile,"r","utf-8"):? ? ? ? (bookName, userName, rating, timestamp) = line.strip().split("\t")? ? ? ? trainSet.setdefault(userName, {})? ? ? ? trainSet[userName].setdefault(bookName, float(rating))? ? ? ? bookUser.setdefault(bookName, [])? ? ? ? bookUser[bookName].append(userName.strip())# 生成用戶-用戶共有書籍矩陣forbinbookUser.keys():# 遍歷特定的書籍中所有的用戶foruinbookUser[b]:? ? ? ? ? ? u2u.setdefault(u, {})forninbookUser[b]:ifu != n:? ? ? ? ? ? ? ? ? ? u2u[u].setdefault(n,[])? ? ? ? ? ? ? ? ? ? u2u[u][n].append(b)returntrainSet, u2u

簡單測(cè)試導(dǎo)入的數(shù)據(jù)以及生成的u2u是否正確

測(cè)試導(dǎo)入數(shù)據(jù)

計(jì)算一個(gè)用戶的平均評(píng)分

defgetAverageRating(user, trainSet):average = (sum(trainSet[user].values())*1.0) / len(trainSet[user].keys())returnaverage

簡單測(cè)試

簡單測(cè)試

計(jì)算用戶相似度

遍歷u2u,利用皮爾遜相關(guān)系數(shù)的計(jì)算保礼,將共同評(píng)分過的書籍矩陣轉(zhuǎn)換為兩個(gè)用戶之間的相似度矩陣

# 計(jì)算用戶相似度defgetUserSim(u2u, trainSet):userSim = {}# 計(jì)算用戶相似度# 對(duì)每個(gè)用戶uforuinu2u.keys():ifu !='':# 將用戶u加入userSim中設(shè)為key沛励,該用戶對(duì)應(yīng)一個(gè)字典userSim.setdefault(u, {})# 獲取用戶u對(duì)電影的平均評(píng)分average_u_rate = getAverageRating(u, trainSet)# 對(duì)與用戶u相關(guān)的每個(gè)用戶nforninu2u[u].keys():ifn !='':# 將用戶n加到用戶u的字典中userSim[u].setdefault(n,0)# 獲取用戶n對(duì)電影的平均評(píng)分average_n_rate = getAverageRating(n, trainSet)# 皮爾遜相關(guān)系數(shù)的分子部分part1 =0# 皮爾遜相關(guān)系數(shù)的分母的一部分part2 =0# 皮爾遜相關(guān)系數(shù)的分母的一部分part3 =0# 對(duì)用戶u和用戶n的共有的每個(gè)電影forminu2u[u][n]:? ? ? ? ? ? ? ? ? ? ? ? part1 += (trainSet[u][m] - average_u_rate) * (trainSet[n][m] - average_n_rate) *1.0part2 += pow(trainSet[u][m] - average_u_rate,2) *1.0part3 += pow(trainSet[n][m] - average_n_rate,2) *1.0part2 = sqrt(part2)? ? ? ? ? ? ? ? ? ? part3 = sqrt(part3)# 若分母為0,相似度為0ifpart2 ==0orpart3 ==0:? ? ? ? ? ? ? ? ? ? ? ? userSim[u][n] =0else:? ? ? ? ? ? ? ? ? ? ? ? userSim[u][n] = part1 / (part2 * part3)returnuserSim

簡單測(cè)試

輸出相似用戶

尋找最近鄰用戶并生成推薦結(jié)果

遍歷trainSet中的所有用戶炮障,生成一個(gè)評(píng)分預(yù)測(cè)pred

# 尋找用戶最近鄰并生成推薦結(jié)果defgetRecommendations(N, trainSet, userSim):pred = {}# 對(duì)每個(gè)用戶foruserintrainSet.keys():# 生成空預(yù)測(cè)表pred.setdefault(user, {})# 獲取該用戶評(píng)過分的書籍interacted_items = trainSet[user].keys()# 獲取該用戶的平均評(píng)分average_u_rate = getAverageRating(user, trainSet)? ? ? ? userSimSum =0# 選取N個(gè)最相似的用戶# lambda x:x[1] 根據(jù)用戶相似度進(jìn)行比較ifuser.strip() !='':? ? ? ? ? ? simUser = sorted(userSim[user.strip()].items(), key=lambdax: x[1], reverse=True)[0:N]# 遍歷相似用戶的用戶名和相似度forn, siminsimUser:# 得到該近鄰用戶的平均評(píng)分average_n_rate = getAverageRating(n, trainSet)# 對(duì)該用戶所有近鄰用戶相似度求和userSimSum += sim# 遍歷該近鄰用戶的所有評(píng)分書籍和具體評(píng)分form, nratingintrainSet[n].items():# 如果這本書該用戶已經(jīng)看過目派,則跳過ifmininteracted_items:continue# 否則將這本書以及這本書的推薦指數(shù)加到這名用戶的推薦列表中else:? ? ? ? ? ? ? ? ? ? ? ? pred[user].setdefault(m,0)? ? ? ? ? ? ? ? ? ? ? ? pred[user][m] += (sim * (nrating - average_n_rate))# 遍歷這名用戶推薦列表中的所有書籍,更新評(píng)分預(yù)測(cè)forminpred[user].keys():ifuserSimSum ==0:? ? ? ? ? ? ? ? ? ? pred[user][m] = average_u_rateelse:? ? ? ? ? ? ? ? ? ? pred[user][m] = average_u_rate + (pred[user][m] *1.0) / userSimSumreturnpred

簡單測(cè)試

輸出推薦書籍

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末胁赢,一起剝皮案震驚了整個(gè)濱河市企蹭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌智末,老刑警劉巖谅摄,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異系馆,居然都是意外死亡送漠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門由蘑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闽寡,“玉大人代兵,你說我怎么就攤上這事∫罚” “怎么了植影?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長淆院。 經(jīng)常有香客問我何乎,道長句惯,這世上最難降的妖魔是什么土辩? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮抢野,結(jié)果婚禮上拷淘,老公的妹妹穿的比我還像新娘。我一直安慰自己指孤,他們只是感情好启涯,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著恃轩,像睡著了一般结洼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上叉跛,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天松忍,我揣著相機(jī)與錄音,去河邊找鬼筷厘。 笑死鸣峭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的酥艳。 我是一名探鬼主播摊溶,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼充石!你這毒婦竟也來了莫换?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤骤铃,失蹤者是張志新(化名)和其女友劉穎浓镜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體劲厌,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡膛薛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了补鼻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哄啄。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡雅任,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出咨跌,到底是詐尸還是另有隱情沪么,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布锌半,位于F島的核電站禽车,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏刊殉。R本人自食惡果不足惜殉摔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望记焊。 院中可真熱鬧逸月,春花似錦、人聲如沸遍膜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓢颅。三九已至恩尾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間挽懦,已是汗流浹背翰意。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留巾兆,地道東北人猎物。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像角塑,于是被迫代替她去往敵國和親蔫磨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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