推荐系统入门实战:协同过滤、矩阵分解与召回排序

系统覆盖推荐系统全流程:User-Item 矩阵、相似度计算、协同过滤(User/Item-based)、矩阵分解(SVD/ALS)、基于内容的推荐、召回-粗排-精排架构、评估指标(准确率/召回率/NDCG)与冷启动问题。

引言

推荐系统是「预测用户会喜欢什么」——电商、视频、新闻、音乐都在用。本文按工业界真实架构推进:先建 User-Item 交互矩阵(一切的地基),再讲协同过滤(User/Item 相似度)、矩阵分解(SVD/ALS,隐因子模型)、基于内容的推荐,然后是召回-粗排-精排的工程架构与评估指标(准确率/召回率/NDCG),最后攻冷启动与新物品问题。

前置:/ml-supervised-regression/(预测思路)、/ml-feature-engineering/(特征处理)。评估指标见 /ml-model-evaluation/。


目录


1. 推荐系统全景:召回-排序-重排

工业级推荐是「漏斗」——候选几百万,最终展示几个:

全量物品(百万级)
  → 召回(粗选,几百~几千)   高速,宽松
  → 精排(CTR 预估,逐条打分) 准,模型
  → 重排(多样性/规则,几十)  业务约束
  → 展示
阶段目标方法
召回别漏掉可能喜欢的协同过滤/矩阵分解/向量召回
精排把最可能的排最前CTR 模型(LR/Wide&Deep/GBDT)
重排多样性/业务规则打散、去重、保位置

心智:先广召回防漏、再精排序排准、最后重排管体验——别在一层追求完美。


2. User-Item 交互矩阵

一切推荐的输入:用户 × 物品的交互矩阵:

       物品1  物品2  物品3  物品4
用户A    5      ?      3      ?
用户B    4      2      ?      5
用户C    ?      ?      1      4

? = 未知交互(我们要预测的)
评分(显式)或点击/购买次数(隐式)

两种交互:

类型含义例子
显式用户主动表达评分 1-5 星
隐式行为推断偏好点击/购买/播放
import pandas as pd

# 用户-物品-交互值 长表 → 矩阵
df = pd.DataFrame({'user': ['A','A','B','B','C','C'],
                   'item': ['i1','i3','i1','i2','i3','i4'],
                   'rating': [5,3,4,2,1,4]})
matrix = df.pivot(index='user', columns='item', values='rating')
print(matrix)

关键认知:矩阵高度稀疏(用户只接触过 0.1% 物品)——推荐就是「填补 ? 值」。


3. 相似度计算:余弦、皮尔逊与杰卡德

推荐的核心度量:相似度——三种主流:

import numpy as np

def cosine(a, b):
    """余弦相似度:向量夹角,范围 [-1, 1]"""
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b) + 1e-9)

def pearson(a, b):
    """皮尔逊:余弦 - 各自均值(去评分尺度差异)"""
    a_c, b_c = a - a.mean(), b - b.mean()
    return cosine(a_c, b_c)

def jaccard(a, b):
    """杰卡德:交集 / 并集(只看"是否交互")"""
    return len(set(a) & set(b)) / len(set(a) | set(b))
相似度适用特点
余弦评分向量简单常用
皮尔逊评分消去「爱打高分」的偏差
杰卡德隐式交互(0/1)不看数值

记忆:评分用余弦/皮尔逊,行为 0/1 用杰卡德。


4. 协同过滤:User-based 与 Item-based

协同过滤(Collaborative Filtering):「相似的人喜欢相似的东西」。

User-based:找与你口味相近的用户,推荐他们喜欢的:

找相似用户(与你评分向量最接近的 10 人)
→ 汇总他们喜欢的、你还没看过的物品
→ 按相似度加权排序

Item-based:推荐「与你喜欢物品相似」的物品:

找到你打过高分的物品
→ 找与它们相似的物品(物品相似度预计算)
→ 推荐

Item-based 是电商主流(物品相似稳定、可离线算、可解释):

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

# 物品相似度矩阵(对矩阵列算余弦)
item_sim = cosine_similarity(matrix.T.fillna(0))
# item_sim[i][j] = 物品 i 与 j 的相似度

def recommend_item_based(matrix, user, item_sim, top_k=5):
    """给 user 推荐:加权汇总其高评分物品的相似物品"""
    ratings = matrix.loc[user].fillna(0)
    scores = {}
    for item in ratings.index:
        if ratings[item] > 0:
            for other, sim in enumerate(item_sim[matrix.columns.get_loc(item)]):
                if matrix.loc[user, matrix.columns[other]] == 0:
                    scores[matrix.columns[other]] = \
                        scores.get(matrix.columns[other], 0) + ratings[item] * sim
    return sorted(scores, key=scores.get, reverse=True)[:top_k]

记忆:User-based 找相似人、Item-based 找相似物——物品相似稳定、可预计算,工程上更优。


5. 矩阵分解:SVD 与 ALS

矩阵分解(隐因子模型):把稀疏矩阵拆成「用户因子 × 物品因子」:

R (用户×物品) ≈ U (用户×k) × V^T (k×物品)

R[u][i] = U[u]·V[i] = 用户 u 对隐因子 k 的偏好 × 物品 i 在这些因子上的成分

k = 隐因子数(如"动作片/剧情片"这类隐含维度,无需人工标注)。

# 用 Surprise 库做 SVD(最简)
from surprise import Dataset, Reader, SVD
from surprise.model_selection import train_test_split

data = Dataset.load_from_df(
    df[['user', 'item', 'rating']],
    Reader(rating_scale=(1, 5)))
trainset, testset = train_test_split(data, test_size=0.2)

model = SVD(n_factors=20, lr_all=0.005, reg_all=0.02)
model.fit(trainset)
pred = model.predict('A', 'i4')   # 预测用户 A 对物品 i4 的评分
方法场景特点
SVD中小数据sklearn/Surprise 易用
ALS大规模分布式Spark MLlib,交替最小二乘
Funk-SVD推荐基准经典,只分解已知评分

矩阵分解优点:捕捉潜在偏好、可处理稀疏、嵌入可做向量召回。

记忆:矩阵分解 = 学「用户/物品的隐因子向量」——分数就是两向量点积;ALS 是它的分布式版本。


6. 基于内容的推荐

协同过滤只看行为;基于内容看「物品本身 + 用户画像」:

# 物品特征:标签 / 类别 / 文本关键词
items = {'i1': ['动作', '科幻'], 'i2': ['爱情', '剧情'], 'i3': ['动作', '武侠']}

# 用户画像 = 其喜欢物品特征的加权汇总
def user_profile(user, items, ratings):
    profile = {}
    for item, r in ratings[user].items():
        for feat in items[item]:
            profile[feat] = profile.get(feat, 0) + r
    return profile

# 推荐:给物品特征与用户画像打点积

对比:

维度协同过滤基于内容
数据用户-物品行为物品属性 + 用户画像
冷启动新用户❌ 无行为无法推荐✅ 有画像即可
冷启动新物品✅ 有交互即可❌ 需特征
惊喜度高(跨类发现)低(只推荐同类)
可解释中等高(“因为你喜欢动作片”)

记忆:协同看「谁喜欢了它」,内容看「它是什么」——两者互补,工业常混合。


7. 召回层实战:多种召回策略

召回要求「快 + 全」——多个策略并行取并集:

召回源(各自独立、并行):
  热门召回      → 全局/类目热门(兜底,防冷门缺货)
  协同过滤召回  → Item-based 相似物品
  矩阵分解召回  → 用户向量 × 物品向量,Top-N 最近邻
  向量召回      → Embedding 相似(深度召回,见下)
  内容召回      → 用户画像匹配
→ 汇总、去重、截断 Top 500 送精排

向量召回(Embedding-based):把用户/物品都映射到向量,近邻即候选:

import numpy as np

# 简化:user/item 因子向量(来自矩阵分解/深度模型)
user_vec = { 'A': np.array([0.9, 0.2, 0.1]) }
item_vec = { 'i1': np.array([0.8, 0.1, 0.2]), 'i4': np.array([0.7, 0.3, 0.05]) }

# 余弦相似度取 Top-N(工业用 Faiss/向量数据库加速)
def recall(user, k=10):
    sims = [(i, float(np.dot(user_vec[user], v))) for i, v in item_vec.items()]
    return sorted(sims, key=lambda x: x[1], reverse=True)[:k]
召回策略优点缺点
热门兜底、快个性化弱
协同过滤个性化、可解释冷启动差
矩阵分解隐因子、可向量化需要足够数据
向量召回表达强、可扩展需要训练嵌入

记忆:召回 = 多路并行取并集——热门兜底 + 个性化主流,向量召回是新兴主力。


8. 精排层:CTR 预估模型

精排目标:预估「点击率(CTR)」,把最可能点击的排最前:

特征:用户特征(年龄/城市/历史偏好)
      物品特征(类目/价格/热度)
      交互特征(用户×物品统计、上下文)
→ 模型输出 P(点击)

精排模型演进:

模型思想
LR(逻辑回归)线性 + 特征交叉手写,可解释
GBDT(树模型)自动找非线性特征组合
Wide & DeepWide 记忆 + Deep 泛化
DeepFMFM 特征交叉 + 深度网络
DIN/序列模型行为序列建模
# 简化:LR/GBDT 精排(特征工程见 /ml-feature-engineering/)
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import GradientBoostingClassifier

# 特征:user_age, item_cat, item_price, hist_ctr, hour...
X = feature_matrix            # (样本, 特征)
y = click_label               # 0/1

model = GradientBoostingClassifier(n_estimators=100, learning_rate=0.1)
model.fit(X_train, y_train)
rank_score = model.predict_proba(X_test)[:, 1]   # 排序分数

精排评估:不只准确率——更关心排序质量(NDCG,见下节)与线上 CTR 提升。

记忆:精排 = CTR 预估模型——特征决定上限,Wide&Deep 这类模型兼顾记忆与泛化。


9. 评估指标与冷启动

评估指标(推荐 ≠ 分类):

import numpy as np

def ndcg(relevant_ranks, k=10):
    """NDCG:排序质量——相关物品越靠前,分越高"""
    dcg = sum(rel / np.log2(i + 2) for i, rel in enumerate(relevant_ranks[:k]))
    idcg = sum(1 / np.log2(i + 2) for i in range(min(k, sum(relevant_ranks))))
    return dcg / idcg if idcg else 0

def recall_at_k(pred_items, true_items, k=10):
    """Recall@K:前 K 个推荐命中真实喜欢的比例"""
    hits = len(set(pred_items[:k]) & set(true_items))
    return hits / max(len(true_items), 1)
指标衡量适用
Precision@K / Recall@K命中率召回/粗排
NDCG@K排序质量(位置加权)精排
AUCCTR 模型区分度精排
覆盖率/多样性生态健康全局

冷启动(Cold Start)——最经典难题:

场景问题解法
新用户无行为热门推荐 + 注册画像 + 探索(随机试探)
新物品无交互内容特征召回 + 多臂老虎机探索
新平台无数据人工/编辑推荐起步,攒数据

铁律:冷启动靠「探索」补——热门兜底 + 随机试探 + 画像特征,纯协同过滤对冷启动无能为力。


10. 速查表

需求做法
数据表示User-Item 交互矩阵
相似度评分→余弦/皮尔逊,行为→杰卡德
相似推荐Item-based 协同过滤(可预计算)
隐因子矩阵分解(SVD/ALS)
无行为新用户基于内容 + 热门兜底
大规模召回向量召回(Faiss/向量库)
精排CTR 模型(GBDT / Wide&Deep)
评估Recall@K / NDCG@K / AUC
冷启动热门 + 探索 + 画像
系统架构召回(多路) → 精排 → 重排

一句话记忆:一切始于 User-Item 矩阵;相似度是基石——Item-based 稳定、矩阵分解出隐因子、向量召回扩规模;精排用 CTR 模型;评估看 NDCG/Recall@K;冷启动靠热门兜底加探索——漏斗式架构缺一不可。


延伸阅读

  • /ml-supervised-classification/ — 分类与 AUC 基础
  • /ml-feature-engineering/ — 精排特征工程
  • /ml-model-evaluation/ — 指标与调参
  • /ml-time-series/ — 兴趣随时间演化的建模
  • [[ai-ml]] — 推荐算法深度专题
  • [[database]] — 向量数据库与召回存储

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「ml」更多文章

  1. 集成学习实战:Bagging、随机森林、梯度提升与 Stacking
  2. 迁移学习实战:预训练模型、特征提取与微调全流程
  3. 计算机视觉入门实战:图像处理与 CNN 图像分类