💡

文章摘要

推荐系统是 AI 在工业界最成功的落地场景之一。本文从协同过滤出发,系统讲解推荐系统的完整架构——召回、粗排、精排、重排,覆盖矩阵分解、双塔模型、DeepFM、DIN 等核心算法,并提供完整的工程实现代码与部署方案

1推荐系统概述:为什么推荐无处不在

推荐系统(Recommendation System)是人工智能在工业界最成功、最广泛的落地场景之一。当你打开淘宝看到「猜你喜欢」,打开抖音看到下一条短视频,打开 Spotify 听到「每日推荐」——背后都是推荐系统在运作。

推荐系统的核心问题可以简化为一个公式:给定用户 u 和候选物品集合 I,预测用户对每个物品的偏好评分 r(u, i),然后选出评分最高的 N 个物品推荐给用户。推荐的本质是信息过滤。 在互联网时代,信息过载是最大的痛点。用户面对海量内容(百万级商品、千万级视频),不可能逐一浏览。推荐系统的作用就是在 用户耐心耗尽之前,把最可能感兴趣的内容递到他面前。

从技术架构上看,现代推荐系统通常遵循漏斗模型:从百万级候选物品中,经过多轮筛选,最终输出几十个推荐结果。这个漏斗分为四个核心阶段:召回(Recall)→ 粗排(Pre-Ranking)→ 精排(Ranking)→ 重排(Re-Ranking)。每一阶段处理的数据量和模型复杂度都不同——召回阶段需要在毫秒内从百万物品中筛选出千级候选,因此模型必须极简;精排阶段可以处理百级候选,因此可以使用复杂的深度学习模型。推荐系统的三大核心挑战:
第一,冷启动问题。新用户没有历史行为,新物品没有被交互过,如何推荐?这是推荐系统最经典的难题。解决方案包括利用注册信息做人口统计学推荐、利用物品的内容特征(如文本描述、图片特征)做内容推荐、以及利用热门物品做兜底推荐。 第二,探索与利用的权衡。如果只推荐用户已经喜欢的类型(利用),用户会陷入信息茧房;如果总是推荐全新类型(探索),用户体验会大幅下降。如何在两者之间找到最优平衡点,是推荐系统的核心课题。 第三,大规模实时性。现代推荐系统需要支持千万级并发用户
,端到端响应时间通常要求在100ms 以内。这意味着模型必须在极短时间内完成从召回排序到返回结果的完整流程。

图表加载中…

💡 一句话理解

理解推荐系统的「漏斗架构」是学习推荐系统的第一步。记住一个核心原则:越靠前的层,速度越重要;越靠后的层,精度越重要。召回模型不需要很准,但必须极快;精排模型不需要极快,但必须很准。

⚠️ 常见踩坑

不要试图用一个模型解决所有问题。很多初学者想用同一个深度学习模型同时做召回和排序,这在工程上是不可行的——召回需要处理百万级候选,复杂度必须是 O(1) 或 O(log n);精排可以处理百级候选,复杂度可以是 O(n)。两者的技术路线完全不同。

2协同过滤:推荐系统的基石

协同过滤(Collaborative Filtering, CF)是推荐系统最经典也最基础的算法。它的核心思想可以概括为一句话:相似的用户喜欢相似的物品

协同过滤分为两大流派:基于用户的协同过滤(User-Based CF)和基于物品的协同过滤 (Item-Based CF)。基于用户的 CF 164:找到与目标用户兴趣相似的其他用户,然后推荐这些相似用户喜欢、但目标用户还没接触过的物品。比如用户 A 和用户 B 都看过电影 X 和 Y,用户 B 还看了电影 Z 且给了高分,那么系统就把电影 Z 推荐给用户 A。 基于物品的 CF284:找到与用户历史喜欢的物品相似的其他物品,然后推荐给用户。比如用户 A 喜欢电影 X,而电影 X 和电影 Z 经常被同一个用户喜欢(共现率高),那么系统就把电影 Z 推荐给用户 A。两者的本质区别: 用户 CF 关注「谁和你喜欢的一样」,物品 CF 关注「你喜欢的那个和什么相似」。在实际工业中,物品 CF 更加常用 ,因为物品之间的相似度相对稳定(物品关系变化慢),而用户兴趣变化快,用户 CF 需要频繁重新计算。相似度计算的核心方法:
余弦相似度
是最常用的相似度度量方式。将用户对物品的评分向量看作空间中的向量,两个向量的夹角余弦值就是它们的相似度。取值范围 [-1, 1],值越大表示越相似。
皮尔逊相关系数
衡量的是两个变量的线性相关程度。与余弦相似度不同,皮尔逊系数会先减去均值再做计算,因此它能消除用户评分偏好的影响 (有些用户习惯性打高分,有些习惯性打低分)。 Jaccard 相似度适用于隐式反馈场景(只有点击/浏览记录,没有明确评分)。它计算的是两个用户交互过的物品的交集除以并集。

python
import numpy as np
from collections import defaultdict

class ItemBasedCF:
    """基于物品的协同过滤推荐系统"""
    
    def __init__(self, k=20):
        self.k = k  # 取最相似的 k 个物品
        self.item_sim = {}  # 物品相似度矩阵
        self.user_items = defaultdict(set)  # 用户交互过的物品
        self.item_users = defaultdict(set)  # 物品被哪些用户交互过
    
    def fit(self, interactions):
        """训练:计算物品共现矩阵和相似度
        interactions: [(user_id, item_id, rating), ...]
        """
        # 构建用户-物品映射
        for user, item, rating in interactions:
            self.user_items[user].add(item)
            self.item_users[item].add(user)
        
        # 计算物品共现矩阵(同时出现的用户数)
        item_cooccurrence = defaultdict(lambda: defaultdict(int))
        for item, users in self.item_users.items():
            for u in users:
                for other_item in self.user_items[u]:
                    if item != other_item:
                        item_cooccurrence[item][other_item] += 1
        
        # 计算余弦相似度
        for item_i, cooccur in item_cooccurrence.items():
            n_i = len(self.item_users[item_i])
            for item_j, count in cooccur.items():
                n_j = len(self.item_users[item_j])
                # 余弦相似度 = 共现数 / sqrt(|Ni| * |Nj|)
                self.item_sim[(item_i, item_j)] = count / np.sqrt(n_i * n_j)
    
    def recommend(self, user_id, top_n=10):
        """为用户推荐物品"""
        scores = defaultdict(float)
        for item_i in self.user_items[user_id]:
            for item_j, sim in self.item_sim.items():
                if item_j[0] == item_i and item_j[1] not in self.user_items[user_id]:
                    scores[item_j[1]] += sim
        # 按得分排序,返回 top_n
        return sorted(scores.items(), key=lambda x: x[1], reverse=True)[:top_n]
图表加载中…

💡 一句话理解

协同过滤的工业实现中,物品相似度矩阵的存储是关键。对于百万级物品库,全量相似度矩阵需要 10^12 个条目,无法放入内存。实际做法是只存储 top-K 最相似的物品对(K 通常取 50-200),将存储量从 O(n^2) 降低到 O(nK)。

⚠️ 常见踩坑

协同过滤有一个根本性局限:它只能推荐已经有人交互过的物品。如果一件新物品还没有任何用户与之交互,协同过滤永远无法推荐它。这就是冷启动问题的根源。解决冷启动需要引入内容特征(物品的文本描述、图片特征等),这就进入了「内容推荐」的范畴。

3矩阵分解:从协同过滤到隐语义模型

协同过滤有一个 致命的工程问题:物品相似度矩阵的存储空间随着物品数量呈平方级增长。当物品库达到百万级时,相似度矩阵需要存储 10^12 个条目,即使只保留 top-K 也变得极其庞大。矩阵分解(Matrix Factorization, MF)完美地解决了这个问题。它的核心思想是:将巨大的用户-物品评分矩阵 R 分解为两个低秩矩阵的乘积——用户隐向量矩阵 P 和物品隐向量矩阵 Q。R ≈ P × Q^T其中 P 的每一行是一个用户的隐向量(Latent Vector),Q 的每一行是一个物品的隐向量。隐向量的维度通常取 32-128。这些隐向量的每一维代表什么?它们没有明确的语义解释,但它们捕捉了用户和物品在 隐语义空间中的位置。直观理解: 假设隐向量维度为 3,在电影推荐的场景中,这三个维度可能分别代表「科幻程度」、「喜剧程度」、「艺术性程度」。用户 A 的隐向量可能是 [0.9, 0.1, 0.8](喜欢科幻和艺术电影),电影 X 的隐向量可能是 [0.85, 0.2, 0.75](科幻 + 艺术),两者的点积很高,说明匹配度好。

矩阵分解的训练目标是最小化预测评分与真实评分之间的误差,同时加入 L2 正则化防止过拟合min Σ(r_ui - p_u · q_i)^2 + λ(||p_u||^2 + ||q_i||^2)
求解方法: 交替最小二乘法(ALS)和随机梯度下降SGD)是最常用的两种优化方法。ALS 交替固定 P 求解 Q、固定 Q 求解 P,每次子问题都是线性最小二乘,有闭式解。SGD 则直接对目标函数求梯度更新参数,在大规模数据集上更高效。

python
import numpy as np

class MatrixFactorization:
    """矩阵分解推荐模型 - SGD 优化"""
    
    def __init__(self, n_users, n_items, n_factors=64, lr=0.01, reg=0.02):
        self.n_factors = n_factors  # 隐向量维度
        self.lr = lr  # 学习率
        self.reg = reg  # L2 正则化系数
        
        # 随机初始化用户和物品隐向量
        np.random.seed(42)
        self.P = np.random.normal(0, 0.1, (n_users, n_factors))
        self.Q = np.random.normal(0, 0.1, (n_items, n_factors))
        self.b_u = np.zeros(n_users)  # 用户偏置
        self.b_i = np.zeros(n_items)  # 物品偏置
        self.mu = 0  # 全局平均评分
    
    def fit(self, interactions, epochs=20):
        """训练:interactions = [(user, item, rating), ...]"""
        self.mu = np.mean([r for _, _, r in interactions])
        
        for epoch in range(epochs):
            np.random.shuffle(interactions)
            total_loss = 0
            
            for user, item, rating in interactions:
                # 预测评分
                pred = self.mu + self.b_u[user] + self.b_i[item] + np.dot(self.P[user], self.Q[item])
                
                # 计算误差
                e = rating - pred
                total_loss += e ** 2
                
                # SGD 更新
                self.b_u[user] += self.lr * (e - self.reg * self.b_u[user])
                self.b_i[item] += self.lr * (e - self.reg * self.b_i[item])
                self.P[user] += self.lr * (e * self.Q[item] - self.reg * self.P[user])
                self.Q[item] += self.lr * (e * self.P[user] - self.reg * self.Q[item])
            
            rmse = np.sqrt(total_loss / len(interactions))
            print(f"Epoch {epoch+1}: RMSE = {rmse:.4f}")
    
    def predict(self, user, item):
        """预测用户对物品的评分"""
        return self.mu + self.b_u[user] + self.b_i[item] + np.dot(self.P[user], self.Q[item])
图表加载中…

💡 一句话理解

矩阵分解的隐向量维度(n_factors)是一个关键超参数。维度太低(如 8),模型表达能力不足;维度太高(如 256),容易过拟合。一个实用的经验法则是:从 64 开始,根据验证集 RMSE 调整。如果训练集和验证集的 RMSE 差距大,降低维度或增加正则化。

⚠️ 常见踩坑

矩阵分解只利用了用户-物品的交互信息,完全没有利用物品本身的内容特征。这意味着它无法解决冷启动问题,也无法捕捉物品的语义信息。在实际工业系统中,矩阵分解通常作为推荐系统的一个基础组件,与内容特征模型组合使用。

4召回层:多路召回策略

现代推荐系统的召回层不再依赖单一策略,而是采用多路召回(Multi-Channel Recall)——并行运行多种召回策略,每种策略从不同角度筛选候选物品,最后合并去重,送入下一层。为什么需要多路召回? 因为用户的需求是 多维度的。一个用户在电商平台上,可能同时有:基于历史偏好的兴趣需求(喜欢买电子产品)、基于当前上下文的即时需求(正在搜索生日礼物)、基于热门趋势的探索需求(想知道最近什么流行)。单一召回策略无法同时满足这些维度。主流召回策略包括:
协同过滤召回
:基于物品 CF 或用户 CF,推荐与用户历史行为相似的物品。这是最基础的召回通道,覆盖用户的核心兴趣 向量召回(Embedding Recall):将用户和物品映射到同一个向量空间,通过近似最近邻搜索(ANN)快速找到最匹配的物品。这是目前工业界最强大的召回方式 ,代表模型是 YouTube 双塔模型。 热门召回 :直接推荐当前平台最热门的物品。这是一个 兜底策略 ,确保新用户和冷启动场景下也有内容可推荐。 上下文召回 :根据用户的当前上下文(时间、地点、设备、搜索词)筛选相关物品。比如用户在晚上 10 点打开视频 App,优先推荐轻松的短视频而不是长篇纪录片。 探索召回:故意推荐一些与用户历史兴趣不太相关的物品,用于 发现用户的新兴趣。这是打破信息茧房的关键机制。 多路召回的合并策略:不同召回通道返回的候选物品合并后,需要去重并统一打分。常用方法是给每个通道分配不同的权重,或者简单截断(每个通道取 top-N,合并后去重)。

图表加载中…

💡 一句话理解

多路召回中,向量召回Embedding Recall)是目前性价比最高的召回方式。它结合了协同过滤的个性化优势和内容推荐的泛化能力,且通过 ANN 搜索(如 FAISSHNSW)可以在毫秒级完成百万物品的匹配。建议优先投入资源建设向量召回通道。

⚠️ 常见踩坑

多路召回的通道数量不是越多越好。每个召回通道都消耗计算资源,而且通道之间存在大量重叠(不同通道可能召回相同的物品)。通常 3-5 个召回通道是性价比最优的选择。过多通道会增加系统复杂度,但边际收益递减。

5双塔模型:向量召回的核心引擎

双塔模型(Two-Tower Model / Dual Encoder)是现代推荐系统向量召回的核心引擎。它的名字来源于其架构:两个独立的神经网络(塔),一个编码用户,一个编码物品,最终将两者的输出向量映射到同一个空间。

用户塔:输入用户的特征(历史行为序列、人口统计学特征、兴趣标签等),输出一个用户隐向量 u。

物品塔:输入物品的特征(文本描述、类别标签、图片特征等),输出一个物品隐向量 v。

训练目标:让正样本(用户实际交互过的物品)的 u · v 内积最大化,让负样本(用户未交互的物品)的 u · v 内积最小化。这等价于在同一个向量空间中,将用户和他喜欢的物品「拉近距离」,将用户和不喜欢的物品「推远距离」。

双塔模型的最大优势:在训练完成后,物品塔的向量可以离线预计算并存储到向量数据库中。当用户请求到来时,只需要用用户塔计算当前用户的向量,然后通过 ANN 搜索在向量数据库中快速找到最相似的物品。这使得召回复杂度从 O(N) 降低到O(log N)

YouTube 推荐模型是双塔模型的经典代表。2016 年,YouTube 发表了里程碑论文,介绍了他们的深度推荐系统:候选生成层(Candidate Generation)使用双塔模型从百万视频中召回数百个候选,排序层(Ranking)使用复杂的深度神经网络对候选精确打分。这套架构至今仍是工业推荐系统的标准范式。

python
import torch
import torch.nn as nn
import torch.nn.functional as F

class TwoTowerModel(nn.Module):
    """双塔模型 - 向量召回核心"""
    
    def __init__(self, user_feature_dim, item_feature_dim, 
                 embedding_dim=128, hidden_dim=256):
        super().__init__()
        
        # 用户塔
        self.user_tower = nn.Sequential(
            nn.Linear(user_feature_dim, hidden_dim),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(hidden_dim, hidden_dim // 2),
            nn.ReLU(),
            nn.Linear(hidden_dim // 2, embedding_dim)
        )
        
        # 物品塔
        self.item_tower = nn.Sequential(
            nn.Linear(item_feature_dim, hidden_dim),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(hidden_dim, hidden_dim // 2),
            nn.ReLU(),
            nn.Linear(hidden_dim // 2, embedding_dim)
        )
    
    def forward(self, user_features, item_features):
        """计算用户-物品相似度"""
        user_vec = F.normalize(self.user_tower(user_features), dim=1)
        item_vec = F.normalize(self.item_tower(item_features), dim=1)
        # 余弦相似度(已归一化,点积即余弦相似度)
        similarity = torch.sum(user_vec * item_vec, dim=1)
        return similarity
    
    def get_user_embedding(self, user_features):
        """离线预计算物品向量后,在线只需调用此方法"""
        return F.normalize(self.user_tower(user_features), dim=1)
    
    def get_item_embedding(self, item_features):
        """离线预计算所有物品向量"""
        return F.normalize(self.item_tower(item_features), dim=1)
    
    def info_nce_loss(self, user_features, pos_item_features, 
                      neg_item_features, temperature=0.07):
        """InfoNCE 损失:拉近正样本,推远负样本"""
        user_vec = self.get_user_embedding(user_features)
        pos_vec = self.get_item_embedding(pos_item_features)
        neg_vec = self.get_item_embedding(neg_item_features)
        
        pos_sim = torch.sum(user_vec * pos_vec, dim=1) / temperature
        neg_sim = torch.sum(user_vec.unsqueeze(1) * neg_vec, dim=2) / temperature
        
        logits = torch.cat([pos_sim.unsqueeze(1), neg_sim], dim=1)
        labels = torch.zeros(user_features.size(0), dtype=torch.long)
        return F.cross_entropy(logits, labels)
图表加载中…

💡 一句话理解

双塔模型训练完成后,物品塔的向量可以离线预计算。这意味着无论物品库有多大,用户请求时的在线计算量只取决于用户塔的复杂度(一次前向传播)加上 ANN 搜索的时间(通常 < 10ms)。这是推荐系统能做到毫秒级响应的关键。

⚠️ 常见踩坑

双塔模型有一个根本限制:用户特征和物品特征在训练完成后无法交叉。也就是说,用户塔只看用户特征,物品塔只看物品特征,两者之间没有交互。这导致双塔模型无法捕捉「用户-物品的细粒度交互模式」(比如某个用户特别喜欢某类物品中的某个子特征)。这个限制需要在精排层弥补。

6精排层:深度学习排序模型

精排层是推荐系统中模型最复杂、精度要求最高的阶段。经过召回和粗排后,候选物品已经缩小到百级,精排模型可以对每个候选物品进行深度、精细的打分。

精排模型的核心任务是学习用户特征、物品特征和上下文特征之间的复杂交互关系。与双塔模型不同,精排模型允许用户特征和物品特征在模型内部充分交叉,从而捕捉细粒度的匹配信号。

DeepFM是精排层的经典模型,它同时学习了低阶特征交互(通过 FM 部分)和高阶特征交互(通过 DNN 部分):

FM 部分(Factorization Machine):学习特征的两两交互。对于类别特征,FM 可以自动学习特征组合的权重,不需要人工设计交叉特征。比如「用户年龄 × 物品类别」这种组合特征,FM 可以自动发现。

DNN 部分(Deep Neural Network):学习高阶特征交互。通过多层全连接网络,自动发现三个或更多特征之间的复杂关系。比如「年轻用户 + 晚上 + 短视频」这种三阶交互,FM 无法捕捉,但 DNN 可以。

DeepFM 的优势是端到端训练,不需要人工特征工程。FM 和 DNN 共享同样的输入 Embedding,同时学习低阶和高阶交互,两者互补。

DIN(Deep Interest Network)是阿里巴巴提出的另一个精排模型,其核心创新是注意力机制:不是平等对待用户的所有历史行为,而是根据候选物品动态调整用户历史行为的权重。比如当候选物品是手机时,用户历史中购买电子产品的行为权重自动提高,购买衣服的行为权重降低。

DIEN(Deep Interest Evolution Network)在 DIN 的基础上增加了兴趣演化建模:用户的兴趣不是静态的,而是随着时间变化的。DIEN 用 GRU 对用户的历史行为序列建模,捕捉兴趣的演化轨迹。

python
import torch
import torch.nn as nn

class DeepFM(nn.Module):
    """DeepFM 推荐模型 - 精排层核心"""
    
    def __init__(self, field_dims, embed_dim=16, mlp_dims=(256, 128, 64)):
        super().__init__()
        self.num_fields = len(field_dims)
        
        # Embedding 层(FM 和 DNN 共享)
        self.embedding = nn.Embedding(sum(field_dims), embed_dim)
        # 一阶偏置
        self.fc = nn.Embedding(sum(field_dims), 1)
        
        # FM 部分:二阶交互
        self.fm_first = nn.ModuleList([
            nn.Embedding(dim, embed_dim) for dim in field_dims
        ])
        
        # DNN 部分:高阶交互
        input_dim = self.num_fields * embed_dim
        mlp_layers = []
        for dim in mlp_dims:
            mlp_layers.extend([
                nn.Linear(input_dim, dim),
                nn.BatchNorm1d(dim),
                nn.ReLU()
            ])
            input_dim = dim
        mlp_layers.append(nn.Linear(mlp_dims[-1], 1))
        self.mlp = nn.Sequential(*mlp_layers)
        self.dropout = nn.Dropout(0.3)
    
    def forward(self, x):
        """x: [batch_size, num_fields] 稀疏特征索引"""
        # 一阶部分
        first_order = self.fc(x).sum(dim=1)
        
        # FM 二阶部分
        feature_embeds = []
        offset = 0
        for dim, emb_layer in zip([1]*self.num_fields, self.fm_first):
            feature_embeds.append(self.embedding(x[:, offset:offset+1]).squeeze(1))
            offset += 1
        feature_embeds = torch.stack(feature_embeds, dim=1)
        
        # FM 交叉项
        sum_squared = torch.sum(feature_embeds, dim=1) ** 2
        squared_sum = torch.sum(feature_embeds ** 2, dim=1)
        fm_second = 0.5 * torch.sum(sum_squared - squared_sum, dim=1, keepdim=True)
        
        # DNN 部分
        dnn_input = feature_embeds.view(-1, self.num_fields * feature_embeds.size(-1))
        dnn_output = self.mlp(self.dropout(dnn_input))
        
        # 最终预测
        return first_order + fm_second + dnn_output
图表加载中…

💡 一句话理解

精排模型的特征工程往往比模型架构本身更重要。好的特征(如用户最近 7 天的行为序列、物品的实时热度、上下文的时段信息)可以让一个简单的 DeepFM 模型超越一个复杂的 DIN 模型但特征工程差的系统。花时间挖掘高质量的特征,比花时间调模型架构的回报率更高。

⚠️ 常见踩坑

精排模型的训练数据质量直接决定上线效果。如果训练数据中的正样本定义不合理(比如把「曝光但未点击」视为负样本,而实际上用户可能只是没看到),模型会学到错误的信号。务必仔细定义正负样本:正样本应该是「用户明确表达偏好的行为」(点击 + 停留超过 N 秒 / 收藏 / 购买),负样本应该是「曝光但明确拒绝的行为」。

7重排层:多样性、去重与业务规则

经过精排后,系统已经有了一个按预测得分排序的物品列表。但这个列表不能直接返回给用户——还需要经过重排层(Re-Ranking)的调整。

重排层的目的不是提升推荐的准确性(精排已经做到了),而是提升推荐结果的用户体验和业务价值。具体来说,重排层需要解决以下问题:

多样性控制:如果精排得分最高的 20 个物品全是同一种类(比如全是手机壳),用户会觉得推荐结果单调乏味。重排层需要在准确性和多样性之间做权衡,确保推荐结果覆盖多个品类。常用方法是MMR(Maximal Marginal Relevance)算法:每次选择下一个物品时,不仅考虑它的相关性得分,还惩罚它与已选物品的相似度。

去重:同一物品的不同 SKU、不同版本不应同时出现在推荐结果中。比如 iPhone 16 的黑色版和白色版不应该同时推荐。

业务规则:推荐系统不是纯技术系统,它需要满足业务约束。比如:广告位插入(每隔 5 个推荐位插入一个广告)、新品扶持(给新上架物品一定的曝光机会)、库存过滤(没货的物品不能推荐)、价格区间控制(根据用户消费能力推荐合适价位的物品)。

打散策略:相同品类或相同来源的物品不应该连续出现。比如不能连续推荐 5 个来自同一个商家的商品。

重排层的算法通常比较简单(贪心算法、动态规划),因为它的核心约束是业务逻辑而非模型精度。

图表加载中…

💡 一句话理解

重排层中最容易被忽视但最重要的约束是新颖性(Novelty)。如果推荐结果全是用户已经知道或买过的物品,用户体验会很差。在重排层中加入新颖性分数(比如基于用户历史行为计算「新鲜度」),可以显著提升用户的探索意愿和长期留存率。

⚠️ 常见踩坑

重排层中的业务规则如果设计不当,会严重损害推荐质量。比如过度的广告插入会让用户觉得「全是广告」,过度的新品扶持会让推荐结果质量下降。业务规则必须设置合理的上限(如广告位最多占 20%),并且要持续监控业务规则对推荐指标的影响。

8冷启动策略:新用户与新物品的推荐难题

冷启动(Cold Start)是推荐系统最经典的难题。它分为三种情况:

用户冷启动:新用户注册后没有任何历史行为数据,无法使用协同过滤或双塔模型做个性化推荐。

物品冷启动:新上架的物品没有被任何用户交互过,协同过滤和向量召回都无法推荐它。

系统冷启动:全新的推荐系统上线,没有任何用户行为数据。

用户冷启动的解决方案:

人口统计学推荐:利用用户注册时提供的信息(年龄、性别、地区、职业)做粗略的个性化。比如 25 岁的女性用户,优先推荐同龄女性用户喜欢的物品。这种方法精度不高,但比随机推荐好得多。

兴趣选择:在新用户注册流程中,加入「选择你感兴趣的类别」环节。让用户主动表达兴趣偏好,系统基于这些选择做初始推荐。这是目前最流行的做法。

热门兜底:在完全没有用户信息的情况下,推荐平台当前最热门的物品。这是一个保底策略,确保用户至少能看到一些内容。

物品冷启动的解决方案:

内容推荐:利用物品自身的内容特征(文本描述、图片、类别标签、价格等)做推荐。比如一篇新文章,可以用它的关键词和分类标签匹配用户的兴趣标签。

协同过滤 + 内容混合:在物品有一定交互数据后,逐步从内容推荐过渡到协同过滤推荐。可以使用一个混合权重:新物品的推荐得分 = alpha × 内容得分 + (1-alpha) × 协同过滤得分,其中 alpha 随着物品交互数据量的增加而递减。

探索策略:使用多臂老虎机(Multi-Armed Bandit, MAB)算法,给新物品一定的探索曝光机会。Thompson Sampling 和 UCB(Upper Confidence Bound)是最常用的 MAB 算法,它们能在探索和利用之间自动找到平衡点。

python
import numpy as np

class ThompsonSampling:
    """Thompson Sampling 多臂老虎机 - 冷启动探索策略"""
    
    def __init__(self, n_items):
        self.n_items = n_items
        # 每个物品的 Beta 分布参数
        # alpha = 成功次数 + 1, beta = 失败次数 + 1
        self.alpha = np.ones(n_items)
        self.beta = np.ones(n_items)
    
    def select_item(self):
        """从每个物品的 Beta 分布中采样,选择最大值对应的物品"""
        samples = np.random.beta(self.alpha, self.beta)
        return np.argmax(samples)
    
    def update(self, item_id, reward):
        """更新物品参数:reward=1 表示用户交互,0 表示未交互"""
        if reward == 1:
            self.alpha[item_id] += 1
        else:
            self.beta[item_id] += 1
    
    def get_exploration_rate(self, item_id):
        """获取物品当前的探索程度(方差越大说明探索空间越大)"""
        mean = self.alpha[item_id] / (self.alpha[item_id] + self.beta[item_id])
        var = (self.alpha[item_id] * self.beta[item_id]) / ((self.alpha[item_id] + self.beta[item_id]) ** 2 * (self.alpha[item_id] + self.beta[item_id] + 1))
        return mean, var

# 冷启动混合推荐策略
def cold_start_recommend(user_profile, new_items, alpha=0.7):
    """新物品的混合推荐,alpha 为内容推荐权重"""
    scores = []
    for item in new_items:
        content_score = compute_content_similarity(user_profile, item)
        cf_score = compute_cf_score(user_profile, item)
        final_score = alpha * content_score + (1 - alpha) * cf_score
        scores.append((item, final_score))
    return sorted(scores, key=lambda x: x[1], reverse=True)
图表加载中…

💡 一句话理解

冷启动问题的最佳实践是分层处理。将物品分为三层:冷启动层(交互 < 100 次)、温启动层(100-10000 次)、成熟层(> 10000 次),每层使用不同的推荐策略。冷启动层以内容推荐 + 探索为主,成熟层以协同过滤 + 深度学习为主。这种分层策略在工业界被广泛采用。

⚠️ 常见踩坑

冷启动探索阶段必须严格控制曝光质量。如果给新用户推荐的内容质量太差,他们可能会直接流失。探索策略的核心原则是:只在新品中做探索,保证探索池的物品本身质量过关(通过内容审核、人工筛选、或内容质量模型过滤)。

9推荐系统的评估指标

评估推荐系统的效果需要多维度指标,单一指标无法全面反映推荐质量。推荐系统的评估指标可以分为三类:

离线评估指标(Offline Metrics):在历史数据上评估模型的预测能力。

Precision@K:在推荐的前 K 个物品中,有多少是用户实际感兴趣的(命中率)。

Recall@K:用户实际感兴趣的物品中,有多少被推荐出来了(覆盖率)。

NDCG@K(Normalized Discounted Cumulative Gain):考虑了排名顺序的指标——排名越靠前的正确推荐权重越大。这是最全面的离线指标

AUC(Area Under ROC Curve):衡量模型区分正负样本的能力,取值范围 0.5-1.0。

在线评估指标(Online Metrics):在真实用户中通过 A/B 测试评估。

CTR(Click-Through Rate):推荐物品的点击率,是最直接的在线指标。

CVR(Conversion Rate):点击后的转化率(购买、收藏、分享等),衡量推荐的「深度价值」。

人均停留时长:用户在平台上的总停留时间,衡量推荐结果的整体吸引力。

多样性指标(Diversity Metrics):

覆盖率(Catalog Coverage):推荐结果覆盖的物品占总物品的比例。覆盖率越高,说明长尾物品的曝光机会越多。

个性化程度(Personalization):不同用户看到的推荐结果的差异程度。如果所有用户看到的推荐都一样,个性化程度为零。

新颖性(Novelty):推荐结果中用户未知物品的比例。

公平性指标(Fairness Metrics):

曝光公平性:不同类别物品(不同商家、不同品类、不同创作者)获得的曝光是否公平。

群体公平性:不同用户群体(不同性别、年龄、地区)获得的推荐质量是否一致。

图表加载中…
指标类型指标名称衡量什么理想值局限性

离线

Precision@K

命中率 前 K 个中有多少正确

越高越好

不考虑排名顺序

离线

NDCG@K

排名敏感的命中率

越高越好

需要真实标签

离线

AUC

正负样本区分能力

0.7 以上

不考虑推荐列表质量

在线

CTR

推荐物品点击率

行业基准以上

点击不等于满意

在线

CVR

点击后转化率

行业基准以上

转化周期长

多样性

覆盖率

推荐结果覆盖的物品占比

越高越好

与精度有冲突

公平性

曝光基尼系数

曝光分布的均衡程度

越低越公平

过度公平可能损害精度

💡 一句话理解

在工业实践中,离线指标的提升不等于在线指标的提升。一个模型可能在离线测试中 NDCG 提高了 5%,但上线后 CTR 反而下降了。这是因为离线评估使用的是历史数据,存在「幸存者偏差」——你只能评估过去推荐过的物品的效果。因此,离线指标用于快速筛选和比较模型,但最终决策必须基于 A/B 测试的在线指标。

⚠️ 常见踩坑

只关注 CTR 是一个危险的陷阱。高 CTR 可能来自「标题党」或「低质但吸引人的内容」,长期来看会损害平台的内容生态和用户信任。推荐系统的优化目标应该是长期用户价值(Long-term User Value),而不是短期点击率。建议将 CTR 和 CVR、停留时长、用户留存率组合成一个综合指标。

10推荐系统架构与工程实践

一个完整的工业级推荐系统不仅需要强大的算法,还需要可靠的工程架构来支撑。推荐系统的工程架构通常包含以下几个核心组件:

特征存储(Feature Store):统一管理用户特征、物品特征和上下文特征。特征需要分为离线特征(用户长期画像、物品静态属性)和实时特征(用户最近行为、物品实时热度)。离线特征可以批量更新(天级),实时特征需要流式更新(秒级)。

模型训练平台:支持离线训练和在线更新。离线训练使用全量历史数据,通常每天或每周运行一次,训练深度排序模型。在线更新使用实时数据流,增量更新召回模型的参数。

在线服务(Serving):推荐服务的核心,需要支持高并发、低延迟。通常分为三层:召回服务(ANN 搜索,< 10ms)、排序服务(深度学习推理,< 50ms)、重排服务(规则计算,< 5ms)。

实时数据流:使用 Kafka 或类似的消息队列,实时采集用户行为数据,更新特征存储,触发在线模型更新。

A/B 测试平台:支持多实验并行,自动分流,实时统计各实验组的指标差异,并提供统计显著性检验。

监控与告警:监控推荐系统的健康度,包括服务延迟、推荐覆盖率、CTR 波动等。当指标异常时自动告警并触发回滚。

推荐系统的技术栈选型:

在 2026 年,推荐系统的技术栈已经相当成熟。召回层FAISS(Facebook 的 ANN 搜索库)或 Milvus(分布式向量数据库)是主流选择。排序层:TensorFlow Recommenders、PyTorch 或 DeepRec(阿里的推荐框架)。特征工程:Feast(特征平台)或自研特征存储。在线服务:Triton Inference Server(NVIDIA)或自研 gRPC 服务。

图表加载中…

💡 一句话理解

推荐系统的灰度发布是保证线上稳定性的关键。模型更新时,不要直接全量切换。正确的做法是:先将 1% 的流量切到新模型,观察核心指标(CTR、延迟、错误率)→ 如果一切正常,逐步扩大到 10% → 50% → 100%。每个阶段至少观察 1 小时。如果任何指标异常,立即回滚。

⚠️ 常见踩坑

推荐系统的数据漂移(Data Drift)是最容易被忽视的运维问题。用户的兴趣会随着时间变化,物品的热度也会波动。如果模型长时间不更新,推荐质量会逐渐下降。解决方案是:定期(每周或每天)用最新数据重新训练模型,并监控训练数据与线上数据的分布差异(如 KL 散度)。当分布差异超过阈值时,触发自动重训。

11推荐系统的未来趋势:大模型与 Agent 时代的推荐

在 2026 年,推荐系统正在经历一场由大语言模型LLM)和AI Agent驱动的范式转变。

LLM 驱动的推荐大语言模型正在改变推荐系统的特征工程方式。传统的推荐系统依赖人工设计的特征(用户标签、物品类别),而 LLM 可以自动从文本中提取语义特征。比如,LLM 可以阅读商品的描述文本,自动提取其风格、适用人群、使用场景等深层特征,这些特征比人工标注的类别标签更丰富、更准确。

LLM 作为推荐接口:用户可以直接用自然语言向推荐系统表达需求,比如「帮我找一双适合跑马拉松的轻便跑鞋,预算 500 以内」。LLM 理解这个需求后,将其转化为推荐系统的查询条件,返回精准的结果。这种交互方式正在从「系统推什么我看什么」转变为「我说什么系统推什么」。

Agent 驱动的推荐AI Agent 可以作为用户的个人购物助理,持续学习用户的偏好,主动推荐物品,甚至代为完成购买决策。与传统的推荐系统不同,Agent 不仅仅是「推荐物品列表」,而是理解用户的完整意图——用户可能不是在找某个具体的物品,而是在解决某个问题(比如「我想给妈妈选一个生日礼物」)。Agent 会通过对话逐步细化需求,最终给出个性化的推荐方案。

多模态推荐:随着视觉、音频、文本多模态模型的成熟,推荐系统开始利用物品的完整多模态信息。比如一件服装,推荐系统不仅利用它的类别和价格标签,还利用它的图片(视觉风格)、描述文本(文字特征)、甚至用户评论的情感分析结果,构建一个更全面的物品表示。

可解释性推荐:用户对「为什么推荐这个给我」的诉求越来越强。传统的推荐系统很难给出解释,而 LLM 可以自动生成推荐理由:「推荐这款耳机给你,因为你最近浏览了三款降噪耳机,这款的主动降噪评分在同等价位中排名第一,而且你的好友小王也购买了它。」

隐私保护推荐:随着数据隐私法规的日益严格,推荐系统需要在保护用户隐私的同时保持个性化。联邦学习(Federated Learning)和差分隐私(Differential Privacy)是实现这一目标的关键技术。联邦学习允许多个平台在不共享原始数据的前提下联合训练推荐模型。

图表加载中…

💡 一句话理解

如果你正在设计新一代推荐系统,建议从LLM 增强特征工程入手——这是投入产出比最高的方向。用 LLM 提取物品的深层语义特征,注入到现有的双塔模型或 DeepFM 中,通常能带来 5-10% 的离线指标提升,而且不需要改动推荐系统的整体架构。

⚠️ 常见踩坑

LLM 在推荐系统中的应用也有风险。幻觉问题Hallucination)是最大隐患——LLM 可能生成不准确的推荐理由或错误的物品特征。在将 LLM 集成到推荐系统时,必须设置验证层:LLM 提取的特征需要经过事实核查(如与商品详情页的实际信息比对)后才能使用。

12总结与扩展阅读

推荐系统是 AI 在工业界最成功的落地场景之一,它的技术栈从经典的协同过滤、矩阵分解,到现代的双塔模型、DeepFM、DIN,再到 LLM 和 Agent 驱动的新范式,已经形成了一个 完整的知识体系

本文覆盖了推荐系统的核心知识:从推荐系统的漏斗架构(召回 → 粗排 → 精排 → 重排),到每个阶段的核心算法和工程实践;从协同过滤的直观理解,到矩阵分解的数学推导;从双塔模型的向量召回,到 DeepFM 的深度排序;从冷启动策略的探索与利用,到推荐系统的多维度评估;再到工程架构和未来趋势。核心要点回顾:
推荐系统遵循漏斗架构,越靠前的层速度越重要,越靠后的层精度越重要。
协同过滤是推荐系统的基石,但它无法解决冷启动问题。 矩阵分解将用户和物品映射到隐语义空间,解决了协同过滤的存储问题。 双塔模型是现代向量召回的核心,通过 ANN 搜索实现毫秒级召回。 DeepFM 和 DIN414是精排层的经典模型,分别学习了特征的低阶和高阶交互。冷启动问题需要内容推荐、探索策略和分层处理来综合解决。推荐系统的评估需要离线指标、在线指标、多样性指标和公平性指标的组合。LLM 和 Agent 522正在改变推荐系统的特征工程、交互方式和意图理解能力。 推荐学习路径:完成本文后,建议继续学习 prac-002(搜索引擎与向量检索),深入理解 ANN 搜索的技术细节;之后学习 prac-003(智能客服与对话系统),了解推荐系统与对话系统的融合方案。

💡 一句话理解

学习推荐系统的最佳方式是搭建一个完整的推荐原型系统。从 MovieLens 或 Amazon Review 公开数据集开始,依次实现:协同过滤召回 → 矩阵分解 → 双塔模型 → DeepFM 精排。每完成一步,用离线指标评估效果。这个过程会让你对推荐系统有深入而全面的理解。

⚠️ 常见踩坑

推荐系统是一个持续迭代的工程系统,不是一次性项目。即使你的模型在离线测试中表现完美,上线后仍然需要持续监控指标、收集反馈、更新模型。推荐系统的成功不取决于初始模型有多好,而取决于迭代速度有多快。