项目实战----基于协同过滤的电影推荐系统


网页版—点击这里

一、数据整理

数据及介绍
MovieLens是推荐系统常用的数据集
MovieLens数据集中,用户对自己看过的电影进行评分,分值为1-5.
MovieLens包括两个大小不同的库。适用于不同规模的算法,
小规模是943个用户对1682部电影做约10000次评分的数据
大规模的是6040个用户对3900部电影做大约100万次评分

导入数据

from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
%matplotlib inline
import numpy as np
import pandas as pd

#设置数据列名
header = ["user_id","item_id","rating","timesamp"]
src_data = pd.read_csv("./ml-100k/u.data",sep='\t',names=header)
src_data.head()

查看结构

src_data.info()
src_data.describe()

查看用户去重后的个数

src_data.user_id.nunique()

查看物品去重后的个数

src_data.item_id.nunique()
#检查事都有重复用户物品打分记录
src_data.duplicated(subset=["user_id","item_id"]).sum()
#每一个电影对应的客户数
item_id_usercnt = src_data.groupby("item_id").count()["user_id"]
item_id_usercnt

画图

import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"]=['SimHei']  # 用于正常显示中文标签
plt.rcParams['axes.unicode_minus']=False  # 用来正常显示负号


plt.title('每个物品对应的用户数量')
plt.xlabel("评价的客户数/人")
plt.ylabel("被评论的电影数")
plt.hist(item_id_usercnt.values)

###每个物品对应的用户数,(10分位,20分位,30分位。。。。100分位)
np.arange(0,1.1,step=0.1)
item_id_usercnt.quantile(q=np.arange(0,1.1,step=0.1))

#每个用户评价电影的个数
user_id_usercnt = src_data.groupby('user_id').count()["item_id"]
user_id_usercnt.values

import matplotlib.pyplot as plt
#画图
plt.hist(user_id_usercnt.values)
user_id_usercnt.quantile(q=np.arange(0,1.1,step=0.1))

二、观察用户-电影矩阵

n_users = src_data.user_id.nunique()
n_items = src_data.item_id.nunique()
print(n_users)
print(n_items)

构建用户-电影评分矩阵

src_data_matrix = np.zeros((n_users,n_items)) 
# print(src_data_matrix)    产生一个类似的全是0元素的矩阵

#src_data.itertuples()###将DataFrame转为元组##############
for line in src_data.itertuples():
#     print(line)
    src_data_matrix[line[1]-1,line[2]-1] = line[3]   #将电影和评分数给line【3】
#     print(line[3])
src_data_matrix

src_data.itertuples

判断矩阵的稀疏性

sparsity = round(len(src_data_matrix.nonzero() \
                    [1])/float(n_users*n_items),3)
sparsity #非常稀疏

三、协同过滤推荐

3.1、基于电影的协同过滤

#使用sklearn.metrics.pairwise中的cosine
from sklearn.metrics.pairwise import pairwise_distances
item_similarity_m = pairwise_distances(src_data_matrix.T,metric="cosine")
item_similarity_m.shape

数据探索
1、电影相似矩阵

#非0值得比例
round(np.sum(item_similarity_m>0)
      /float(item_similarity_m.shape[0]
             *item_similarity_m.shape[1]),3)
#相似矩阵为对称矩阵
item_similarity_m[0:5,0:5].round(2)

#因为是对称的,分析上三角,得到分位数
item_similarity_m_triu = np.triu(item_similarity_m,k=1)
item_sim_nonzero = np.round(item_similarity_m_triu[item_similarity_m_triu.nonzero()],3)
np.percentile(item_sim_nonzero,np.arange(0,101,10))

#相似度得分比较大,相似度没有区分性
"""
#知识点:上三角np.triu
arr = np.linspace(1,9,9).reshape(3,3)
np.triu(arr,k=1)
"""

#########预测

# 得到预测矩阵P
user_item_prediction = src_data_matrix.dot(item_similarity_m)/ np.array([np.abs(item_similarity_m).sum(axis=1)])
user_item_prediction

# 只取预测数据集中有评分的数据集,进行评估
from sklearn.metrics import mean_squared_error
from math import sqrt
prediction_flatten = user_item_prediction[src_data_matrix.nonzero()] 
user_item_matrix_flatten = src_data_matrix[src_data_matrix.nonzero()]
sqrt(mean_squared_error(prediction_flatten, user_item_matrix_flatten))

# 测试数据集构建
test_data_matrix = np.zeros((n_users, n_items))
for line in src_data.itertuples():
    test_data_matrix[line[1]-1, line[2]-1] = line[3]

# 预测矩阵
item_prediction = src_data_matrix.dot(item_similarity_m) / np.array([np.abs(item_similarity_m).sum(axis=1)])     

# 只取预测数据集中有评分的数据集
from sklearn.metrics import mean_squared_error
from math import sqrt
prediction_flatten = item_prediction[test_data_matrix.nonzero()] 
test_data_matrix_flatten = test_data_matrix[test_data_matrix.nonzero()]
sqrt(mean_squared_error(prediction_flatten, test_data_matrix_flatten))

2、单模型结果提升



# 相似度算法指定为欧氏距离
item_similarity_m = pairwise_distances(src_data_matrix.T, metric='euclidean')
item_similarity_m

from sklearn.model_selection import train_test_split
train_data, test_data = train_test_split(src_data, test_size=0.2)

3.2、基于用户的协同过滤推荐

#导入模块
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
import numpy as np
import pandas as pd
header = ['user_id', 'item_id', 'rating', 'timestamp']
src_data = pd.read_csv('ml-100k/u.data', sep='\t', names=header)
src_data.head()

# 用户、物品数统计
n_users = src_data.user_id.nunique()
n_items = src_data.item_id.nunique() 
# 训练集、测试集分离
from sklearn.model_selection import train_test_split
train_data, test_data = train_test_split(src_data, test_size=0.3)

# 训练集 用户-物品矩阵
train_data_matrix = np.zeros((n_users, n_items))
for line in train_data.itertuples():
    train_data_matrix[line[1]-1, line[2]-1] = line[3] 

1、用户相似度矩阵

#采用余弦距离
from sklearn.metrics.pairwise import pairwise_distances
user_similarity_m = pairwise_distances(train_data_matrix,metric="cosine")

2、数据探索
2.1、用户相似矩阵

#物品相似矩阵,行列
user_similarity_m.shape

#非0 比例
#round四舍五入
round(np.sum(user_similarity_m>0)/float(user_similarity_m.shape[0]
                                        *user_similarity_m.shape[1]),3)

# 相似矩阵为对称矩阵
user_similarity_m[0:5, 0:5].round(2)


#现在我们分析上三角,得到等分位数
user_similarity_m_triu = np.triu(user_similarity_m,k=1)
item_sim_nonzero2 = np.round(user_similarity_m_triu[
            user_similarity_m_triu.nonzero()
            ],3)
np.percentile(item_sim_nonzero2,np.arange(0,101,10))

"""
##############可以看出相似度得分都偏大,相似度没有区分性
"
# 得到预测矩阵P
mean_user_rating = train_data_matrix.mean(axis=1)
ratings_diff = (train_data_matrix - mean_user_rating[:, np.newaxis])   #升维度
user_prediction = mean_user_rating[:, np.newaxis] + \
    user_similarity_m.dot(ratings_diff) / \
    np.array([np.abs(user_similarity_m).sum(axis=1)]).T

2.2、训练数据

from sklearn.metrics import mean_squared_error
from math import sqrt
prediction_flatten = user_prediction[train_data_matrix.nonzero()] 
train_data_matrix_flatten = train_data_matrix[train_data_matrix.nonzero()]
sqrt(mean_squared_error(prediction_flatten, train_data_matrix_flatten))

2.3、测试集预测

# 测试数据集构建
test_data_matrix = np.zeros((n_users, n_items))
for line in test_data.itertuples():
    test_data_matrix[line[1]-1, line[2]-1] = line[3]

2.4、只取预测数据集中有评分的数据集

from sklearn.metrics import mean_squared_error
from math import sqrt
prediction_flatten = user_prediction[test_data_matrix.nonzero()] 
test_data_matrix_flatten = test_data_matrix[test_data_matrix.nonzero()]
sqrt(mean_squared_error(prediction_flatten, test_data_matrix_flatten))

3、提升

#####相似度算法指定为欧氏距离
user_similarity_m = pairwise_distances(train_data_matrix, \
                                    metric='euclidean')
train_data, test_data = train_test_split(src_data, test_size=0.2)

3.3、基于SVD的协同过滤

import scipy.sparse as sp
from scipy.sparse.linalg import svds

#get SVD components from train matrix. Choose k.
u, s, vt = svds(train_data_matrix, k = 20)
s_diag_matrix=np.diag(s)
svd_prediction = np.dot(np.dot(u, s_diag_matrix), vt)
u.shape
s.shape
vt.shape
s_diag_matrix.shape
svd_prediction.shape
"""
(943, 20)
(20,)
(20, 1682)
(20, 20)
(943, 1682)
"""
# 查看预测矩阵值分布
pd.Series(np.percentile(svd_prediction, np.arange(0, 101, 10))).map("{:.2f}".format)
# 查看训练数据矩阵值分布
pd.Series(np.percentile( train_data_matrix, np.arange(0, 101, 10))).map("{:.2f}".format)
# 查看训练数据矩阵非0值分布
pd.Series(np.percentile( train_data_matrix[train_data_matrix.nonzero()],\
                 np.arange(0, 101, 10))).map("{:.2f}".format)

## 预测值限定最小值和最大值
# 将预测值中小于0的值,赋值为0
svd_prediction[svd_prediction<0] = 0
# 将预测值中大于5的值,赋值为5
svd_prediction[svd_prediction>5] = 5

评估

# 只取预测数据集中有评分的数据集,进行评估
from sklearn.metrics import mean_squared_error
from math import sqrt
prediction_flatten = svd_prediction[train_data_matrix.nonzero()] 
train_data_matrix_flatten = train_data_matrix[train_data_matrix.nonzero()]
sqrt(mean_squared_error(prediction_flatten, train_data_matrix_flatten))


# 只取预测数据集中有评分的数据集
from sklearn.metrics import mean_squared_error
from math import sqrt
prediction_flatten = svd_prediction[test_data_matrix.nonzero()] 
test_data_matrix_flatten = test_data_matrix[test_data_matrix.nonzero()]
sqrt(mean_squared_error(prediction_flatten, test_data_matrix_flatten))

猜你喜欢

转载自blog.csdn.net/Sakura55/article/details/81364961
今日推荐