用户 | 游戏 | 学习 |
---|---|---|
A | 7 | 7 |
B | 6 | 8 |
C | 1 | 10 |
D | 9 | 1 |
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 的特征向量就为 V A = < 0 , 0 , 1.0 , 0 , 3.5 , 0 > V_A=<0,0,1.0,0,3.5,0> VA=<0,0,1.0,0,3.5,0>
2、现在有了特征矩阵,我们就可以来计算用户之间的距离了,为了后续计算的方便,我们不妨将所有用户之间的距离都计算一遍,也就是计算一个距离矩阵:
* | A | B | C | D | E | F | G |
---|---|---|---|---|---|---|---|
A | ∞ \infty ∞ | 6.61 | 7.42 | 5.96 | 6.12 | 5.94 | 6.89 |
B | 6.61 | ∞ \infty ∞ | 2.29 | 4.5 | 4.44 | 1.73 | 4.5 |
C | 7.42 | 2.29 | ∞ \infty ∞ | 4.24 | 6.24 | 2.6 | 4.58 |
D | 5.96 | 4.5 | 4.24 | ∞ \infty ∞ | 6.32 | 3.12 | 4.42 |
E | 6.12 | 4.44 | 6.24 | 6.32 | ∞ \infty ∞ | 4.44 | 5.7 |
F | 5.94 | 1.73 | 2.6 | 3.12 | 4.44 | ∞ \infty ∞ | 4.21 |
G | 6.89 | 4.5 | 4.58 | 4.42 | 5.7 | 4.21 | ∞ \infty ∞ |
我们随便找两个用户来作为示例,例如要计算AB两个用户之间的距离,那么我们读前一个表可以知道 V A = < 0 , 0 , 1.0 , 0 , 3.5 , 0 > , V B = < 2.5 , 3.0 , 3.5 , 3.5 , 2.5 , 3.0 > V_A=<0,0,1.0,0,3.5,0>,V_B=<2.5,3.0,3.5,3.5,2.5,3.0> VA=<0,0,1.0,0,3.5,0>,VB=<2.5,3.0,3.5,3.5,2.5,3.0>,那么AB之间的距离可以表示为
D A B = ( 0 − 2.5 ) 2 + ( 0 − 3.0 ) 2 + ( 1 − 3.5 ) 2 + ( 0 − 3.5 ) 2 + ( 3.5 − 2.5 ) 2 + ( 0 − 3.0 ) 2 = 6.61 \displaystyle D_{AB}=\sqrt{(0-2.5)^2+(0-3.0)^2+(1-3.5)^2+(0-3.5)^2+(3.5-2.5)^2+(0-3.0)^2}=6.61 DAB=(0−2.5)2+(0−3.0)2+(1−3.5)2+(0−3.5)2+(3.5−2.5)2+(0−3.0)2=6.61
因为用户和自己求距离必然会是0,但是为了后面我们要选取相似的用户时不选到自己,那么我们把用户和自己求距离这里置为无限大即可。
下只能在我们引入UserCF算法公式:
p ( u , i ) = ∑ v ∈ S ( u , k ) ∩ N ( i ) w u v r v i \displaystyle p(u,i)=\sum_{v\in S(u,k)\cap N(i)}{w_{uv}r_{vi}} p(u,i)=v∈S(u,k)∩N(i)∑wuvrvi
先来解释一下公式中的参数含义:
(1) S ( u , k ) S(u,k) S(u,k) 就是计算和用户 u 最相似的 k 个用户
(2) N ( i ) N(i) N(i)表示对电影 i 有过评分记录的用户集合
(3) w u v 表 示 用 户 u 和 用 户 v 之 间 的 相 似 度 w_{uv}表示用户u和用户v之间的相似度 wuv表示用户u和用户v之间的相似度
(4) r v i r_{vi} rvi表示用户 v 对电影 i 喜好度,也就是用户 v 对 电影i的评分
然后通过这个公式计算出来值就是推荐指数(为了方便,我们这里就用距离代表用户之间的相似度,所以最后求出来的推荐度应该是越小,该物品就越值得推荐)
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}
}
# 将整个数据集转换成矩阵形式,如果用户没有对该电影进行评分则该位置置为 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
# 计算两个用户的欧式距离
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
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
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
def interest_degree(data_sets,v,i):
return data_sets[v][i]
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
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(杰卡德距离),余弦距离您可以尝试使用不同的距离计算公式来尝试,然后调整您推荐算法的准确率。