机器学习-推荐系统

推荐系统是一种信息过滤系统,用于预测用户对物品的评分或偏好。这个定义不是很好理解,因为它用“怎么做”来定义“是什么”,我们可以换个角度来理解:

推荐引擎

推荐引擎意在把最需要的推荐给用户。

在不同的机器学习场景中通常需要分析相似样本。而统计相似样本的方式可以基于欧氏距离分数,也可基于皮氏距离分数。

欧氏距离分数
$$
欧氏距离分数 = \frac{1}{1+欧氏距离}
$$
计算所得欧氏距离分数区间处于:[0, 1],越趋于0样本间的欧氏距离越远,样本越不相似;越趋于1,样本间的欧氏距离越近,越相似。

构建样本之间的欧氏距离得分矩阵:
$$
\left[ \begin{array}{c} & a & b & c & d & .. \ a & 1 & 0.2 & 0.3 & 0.4 & .. \ b & 0.2 & 1 & x & x & .. \ c & 0.3 & x & 1 & x & .. \ d & 0.4 & x & x & 1 & .. \ .. & .. & .. & .. & .. & .. \ \end{array} \right]
$$
案例:解析ratings.json,根据每个用户对已观看电影的打分计算样本间的欧氏距离,输出欧氏距离得分矩阵。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import json
import numpy as np

with open('../data/ratings.json', 'r') as f:
ratings = json.loads(f.read())
users, scmat = list(ratings.keys()), []
for user1 in users:
scrow = []
for user2 in users:
movies = set()
for movie in ratings[user1]:
if movie in ratings[user2]:
movies.add(movie)
if len(movies) == 0:
score = 0
else:
x, y = [], []
for movie in movies:
x.append(ratings[user1][movie])
y.append(ratings[user2][movie])
x = np.array(x)
y = np.array(y)
score = 1 / (1 + np.sqrt(((x - y) ** 2).sum()))
scrow.append(score)
scmat.append(scrow)
users = np.array(users)
scmat = np.array(scmat)
for scrow in scmat:
print(' '.join('{:.2f}'.format(score) for score in scrow))

皮尔逊相关系数

1
2
3
A = [1,2,3,1,2] 
B = [3,4,5,3,4]
m = np.corrcoef(A, B)

皮尔逊相关系数 = 协方差 / 标准差之积

相关系数处于[-1, 1]区间。越靠近-1代表两组样本反相关,越靠近1代表两组样本正相关。

案例:使用皮尔逊相关系数计算两用户对一组电影评分的相关性。

1
score = np.corrcoef(x, y)[0, 1]

按照相似度从高到低排列每个用户的相似用户

1
2
3
4
5
6
7
8
# scmat矩阵中每一行为 每一个用户对所有用户的皮尔逊相关系数
for i, user in enumerate(users):
# 拿到所有相似用户与相似用户所对应的皮尔逊相关系数
sorted_indices = scmat[i].argsort()[::-1]
sorted_indices = sorted_indices[sorted_indices != i]
similar_users = users[sorted_indices]
similar_scores = scmat[i, sorted_indices]
print(user, similar_users, similar_scores, sep='\n')

生成推荐清单

  1. 找到所有皮尔逊系数正相关的用户
  2. 遍历当前用户的每个相似用户,拿到相似用户看过但是当前用户没有看过的电影作为推荐电影
  3. 多个相似用户有可能推荐同一部电影,则取每个相似用户对该电影的评分得均值作为推荐度。
  4. 可以把相似用户的皮尔逊系数作为权重,皮尔逊系数越大,推荐度越高。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 找到所有皮尔逊系数正相关的用户
positive_mask = similar_scores > 0
similar_users = similar_users[positive_mask]
# 相似用户对应的皮尔逊相关系数
similar_scores = similar_scores[positive_mask]
#存储对于当前用户所推荐的电影以及电影的推荐度(推荐电影的平均分)
recomm_movies = {}
#遍历当前用户的每个相似用户
for i, similar_user in enumerate(similar_users):
#拿到相似用户看过但是当前用户没有看过的电影
for movie, score in ratings[similar_user].items():
if (movie not in ratings[user].keys()):
if movie not in recomm_movies:
recomm_movies[movie] = []
else:
recomm_movies[movie].append(score)

print(user)
movie_list = sorted(recomm_movies.items(), key=lambda x:np.average(x[1]), reverse=True)
print(movie_list)