Deployment 和 DevOps¶
Deployment 是模型从研究成果变为产品的关键一步。本文涵盖用于 ML 的 Docker、模型服务、实验追踪、可复现性、生产监控、特征存储和 pipeline 编排——这些基础设施将训练好的模型从 Notebook 推送给数百万用户。
-
只能在你自己的笔记本上运行的模型,不过是个原型。能够在规模化场景下稳定运行、以毫秒级延迟返回预测结果、从故障中自动恢复、并且可以在不停机的情况下更新的模型,才是真正的产品。两者之间的差距,就是 deployment 和 DevOps。
-
大多数 ML 工程师花在 deployment、监控和调试生产问题上的时间,比训练模型的时间更多。对于任何构建真实 ML 系统的人来说,理解这套基础设施不是可选项。
Docker for ML¶
-
我们在第 13 章(操作系统)中从概念层面介绍了容器。这里我们聚焦实践层面:为 ML 工作负载编写 Dockerfile。
-
Dockerfile 是构建容器镜像的配方:
# 以官方 CUDA 基础镜像为起点
FROM nvidia/cuda:12.1.0-cudnn8-runtime-ubuntu22.04
# 系统依赖
RUN apt-get update && apt-get install -y \
python3.11 python3-pip git \
&& rm -rf /var/lib/apt/lists/*
# Python 依赖(单独安装以利用缓存)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制源码(变动频繁,放在最后一层)
COPY src/ /app/src/
COPY configs/ /app/configs/
WORKDIR /app
# 入口点
CMD ["python3", "src/scripts/serve.py", "--config", "configs/serve.yaml"]
-
Layer 缓存:Docker 会缓存每一层。如果
requirements.txt没有变动,重新构建时会跳过pip install。将不常变动的层(系统包、pip install)放在经常变动的层(源码)之前。这能将 10 分钟的构建变成 10 秒的重建。 -
GPU 访问:使用
nvidia/cuda基础镜像,并以docker run --gpus all运行。nvidia-container-toolkit提供从宿主机到容器的 GPU 直通。 -
多阶段构建通过将构建环境与运行时分离来缩减镜像体积:
# 构建阶段:安装构建工具,编译依赖
FROM python:3.11 AS builder
COPY requirements.txt .
RUN pip install --user -r requirements.txt
# 运行时阶段:仅包含运行时依赖
FROM nvidia/cuda:12.1.0-cudnn8-runtime-ubuntu22.04
COPY --from=builder /root/.local /root/.local
COPY src/ /app/src/
ENV PATH=/root/.local/bin:$PATH
-
最终镜像只包含运行时库,不包含编译器、头文件或构建工具。5 GB 的构建镜像变成 2 GB 的运行时镜像。
-
Docker Compose 可运行多容器配置(模型服务 + 负载均衡 + 监控):
# docker-compose.yml
services:
model:
build: .
ports:
- "8080:8080"
deploy:
resources:
reservations:
devices:
- capabilities: [gpu]
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
模型服务¶
-
模型服务(Model Serving)是将推理作为服务运行的过程:接收请求、运行模型、返回预测结果。
-
FastAPI(见第 03 文件)是低到中等 throughput 场景下最简单的方案。对于高 throughput 和 GPU 优化服务,需要使用专用工具:
-
Triton Inference Server(NVIDIA):以 TensorRT、ONNX、PyTorch 和 TensorFlow 格式提供模型服务。特性:
- Dynamic batching:收集单条请求并批量处理以提高 GPU 效率。一批单条请求被合并为 32 条一组,dramatically 提升 throughput。
- 模型集成(Model ensembles):在单个请求中串联多个模型(预处理 → 模型 → 后处理)。
- 多模型服务(Multi-model serving):在同一 GPU 上同时服务多个模型,共享资源。
- 并发模型执行(Concurrent model execution):在同一 GPU 上并行运行多个推理请求。
-
TorchServe(PyTorch):通过 REST/gRPC API 提供 PyTorch 模型服务。支持模型版本管理、A/B 测试和自定义 handler。
-
vLLM:专为 LLM 服务设计。实现了 PagedAttention(高效 KV cache 管理)、连续批处理和跨 GPU 的 tensor 并行。相比朴素服务方式,对大型语言模型的 throughput 提升达 10-20 倍。
-
Cactus (github.com/cactus-compute/cactus):专为移动端和边缘设备端侧服务设计的低 latency AI 引擎。Cactus 提供完全在设备上运行的 OpenAI 兼容 API(聊天补全、流式输出、工具调用、语音转录、embeddings、RAG、视觉),并在本地模型无法处理请求时自动 fallback 到云端。这种混合架构意味着无论推理在本地还是云端运行,应用代码使用相同的 API——引擎根据模型置信度和设备能力自行决策。SDK 支持 Python、Swift、Kotlin、Flutter、React Native 和 Rust,并在 HuggingFace 上提供预转换的模型权重。支持多模态推理(LLM、视觉、语音),使用自定义 ARM SIMD kernel 在 ARM CPU 上实现最快推理,以及零拷贝内存映射以降低 10 倍 RAM 占用(见第 16、17 章)。
-
模型格式优化:
- ONNX:用于互操作性的开放格式。从 PyTorch/TensorFlow 导出,可在任何地方运行。
- TensorRT:NVIDIA 的优化器。融合层、选择最优 kernel、量化权重。通常比 PyTorch 在 NVIDIA GPU 上快 2-5 倍。
- GGUF/GGML:用于 CPU 高效推理的格式,在消费级硬件上运行 LLM 时非常流行。
实验追踪¶
-
没有实验追踪,ML 研究会退化成:"我觉得上周二那个我改了某些配置的模型是最好的,但我记不清改了什么了。"
-
Weights & Biases (W&B):最流行的实验追踪工具。从训练脚本中记录任何内容:
import wandb
wandb.init(project="my-project", config={
"model": "transformer",
"lr": 3e-4,
"batch_size": 64,
})
for epoch in range(num_epochs):
train_loss = train_one_epoch()
val_loss = validate()
wandb.log({
"train/loss": train_loss,
"val/loss": val_loss,
"epoch": epoch,
})
# 将模型记录为 artifact
if val_loss < best_loss:
wandb.save("best_model.pt")
wandb.finish()
-
W&B 提供:用于比较多次运行的 dashboard、超参数扫描工具、模型注册表、数据集版本管理和团队协作功能。
-
MLflow:开源替代方案。可在本地或服务器上运行:
import mlflow
mlflow.set_experiment("my-experiment")
with mlflow.start_run():
mlflow.log_params({"lr": 3e-4, "batch_size": 64})
mlflow.log_metric("val_loss", 0.042, step=epoch)
mlflow.pytorch.log_model(model, "model")
- 模型注册表(Model registry):训练模型的中央存储,具备版本管理、阶段划分(开发 → 预发布 → 生产)和元数据管理。W&B 和 MLflow 均提供注册表。注册表回答:"当前生产中是哪个模型、谁训练的、验证准确率是多少、是什么代码和数据生成的?"
可复现性¶
-
可复现性意味着:给定相同的代码、数据和配置,能够产生相同的模型。由于 GPU 操作的非确定性、数据打乱和浮点数累加,这在 ML 中出人意料地困难。
-
可复现性检查清单:
| 什么 | 如何 |
|---|---|
| 代码版本 | Git commit hash |
| 配置/超参数 | 配置文件(版本化到 git 或记录到 W&B) |
| 随机种子 | 设置并记录所有种子(Python、NumPy、PyTorch、CUDA) |
| 数据版本 | DVC hash、数据集版本标签或 S3 对象版本 |
| 依赖项 | pip freeze、Docker 镜像 hash 或 lockfile |
| 硬件 | GPU 型号、GPU 数量、CUDA 版本 |
| 非确定性 | torch.backends.cudnn.deterministic = True(较慢但可复现) |
-
固定所有版本:
pip install torch==2.2.1而非torch>=2.0。次要版本升级可能改变数值行为、优化器实现或默认超参数。 -
Docker 用于可复现性:Docker 镜像固定了操作系统、系统库、Python 版本和 pip 包。镜像 hash 是完整的环境指纹。如果能复现 Docker 镜像,就能复现训练。
生产监控¶
-
Deployment 模型并不是终点——它是一系列新问题的开始。随着真实世界的变化,模型会随时间退化(概念漂移(concept drift)),输入数据分布也会发生变化(数据漂移(data drift))。
-
监控什么:
-
Latency:推理需要多长时间?追踪 p50(中位数)、p95 和 p99。p99 为 500ms 意味着 1% 的用户需要等待半秒,这在某些场景下是不可接受的。
-
Throughput:每秒处理多少请求?系统是否跟得上需求?
-
错误率:多少比例的请求失败(异常、超时、无效输入)?
-
模型指标:在留出集上的准确率、精确率、召回率。如果生产中有标注数据可用(例如用户纠正),则追踪在线指标。
-
数据漂移:输入数据的分布是否发生了变化?在白天照片上训练的模型可能在夜间照片上失效。统计检验(KS 检验、PSI)将训练分布与实时分布进行比较。
-
特征漂移:单个特征的分布是否发生了变化?训练时正态分布但现在呈双峰分布的特征,说明数据 pipeline 存在问题。
-
-
工具:
- Prometheus + Grafana:基础设施监控的标准方案。Prometheus 收集指标,Grafana 在带告警的 dashboard 中可视化展示。
- Evidently AI:开源 ML 监控工具。生成数据漂移、模型性能和数据质量报告。
-
告警:不要只是看 dashboard——设置自动告警。"如果 p99 latency 超过 200ms 持续 5 分钟,发送 Slack 通知。""如果数据漂移分数超过阈值,呼叫值班工程师。"
特征存储¶
-
特征存储(Feature Store)是预计算特征的集中式仓库,在训练和服务之间共享。它解决两个问题:
-
训练-服务偏斜(Training-serving skew):训练时使用的特征必须与服务时使用的完全一致。如果训练用某种方式计算
user_age_at_signup,而服务用另一种方式计算,模型的预测结果会悄悄出错。 -
特征复用(Feature reuse):多个模型通常使用相同的特征(用户人口统计、item embeddings、聚合统计)。统一计算并共享,避免重复和不一致。
-
-
Feast 是最流行的开源特征存储。它管理在线特征(低 latency,从 Redis 或 DynamoDB 提供)和离线特征(批量处理,存储在数据仓库中用于训练)。
-
特征存储对于推荐系统、欺诈检测以及任何从原始数据 pipeline 计算特征的应用都至关重要。
Pipeline 编排¶
-
生产 ML 系统不只是一个模型。它是一条 pipeline:数据摄取 → 预处理 → 特征计算 → 训练 → 评估 → deployment → 监控。每个步骤依赖于前一步,可以独立失败,并且可能需要按不同的计划运行。
-
编排器(Orchestrators)管理这些 pipeline:
-
Apache Airflow:数据 pipeline 编排的标准工具。DAG(有向无环图)定义任务依赖关系。每个任务独立运行,失败时可重试,并通过 Web UI 进行监控。
# airflow DAG 示例(简化版)
from airflow import DAG
from airflow.operators.python import PythonOperator
dag = DAG("training_pipeline", schedule="@daily")
preprocess = PythonOperator(task_id="preprocess", python_callable=preprocess_data, dag=dag)
train = PythonOperator(task_id="train", python_callable=train_model, dag=dag)
evaluate = PythonOperator(task_id="evaluate", python_callable=evaluate_model, dag=dag)
deploy = PythonOperator(task_id="deploy", python_callable=deploy_model, dag=dag)
preprocess >> train >> evaluate >> deploy
-
Kubeflow Pipelines:基于 Kubernetes 的 ML 专用编排工具。每个步骤在容器中运行,GPU 资源按需分配,实验自动追踪。
-
Prefect 和 Dagster:Airflow 的现代替代方案,具有更好的开发体验、原生 Python API 和内置数据血缘追踪。
-
何时使用编排器:当你的 pipeline 有超过 2-3 个步骤、按计划运行、涉及多个团队或服务,或需要从失败中自动恢复时。单脚本训练任务不需要编排器。但每天重新训练的 pipeline——需要从 5 个来源摄取数据、训练 3 个模型、评估并部署最优模型——则绝对需要。