协同过滤算法电影推荐系统详解:Python实现指南

一、项目背景与目标

1.1 背景

协同过滤(Collaborative Filtering, CF)是推荐系统中最常用的技术之一,分为两种主要类型:

  • 基于用户的协同过滤(UserCF) :通过分析用户之间的相似性,找到与目标用户兴趣相近的其他用户,并推荐这些用户喜欢的电影。
  • 基于物品的协同过滤(ItemCF) :通过分析电影之间的相似性,为目标用户推荐与其已评分电影相似的其他电影。
  • 1.2 目标

    本项目的目标是:

    1. 计算用户之间的杰尔德相似性和物品之间的杰尔德相似性。
    2. 为每个用户找到最相似的两个用户。
    3. 基于用户协同过滤和物品协同过滤为指定用户推荐 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 构建一个基于协同过滤算法的电影推荐系统。通过计算用户和电影之间的相似性,我们可以为用户提供个性化的电影推荐。本项目的主要步骤包括:

    1. 数据加载与预处理。
    2. 用户和电影相似性计算。
    3. 基于用户和基于物品的协同过滤推荐。

    八、不足之处与改进方向

    8.1 杰尔德相似性的局限性

    在本项目中,我们使用了杰尔德相似性(Jaccard Similarity)来计算用户和电影之间的相似度。然而,这种方法存在一定的局限性,尤其是在处理评分数据时:

  • 评分信息的丢失 :杰尔德相似性仅关注用户是否对某部电影进行了评分,而忽略了评分的具体数值。例如,用户 A 和用户 B 对同一部电影分别评分为 5 分和 1 分,这两种评分在杰尔德相似性中被视为完全相同(即都为“有评分”),这显然不符合实际情况。
  • 不适合区间分值的数据 :电影评分是一个具有连续区间的数值(如 1 到 5 分),而杰尔德相似性更适合处理二值化数据(如“喜欢/不喜欢”或“已评分/未评分”)。因此,杰尔德相似性在这种场景下可能会导致推荐结果不够精准。
  • 8.2 改进方向:使用余弦相似性

    为了更好地利用评分数据中的区间信息,可以改用 余弦相似性(Cosine Similarity) 。余弦相似性通过计算两个向量之间的夹角余弦值来衡量相似性,能够直接考虑评分的具体数值,从而更准确地反映用户之间的兴趣相似度或电影之间的内容相似度。

    8.3 其他改进方向

    除了更换相似性度量方法外,还可以从以下几个方面进一步优化推荐系统:

    1. 数据预处理
    2. 对评分矩阵进行归一化处理,减少不同用户评分尺度的差异。
    3. 使用矩阵分解技术(如 SVD)降低稀疏性问题的影响。
    4. 模型扩展
    5. 引入基于内容的推荐算法,结合电影的类型、标签等特征进行推荐。
    6. 使用混合推荐模型,将协同过滤与深度学习相结合,进一步提升推荐效果。
    7. 冷启动问题
    8. 针对新用户或新电影的冷启动问题,可以引入基于流行度的推荐策略或利用外部数据源(如 IMDb 或 TMDB 的元数据)。

    作者:铠哥不喝粥

    物联沃分享整理
    物联沃-IOTWORD物联网 » 协同过滤算法电影推荐系统详解:Python实现指南

    发表回复