跳转至

部署与 DevOps

一句话总结:从容器化、模型服务、实验追踪、复现性到生产监控、特征仓库与流水线编排,打通把训练好的模型从 notebook 交付到百万用户手上的全套基础设施。

部署是模型从"研究产物"变成"产品"的转折点。 本节讲 ML 场景下的 Docker、模型服务、实验追踪、复现性、生产监控、特征仓库以及流水线编排 —— 这套基础设施把一个训练好的模型从 notebook 送到百万级用户面前.

  • 只能在你笔记本上跑的模型是原型。 能在大规模场景下稳定运行、毫秒级响应、故障自恢复、无停机更新的模型才是产品。 两者之间的鸿沟叫 部署与 DevOps.

  • 大多数 ML 工程师花在部署、监控、排查线上问题上的时间远多于训练模型。 对任何做真实 ML 系统的人来说,搞懂这套基础设施都不是可选项。

ML 场景下的 Docker

  • 我们在第 13 章 (操作系统 (Operating System, OS)) 已经从概念上讲过容器。 这里聚焦实操:怎么给 ML 负载写 Dockerfile.

  • Dockerfile 是构建容器镜像 (image) 的一份配方:

# 从官方 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"]
  • 层缓存: Docker 会缓存每一层。 如果 requirements.txt 没变,重新构建时 pip install 会被跳过。 把不常变的层 (系统包、pip install) 放前面,常变的层 (源码) 放后面 —— 一次 10 分钟的构建就变成了 10 秒的重建。

  • GPU 接入:用 nvidia/cuda 基础镜像,运行时加 docker run --gpus all. nvidia-container-toolkit 负责把 GPU 从宿主机直通给容器。

  • 多阶段构建 (multi-stage build) 把构建环境和运行时环境分开,显著减小镜像体积:

# 构建阶段: 安装编译工具, 编译依赖
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 用来跑多容器组合 (模型服务 + 负载均衡 + 监控),当前推荐使用 Compose v2 格式 (compose.yaml):

# compose.yaml
services:
  model:
    build: .
    ports:
      - "8080:8080"
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              capabilities: [gpu]
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"

模型服务

  • 模型服务 (model serving) 就是把推理作为一个服务跑起来:收请求 → 跑模型 → 返回预测。

  • FastAPI (第 03 节讲过) 是低到中等吞吐场景下最简单的方案。 高吞吐、GPU 优化服务场景下,用专用工具:

  • Triton Inference Server (NVIDIA 出品):支持 TensorRT、ONNX、PyTorch、TensorFlow 多种模型格式。 特性:

    • 动态批处理 (dynamic batching):把零散到来的请求攒起来批量送 GPU. 一串单条请求被合并成 32 一组,吞吐大幅提升。
    • 模型集成 (model ensembles):一次请求中把多个模型串起来 (预处理器 → 模型 → 后处理器).
    • 多模型服务:同一块 GPU 上同时服务多个模型,共享资源。
    • 并发模型执行:在同一块 GPU 上并行处理多个推理请求。
  • TorchServe (PyTorch 官方):通过 REST/gRPC 接口 (Application Programming Interface, API) 服务 PyTorch 模型。 支持模型版本管理、A/B 测试、自定义处理器。

  • vLLM:专为大语言模型 (Large Language Model, LLM) 服务设计。 实现了 PagedAttention (高效的 KV 缓存管理)、连续批处理、跨 GPU 的张量并行。 对大语言模型的吞吐,相比朴素服务方式提升 10-20 倍。

  • llama.cpp (github.com/ggml-org/llama.cpp):面向 CPU 和边缘设备的高效 LLM 推理引擎,基于 GGML 库实现。 通过 ARM NEON 和 Apple Silicon 的 SIMD 优化实现消费级硬件上的最快推理。 支持 4-bit、5-bit、8-bit 等多种量化格式,可在手机、树莓派等设备上运行大模型。 零拷贝内存映射 (memory-mapped) 使模型加载瞬间完成,内存占用极低。 提供 OpenAI 兼容 HTTP 服务器、Python 绑定、以及多种语言的绑定接口。

  • Core ML (Apple):苹果官方端上机器学习框架,针对 iPhone/Mac 的神经网络引擎 (Neural Engine, ANE) 和 GPU 深度优化。 支持 PyTorch 和 TensorFlow 模型通过 coremltools 转换后部署。 在 iOS/macOS 生态中,Core ML 模型可充分利用 Apple Silicon 的统一内存架构,实现低延迟推理。

  • MLX (github.com/ml-explore/mlx): Apple 推出的机器学习框架,专为 Apple Silicon (M 系列、A 系列芯片) 设计。 利用统一内存架构实现 CPU 和 GPU 间的零拷贝张量操作,无需显存搬运。 支持 LLM、视觉、语音等多模态推理,提供 Python 和 Swift API. 在端上 AI 推理场景中,这套生态 (llama.cpp + Core ML + MLX) 覆盖了从跨平台 CPU 推理到 Apple 原生加速的完整链路。

  • 模型格式优化:

    • ONNX:开放格式,主打互操作性。 从 PyTorch/TensorFlow 导出后,到哪都能跑。
    • TensorRT: NVIDIA 的优化器。 会做算子融合、挑选最优核函数、权重量化。 在 NVIDIA GPU (Graphics Processing Unit, GPU) 上通常比 PyTorch 快 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 提供:对比多次运行的仪表盘、超参搜索工具、模型仓库、数据集版本管理、团队协作。

  • 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):训练好的模型的集中存储,带版本、阶段管理 (dev → staging → production) 和元数据。 W&B 和 MLflow 都提供了模型仓库。 仓库回答的问题是:"现在生产上跑的是哪个模型?谁训练的?验证集精度多少?是哪份代码 + 哪份数据产出的?"

复现性

  • 复现性 (reproducibility) 的意思是:给定同样的代码、数据、配置,产出同样的模型。 在 ML 里这件事出奇地难 —— GPU 运算的非确定性、数据打乱、浮点累加都是坑。

  • 复现性 checklist:

项目 做法
代码版本 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 镜像,就能复现训练。

生产监控

  • 部署完模型不是终点 —— 是一堆新问题的起点。 模型会随时间退化,因为真实世界在变 (概念漂移 (concept drift)),输入数据分布也在漂 (数据漂移 (data drift)).

  • 要监控什么:

    • 延迟 (latency):一次推理要多久?要盯 p50 (中位数)、p95、p99. p99 = 500 ms 意味着每 100 个用户里就有一个要等半秒,可能已经不能忍了。

    • 吞吐 (throughput):每秒能处理多少请求?系统跟得上需求吗?

    • 错误率:有多少比例的请求失败 (异常、超时、非法输入)?

    • 模型指标:在留出集上的精确率 (precision)、召回率 (recall)、准确率。 如果生产上有标注数据 (比如用户纠错),就跟踪在线指标。

    • 数据漂移:进来的数据分布有变化吗?一个在白天照片上训练的模型可能在夜间照片上翻车。 用统计检验 (KS 检验、PSI) 对比训练分布和线上分布。

    • 特征漂移:单个特征的分布变了吗?训练时正态分布的特征现在变成双峰的,说明数据流水线出问题了。

  • 工具:

    • Prometheus + Grafana:基础设施监控的标配。 Prometheus 采指标,Grafana 做仪表盘 + 告警。
    • Evidently AI:开源的 ML 监控工具。 生成关于数据漂移、模型表现、数据质量的报告。
  • 告警:不要只做仪表盘 —— 一定要配自动告警。 比如"p99 延迟连续 5 分钟超过 200 ms 就发 Slack 通知", "数据漂移分数超过阈值就 page 值班工程师".

特征仓库

  • 特征仓库 (feature store) 是一份预计算特征的集中仓库,训练和服务共用。 它解决两个问题:

    • 训练-服务偏斜 (training-serving skew):训练用的特征必须和服务用的特征完全一致。 如果训练用一种方式算 user_age_at_signup,服务时算法不一样,模型的预测就会悄悄错。

    • 特征复用:多个模型经常用同一批特征 (用户画像、物品嵌入 (embedding)、聚合统计). 算一次共享给大家,避免重复和不一致。

  • Feast 是最流行的开源特征仓库。 它同时管理在线特征 (低延迟,从 Redis 或 DynamoDB 提供服务) 和离线特征 (批处理,存在数据仓库里给训练用).

  • 对推荐系统、反欺诈、以及任何"特征由原始数据流水线算出来"的应用,特征仓库都是关键组件。

流水线编排

  • 生产 ML 系统不只是一个模型。 它是一条 流水线:数据摄入 → 预处理 → 特征计算 → 训练 → 评估 → 部署 → 监控。 每一步依赖前一步,都可能独立失败,也可能各有各的运行时刻表。

  • 编排器 (orchestrator) 用来管理这些流水线:

  • Apache Airflow:数据流水线编排的行业标准。 用有向无环图 (Directed Acyclic Graph, 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,自带数据血缘 (data lineage).

  • 什么时候该上编排:当流水线超过 2-3 步、按计划运行、涉及多团队或多服务、或者需要自动故障恢复时。 单脚本训练任务用不上编排器。 但一个每天摄入 5 个数据源、训练 3 个模型、评估后择优部署的日常再训练流水线,绝对需要。


📌 本节要点

  • 部署即产品化:只有可扩缩、低延迟、故障自恢复、可无停机更新的模型才算产品,从原型到产品的差距全在 DevOps.
  • Dockerfile 层序:不常变的系统包/依赖放前面,频繁变的源码放最后,层缓存能把 10 分钟构建变成 10 秒重建。
  • 多阶段构建:构建阶段带编译器、运行时阶段只留运行时库,镜像从 5 GB 瘦到 2 GB.
  • 专用推理服务:高吞吐场景用 Triton (动态批处理 + 多模型)、TorchServe、vLLM (LLM 首选),端上推理用 llama.cpp (跨平台 CPU) 或 Core ML/MLX (Apple Silicon 原生加速),而非裸 FastAPI.
  • 模型格式优化: ONNX 主打互操作,TensorRT 在 NVIDIA GPU 上快 2-5 倍,GGUF/GGML 面向 CPU 消费级硬件。
  • 实验追踪: W&B 和 MLflow 记录配置、指标、模型 artifact,附带模型仓库支持 dev → staging → production 的阶段管理。
  • 复现性七要素:代码 hash、配置、种子、数据版本、依赖、硬件、非确定性开关全都要固定。
  • 生产监控四类:延迟 (p50/p95/p99)、吞吐、错误率、模型/数据/特征漂移,必须配自动告警。
  • 特征仓库: Feast 类工具解决训练-服务偏斜和特征复用,是推荐/反欺诈系统的关键组件。
  • 流水线编排: Airflow/Kubeflow/Prefect/Dagster 用 DAG 管理多步流程,超过 2-3 步或需要定时/自动恢复时必上。