cswork / 系统设计

第 24 章 / 共 57 章 · 约 5 分钟

ML 案例 1:推荐排序(视频首页)

本页目录
  1. 1. 需求澄清
  2. 2. 问题形式化
  3. 3. 数据与特征
  4. 4. 模型
  5. 5. 训练与评估
  6. 6. 服务与监控
  7. 7. 回答分级
  8. 8. 不同级别的期望
  9. 章节测验

先花 10 分钟自己设计:需求、核心实体、API 和高层架构,各写几行。写完或者跳过之后,下面的讲解才会展开。

先写你的思路

只存在这台设备上;登录后会存进你的账号。

推荐是 ML 系统设计里最常见的题。“设计视频 App 的首页推荐”听起来是一个模型,实际上是一条多阶段漏斗:在 200 毫秒内,从几千万个视频里挑出用户最可能看完、最不会反感的二十几个。面试官主要看三点:漏斗每一层为什么这样设计,优化目标和标签怎么定,以及怎么避免系统只会推它推过的东西。

1. 需求澄清

功能需求

  • 用户每次打开首页,返回一屏个性化视频,可以下拉刷新
  • 新用户、新视频也要能被推荐(冷启动)
  • 过滤已看过、已屏蔽和违规的内容

非功能需求

  • 规模:1 亿日活,候选视频约 5000 万;平均约 1.2 万 QPS,峰值约 3 万
  • 延迟:推荐接口 p99 < 200 ms
  • 新鲜度:新上传的视频几小时内就有机会被推荐
  • 多样性:同一个创作者、同一个话题不要刷屏

业务目标:提升用户的长期满意度和活跃(人均观看时长、次日回访),而不是单纯的点击率。

2. 问题形式化

没有哪个重模型能在 200 ms 内给 5000 万个视频打分,所以拆成三层:

推荐漏斗:召回、精排、重排
滚动到此处后加载图表…
图表可横向滚动,展开后可缩放查看。
查看图表源码
flowchart LR
  accTitle: 推荐漏斗:召回、精排、重排
  pool[("候选池:约 5000 万")] --> recall["多路召回:约 1000"]
  recall --> rank["精排模型:打分后取前 100"]
  rank --> rerank["重排:多样性、新鲜度、业务规则"]
  rerank --> feed["首页一屏:约 20"]

精排模型做多任务预测:点击概率 P(click)、点击后的预期观看时长、点赞概率、“不感兴趣”概率。最终的排序分数是加权组合,例如:

score = P(click) × E[观看时长 | click] + w1 × P(like) − w2 × P(不感兴趣)

权重不是拍脑袋定的,而是通过线上实验调出来,让人均观看时长和留存最好。只优化点击率,会把标题党推上来。

3. 数据与特征

样本来自曝光日志:每次展示记录用户、视频、位置、时间,以及之后的点击、观看时长、点赞、分享、跳过和“不感兴趣”。

标签:点击后观看超过 30 秒(或完播率超过一定比例)记为正样本;曝光后没有点击记为负样本。观看时长作为回归目标单独训练。

特征:

类别例子
用户观看历史的 embedding、偏好话题、语言、活跃度
视频话题、创作者、时长、上传时间、近 1 小时 / 1 天的点击率和完播率
上下文时间、设备、网络类型
交叉用户对这个创作者 / 话题的历史点击率,与本次会话里刚看过的视频的相似度

实时特征:本次会话最近看过的几个视频,通过 Kafka 流式写入在线特征库,几秒内就能用上。

两个偏差要主动处理:

  • 位置偏差:排在第一位的视频天然点击多。训练时把位置作为特征(或单独的浅层网络)输入,线上打分时统一设成同一个位置,让模型学到与位置无关的兴趣
  • 选择偏差与反馈闭环:模型只能从自己展示过的视频里得到反馈,没推过的视频永远没有数据。留出一小部分流量(比如 1%~5%)做探索,展示模型不那么确定的内容

所有特征都按曝光那一刻的值记录,避免用到“未来”的统计量。

4. 模型

召回(多路并行):

  • 双塔模型:用户塔和视频塔各输出一个向量,用内积表示匹配程度。视频向量离线算好,放进 ANN 索引(如 HNSW);请求时只算一次用户向量,几毫秒内找出最近的几百个。训练用 batch 内负样本,并对热门视频做采样校正,否则模型只会推热门
  • 协同过滤:看过 A 的人也看了 B
  • 订阅:关注的创作者发布的新视频
  • 热门与新视频:保证新鲜度,也给冷启动内容机会

精排:先用 GBDT 或逻辑回归做基线;主模型是深度多任务网络,ID 类特征做 embedding,共享底层、每个任务一个输出头(如 MMoE)。

重排:去掉已看过和违规的内容;按创作者、话题打散,避免连续出现同类;给新视频适当加权。

冷启动:

  • 新用户:按地区和语言的热门内容,加上注册时选的兴趣,前几次会话加大探索比例
  • 新视频:用标题、封面、音频的内容 embedding 映射进双塔的视频向量空间,再分配少量探索流量收集真实反馈

5. 训练与评估

离线(按时间切分):

  • 召回:Recall@k,用户后来真正看了的视频有没有出现在召回结果里
  • 精排:每个任务的 AUC、观看时长的误差;在历史会话上算 NDCG@k
  • 校准:预测点击率与实际点击率之比接近 1,加权组合时各项才有可比性

线上 A/B:

  • 主指标:人均观看时长、次日 / 7 日回访
  • 护栏指标:接口延迟、“不感兴趣”比例、举报率、创作者覆盖度
  • 至少跑一到两周,覆盖周末,避开新奇效应

6. 服务与监控

一次请求:Feed API 并行调用各路召回 → 合并去重 → 批量从在线特征库取用户和视频特征 → 精排模型批量打分 → 重排 → 返回,同时把曝光和当时的特征写入日志,作为下一轮的训练样本。

延迟预算大致是:召回 40 ms、取特征 20 ms、精排 60 ms、重排 10 ms,剩下的留给网络和序列化。

  • 视频向量和 ANN 索引每天重建,新视频增量插入
  • 模型每天重训,或做增量训练,保证新视频的 embedding 及时更新
  • 监控:特征缺失率和分布漂移、预测分数分布、各类用户的点击率和观看时长、召回结果为空的比例
  • 兜底:精排超时就按召回分数或热门榜返回,首页不能白屏

7. 回答分级

漏斗与召回

  • Bad:一个模型给所有视频打分,或者只有一路召回。
  • Good:召回、精排、重排三层,说清每层的候选量和延迟;召回用双塔加 ANN,再配协同过滤和热门。
  • Great:讲清双塔训练的负样本和热门校正、ANN 索引怎么更新、每一路召回各自解决什么问题,以及新视频怎么进入向量空间。

目标与标签

  • Bad:只预测点击率。
  • Good:多任务预测点击、时长、点赞和负反馈,再加权组合;标签有明确的观看时长门槛。
  • Great:说明权重由线上实验确定,解释点击和长期满意度之间的差距;处理位置偏差,所有特征按曝光时刻记录。

评估与反馈闭环

  • Bad:只报离线 AUC。
  • Good:召回看 Recall@k,精排看 AUC 和 NDCG,线上用观看时长和留存做 A/B,并设置护栏指标。
  • Great:指出反馈闭环和选择偏差,留出探索流量;讨论多样性和创作者生态;给出精排超时时的兜底。

8. 不同级别的期望

级别期望
Mid(L4)在提示下给出召回加精排的结构、合理的标签和特征,知道离线指标和 A/B 测试
Senior(L5)主动设计多路召回和多任务精排,处理位置偏差和冷启动,给出延迟预算和线上指标
Staff(L6+)从长期满意度出发定目标,处理反馈闭环和探索,讨论创作者生态与多样性、重训节奏、监控和兜底

章节测验

6 道选择题
  1. 为什么推荐系统要拆成召回和精排两层?
  2. 双塔召回模型在服务时最大的优势是什么?
  3. 处理位置偏差的常见做法是?
  4. 一个刚上传、还没有任何互动数据的视频,怎么让它有机会被推荐?
  5. 为什么要留出一小部分流量做探索?
  6. 排序分数是多个任务预测值的加权和。这些权重通常怎么确定?

章节讨论

正在加载讨论…