此篇使用樸素的代碼介紹基于鄰域的協(xié)同過濾算法機(jī)制插龄。
為了使說明過程更清楚刽肠,這里使用自已編造的數(shù)據(jù)唇兑。每一行記錄著某用戶對(duì)某本書的評(píng)分坎弯,評(píng)分區(qū)間為1至5纺涤。
import pandas as pd
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'])
print (df.head())
print ('-----')
user_count = df['user_id'].unique().shape[0]
book_count = df['book_id'].unique().shape[0]
print ('user_count: ', user_count)
print ('book_count: ', book_count)
user_id book_id rating
0 user_001 book_001 4
1 user_001 book_002 3
2 user_001 book_005 5
3 user_002 book_001 5
4 user_002 book_003 4
-----
user_count: 6
book_count: 6
生成用戶物品關(guān)系矩陣
現(xiàn)在根據(jù)加載進(jìn)來的數(shù)據(jù)生成推薦系統(tǒng)中至關(guān)重要的用戶物品關(guān)系矩陣译暂。可以理解為數(shù)據(jù)庫中的一張表撩炊,一本書為一列外永,一行對(duì)應(yīng)一個(gè)用戶,當(dāng)用戶看過某本書并進(jìn)行評(píng)分后拧咳,在對(duì)應(yīng)的位置填入分?jǐn)?shù)伯顶,其他位置均置為0,表示尚未看過骆膝。
需要注意的是祭衩,矩陣取值要用下標(biāo)表示,比如matrix[2][2]
對(duì)應(yīng)的是第三個(gè)用戶對(duì)第三本書的評(píng)分情況阅签,所以這里要做一個(gè)user_id
, book_id
到該矩陣坐標(biāo)的對(duì)應(yīng)關(guān)系掐暮,使用pandas的Series表示。
user_id_index_series = pd.Series(range(user_count), index=['user_001', 'user_002', 'user_003', 'user_004', 'user_005', 'user_006'])
book_id_index_series = pd.Series(range(book_count), index=['book_001', 'book_002', 'book_003', 'book_004', 'book_005', 'book_006'])
import numpy as np
def construct_user_item_matrix(df):
user_item_matrix = np.zeros((user_count, user_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], book_id_index_series[book_id]] = rating
return user_item_matrix
user_book_matrix = construct_user_item_matrix(df)
print ('用戶關(guān)系矩陣長這樣:')
print ('-----')
print (user_book_matrix)
用戶關(guān)系矩陣長這樣:
-----
[[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]]
計(jì)算相似度矩陣
所謂相似度政钟,我們這里使用余弦相似度路克,其他的還有皮爾遜相關(guān)度、歐式距離养交、杰卡德相似度等精算,個(gè)中差別暫不細(xì)表。
計(jì)算公式為:
現(xiàn)在已經(jīng)拿到了user_book_matrix
碎连,每個(gè)用戶灰羽、每個(gè)物品都可以對(duì)應(yīng)一個(gè)向量,比如user_book_matrix[2]
為代表user_003
的向量等于[4, 0, 5, 3, 4, 0]
鱼辙,而user_book_matrix[:,2]
則代表了book_003
:[0廉嚼, 4, 5座每, 0前鹅, 0, 2]
峭梳。
這樣基于用戶和基于物品便分別可以計(jì)算出用戶相似度矩陣與物品相似度矩陣舰绘。
以用戶相似度矩陣為例,計(jì)算后會(huì)得到一個(gè)形狀為(user_count, user_count)的矩陣葱椭,比如user_similarity_matrix[2][3]
的值為0.5捂寿,則表示user_002
與user_003
的余弦相似度為0.5。
此矩陣為對(duì)稱矩陣孵运,相應(yīng)地秦陋,user_similarity_matrix[3][2]
亦為0.5,而用戶與自己自然是最相似的治笨,遂有user_similarity_matrix[n][n]
總是等于1驳概。
def cosine_similarity(vec1, vec2):
return round(vec1.dot(vec2)/(np.linalg.norm(vec1)*np.linalg.norm(vec2)), 2)
def construct_similarity_matrix(user_item_matrix, dim='user'):
if dim == 'user':
similarity_matrix = np.zeros((user_count, user_count))
count = user_count
else:
similarity_matrix = np.zeros((book_count, book_count))
count = book_count
get_vector = lambda i: user_item_matrix[i] if dim == 'user' else user_item_matrix[:,i]
for i in range(user_count):
i_vector = get_vector(i)
similarity_matrix[i][i] = cosine_similarity(i_vector, i_vector)
for j in range(i, count):
j_vector = get_vector(j)
similarity = cosine_similarity(i_vector, j_vector)
similarity_matrix[i][j] = similarity
similarity_matrix[j][i] = similarity
return similarity_matrix
user_similarity_matrix = construct_similarity_matrix(user_book_matrix)
book_similarity_matrix = construct_similarity_matrix(user_book_matrix, dim='book')
print ('user_similarity_matrix:')
print (user_similarity_matrix)
print ('book_similarity_matrix:')
print (book_similarity_matrix)
user_similarity_matrix:
[[1. 0.75 0.63 0.22 0.3 0. ]
[0.75 1. 0.91 0. 0. 0.16]
[0.63 0.91 1. 0. 0. 0.4 ]
[0.22 0. 0. 1. 0.97 0.64]
[0.3 0. 0. 0.97 1. 0.53]
[0. 0.16 0.4 0.64 0.53 1. ]]
book_similarity_matrix:
[[1. 0.27 0.79 0.32 0.98 0. ]
[0.27 1. 0. 0. 0.34 0.65]
[0.79 0. 1. 0.69 0.71 0.18]
[0.32 0. 0.69 1. 0.32 0.49]
[0.98 0.34 0.71 0.32 1. 0. ]
[0. 0.65 0.18 0.49 0. 1. ]]
推薦
有了相似度矩陣赤嚼,可以開始進(jìn)行推薦。
首先可以為用戶推薦與其品味相同的用戶列表顺又,這在知乎更卒、豆瓣、網(wǎng)易云音樂這樣具有社交屬性的產(chǎn)品中很有意義稚照。
做法很簡單蹂空,要為用戶A推薦K位品味相似的用戶(此處K取3),則將user_similarity_matrix
中關(guān)于A的那一行的值排序從最高往下取出K位即可果录。
def recommend_similar_users(user_id, n=3):
user_index = user_id_index_series[user_id]
similar_users_index = pd.Series(user_similarity_matrix[user_index]).drop(index=user_index).sort_values(ascending=False).index[:n]
return np.array(similar_users_index)
print ('recommend user_indexes %s to user_001' % recommend_similar_users('user_001'))
recommend user_indexes [1 2 4] to user_001
同時(shí)在物品維度上枕,類似的推薦也是很有用的,比如QQ音樂給用戶正在聽的音樂推薦相似的歌曲弱恒,還有亞馬遜中對(duì)用戶剛購買的物品推薦相似的物品辨萍。
代碼與推薦相似用戶相同,無需做其他處理斤彼。
def recommend_similar_items(item_id, n=3):
item_index = book_id_index_series[item_id]
similar_item_index = pd.Series(book_similarity_matrix[item_index]).drop(index=item_index).sort_values(ascending=False).index[:n]
return np.array(similar_item_index)
print ('recommend item_indexes %s to book_001' % recommend_similar_items('book_001'))
recommend item_indexes [4 2 3] to book_001
接下來是為用戶推薦書籍分瘦,首先選出與該用戶最相似的K個(gè)用戶,然后找出這K個(gè)用戶評(píng)過分的書籍的集合琉苇,再去掉該用戶已經(jīng)評(píng)過分的部分。
在剩下的書籍中悦施,根據(jù)下面的公式并扇,計(jì)算出該用戶為某書籍的預(yù)計(jì)評(píng)分,將評(píng)分從高到低排序輸出即可抡诞。
def recommend_item_to_user(user_id):
user_index = user_id_index_series[user_id]
similar_users = recommend_similar_users(user_id, 2)
recommend_set = set()
for similar_user in similar_users:
recommend_set = recommend_set.union(np.nonzero(user_book_matrix[similar_user])[0])
recommend_set = recommend_set.difference(np.nonzero(user_book_matrix[user_index])[0])
predict = pd.Series([0.0]*len(recommend_set), index=list(recommend_set))
for book_index in recommend_set:
fenzi = 0
fenmu = 0
for j in similar_users:
if user_book_matrix[j][book_index] == 0:
continue # 相似用戶未看過該書則不計(jì)入統(tǒng)計(jì).
fenzi += user_book_matrix[j][book_index] * user_similarity_matrix[j][user_index]
fenmu += user_similarity_matrix[j][user_index]
if fenmu == 0:
continue
predict[book_index] = round(fenzi/fenmu, 2)
return predict.sort_values(ascending=False)
recommend_item_to_user('user_005')
3 4.0
2 2.0
dtype: float64
以上是利用用戶相似度矩陣來為用戶推薦物品穷蛹,同樣也可以反過來為利用物品相似度矩陣來為用戶推薦書籍。
做法是昼汗,找出該用戶讀過的所有書肴熏,為每本書找出兩本與該書最相似的書籍,將找出來的所有書去掉用戶已讀過的顷窒,然后為書籍預(yù)測被用戶評(píng)分的分值蛙吏。
這里的確有些繞,容易與上文纏在一起搞亂掉鞋吉,遂舉例如下:
比如user_001
讀過書book_001
, book_002
鸦做,book_005
,找到的書籍集合再去掉用戶已讀過的結(jié)果為{'book_003', 'book_006'}
谓着,要為book_003
預(yù)測分?jǐn)?shù)泼诱,需要注意到它同時(shí)被book_001
與book_005
找出,要根據(jù)它們赊锚、用戶對(duì)book_001
與book_005
的評(píng)分以及相似度套用至上文公式治筒,來得出對(duì)book_003
的分?jǐn)?shù)為:(4*0.79+5*0.71)/(0.79+0.71)=4.47
屉栓。
則基于物品為用戶推薦物品的函數(shù)為:
def recommend_item_to_user_ib(user_id):
user_index = user_id_index_series[user_id]
user_read_books = np.nonzero(user_book_matrix[user_index])[0]
book_set = set()
book_relation = dict()
for book in user_read_books:
relative_books = recommend_similar_items(book, 2)
book_set = book_set.union(relative_books)
book_relation[book] = relative_books
book_set = book_set.difference(user_read_books)
predict = pd.Series([0.0]*len(book_set), index=list(book_set))
for book in book_set:
fenzi = 0
fenmu = 0
for similar_book, relative_books in book_relation.items():
if book in relative_books:
fenzi += book_similarity_matrix[book][similar_book] * user_book_matrix[user_index][similar_book]
fenmu += book_similarity_matrix[book][similar_book]
predict[book] = round(fenzi/fenmu, 2)
return predict.sort_values(ascending=False)
recommend_item_to_user_ib('user_001')
2 4.47
5 3.00
dtype: float64
總結(jié)
以上是基于領(lǐng)域的協(xié)同過濾的運(yùn)作機(jī)制介紹,只用了兩個(gè)簡單的數(shù)學(xué)公式耸袜,加上各種代碼處理友多,便可以為用戶做出一些推薦。
就給用戶推薦物品而言句灌,基于用戶與基于物品各有特點(diǎn)夷陋。
基于用戶給出的推薦結(jié)果,更依賴于當(dāng)前用戶相近的用戶群體的社會(huì)化行為胰锌,考慮到計(jì)算代價(jià)骗绕,它適合于用戶數(shù)較少的情況,同時(shí)资昧,對(duì)于新加入的物品的冷啟動(dòng)問題比較友好酬土,然而相對(duì)于物品的相似性,根據(jù)用戶之間的相似性做出的推薦的解釋性是比較弱的格带,實(shí)時(shí)性方面撤缴,用戶新的行為不一定會(huì)導(dǎo)致結(jié)果的變化。
基于物品給出的推薦結(jié)果叽唱,更側(cè)重于用戶自身的個(gè)體行為屈呕,適用于物品數(shù)較少的情況,對(duì)長尾物品的發(fā)掘好于基于用戶棺亭,同時(shí)虎眨,新加入的用戶可以很快得到推薦,并且物品之間的關(guān)聯(lián)性更易懂镶摘,是更易于解釋的嗽桩,而且用戶新的行為一定能導(dǎo)致結(jié)果的變化。
顯然凄敢,基于物品總體上要優(yōu)于基于用戶碌冶,歷史上,也的確是基于用戶先被發(fā)明出來涝缝,之后Amazon發(fā)明了基于物品的算法扑庞,現(xiàn)在基于用戶的產(chǎn)品已經(jīng)比較少了。
參考
Intro to Recommender Systems: Collaborative Filtering
【近鄰?fù)扑]】人以群分俊卤,你是什么人就看到什么世界
架構(gòu)師特刊:推薦系統(tǒng)(理論篇)
美團(tuán)推薦算法實(shí)踐