0%

KNN在推荐领域的应用

介绍KNN在推荐领域的应用。

前言

前面已经初步介绍了推荐系统,同时作者也介绍了机器学习算法KNN,那么本文着重介绍如何具体将KNN算法应用于推荐领域。

思想

利用与该用户喜好相似(皮尔逊相关)的前N个用户,对某电影的评分来预测该用户对某电影的评分

评分预测公式:

数据集获取

数据集来自于MovieLens Latest Datasets Small

为方便单机实验,本文采用ml-latest-small.zip

实现流程

  1. 数据获取。

  2. 数据准备(为提高速度,本文采用pandas的read_pickle&to_pickle进行中间结果缓存)

    缓存&计算皮尔逊相似度。

  3. 预测(核心)

    1. 找到皮尔逊正相关前N个相似用户。
    2. 利用pred(u,i)公式计算相似用户对某个电影的评分,即预测出的评分。
  4. 对上述算法流程调用&调试。

对应代码

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import os

import pandas as pd
import numpy as np

DATA_PATH = "./data/ml-latest-small/ratings.csv"
CACHE_DIR = "./data/"

def load_data(data_path):
'''
加载数据
:param data_path: 数据集路径
:param cache_path: 数据集缓存路径
:return: 用户-物品评分矩阵
'''
# 数据集缓存地址
cache_path = os.path.join(CACHE_DIR, "ratings_matrix.cache")

print("开始加载数据集...")
if os.path.exists(cache_path): # 判断是否存在缓存文件
print("加载缓存中...")
ratings_matrix = pd.read_pickle(cache_path)
print("从缓存加载数据集完毕")
else:
print("加载新数据中...")
# 设置要加载的数据字段的类型
dtype = {"userId": np.int32, "movieId": np.int32, "rating": np.float32}
# 加载数据,分别是用户ID,电影ID,已经用户对电影的对应评分(使用前3列)
ratings = pd.read_csv(data_path, dtype=dtype, usecols=range(3))
# 透视表,将电影ID转换为列名称,转换成为一个User-Movie的评分矩阵
ratings_matrix = ratings.pivot_table(index=["userId"], columns=["movieId"], values="rating")
# 存入缓存文件
ratings_matrix.to_pickle(cache_path)
print("数据集加载完毕")
return ratings_matrix

def compute_pearson_similarity(ratings_matrix):
'''
计算用户皮尔逊相关系数
:param ratings_matrix: 用户-物品评分矩阵
:return: 相似度矩阵
'''
user_similarity_cache_path = os.path.join(CACHE_DIR, "user_similarity.cache")
# 基于皮尔逊相关系数计算相似度
# 用户相似度

if os.path.exists(user_similarity_cache_path):
print("正从缓存加载用户相似度矩阵")
similarity = pd.read_pickle(user_similarity_cache_path)
else:
print("开始计算用户相似度矩阵")
similarity = ratings_matrix.T.corr()
similarity.to_pickle(user_similarity_cache_path)

print("相似度矩阵计算/加载完毕")
return similarity

def predict(uid, iid, ratings_matrix, user_similar):
'''
预测给定用户对给定物品的评分值
:param uid: 用户ID
:param iid: 物品ID
:param ratings_matrix: 用户-物品评分矩阵
:param user_similar: 用户两两相似度矩阵
:return: 预测的评分值
'''
print("开始预测用户<%d>对电影<%d>的评分..."%(uid, iid))
# 1. 找出uid用户的相似用户
similar_users = user_similar[uid].drop([uid]).dropna().sort_values(ascending=False)[:25]
# 相似用户筛选规则:正相关的用户
similar_users = similar_users.where(similar_users>0).dropna()
if similar_users.empty is True:
raise Exception("用户<%d>没有相似的用户" % uid)

# 2. 从uid用户的近邻相似用户中筛选出对iid物品有评分记录的近邻用户
ids = set(ratings_matrix[iid].dropna().index)&set(similar_users.index)
finally_similar_users = similar_users.ix[list(ids)]

if finally_similar_users.empty is True:
raise Exception("用户<%d>相似的用户没有对<%d>电影的评分" % (uid, iid))

# 3. 结合uid用户与其近邻用户的相似度预测uid用户对iid物品的评分
sum_up = 0 # 评分预测公式的分子部分的值
sum_down = 0 # 评分预测公式的分母部分的值
for sim_uid, similarity in finally_similar_users.iteritems():
# 近邻用户的评分数据
# sim_user_rated_movies = ratings_matrix.ix[sim_uid].dropna()
# 近邻用户对iid物品的评分
# sim_user_rating_for_item = sim_user_rated_movies[iid]
sim_user_rating_for_item = ratings_matrix.ix[sim_uid, iid]
# 计算分子的值
sum_up += similarity * sim_user_rating_for_item
# 计算分母的值
sum_down += similarity

# 计算预测的评分值并返回
predict_rating = sum_up/sum_down
print("预测出用户<%d>对电影<%d>的评分:%0.2f" % (uid, iid, predict_rating))
return round(predict_rating, 2)

if __name__ == '__main__':
ratings_matrix = load_data(DATA_PATH)
print(ratings_matrix.head())
user_similar = compute_pearson_similarity(ratings_matrix)
print(user_similar.head())
# 预测用户1对物品1的评分
predict(1, 1, ratings_matrix, user_similar)
# 预测用户1对物品2的评分
predict(1, 2, ratings_matrix, user_similar)

参考&致谢

  1. python中dataframe常见操作:取行、列、切片、统计特征值
  2. Python 基本操作- 数据选取loc、iloc、ix函数
  3. 黑马机器学习讲义
觉得不错?