协同过滤算法电影推荐系统详解:Python实现指南
一、项目背景与目标
1.1 背景
协同过滤(Collaborative Filtering, CF)是推荐系统中最常用的技术之一,分为两种主要类型:
1.2 目标
本项目的目标是:
- 计算用户之间的杰尔德相似性和物品之间的杰尔德相似性。
- 为每个用户找到最相似的两个用户。
- 基于用户协同过滤和物品协同过滤为指定用户推荐 3 到 5 部电影。
二、数据准备
2.1 数据集来源
我们使用 MovieLens 提供的小型数据集 ml-latest-small
,包含以下四个文件:
获取数据点这里
ratings.csv
:用户对电影的评分信息(用户ID、电影ID、评分、时间戳)。movies.csv
:电影的基本信息(电影ID、电影标题、电影类型)。links.csv
:电影在外部数据库中的链接信息(电影ID、IMDb ID、TMDB ID)。tags.csv
:用户对电影的标签信息(用户ID、电影ID、标签、时间戳)。2.2 数据加载与预览
加载数据并显示前五行数据,快速了解数据结构:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics.pairwise import pairwise_distances
from collections import defaultdict
import warnings
# 忽略 DataConversionWarning 警告
warnings.filterwarnings("ignore", category=UserWarning)
warnings.filterwarnings("ignore", category=RuntimeWarning)
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei'] # 使用黑体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 加载数据
ratings_path = r"d:\Users\Administrator\Desktop\3 电影推荐系统(学生)\ml-latest-small\ratings.csv"
movies_path = r"d:\Users\Administrator\Desktop\3 电影推荐系统(学生)\ml-latest-small\movies.csv"
links_path = r"d:\Users\Administrator\Desktop\3 电影推荐系统(学生)\ml-latest-small\links.csv"
tags_path = r"d:\Users\Administrator\Desktop\3 电影推荐系统(学生)\ml-latest-small\tags.csv"
ratings = pd.read_csv(ratings_path)
movies = pd.read_csv(movies_path)
links = pd.read_csv(links_path)
tags = pd.read_csv(tags_path)
# 显示每个数据集的前五个数据
print("\n评分数据 (ratings) 的前五行:")
display(ratings.head())
print("\n电影数据 (movies) 的前五行:")
display(movies.head())
print("\n链接数据 (links) 的前五行:")
display(links.head())
print("\n标签数据 (tags) 的前五行:")
display(tags.head())
三、数据处理与特征工程
3.1 构建用户-电影评分矩阵
为了便于后续计算用户和电影之间的相似性,我们将评分数据转换为用户-电影评分矩阵,其中行表示用户,列表示电影,矩阵值为评分:
# 构建用户-电影评分矩阵
user_movie_matrix = ratings.pivot(index='userId', columns='movieId', values='rating').fillna(0)
# 用户-电影评分矩阵稀疏性可视化
plt.figure(figsize=(10, 6))
sns.heatmap(user_movie_matrix != 0, cbar=False, cmap='viridis')
plt.title("用户-电影评分矩阵稀疏性")
plt.xlabel("电影ID")
plt.ylabel("用户ID")
plt.show()
3.2 数据稀疏性可视化
用户-电影评分矩阵通常具有较高的稀疏性,我们可以通过热力图直观地展示其分布情况:
四、模型构建
4.1 用户相似性计算
使用杰尔德相似性(Jaccard Similarity)计算用户之间的相似度,并生成用户相似性矩阵:
# 计算用户之间的杰尔德相似性
user_similarity = 1 - pairwise_distances(user_movie_matrix.to_numpy(), metric="jaccard")
user_similarity_df = pd.DataFrame(user_similarity, index=user_movie_matrix.index, columns=user_movie_matrix.index)
# 用户相似性矩阵可视化
plt.figure(figsize=(10, 8))
sns.heatmap(user_similarity_df, annot=True, cmap='coolwarm')
plt.title("用户相似性矩阵(杰尔德相似性)")
plt.xlabel("用户ID")
plt.ylabel("用户ID")
plt.show()
4.2 物品相似性计算
同样使用杰尔德相似性计算电影之间的相似度,并生成电影相似性矩阵
# 计算物品之间的杰尔德相似性
item_similarity = 1 - pairwise_distances(user_movie_matrix.T.to_numpy(), metric="jaccard")
item_similarity_df = pd.DataFrame(item_similarity, index=user_movie_matrix.columns, columns=user_movie_matrix.columns)
# 物品相似性矩阵可视化
plt.figure(figsize=(10, 8))
sns.heatmap(item_similarity_df, annot=True, cmap='coolwarm')
plt.title("物品相似性矩阵(杰尔德相似性)")
plt.xlabel("电影ID")
plt.ylabel("电影ID")
plt.show()
五、推荐系统实现
5.1 基于用户的协同过滤(UserCF)
为每个用户找到最相似的两个用户,并为其推荐未评分的电影:
# 找到每个用户最相似的两个用户
most_similar_users = {}
for user in user_similarity_df.index:
similar_users = user_similarity_df[user].sort_values(ascending=False)[1:3]
most_similar_users[user] = similar_users
# 输出结果
print("每个用户最相似的两个用户:")
for user, similar_users in most_similar_users.items():
print(f"用户 {user} 最相似的两个用户:")
display(similar_users)
基于用户的协同过滤函数
# 基于用户的协同过滤推荐函数
def recommend_movies_usercf(user_id, top_n=5):
user_ratings = user_movie_matrix.loc[user_id]
unrated_movies = user_ratings[user_ratings == 0].index
similar_users = most_similar_users[user_id].index
recommendations = defaultdict(float)
for movie in unrated_movies:
for similar_user in similar_users:
if user_movie_matrix.loc[similar_user, movie] > 0:
recommendations[movie] += user_similarity_df.loc[user_id, similar_user] * user_movie_matrix.loc[similar_user, movie]
recommendations = sorted(recommendations.items(), key=lambda x: x[1], reverse=True)[:top_n]
return [(movies[movies['movieId'] == movie]['title'].values[0], score) for movie, score in recommendations]
5.2 基于物品的协同过滤(ItemCF)
为目标用户推荐与其已评分电影相似的其他电影:
# 基于物品的协同过滤推荐函数
def recommend_movies_itemcf(user_id, top_n=5):
user_ratings = user_movie_matrix.loc[user_id]
rated_movies = user_ratings[user_ratings > 0].index
recommendations = defaultdict(float)
for movie in item_similarity_df.index:
if movie not in rated_movies:
for rated_movie in rated_movies:
recommendations[movie] += item_similarity_df.loc[movie, rated_movie] * user_ratings[rated_movie]
recommendations = sorted(recommendations.items(), key=lambda x: x[1], reverse=True)[:top_n]
return [(movies[movies['movieId'] == movie]['title'].values[0], score) for movie, score in recommendations]
六、结果展示
6.1 示例推荐
为用户 1 和用户 16分别推荐电影:
(可自定义)
user_ids = [1, 16]
for user_id in user_ids:
recommendations_usercf = recommend_movies_usercf(user_id)
print(f"\n为用户 {user_id} 推荐的电影(基于用户的协同过滤):")
for title, score in recommendations_usercf:
print(f"{title} (推荐分数: {score:.2f})")
recommendations_itemcf = recommend_movies_itemcf(user_id)
print(f"\n为用户 {user_id} 推荐的电影(基于物品的协同过滤):")
for title, score in recommendations_itemcf:
print(f"{title} (推荐分数: {score:.2f})")
按照相似度推荐评分最高的几个进行推荐
七、总结
本文介绍了如何使用 Python 构建一个基于协同过滤算法的电影推荐系统。通过计算用户和电影之间的相似性,我们可以为用户提供个性化的电影推荐。本项目的主要步骤包括:
- 数据加载与预处理。
- 用户和电影相似性计算。
- 基于用户和基于物品的协同过滤推荐。
八、不足之处与改进方向
8.1 杰尔德相似性的局限性
在本项目中,我们使用了杰尔德相似性(Jaccard Similarity)来计算用户和电影之间的相似度。然而,这种方法存在一定的局限性,尤其是在处理评分数据时:
8.2 改进方向:使用余弦相似性
为了更好地利用评分数据中的区间信息,可以改用 余弦相似性(Cosine Similarity) 。余弦相似性通过计算两个向量之间的夹角余弦值来衡量相似性,能够直接考虑评分的具体数值,从而更准确地反映用户之间的兴趣相似度或电影之间的内容相似度。
8.3 其他改进方向
除了更换相似性度量方法外,还可以从以下几个方面进一步优化推荐系统:
- 数据预处理 :
- 对评分矩阵进行归一化处理,减少不同用户评分尺度的差异。
- 使用矩阵分解技术(如 SVD)降低稀疏性问题的影响。
- 模型扩展 :
- 引入基于内容的推荐算法,结合电影的类型、标签等特征进行推荐。
- 使用混合推荐模型,将协同过滤与深度学习相结合,进一步提升推荐效果。
- 冷启动问题 :
- 针对新用户或新电影的冷启动问题,可以引入基于流行度的推荐策略或利用外部数据源(如 IMDb 或 TMDB 的元数据)。
作者:铠哥不喝粥