AI+电商:智能推荐系统实战
AI 导读
AI+电商:智能推荐系统实战 灵阙学院 | 行业 AI 系列 引言:你以为你在"逛",其实你在被"导航" 打开某电商 App,首页瀑布流里出现一双跑鞋——你昨天刚在社交平台聊过跑步。你没搜索、没收藏,但推荐系统已经"知道"了。点进去看了 30 秒没下单,退出后发现"猜你喜欢"里出现了三双不同价位的替代款。 这不是巧合。这是推荐系统在毫秒级完成了"召回 -> 粗排 -> 精排 ->...
AI+电商:智能推荐系统实战
灵阙学院 | 行业 AI 系列
引言:你以为你在"逛",其实你在被"导航"
打开某电商 App,首页瀑布流里出现一双跑鞋——你昨天刚在社交平台聊过跑步。你没搜索、没收藏,但推荐系统已经"知道"了。点进去看了 30 秒没下单,退出后发现"猜你喜欢"里出现了三双不同价位的替代款。
这不是巧合。这是推荐系统在毫秒级完成了"召回 -> 粗排 -> 精排 -> 重排"的四阶段管道。2025 年的头部电商,推荐系统贡献超过 40% 的 GMV。在信息过载的时代,推荐系统是连接"人"和"货"最高效的桥梁。
但搭建一个工业级推荐系统,远比在 Jupyter Notebook 里跑一个 collaborative filtering 复杂得多。本文从架构到算法到工程,拆解电商推荐系统的完整技术栈。
一、推荐系统全局架构
┌────────────────────────────────────────────────────────────────┐
│ 电商推荐系统四阶段管道 │
├────────────────────────────────────────────────────────────────┤
│ │
│ 全量商品池 (千万级) │
│ | │
│ +--------+ 目标:从千万中选出千级候选 │
│ | 召回层 | 多路并行:协同过滤 / 向量检索 / 热门 / 规则 │
│ | Recall | 延迟预算:< 50ms │
│ +---+----+ │
│ | ~1000 候选 │
│ +---v------+ 目标:轻量排序,筛到百级 │
│ | 粗排层 | 模型:双塔 / 浅层 NN │
│ | Pre-rank | 延迟预算:< 20ms │
│ +---+------+ │
│ | ~100 候选 │
│ +---v------+ 目标:精确预估 CTR/CVR/GMV │
│ | 精排层 | 模型:DIN / DIEN / DCN-V2 / 多目标 │
│ | Ranking | 延迟预算:< 50ms │
│ +---+------+ │
│ | ~30 候选 │
│ +---v------+ 目标:多样性、新鲜度、商业策略 │
│ | 重排层 | 逻辑:MMR 去重 / 类目打散 / 运营插入 │
│ | Re-rank | 延迟预算:< 10ms │
│ +---+------+ │
│ | │
│ 最终展示 (~10-20 items per page) │
└────────────────────────────────────────────────────────────────┘
1.1 为什么需要四阶段?
核心矛盾:精度与延迟的 trade-off。
| 阶段 | 候选量 | 模型复杂度 | 延迟 |
|---|---|---|---|
| 召回 | 千万 -> 千 | 极低(向量内积) | < 50ms |
| 粗排 | 千 -> 百 | 低(双塔/浅层) | < 20ms |
| 精排 | 百 -> 30 | 高(深度交叉网络) | < 50ms |
| 重排 | 30 -> 10-20 | 规则+轻量模型 | < 10ms |
如果直接用精排模型扫千万商品,延迟会到分钟级。四阶段管道是工程和算法的平衡点。
二、召回层:多路并行
2.1 召回策略矩阵
| 召回路 | 原理 | 优势 | 劣势 |
|---|---|---|---|
| ItemCF | 买了 A 的人也买了 B | 可解释、冷启快 | 热门偏差 |
| UserCF | 相似用户喜欢什么 | 发现潜在兴趣 | 稀疏性 |
| 向量召回 | User/Item 向量近邻 | 泛化强 | 需大量数据 |
| 热门召回 | 全局/类目 TopN | 保底兜底 | 无个性化 |
| 标签召回 | 用户兴趣标签匹配 | 可控性强 | 依赖标签体系 |
| 图召回 | 用户-商品二部图游走 | 高阶关系 | 计算开销大 |
2.2 双塔模型实现
"""
双塔模型 (Two-Tower Model)
训练后分别导出 user_embedding 和 item_embedding
在线用 Faiss/Milvus 做 ANN 检索
"""
import torch
import torch.nn as nn
import torch.nn.functional as F
class UserTower(nn.Module):
"""用户塔:编码用户特征为固定维度向量"""
def __init__(self, num_users: int, num_categories: int, dim: int = 64):
super().__init__()
self.user_emb = nn.Embedding(num_users, dim)
self.age_fc = nn.Linear(1, 16)
self.gender_emb = nn.Embedding(3, 8)
self.cat_emb = nn.Embedding(num_categories, 32)
self.fc = nn.Sequential(
nn.Linear(dim + 16 + 8 + 32, 128),
nn.ReLU(),
nn.Linear(128, dim),
)
def forward(self, user_id, age, gender, hist_categories):
u = self.user_emb(user_id)
a = self.age_fc(age.unsqueeze(-1).float())
g = self.gender_emb(gender)
h = self.cat_emb(hist_categories).mean(dim=1) # mean pooling
x = torch.cat([u, a, g, h], dim=-1)
return F.normalize(self.fc(x), dim=-1)
class ItemTower(nn.Module):
"""商品塔:编码商品特征为固定维度向量"""
def __init__(self, num_items: int, num_cats: int, num_brands: int, dim: int = 64):
super().__init__()
self.item_emb = nn.Embedding(num_items, dim)
self.cat_emb = nn.Embedding(num_cats, 32)
self.brand_emb = nn.Embedding(num_brands, 16)
self.price_fc = nn.Linear(1, 16)
self.fc = nn.Sequential(
nn.Linear(dim + 32 + 16 + 16, 128),
nn.ReLU(),
nn.Linear(128, dim),
)
def forward(self, item_id, category, brand, price):
i = self.item_emb(item_id)
c = self.cat_emb(category)
b = self.brand_emb(brand)
p = self.price_fc(price.unsqueeze(-1).float())
x = torch.cat([i, c, b, p], dim=-1)
return F.normalize(self.fc(x), dim=-1)
class TwoTowerModel(nn.Module):
"""训练时计算余弦相似度,推理时分别导出向量"""
def __init__(self, user_tower, item_tower):
super().__init__()
self.user_tower = user_tower
self.item_tower = item_tower
self.temperature = nn.Parameter(torch.tensor(0.07))
def forward(self, user_feats, item_feats):
u_vec = self.user_tower(**user_feats)
i_vec = self.item_tower(**item_feats)
return torch.matmul(u_vec, i_vec.T) / self.temperature.exp()
2.3 ANN 检索:毫秒级从千万中找到 Top-K
"""使用 Faiss 构建 ANN 索引,线上 < 5ms 召回"""
import faiss
import numpy as np
def build_index(embeddings: dict[int, np.ndarray], dim: int = 64):
"""构建 IVF-PQ 索引,支持千万级商品"""
ids = list(embeddings.keys())
vecs = np.stack([embeddings[i] for i in ids]).astype("float32")
quantizer = faiss.IndexFlatIP(dim)
index = faiss.IndexIVFPQ(quantizer, dim, 256, 8, 8)
index.train(vecs)
index.add(vecs)
index.nprobe = 32
return index, ids
def recall_topk(index, ids, user_vec, k=200):
"""在线召回 top-K"""
scores, indices = index.search(user_vec.reshape(1, -1).astype("float32"), k)
return [(ids[idx], float(s)) for idx, s in zip(indices[0], scores[0]) if idx >= 0]
三、精排层:CTR/CVR 多目标预估
3.1 工业常用模型对比
| 模型 | 核心思想 | 优势 | 复杂度 |
|---|---|---|---|
| DeepFM | FM + DNN 并行 | 低阶+高阶特征交叉 | 中 |
| DIN | 注意力加权用户历史 | 捕捉与候选相关的行为 | 中高 |
| DIEN | 序列建模用户兴趣演化 | 理解兴趣变迁 | 高 |
| DCN-V2 | 显式交叉网络 | 自动特征交叉 | 中 |
| MMOE | 多门混合专家 | 多目标优化 | 高 |
3.2 DIN 注意力层
class DINAttention(nn.Module):
"""
DIN 核心创新:不同目标商品,用户历史中相关行为权重不同。
看过的鞋子对当前推荐鞋子更有参考价值。
"""
def __init__(self, dim: int):
super().__init__()
self.attn = nn.Sequential(
nn.Linear(dim * 4, 64), nn.ReLU(), nn.Linear(64, 1),
)
def forward(self, target, history, mask):
"""
target: (B, D) 候选商品向量
history: (B, L, D) 用户历史行为序列
mask: (B, L) 填充位置为 False
"""
B, L, D = history.shape
t = target.unsqueeze(1).expand(-1, L, -1)
# 拼接: target, history, 差, 积
inp = torch.cat([t, history, t - history, t * history], dim=-1)
scores = self.attn(inp).squeeze(-1) # (B, L)
scores = scores.masked_fill(~mask.bool(), float("-inf"))
weights = torch.softmax(scores, dim=-1)
return torch.bmm(weights.unsqueeze(1), history).squeeze(1)
3.3 多目标融合
最终得分 = w1 * pCTR + w2 * pCVR + w3 * pGMV + w4 * freshness + w5 * diversity
其中:
pCTR = 预估点击率
pCVR = 预估转化率
pGMV = 预估客单价 * pCVR
freshness = 商品上架时间衰减
diversity = 与已展示商品的类目差异度
权重配比是一门艺术:偏重 pGMV 导致高价霸屏,偏重 pCTR 导致标题党泛滥。通常需要持续 A/B 测试调优。
四、冷启动:推荐系统的阿喀琉斯之踵
4.1 三种冷启动场景
| 类型 | 场景 | 策略 |
|---|---|---|
| 用户冷启动 | 新注册用户 | 引导选兴趣 + 热门兜底 + 探索-利用 |
| 商品冷启动 | 新上架商品 | 内容特征召回 + 流量扶持 + 类似嫁接 |
| 系统冷启动 | 新平台 | 规则策略 + 热门排行 + 快速积累行为 |
4.2 新用户首屏策略
新用户首次打开 App:
Step 1: 引导页收集 3-5 个兴趣标签
Step 2: 基于标签 + 人口属性初始化画像
Step 3: 首屏混合策略
40% 基于初始画像的个性化推荐
30% 全站热门(保底)
20% Exploration(随机探索不同品类)
10% 新品 / 运营活动
Step 4: 实时根据点击/停留行为更新画像
Step 5: 第 5 次打开后切换到正常推荐
4.3 新商品冷启动:内容理解
def cold_start_embedding(
title: str, image_url: str, attributes: dict,
text_encoder, image_encoder,
) -> np.ndarray:
"""
新商品无行为数据,但有标题/图片/属性。
融合多模态特征,映射到与热商品相同的向量空间。
"""
text_vec = text_encoder.encode(title)
img_vec = image_encoder.encode(image_url)
attr_vec = encode_attributes(attributes)
combined = np.concatenate([
0.4 * text_vec, 0.3 * img_vec, 0.3 * attr_vec,
])
return combined / np.linalg.norm(combined)
五、实时推荐:从 T+1 到毫秒级
5.1 实时特征管道
用户行为事件流 (Kafka)
|
+---v---+
| Flink | 实时计算:
| 流处理 | - 最近 30min 点击品类分布
+---+---+ - 实时 Session 兴趣向量
| - 价格敏感度变化
+---v---+
| Redis | 存储实时特征
| 特征库| TTL = 30min ~ 24h
+---+---+
|
+---v----+
| 推理 | 合并离线 + 实时特征
| 服务 | -> 精排模型推理
+--------+
5.2 Session-based 推荐模型选择
| 方法 | 原理 | 延迟 | 适用场景 |
|---|---|---|---|
| Item-KNN (Session) | 当前点击物品的近邻 | 极低 | 未登录用户 |
| GRU4Rec | GRU 编码点击序列 | 中 | 短 Session |
| SASRec | Self-Attention 编码 | 中高 | 中等 Session |
| BERT4Rec | 双向 Transformer | 高 | 长 Session |
六、A/B 测试框架
推荐系统的每次迭代都必须经过线上实验验证。
6.1 分流与指标
用户请求
|
+-v----------+
| 分流网关 | user_id hash 分配实验组
+--+-----+---+
| |
实验A 实验B 指标收集 -> 数据仓库 -> 统计检验
新模型 旧模型
6.2 关键指标
| 指标 | 定义 | 重要性 |
|---|---|---|
| CTR | 点击 / 曝光 | 核心 |
| CVR | 下单 / 点击 | 核心 |
| GMV/UV | 人均成交额 | 核心 |
| 多样性 (ILS) | 推荐列表品类覆盖率 | 体验 |
| 新鲜度 | 新商品曝光占比 | 生态 |
| 覆盖率 | 被推出的商品占总商品比 | 生态 |
6.3 统计显著性检查清单
- 样本量充足:Power Analysis 计算最小样本量
- 统计显著:p-value < 0.05 且置信区间不跨零
- 业务显著:CTR 提升 0.01% 不值得上线
- 观察期:至少覆盖完整周期(通常 7 天以上)
- SRM 检查:Sample Ratio Mismatch 检测分流是否均匀
七、常见错误与避坑指南
| 错误 | 后果 | 正确做法 |
|---|---|---|
| 离线好就上线 | 线上效果可能反降 | 离线只做筛选,A/B 定生死 |
| 召回只有一路 | 推荐同质化 | 至少 3-5 路并行 |
| 特征穿越 | 训练用了未来信息 | 严格按时间切分 |
| 忽略位置偏差 | 第一位天然 CTR 高 | 训练时加 position feature |
| 只优化 CTR | 标题党泛滥 | 多目标 + 长期留存指标 |
| 推荐全是同类 | "推荐不懂我" | MMR + 类目打散 + 多样性约束 |
| 冷启动硬编码 | 维护成本高 | 模型化的冷启动策略 |
| 没有降级方案 | 模型挂了就白屏 | 热门/规则作为降级兜底 |
| 不监控特征漂移 | 性能无声衰退 | 特征分布自动对比 + 告警 |
八、系统可观测性
8.1 监控分层
秒级实时:
- 推理延迟 P50/P99
- QPS / 错误率
- 特征缺失率
小时级:
- 各路召回命中率
- CTR/CVR 趋势
- 热门商品集中度
日级:
- 推荐覆盖率(长尾有没有被推出去)
- 新用户留存与推荐相关性
- 模型特征漂移检测
8.2 告警规则
| 指标 | 阈值 | 动作 |
|---|---|---|
| P99 延迟 > 200ms | 持续 5 分钟 | 自动降级到缓存策略 |
| 错误率 > 1% | 持续 1 分钟 | 告警 + 自动回滚模型版本 |
| CTR 日环比降 > 20% | 持续 24 小时 | 人工排查 |
| 召回结果为空 | 单次 | 降级到热门兜底 |
九、总结
电商推荐系统是"数据飞轮"的典型案例:更好的推荐 -> 更多点击/购买 -> 更多行为数据 -> 更好的模型 -> 更好的推荐。但飞轮转起来之前,你需要扎实的工程基础。
三条核心建议:
- 架构先行:四阶段管道是骨架,先把数据流跑通再优化算法
- 多路召回:不要把赌注押在单一召回策略上
- A/B 测试文化:每一次模型迭代都必须有可衡量的线上验证
Maurice | maurice_wen@proton.me