文章嚮導
一、什麼是基於用戶協同過濾算法
- 我覺得概念講高大上了,並沒有什麼用,反而還難以理解。我就用通俗的語言來描述一下這個算法的邏輯吧。這個算法的核心就是這樣的:當前如果有一個用戶 A 他正在等待被推薦,這時我們就會找出平時的行爲和 A 相似的用戶 B ,然後我們再講用戶 B 在意或者感興趣的且 A 還沒瀏覽過的商品推薦給 A ;這就是基於用戶的協同過濾算法。
二、如何計算相似度
- 在前面我們瞭解到了UserCF的算法邏輯了之後,可能你會發現概念中有一個很重要的點就是,計算與A相似的用戶,我們用什麼一個標準來衡量兩個用戶的相似程度了;其實已經引出概念了,我們就是需要用相似度來表示兩個用戶之間的相似程度,但是相似度該如何計算呢?
- 先別急,我們先來想想如果要描述一個人該怎麼描述,當然是用一些這個人的特徵來描述,對吧。我們先來看個用戶的特徵表
用戶 | 遊戲 | 學習 |
---|---|---|
A | 7 | 7 |
B | 6 | 8 |
C | 1 | 10 |
D | 9 | 1 |
- 上面這個中的數字代表各個用戶對學習和遊戲的看法(也可以看做是評分)滿分是10分,我們可以很容易的就把上面的數據加載到圖中來:
把圖畫出來了和你可以很容易的看出 AB 兩點捱得最近,也就是 B 與 A 相較於其他的點與 A 的相似度最高,那麼如何計算相似度呢?上面都已經說了是捱得比較近,捱得比較近指得是是很麼,肯定就是距離了唄,那距離公式就多了,我們平時最常用的就是歐式距離,這裏我們評判用戶的特徵就只用兩個方面,所以就是一個二維,但在實際場景當中,往往用於衡量一個用戶有很多的特徵倆描述,這時我們如果還想使用歐氏距離,就要把它推導至N維,也就是這樣:
至於如何公式的推導,想進一步瞭解的小夥伴可以看博主往期專門講距離公式的博文,上面有具體的推導過程, 以及其他的一些常用的距離公式算法推導,跳轉地址 - 有了上面的概念,我們就知道,我們可以使用兩個用戶之間的 ‘特徵距離‘ 來衡量用戶的的相似度了,不過你先要明確一個概念就是,用戶的特徵距離值越大(也可以說兩個用戶離得越遠)那麼兩個用戶的相似度也就越小,意思就是距離是和相似度呈反比的。
三、算法思路
-
1、首先我們就需要將用戶的一些特徵數據轉換成數值來衡量,因爲畢竟計算機還是比較擅長於數值的處理。
例如現在有一份觀影數據表,表的含義就是各個用戶的觀影情況,以及對此電影的打分。用戶名 觀影名稱 評分 A 老炮兒 3.5 A 唐人街探案 1.0 B 老炮兒 2.5 B 唐人街探案 3.5 B 星球大戰 3.0 B 尋龍訣 3.5 B 神探夏洛克 2.5 B 小門神 3.0 C 老炮兒 3.0 C 唐人街探案 3.5 C 星球大戰 1.5 C 尋龍訣 5.0 C 神探夏洛克 3.0 C 小門神 3.5 D 老炮兒 2.5 D 唐人街探案 3.5 D 尋龍訣 3.5 D 神探夏洛克 4.0 E 老炮兒 3.5 E 唐人街探案 2.0 E 星球大戰 4.5 E 神探夏洛克 3.5 E 小門神 2.0 F 老炮兒 3.0 F 唐人街探案 4.0 F 星球大戰 2.0 F 尋龍訣 3.0 F 神探夏洛克 3.0 F 小門神 2.0 G 老炮兒 4.5 G 唐人街探案 1.5 G 星球大戰 3.0 G 尋龍訣 5.0 G 神探夏洛克 3.5 上面的表有一個很關鍵的指標就是評分,可以通過這個值的大小來看出用戶對該電影的喜愛度(也可以說是感興趣程度),我們都知道描述一個用戶的特徵是多維度的,而且作爲分析需要,我們描述各個用戶都應該從相同的維度來描述。根據上面的思路,再根據表的結構,我們可以將上面的錶轉換成下面的格式:
* 神探夏洛克 小門神 唐人街探案 尋龍訣 老炮兒 星球大戰 A 0 0 1.0 0 3.5 0 B 2.5 3.0 3.5 3.5 2.5 3.0 C 3.0 3.5 3.5 5.0 3.0 1.5 D 4.0 0 3.5 3.5 2.5 0 E 3.5 2.0 2.0 0 3.5 4.5 F 3.0 2.0 4.0 3.0 3.0 2.0 G 3.5 0 1.5 5.0 4.5 3.0 轉換成上面這個表的思路其實就是現將所有(不重複的電影名列出來)然後如果該用戶沒看過該電影,我們就將該用戶對該電影的評分置爲 0,通過這樣的轉換我們就將用戶的特徵由不規則的文本類型,轉換成了標準的數值特徵,例如:用戶 A 的特徵向量就爲
-
2、現在有了特徵矩陣,我們就可以來計算用戶之間的距離了,爲了後續計算的方便,我們不妨將所有用戶之間的距離都計算一遍,也就是計算一個距離矩陣:
* A B C D E F G A 6.61 7.42 5.96 6.12 5.94 6.89 B 6.61 2.29 4.5 4.44 1.73 4.5 C 7.42 2.29 4.24 6.24 2.6 4.58 D 5.96 4.5 4.24 6.32 3.12 4.42 E 6.12 4.44 6.24 6.32 4.44 5.7 F 5.94 1.73 2.6 3.12 4.44 4.21 G 6.89 4.5 4.58 4.42 5.7 4.21 我們隨便找兩個用戶來作爲示例,例如要計算AB兩個用戶之間的距離,那麼我們讀前一個表可以知道,那麼AB之間的距離可以表示爲
因爲用戶和自己求距離必然會是0,但是爲了後面我們要選取相似的用戶時不選到自己,那麼我們把用戶和自己求距離這裏置爲無限大即可。 -
下只能在我們引入UserCF算法公式:
先來解釋一下公式中的參數含義:
(1) 就是計算和用戶 u 最相似的 k 個用戶
(2) 表示對電影 i 有過評分記錄的用戶集合
(3)
(4) 表示用戶 v 對電影 i 喜好度,也就是用戶 v 對 電影i的評分
然後通過這個公式計算出來值就是推薦指數(爲了方便,我們這裏就用距離代表用戶之間的相似度,所以最後求出來的推薦度應該是越小,該物品就越值得推薦)
四、代碼實現
1、準備數據
import numpy as np
movies_data_dict = {
'A': {'老炮兒':3.5,'唐人街探案': 1.0},
'B': {'老炮兒':2.5,'唐人街探案': 3.5,'星球大戰': 3.0, '尋龍訣': 3.5, '神探夏洛克': 2.5, '小門神': 3.0},
'C': {'老炮兒':3.0,'唐人街探案': 3.5,'星球大戰': 1.5, '尋龍訣': 5.0, '神探夏洛克': 3.0, '小門神': 3.5},
'D': {'老炮兒':2.5,'唐人街探案': 3.5,'尋龍訣': 3.5, '神探夏洛克': 4.0},
'E': {'老炮兒':3.5,'唐人街探案': 2.0,'星球大戰': 4.5, '神探夏洛克': 3.5,'小門神': 2.0},
'F': {'老炮兒':3.0,'唐人街探案': 4.0,'星球大戰': 2.0, '尋龍訣': 3.0,'神探夏洛克': 3.0, '小門神': 2.0},
'G': {'老炮兒':4.5,'唐人街探案': 1.5,'星球大戰': 3.0, '尋龍訣': 5.0,'神探夏洛克': 3.5}
}
2、編寫將數據標準化的函數
# 將整個數據集轉換成矩陣形式,如果用戶沒有對該電影進行評分則該位置置爲 0
def MoveiesScaler(data):
movie_index = {}
i = 0
for _,ratings in data.items():
for movie,score in ratings.items():
if movie_index.get(movie) is None:
movie_index[movie] = i
i += 1
result_data = {}
for user,ratings in data.items():
user_ratings = np.zeros(len(movie_index))
for movie,score in ratings.items():
user_ratings[movie_index[movie]] = score
result_data[user] = user_ratings
return movie_index,result_data
3、編寫特徵距離計算函數
# 計算兩個用戶的歐式距離
def EuclideanDistance(feature1,feature2):
return np.sqrt(np.sum(np.power(feature1-feature2,2)))
# 計算所有用戶倆倆之間的距離,也就是距離矩陣,用戶對自己求距離時將距離置爲無窮大方便後續處理
def DistanceMatrix(user_dict):
result_matrix = np.zeros((len(user_dict),len(user_dict)))
user_index = dict(zip(user_dict.keys(),range(len(user_dict))))
features = list(user_dict.values())
for r in range(len(features)):
for c in range(r,len(features)):
if c == r:
result_matrix[r][c] = np.Inf
else:
ed = EuclideanDistance(features[r],features[c])
result_matrix[r][c] = ed
result_matrix[c][r] = ed
return user_index,result_matrix
4、 和用戶u最相似的k個用戶
def similarity_max_k(similarity_matrix,user_index,u,k):
similarity_list = similarity_matrix[user_index[u]]
user_sim_map = dict(zip(similarity_list.tolist(),user_index.keys()))
least_k_keys = sorted(user_sim_map)[:k]
least_k = set({user_sim_map[k] for k in least_k_keys})
return least_k
5、對電影 i 有過評分的用戶
def user_with_item(data_sets,i):
user_set = set()
for user,ratings in data_sets.items():
if(ratings[i] != 0):
user_set.add(user)
return user_set
6、 用戶 v 對 電影i的評分
def interest_degree(data_sets,v,i):
return data_sets[v][i]
7、將前面的函數組裝起來就是UserCF的公式實現
def recommended_score(datasets,u,k,i):
user_index,similarity_matrix = DistanceMatrix(data_sets)
similarity_k_set = similarity_max_k(similarity_matrix,user_index,u,k)
user_item_set = user_with_item(data_sets,i)
iter_set = similarity_k_set & user_item_set
result = 0
for v in iter_set:
v_i = user_index[v]
u_i = user_index[u]
w = similarity_matrix[v_i][u_i]
result += (w*interest_degree(data_sets,v,i))
return result
8、查看各個商品對用戶的推薦度
for i in range(6):
print(recommended_score(movies_data_dict,"A",2,i))
out:
32.70698224032311
44.60234092774856
11.874342087037917
38.6651698842296
41.64426370618284
11.874342087037917
我們可以尋找出推薦度最小的(因爲推薦度公式那兒我們已經換成了距離來計算,所以要越小越好)那幾個商品,且用戶A還沒有看過的,那麼我們就可以把這些商品推薦給他。
補充:我們可以使用不同的距離公式來計算兩個用戶之間的特徵距離,比較好的就是 Jaccard(傑卡德距離),餘弦距離您可以嘗試使用不同的距離計算公式來嘗試,然後調整您推薦算法的準確率。