用AI从零理解推荐系统

数字时代的"读心术"

互联网信息爆炸的时代,每当我们打开一些短视频平台时推荐的影视剧总有一款符合你的口味,电商平台首页展示的商品恰好是你最近需要的物品,这些看似神奇的"读心术"背后,正是推荐系统在发挥作用。其中,协同过滤算法作为最经典的推荐技术之一,就像一位善于观察的茶馆老板:通过记录不同茶客的口味偏好,总能给新客人推荐最合适的茶点。本文将带您深入浅出地理解这项技术,揭秘现代推荐系统的运行逻辑。

协同过滤的本质

1.1 推荐系统的底层逻辑

想象周末晚上,你和三位朋友相约看电影。朋友A喜欢《肖申克的救赎》,朋友B钟爱《阿甘正传》,而朋友C偏好《盗梦空间》。当第四位朋友加入时,系统会根据他的观影历史,自动匹配相似朋友的喜好进行推荐。这种基于群体行为模式的推荐方式,正是协同过滤的核心思想。

1.2 两种观察视角

协同过滤主要分为两种实现方式:基于用户的推荐和基于物品的推荐。前者如同寻找品味相似的朋友,后者好比发现属性相近的商品。以电影推荐为例:

  • 用户视角:发现与您观影品味高度重合的"影迷伙伴"
  • 物品视角:挖掘与您喜爱电影类型相似的"系列作品"

这两种方法都需要构建一个"兴趣地图"。当我们用数学语言描述时,就是构建用户-物品评分矩阵。以6位用户对5部电影的评分为例,这个矩阵就像一张数字化的口味对照表:

用户\电影 霸王别姬 大闹天宫 无间道 卧虎藏龙 让子弹飞
用户1 5 3 4 4 0
用户2 4 0 3 5 4
... ... ... ... ... ...

其中0表示未观看,数字越大代表越喜欢。这个简单的表格,就是推荐系统认知世界的起点。

1.3 数据准备

我们先用Python创建一个迷你电影数据库:

python 复制代码
# 电影信息表
movies = {
    'movie_id': [101, 102, 103, 104, 105],
    'title': ['霸王别姬', '大闹天宫', '无间道', '卧虎藏龙', '让子弹飞'],
    'genre': ['剧情/爱情', '动画/奇幻', '犯罪/惊悚', '动作/武侠', '喜剧/动作'],

    'year': [1993, 1961, 2002, 2000, 2010]
}


# 用户评分表
ratings = {
    'user_id': [1,1,1,1, 2,2,2,2, 3,3,3,3, 4,4,4, 5,5,5,5, 6,6,6,6],
    'movie_id': [101,102,103,104, 101,103,104,105, 101,102,104,105, 102,103,105, 101,102,103,104, 101,102,104,105],
    'rating': [5,3,4,4, 4,3,5,4, 5,4,3,3, 2,4,5, 4,5,4,3, 4,3,4,5]
}

算法实现的三步曲

2.1 测量相似度的"尺子"

余弦相似度算法是最常用的测量工具。它不比较绝对距离,而是观察向量方向的一致性。就像比较两个旅行者的罗盘指向:方向越一致(夹角越小),相似度越高。计算公式为:

余弦相似度计算公式:

相似度 = (A·B) / (||A|| * ||B||)

当两个用户的评分模式完全相同时,得分将达到满分1;完全相反则为-1;毫无关联时接近0。

计算用户相似度的核心代码如下:

python 复制代码
# 关键代码段:计算用户相似度
user_similarity = cosine_similarity(user_movie_matrix)
user_sim_df = pd.DataFrame(user_similarity, 
                         index=user_movie_matrix.index,
                         columns=user_movie_matrix.index)

# 关键代码段:计算物品相似度
item_similarity = cosine_similarity(user_movie_matrix.T)
item_sim_df = pd.DataFrame(item_similarity,
                          index=user_movie_matrix.columns,
                          columns=user_movie_matrix.columns)

2.2 寻找兴趣邻居

基于用户的推荐会寻找最近的"邻居",就像在兴趣地图上画同心圆。系统设定相似用户的数量参数(通常3-5人),排除已观看的影片后,根据邻居的加权评分生成推荐列表。例如用户A的邻居们对《让子弹飞》评分很高,即使A从未看过,系统也会将其列入推荐。

2.3 生成推荐列表

最终的推荐分数通过加权计算得出,类似于民主投票机制。每个邻居的投票权重由其相似度决定,最终结果既反映群体偏好,又突出高相似用户的意见。这个过程就像举办电影沙龙:与主人品味越相近的客人,对片单的影响力越大。

评估推荐

3.1 评估实验设计

我们采用经典的训练集-测试集划分法,用RMSE(均方根误差)作为评估指标。这就像老师出题时故意隐藏部分题目,通过学生的答题准确率检验教学效果。

3.2 评估过程揭秘

  1. 数据分割:随机保留20%的评分作为测试数据
  2. 模拟预测:在训练集上生成推荐,预测测试集的评分
  3. 误差计算:比较预测值与真实值的偏离程度
python 复制代码
# 评估函数核心逻辑
train_df, test_df = train_test_split(ratings_df, test_size=0.2)
predictions = []
actuals = []
for _, row in test_df.iterrows():
    # 获取推荐并预测评分
    # 计算RMSE...

3.3 结果解读与优化

在我们的实验中:

在数字迷雾中点亮明灯

从1992年施乐公司的首个推荐系统,到今日影响数十亿人的智能平台,协同过滤技术始终在回答同一个问题:如何在海量信息中建立有意义的连接?当我们理解其原理时,就能更理性地看待推荐结果,既享受技术便利,又保持独立思考。正如望远镜扩展了人类的视野,推荐算法正在重塑我们认知数字世界的方式,而这趟探索之旅,才刚刚开始。你可以进一步扩展这个系统:

  1. 使用更大的数据集(如完整的MovieLens数据集)
  2. 实现更高级的推荐算法(如矩阵分解、深度学习模型)
  3. 添加用户界面使系统更加友好
  4. 部署为Web应用或API服务

协同过滤是推荐系统的基础算法之一,理解它的工作原理对于构建更复杂的推荐系统至关重要。

(提示:完整代码如下:)

python 复制代码
import pandas as pd
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error

# 1. 数据准备(增加更多评分数据避免稀疏性问题)
rating_data = {
    'user_id': [1,1,1,1, 2,2,2,2, 3,3,3,3, 4,4,4, 5,5,5,5, 6,6,6,6],
    'movie_id': [101,102,103,104, 101,103,104,105, 101,102,104,105, 102,103,105, 101,102,103,104, 101,102,104,105],
    'rating': [5,3,4,4, 4,3,5,4, 5,4,3,3, 2,4,5, 4,5,4,3, 4,3,4,5]
}

movie_data = {
    'movie_id': [101,102,103,104,105],
    'title': ['霸王别姬','大闹天宫','无间道','卧虎藏龙','让子弹飞'],
    'genre': ['剧情/爱情','动画/奇幻','犯罪/惊悚','动作/武侠','喜剧/动作'],
    'year': [1993,1961,2002,2000,2010]
}

ratings_df = pd.DataFrame(rating_data)
movies_df = pd.DataFrame(movie_data)

# 2. 基于用户的协同过滤(增加健壮性检查)
def user_based_recommendation(user_id, ratings_df, movies_df, n_similar_users=3, min_common_movies=1):
    try:
        # 创建用户-电影评分矩阵
        user_movie_matrix = ratings_df.pivot_table(
            index='user_id', 
            columns='movie_id', 
            values='rating'
        ).fillna(0)
        
        if user_id not in user_movie_matrix.index:
            return pd.DataFrame(columns=movies_df.columns)
            
        # 计算用户相似度
        user_similarity = cosine_similarity(user_movie_matrix)
        user_sim_df = pd.DataFrame(
            user_similarity,
            index=user_movie_matrix.index,
            columns=user_movie_matrix.index
        )
        
        # 获取相似用户(至少有min_common_movies部共同电影)
        similar_users = user_sim_df[user_id].sort_values(ascending=False)[1:n_similar_users+1]
        similar_users = similar_users[similar_users > 0]  # 只保留有相似度的用户
        
        if len(similar_users) == 0:
            return pd.DataFrame(columns=movies_df.columns)
            
        # 计算推荐分数
        similar_users_ratings = user_movie_matrix.loc[similar_users.index]
        weighted_ratings = similar_users_ratings.mul(similar_users, axis=0)
        recommendation_scores = weighted_ratings.sum(axis=0) / similar_users.sum()
        
        # 过滤已观看电影
        watched_movies = ratings_df[ratings_df['user_id'] == user_id]['movie_id']
        recommendation_scores = recommendation_scores.drop(watched_movies, errors='ignore')
        
        # 获取推荐电影
        recommended_movies = recommendation_scores.sort_values(ascending=False).index.tolist()
        return movies_df[movies_df['movie_id'].isin(recommended_movies)]
    
    except Exception as e:
        print(f"为用户{user_id}生成推荐时出错: {str(e)}")
        return pd.DataFrame(columns=movies_df.columns)

# 3. 基于物品的协同过滤(增加健壮性检查)
def item_based_recommendation(user_id, ratings_df, movies_df, n_similar_items=3):
    try:
        # 创建用户-电影评分矩阵
        user_movie_matrix = ratings_df.pivot_table(
            index='user_id',
            columns='movie_id',
            values='rating'
        ).fillna(0)
        
        if user_id not in user_movie_matrix.index:
            return pd.DataFrame(columns=movies_df.columns)
            
        # 计算物品相似度
        item_similarity = cosine_similarity(user_movie_matrix.T)
        item_sim_df = pd.DataFrame(
            item_similarity,
            index=user_movie_matrix.columns,
            columns=user_movie_matrix.columns
        )
        
        # 获取用户历史评分
        user_ratings = ratings_df[ratings_df['user_id'] == user_id]
        
        if len(user_ratings) == 0:
            return pd.DataFrame(columns=movies_df.columns)
            
        recommendations = {}
        for movie_id, rating in zip(user_ratings['movie_id'], user_ratings['rating']):
            # 获取相似电影
            similar_movies = item_sim_df[movie_id].sort_values(ascending=False)[1:n_similar_items+1]
            similar_movies = similar_movies[similar_movies > 0]  # 只保留有相似度的电影
            
            for similar_movie_id, similarity in similar_movies.items():
                if similar_movie_id not in user_ratings['movie_id'].values:
                    recommendations[similar_movie_id] = recommendations.get(similar_movie_id, 0) + similarity * rating
        
        # 排序推荐结果
        if len(recommendations) == 0:
            return pd.DataFrame(columns=movies_df.columns)
            
        recommended_movies = sorted(recommendations.items(), key=lambda x: x[1], reverse=True)
        recommended_movie_ids = [movie[0] for movie in recommended_movies]
        
        return movies_df[movies_df['movie_id'].isin(recommended_movie_ids)]
    
    except Exception as e:
        print(f"为用户{user_id}生成推荐时出错: {str(e)}")
        return pd.DataFrame(columns=movies_df.columns)

# 4. 评估函数(增加健壮性处理)
def evaluate_recommendation(ratings_df, recommendation_func, test_size=0.2):
    try:
        train_df, test_df = train_test_split(ratings_df, test_size=test_size, random_state=42)
        
        predictions = []
        actuals = []
        
        for _, row in test_df.iterrows():
            user_id = row['user_id']
            movie_id = row['movie_id']
            actual_rating = row['rating']
            
            try:
                # 获取推荐结果
                recommendations = recommendation_func(user_id, train_df, movies_df)
                
                # 预测评分
                if movie_id in recommendations['movie_id'].values:
                    predicted_rating = train_df[(train_df['movie_id'] == movie_id)]['rating'].mean()
                    if np.isnan(predicted_rating):
                        predicted_rating = train_df['rating'].mean()
                else:
                    predicted_rating = train_df['rating'].mean()
                
                predictions.append(predicted_rating)
                actuals.append(actual_rating)
            except Exception as e:
                print(f"评估用户{user_id}对电影{movie_id}时出错: {str(e)}")
                continue
        
        if len(predictions) == 0:
            return np.nan
            
        return np.sqrt(mean_squared_error(actuals, predictions))
    except Exception as e:
        print(f"评估过程中出错: {str(e)}")
        return np.nan

# 5. 美观显示推荐结果
def display_recommendations(recommendations, algorithm_name):
    print(f"\n=== {algorithm_name}推荐结果 ===")
    if recommendations.empty:
        print("暂无推荐电影")
    else:
        for _, movie in recommendations.iterrows():
            print(f"\n电影: {movie['title']}")
            print(f"类型: {movie['genre']}")
            print(f"年份: {movie['year']}")

# 6. 使用推荐系统
target_user_id = 1

print("=== 电影数据集 ===")
print(movies_df)

# 基于用户的推荐
user_based_rec = user_based_recommendation(target_user_id, ratings_df, movies_df)
display_recommendations(user_based_rec, "基于用户")

# 基于物品的推荐
item_based_rec = item_based_recommendation(target_user_id, ratings_df, movies_df)
display_recommendations(item_based_rec, "基于物品")

# 评估模型
print("\n=== 模型评估 ===")
user_based_rmse = evaluate_recommendation(ratings_df, user_based_recommendation)
item_based_rmse = evaluate_recommendation(ratings_df, item_based_recommendation)

print(f"基于用户的推荐RMSE: {user_based_rmse:.4f}" if not np.isnan(user_based_rmse) else "基于用户的推荐评估失败")
print(f"基于物品的推荐RMSE: {item_based_rmse:.4f}" if not np.isnan(item_based_rmse) else "基于物品的推荐评估失败")
相关推荐
GoMaxAi9 小时前
金融行业 AI 报告自动化:Word+PPT 双引擎生成方案
人工智能·unity·ai作画·金融·自动化·aigc·word
AI绘画咪酱13 小时前
【CSDN首发】Stable Diffusion从零到精通学习路线分享
人工智能·学习·macos·ai作画·stable diffusion·aigc
fleur14 小时前
LoRA微调大模型实践
llm
win4r14 小时前
🚀多维度测评OpenAI最新GPT-4.1模型!百万token上下文窗口!编程能力和指令遵循能力大幅提升!Cline+GPT-4.1十分钟零代码开发macOS
chatgpt·openai·ai编程
仙人掌_lz14 小时前
详解如何复现DeepSeek R1:从零开始利用Python构建
开发语言·python·ai·llm·deepseek
量子位15 小时前
字节视频基础大模型发布!单 GPU 就可生成 1080P,蒋路领衔 Seed 视频团队曝光
人工智能·llm
量子位15 小时前
北京队再上大分:新 AI 一句话就能搞开发,代码实时可见 | 免费可用
人工智能·aigc
仙人掌_lz17 小时前
如何在本地使用Ollama运行 Hugging Face 模型
java·人工智能·servlet·ai·大模型·llm·ollama
十分钟空间17 小时前
MCP(Model Context Protocol)技术与项目集成指南
ai编程·mcp
vil du17 小时前
c# AI编程助手 — Fitten Code
开发语言·c#·ai编程