一、引言

        在大数据时代,推荐系统已成为解决信息过载的重要工具。其中,协同过滤(Collaborative Filtering)作为推荐系统的经典算法,通过分析用户与物品的交互行为,能够精准捕捉用户偏好,广泛应用于电商、流媒体等场景。本文将基于 Python 实现一个电影推荐系统,详细讲解用户 - 用户协同过滤(UBCF)和物品 - 物品协同过滤(IBCF)的核心逻辑,并提供完整的代码示例。

二、技术原理:协同过滤核心概念

1. 协同过滤分类

  • 用户 - 用户协同过滤(User-Based CF):通过计算用户之间的相似度,找到与目标用户兴趣相似的 “邻居用户”,基于邻居的历史行为生成推荐。
  • 物品 - 物品协同过滤(Item-Based CF):计算物品之间的相似度,为用户推荐与其历史交互物品相似的其他物品。

2. 余弦相似度(Cosine Similarity)

        用于衡量向量之间的相似程度,公式为:
        cosine相似度=(A⋅B)​/(∣∣A∣∣×∣∣B∣∣)

        在推荐系统中,通过用户 - 物品评分矩阵构建向量,计算用户或物品之间的相似度。

三、代码实现:电影推荐系统构建

1. 数据准备

        数据源自 MovieLens 数据集(ml-latest-small),由明尼苏达大学 GroupLens 研究组提供,包含 610 名用户对 9742 部电影的 100836 条评分和 3683 条标签,时间跨度为 1996 年 3 月 29 日至 2018 年 9 月 24 日。数据集聚焦用户对电影的 5 星评分和 自由文本标签,用户随机选取且至少评分 20 部电影,无 demographic 信息,仅以匿名 ID 标识。

字段说明

1. ratings.csv - 用户评分数据
  • 字段userId, movieId, rating, timestamp
  • 说明
    • userId:用户唯一标识(匿名化 ID,在 ratings.csv 和 tags.csv 中一致)。
    • movieId:电影唯一标识(与 MovieLens 网站一致,如 1 对应 Toy Story)。
    • rating:用户评分(0.5 星到 5 星,半星递增,如 3.0、4.5)。
    • timestamp:评分时间戳(UTC 时间,从 1970 年 1 月 1 日午夜开始的秒数)。
  • 数据组织:按 userId 排序,同用户内按 movieId 排序。
2. movies.csv - 电影元数据
  • 字段movieId, title, genres
  • 说明
    • movieId:电影唯一标识(与其他文件一致)。
    • title:电影标题(包含发行年份,如 Toy Story (1995),可能存在人工录入或导入误差)。
    • genres:电影类型(管道分隔列表,可选值包括 Action、Adventure、Comedy 等 19 种,无类型时标注 (no genres listed))。
3. tags.csv - 用户标签数据
  • 字段userId, movieId, tag, timestamp
  • 说明
    • userId, movieId:同 ratings.csv,标识用户和电影。
    • tag:用户自定义标签(短文本,如 "funny"、"scifi",反映用户对电影的主观描述)。
    • timestamp:标签添加时间戳(格式同评分时间戳)。
  • 数据组织:按 userId 排序,同用户内按 movieId 排序。
4. links.csv - 电影外部链接数据
  • 字段movieId, imdbId, tmdbId
  • 说明
  • 用途:用于关联外部电影数据库,获取更多元数据(如海报、剧情简介)。

数据格式与编码

  • 所有文件为 UTF-8 编码的 CSV 格式,含单标题行,含逗号的字段用双引号转义(如电影标题 Misérables, Les (1995))。
  • 数据可从 GroupLens 官网 公开下载,使用时需遵守 使用许可,引用请参考指定文献。

通过网盘分享的文件:协调算法电影数据
链接: https://pan.baidu.com/s/1ss7sm-PJdQGv1hpphFeEXw?pwd=1111 提取码: 1111 

数据加载与清洗 

import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity

# 加载数据
def load_data():
    links = pd.read_csv('links.csv')
    movies = pd.read_csv('movies.csv')
    ratings = pd.read_csv('ratings.csv')
    tags = pd.read_csv('tags.csv')
    return links, movies, ratings, tags

# 数据准备:构建用户-电影评分矩阵
def data_preparation(ratings):
    ratings = ratings.dropna()  # 移除缺失值
    # 转换为用户-电影矩阵,缺失值填充为0
    user_movie_matrix = ratings.pivot(index='userId', columns='movieId', values='rating').fillna(0)
    return user_movie_matrix

2. 用户 - 用户协同过滤(UBCF)

核心逻辑

  1. 计算用户之间的余弦相似度,找到相似用户;
  2. 从相似用户的评分中筛选目标用户未评分的电影。
# 用户-用户协同过滤
def user_based_collaborative_filtering(user_id, user_movie_matrix, top_n=5):
    user_index = user_movie_matrix.index.get_loc(user_id)  # 获取目标用户索引
    similarity_matrix = cosine_similarity(user_movie_matrix)  # 计算用户相似度矩阵
    user_similarities = similarity_matrix[user_index]  # 目标用户与所有用户的相似度
    
    # 找到相似度最高的top_n用户(排除自身)
    similar_users_indices = user_similarities.argsort()[-top_n - 1:-1][::-1]
    similar_users = user_movie_matrix.index[similar_users_indices]
    
    recommended_movies = []
    for similar_user in similar_users:
        # 提取相似用户评分>0且目标用户未评分的电影
        similar_user_ratings = user_movie_matrix.loc[similar_user]
        unrated_movies = similar_user_ratings[
            (similar_user_ratings > 0) & (user_movie_matrix.loc[user_id] == 0)
        ]
        recommended_movies.extend(unrated_movies.index)
    
    return list(set(recommended_movies))  # 去重

3. 物品 - 物品协同过滤(IBCF)

核心逻辑

  1. 将用户 - 电影矩阵转置为电影 - 用户矩阵,计算电影之间的相似度;
  2. 推荐与用户历史评分电影相似的未评分电影。
# 物品-物品协同过滤
def item_based_collaborative_filtering(user_id, user_movie_matrix, top_n=5):
    movie_user_matrix = user_movie_matrix.T  # 转置为电影-用户矩阵
    user_ratings = user_movie_matrix.loc[user_id]  # 目标用户的评分
    
    for movie_id in user_ratings[user_ratings > 0].index:  # 遍历用户评分>0的电影
        movie_index = movie_user_matrix.columns.get_loc(movie_id)  # 获取电影索引
        similarity_matrix = cosine_similarity(movie_user_matrix)  # 计算电影相似度矩阵
        movie_similarities = similarity_matrix[movie_index]  # 目标电影与所有电影的相似度
        
        # 找到相似度最高的top_n电影(排除自身)
        similar_movies_indices = movie_similarities.argsort()[-top_n - 1:-1][::-1]
        similar_movies = movie_user_matrix.index[similar_movies_indices]
        
        # 筛选用户未评分的电影
        for similar_movie in similar_movies:
            if user_ratings[similar_movie] == 0:
                recommended_movies.append(similar_movie)
    
    return list(set(recommended_movies))  # 去重

4. 推荐函数与结果展示 

# 推荐主函数
def recommend_movies(user_id, user_movie_matrix, method='UBCF', top_n=5):
    if method == 'UBCF':
        return user_based_collaborative_filtering(user_id, user_movie_matrix, top_n)
    elif method == 'IBCF':
        return item_based_collaborative_filtering(user_id, user_movie_matrix, top_n)
    else:
        raise ValueError("无效的推荐方法,请选择'UBCF'或'IBCF'")

# 主程序
if __name__ == "__main__":
    links, movies, ratings, tags = load_data()
    user_movie_matrix = data_preparation(ratings)
    
    target_user_id = 47  # 目标用户ID
    recommendation_method = 'IBCF'  # 可选'UBCF'或'IBCF'
    
    recommended_movie_ids = recommend_movies(target_user_id, user_movie_matrix, method=recommendation_method)
    recommended_movies = movies[movies['movieId'].isin(recommended_movie_ids)]
    
    print(f"为用户 {target_user_id} 使用 {recommendation_method} 推荐的电影:")
    print(recommended_movies[['movieId', 'title', 'genres']])

四、结果展示

        运行代码后,输出类似以下结果(以用户 ID=47,IBCF 方法为例):

五、总结与改进方向

1. 优点

  • 原理简单:无需物品或用户的额外特征,仅依赖交互数据即可实现。
  • 可解释性:推荐结果基于用户或物品的相似性,逻辑清晰。

2. 局限性

  • 冷启动问题:新用户或新物品缺乏交互数据时,推荐效果差。
  • 稀疏性问题:评分矩阵稀疏时,相似度计算不准确。

3. 改进方向

  • 混合推荐:结合内容过滤(如电影类型、导演等特征)提升推荐效果。
  • 矩阵分解:使用 SVD 等方法处理稀疏矩阵,降低维度。
  • 深度学习:引入神经网络(如 Neural Collaborative Filtering)挖掘非线性关系。

 

 

 

 

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐