本课目标
理解推荐系统的核心思想,手写 User-Based 协同过滤与 SVD 矩阵分解,完成一个电影推荐器,串联课程全部知识。
推荐系统是 ML 商业价值最直观的体现——"猜你喜欢"四个字撑起了淘宝、抖音和 Netflix 的核心业务。本讲用 MovieLens 数据集走两条经典路线,并告诉你:真实工业推荐是一个多级漏斗,本讲的思想是漏斗的第一层。
核心内容
路线一:User-Based 协同过滤
思想一句话:找口味跟你相似的人,看他们喜欢什么。不需要理解电影内容,只看行为数据。
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
user_sim = cosine_similarity(np.nan_to_num(ratings))
def recommend(user_id, k=10, n_neighbors=20):
sims = user_sim[user_id]
neighbors = np.argsort(sims)[::-1][1:n_neighbors+1]
scores = {}
for movie in unseen_movies(user_id):
rated = [n for n in neighbors if not np.isnan(ratings[n, movie])]
if rated:
w = sims[rated]
scores[movie] = w @ ratings[rated, movie] / (np.abs(w).sum() + 1e-9)
return sorted(scores, key=scores.get, reverse=True)[:k]
协同过滤的优雅在于:它不需要任何领域知识。不需要理解电影类型、导演风格、演员阵容——只看"谁跟你行为相似"。这是它统治工业界二十年的原因。
痛点也明显:冷启动(新用户没行为数据,推荐不了)和稀疏性(百万用户 × 百万电影,99.99% 的格子是空的)。
路线二:SVD 矩阵分解
把"用户×电影"的大矩阵分解成两个小矩阵:用户×隐因子 和 隐因子×电影。隐因子是模型自动发现的"潜在维度"——可能对应"动作片偏好""文艺片倾向",不需要人工定义:
from surprise import SVD, Dataset, Reader
from surprise.model_selection import cross_validate
reader = Reader(rating_scale=(1, 5))
data = Dataset.load_from_df(ratings_df[['user', 'movie', 'rating']], reader)
svd = SVD(n_factors=50, n_epochs=20, lr_all=0.005, reg_all=0.02)
cross_validate(svd, data, measures=['RMSE'], cv=5)
n_factors 是核心超参:太小表达能力不足(每个人只有一种口味),太大过拟合(模型记住了噪音)。网格搜索找甜点。
真实工业推荐:一个多级漏斗
协同过滤只是"召回"层的一种策略。完整流程是:召回(几百个候选)→ 粗排 → 精排(几十个)→ 重排。每层用的模型越来越复杂、也越来越贵。但思想内核不变——用行为数据缩小候选集,用模型给候选集打分。
课程收官
回顾八讲:工程全景 → sklearn 工具链 → 两大深度学习框架 → 图像/文本实战 → 评估部署 → 推荐综合。你现在拥有的不是一堆 demo,而是一套方法论:
- 先定义问题与指标,再选模型
- 永远先跑基线
- 数据优先于模型
- Pipeline 防泄露
- ONNX 跨栈部署
这套方法论放到 LLM 时代依然成立。不同的只是模型的名字——方法论永远不过时。
动手练习
- 实现 User-Based CF,给自己构造评分向量,看推荐是否符合直觉
- 调 SVD 的
n_factors∈ {10, 50, 100, 200},画 RMSE 曲线找最优值