跳转至

# ML系统设计

ML系统设计将文件01-03中的基础设施模式应用于机器学习的特定挑战。本文件涵盖ML生命周期、数据管理、训练基础设施、模型评估、服务策略、特征工程、ML流水线和监控

- 像"为YouTube设计一个推荐系统"这样的系统设计面试问题并不是要求你描述推荐算法。它要求你设计整个系统：数据流水线、特征工程、模型训练、评估、服务、监控和迭代。本文件提供了框架。

## ML系统生命周期

- 每个ML系统都遵循相同的生命周期，无论是垃圾邮件分类器还是基础模型：

    问题定义 → 数据 → 特征 → 训练 → 评估 → 部署 → 监控 → 迭代
           ↑                                                        │
           └────────────────────────────────────────────────────────┘

### 问题定义

- 在接触数据或模型之前，先定义：
  - 预测什么？（点击概率、下一个令牌、目标边界框）
  - 用户是谁？（最终用户、内部分析师、其他ML模型）
  - 约束是什么？（延迟<100ms、离线批量操作可以、必须在设备上运行）
  - 业务指标是什么？（收入、参与度、准确率）以及ML指标如何与之关联？
  - 基线是什么？（启发式方法、基于规则的系统、现有模型）——你必须击败它才能证明ML系统的价值。
- 常见错误：在理解问题之前直接跳到模型架构。"我们应该使用Transformer"不是系统设计的答案。"我们需要在200ms内预测1000万个候选的点击概率，因此我们需要一个两阶段系统：快速检索然后一个小型排序模型"才是。

## 数据管理

### 数据收集和标注

- 显式标签：人类标注数据（点击/不点击、目标边界框、对话质量评分）。昂贵（取决于复杂度，每个标签约\(0.02-\)10）、缓慢且主观。
- 隐式标签：从用户行为中推导标签。点击、停留时间、购买、跳过。廉价且丰富，但有噪声（点击不意味着满意；跳过不意味着不喜欢）。
- 程序化标注（Snorkel）：编写标注函数（启发式方法、正则表达式、现有模型），对每个样本进行投票。统计汇总投票以产生概率标签。可扩展到数百万样本，具有中等准确度。
- 主动学习：模型识别最不确定的样本，并请求人工标注这些样本。这最大限度地提高了标注效率：1000个主动选择的标签可以匹配10000个随机标签的质量。

### 数据质量

- 数据验证：检查每批传入数据的模式违反（字段缺失、类型错误）、分布偏移（平均值显著变化）和数量异常（预期100万行，收到50万行）。
- Great Expectations和TFX Data Validation是定义数据期望并在违反时发出告警的工具。
- 数据版本管理：每次训练运行应该是可重现的。DVC（第15章）将数据文件与代码一起追踪。每个数据集版本获得一个哈希值；训练配置引用该哈希值。

### 特征存储

- 特征存储（第15章）为训练和服务提供一致的特征。关键概念：
  - 离线特征：从批处理流水线（Spark）计算，存储在数据仓库中。在训练和批量推理期间使用。示例：用户过去30天的平均会话时长、商品的总购买次数。
  - 在线特征：实时计算或预先计算并从低延迟存储（Redis、DynamoDB）提供服务。在实时推理期间使用。示例：用户最近的5个操作、当前购物车内容。
  - 训练-服务偏差：如果特征计算在训练和服务之间不同，模型在推理时看到的特征值与训练时不同。特征存储通过对两者使用相同的计算来消除此问题。

## 训练基础设施

- 对于本书的读者，分布式训练在第6章（数据并行、模型并行、混合精度、缩放定律）中已有深入介绍。这里我们关注系统方面：
- 实验跟踪（W&B、MLflow——第15章）：每次训练运行记录超参数、指标、git提交、数据版本和硬件。这是模型版本控制的ML等价物。
- 超参数调优：自动化搜索超参数。方法：网格搜索（穷尽，昂贵）、随机搜索（出奇地有效）、贝叶斯优化（对目标建模，在改进可能性高的地方采样）和ASHA（异步连续减半：启动许多试验，早期淘汰表现不佳的）。
- 训练流水线编排（Airflow、Kubeflow——第15章）：自动化数据准备→训练→评估→注册的序列。安排每日重新训练。在失败时发出告警。

## 模型评估

### 离线评估

- 保留测试集：在模型训练时从未见过的数据上评估。标准做法，但如果测试集不代表生产数据，可能会产生误导。
- 基于分片的评估：在子组上评估（按用户人口统计、内容类型、语言、时间段）。一个总体准确率95%的模型可能在特定少数群体上的准确率只有70%——不可接受。
- 回测：对于时间序列或顺序预测，按时间顺序在历史数据上进行评估。使用截至时间\(t\)的数据训练，在\(t\)到\(t + \Delta t\)的数据上评估。避免使用未来数据进行训练导致的数据泄露。

### 在线评估

- A/B测试：将实时流量随机分为对照组（旧模型）和实验组（新模型）。以统计显著性比较业务指标（收入、参与度、留存率）。评估ML变更的黄金标准。
  - 样本量：你需要足够的数据来检测预期的效应量。点击率0.1%的改进需要数百万次展示才能以显著性检测到。
  - 时长：运行至少一个完整周期（大多数产品1-2周）以捕获日-周效应。
  - 护栏指标：监控不应变化的指标（页面加载时间、错误率、崩溃率）以及目标指标。一个增加收入但同时增加崩溃率的模型是净负面的。
- 影子部署：在生产中与新模型并行运行旧模型。两者接收相同的请求，但只有旧模型的预测会提供给用户。比较输出。这能在不影响用户的情况下捕获bug和质量问题。
- 交错实验：对于排序问题，将旧模型和新模型的结果交错在一个列表中。用户与交错列表交互，你测量哪个模型的结果获得更多参与。相比A/B测试需要更少的用户即可达到显著性。

## 模型服务

### 批量vs实时

- 批量推理：预先计算所有可能输入的预测结果。存储在数据库/缓存中。从缓存提供服务。适用于：输入空间有限（每晚为所有用户推荐）、新鲜度不重要（每日预测即可）、延迟容忍度高。
- 实时推理：按需为每个请求计算预测结果。适用于：输入空间无限（任何用户查询）、新鲜度重要（立即为此特定查询进行预测）、延迟必须低。
- 许多系统两者都用：批量预计算一组候选结果（便宜，覆盖80%的流量），实时处理其余部分（昂贵，覆盖尾部查询和新用户）。

### 模型版本管理和注册表

- 模型注册表（MLflow、W&B、SageMaker）存储训练好的模型及其元数据：
  - 版本号和训练日期。
  - 训练配置和数据版本。
  - 评估指标（准确率、延迟、内存使用）。
  - 阶段：开发→预发布→生产→归档。
- 回滚：如果新模型在生产中导致指标下降，立即恢复到前一个版本。注册表使这成为一键操作。

## 特征工程

- 特征工程将原始数据转换为模型所需的输入。它通常是ML中杠杆率最高的活动：更好的特征能改进每个模型，而更好的模型受限于它们收到的特征。

### 在线vs离线特征

- 离线特征是预先计算的，变化缓慢（用户人口统计、30天聚合）。由批处理流水线（Spark）计算，存储在特征存储中。
- 在线特征反映当前状态，变化迅速（购物车中的商品、最近操作、当前位置）。从事件流实时计算或从快速存储中查找。
- 特征新鲜度：某些特征需要秒级新鲜度（欺诈检测：此交易相对于最近5笔交易是否异常？）。其他的可以容忍小时级陈旧度（推荐：根据用户历史，该用户偏好什么类型？）。更新鲜的特征计算和服务更昂贵。

### 常见特征模式

- 计数特征：时间窗口内的事件计数（过去7天的购买次数、过去24小时的登录次数）。
- 嵌入特征：分类变量的学习嵌入（用户嵌入、商品嵌入、查询嵌入）。这些是双塔模型和类似架构的输入。
- 交叉特征：两个或多个特征的组合（user_age × item_category）。捕获单个特征无法捕获的交互。
- 时间特征：自上次操作以来的时间、星期几、一天中的小时。捕获时间模式。
- 聚合特征：数值特征在某个组上的均值、中位数、最小值、最大值、标准差（此卖家的商品平均评分）。

## ML流水线

- ML流水线编排从数据到部署模型的整个工作流程：

    数据摄入 → 验证 → 特征工程 → 训练 → 评估 → 注册 → 部署 → 监控

- 每个步骤是编排器（Airflow、Kubeflow、Metaflow——第15章）中的一个任务。流水线：
  - 按计划运行（每日重新训练）或触发运行（新数据可用）。
  - 是幂等的（重新运行产生相同结果）。
  - 有重试逻辑（如果特征计算失败，使用退避重试3次）。
  - 产生制品（训练好的模型、评估报告、特征统计），这些制品被版本化管理并存储。
- Metaflow（Netflix/Outerbounds）特别适合ML：它对代码、数据和模型一起进行版本管理，支持相同代码的本地开发和云执行，并与K8s和AWS集成。

## 监控

- 我们在第15章（Prometheus、Grafana、告警）中介绍了监控基础。这里我们关注ML特定的监控：

### 数据漂移

- 数据漂移发生在传入数据的分布相对于训练数据发生变化时。在夏季数据上训练的模型可能在冬季数据上表现不佳（不同的用户行为、不同的产品可用性）。
- 检测：使用统计测试比较传入特征分布与训练分布：
  - KS检验（Kolmogorov-Smirnov）：比较两个经验分布。检验它们是否来自相同的底层分布。
  - PSI（总体稳定性指数）：衡量分布偏移了多少。PSI < 0.1为稳定，0.1-0.25为中度偏移，> 0.25为显著偏移。
  - 嵌入漂移：使用质心距离或MMD（最大均值差异）比较传入查询的嵌入分布与训练集。

### 概念漂移

- 概念漂移发生在输入和输出之间的关系发生变化时。特征看起来相同，但正确的预测不同。示例：用户偏好在一场文化活动、流行病或产品变更后发生转变。
- 概念漂移比数据漂移更难检测，因为它需要带标签的数据。监控代理指标：点击率、转化率、用户满意度评分。持续下降可能表明概念漂移。

### 模型退化

- 模型会因多种原因随时间退化：数据漂移、概念漂移、特征流水线错误（特征开始返回空值）以及上游数据变化（第三方API更改其响应格式）。
- 响应：检测到退化时，行动取决于严重程度：
  - 轻度：在最近数据上重新训练（定时重新训练可处理此情况）。
  - 中度：调查根本原因（哪个特征发生了变化？哪个用户群体受到影响？）。
  - 严重：立即回滚到以前的模型版本，然后调查。

### 反馈循环

- ML系统创建反馈循环：模型的预测影响用户行为，后者成为下一个模型版本的训练数据。这些循环可能是良性的，也可能是恶性的。
- 正反馈循环（危险的）：推荐模型主要展示热门商品→用户点击热门商品（因为他们只看到这些）→模型了解到热门商品更受欢迎→多样性崩溃。模型创造了确认其偏见的数据。
- 负反馈循环（也危险的）：欺诈检测模型捕获了所有A类欺诈→没有A类欺诈进入训练数据→下一个模型未学会检测A类→A类欺诈重新出现。
- 缓解措施：
  - 探索：展示一些模型不确定的商品（epsilon-greedy、Thompson采样）。这生成了多样化的训练数据。
  - 反事实日志记录：记录模型本会预测的结果，而不仅仅是用户看到的结果。在反事实数据上训练以消除偏差。
  - 保留集：随机将一部分流量用于无模型过滤的服务。未经过滤的数据为评估模型质量提供了真实依据。
  - 延迟标签：在使用数据训练之前等待真实结果。今天点击的推荐可能明天就后悔。欺诈预测必须等待退款窗口（30-90天）。

### 嵌入表管理

- 大规模ML系统通常有包含1亿+条目的嵌入表（每个用户、商品、广告或实体一个嵌入）。大规模管理这些是系统挑战：
- 存储：1亿实体×256维×float16 = 50 GB。不适合GPU内存。解决方案：存储在CPU内存中并配合GPU端缓存，跨多台机器分片，或使用哈希嵌入（将实体哈希到固定大小的表，接受冲突）。
- 更新：嵌入随模型重新训练而变化。向服务部署新的嵌入表需要：在不中断实时流量时加载50 GB到内存，验证正确性，以及在指标下降时回滚。对嵌入表使用蓝绿部署。
- 陈旧度：新创建的用户没有嵌入（冷启动问题）。解决方案：使用默认嵌入，通过特征到嵌入模型从用户特征派生嵌入，或回退到非个性化模型。

### 公平性和偏见

- ML系统可能会系统性地对待不同群体不同，通常反映了训练数据中的偏见。公平性监控是一种责任，不是可选功能。
- 监控指标：
  - 人口统计均等：不同群体（性别、种族、年龄）的正预测率是否不同？
  - 均等机会：不同群体的真阳性率是否不同？（招聘模型应该同样擅长识别所有群体的合格候选人。）
  - 校准：如果模型说P(合格) = 0.7对于群体A，那么群体A中实际上有70%是合格的吗？对于群体B也是同样？
- 实际步骤：
  - 在分片（子组）上评估模型性能，而不仅仅是总体指标。
  - 在模型评估流水线中纳入公平性指标（一个提高总体准确率但降低特定群体性能的模型未经审查不应部署）。
  - 记录已知的限制和失败模式。
  - 为在敏感领域（招聘、贷款、刑事司法、医疗）部署的模型建立审查流程。