引言
训练出一个好模型只完成了一半——「能上线、可回滚、看得见」才是生产。本文讲透模型上线的工程链路:把 PyTorch 模型导出 ONNX(跨框架/硬件)、用 FastAPI 起推理服务、Docker 打包、批处理与在线推理的取舍、A/B 测试与版本管理,最后给监控告警——让 ML 项目从「notebook 里跑通」变成「生产里稳跑」。
前置:/ml-deep-learning-advanced/(模型训练)、/ml-model-evaluation/(评估指标)。服务部署见 [[devops]]。
目录
- 1. MLOps 全景:从 Notebook 到生产
- 2. 在线推理 vs 批处理
- 3. 模型导出:ONNX 与 TorchScript
- 4. FastAPI 推理服务
- 5. Docker 部署与 GPU 支持
- 6. 推理优化:量化与缓存
- 7. 模型版本管理与回滚
- 8. A/B 测试与金丝雀发布
- 9. 监控与告警
- 10. 速查表
- 延伸阅读
1. MLOps 全景:从 Notebook 到生产
MLOps = ML + DevOps:让模型「可重复训练、可上线、可监控」。
数据 → 训练实验(记录) → 模型注册(版本) → 部署(在线/批处理)
↑ ↓
└──── 反馈/监控 ──────────────────── 推理结果
三大支柱:
| 支柱 | 工具/实践 |
|---|---|
| 实验跟踪 | MLflow / W&B(记录参数、指标、产物) |
| 模型注册 | 版本号 + 元数据 + 审批 |
| 部署监控 | 服务指标 + 模型漂移 |
心智:模型是「版本化的制品」——像代码一样管理:训练、打包、上线、回滚。
2. 在线推理 vs 批处理
| 维度 | 在线(在线推理) | 批处理(离线) |
|---|---|---|
| 延迟 | 毫秒~秒级 | 分钟~小时级 |
| 触发 | 实时请求 | 定时/事件 |
| 适用 | 推荐、风控、实时翻译 | 报表、离线打标、召回 |
| 部署 | HTTP 服务 | 任务队列/调度 |
| 技术 | FastAPI + GPU | Spark / Celery / Cron |
决策问题:
用户要等结果吗?→ 在线
能接受几分钟后出结果吗?→ 批处理(成本更低、可重试)
记忆:实时体验走在线,离线分析走批处理——批处理还能用「大量样本重放」验证模型质量。
3. 模型导出:ONNX 与 TorchScript
为什么导出:PyTorch 推理依赖训练框架,导出成标准格式(ONNX)才能跨框架、跨硬件(CPU/GPU/边缘)。
import torch
model = torch.load('model.pth') # 或加载你的模型
model.eval()
dummy = torch.randn(1, 3, 224, 224) # 示例输入(shape 固定)
torch.onnx.export(
model, dummy, 'model.onnx',
input_names=['input'], output_names=['output'],
dynamic_axes={'input': {0: 'batch_size'}, # 动态 batch
'output': {0: 'batch_size'}},
)
ONNX Runtime 推理(比纯 PyTorch 快 + 跨硬件):
import onnxruntime as ort
sess = ort.InferenceSession('model.onnx', providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
result = sess.run(['output'], {'input': input_numpy})
| 导出格式 | 特点 | 适用 |
|---|---|---|
| ONNX | 跨框架/硬件标准 | 生产首选 |
| TorchScript | PyTorch 原生 | 无外部依赖时 |
| TFLite | 移动/嵌入式 | Android |
| OpenVINO | Intel 硬件加速 | 边缘 |
记忆:ONNX = 模型的「通用编译产物」——训练框架随便,部署统一走 ONNX。
4. FastAPI 推理服务
FastAPI:Python 高性能 Web 框架,自带异步与数据校验:
from fastapi import FastAPI
from pydantic import BaseModel
import numpy as np, onnxruntime as ort
app = FastAPI()
sess = ort.InferenceSession('model.onnx') # 启动时加载一次
class PredictReq(BaseModel):
features: list[float]
@app.get('/health')
def health(): return {'status': 'ok'}
@app.post('/predict')
def predict(req: PredictReq):
x = np.array([req.features], dtype=np.float32)
out = sess.run(['output'], {'input': x})[0]
return {'prediction': float(out[0][0]), 'confidence': ...}
生产要点:
| 要点 | 说明 |
|---|---|
| 模型加载一次 | 全局单例,别每请求加载 |
| 输入校验 | Pydantic 强类型 |
| 批处理 | 请求合并 batch 提高吞吐 |
| 异步 | 非阻塞调用(模型推理可用线程池) |
| 超时/限流 | 防单个慢请求拖垮 |
| 健康检查 | /health 探活 |
心智:服务层很薄——只做「收请求 → 转张量 → 推理 → 返回」,业务逻辑别塞进服务。
5. Docker 部署与 GPU 支持
Dockerfile(CPU 推理):
FROM python:3.11-slim
WORKDIR /app
# 只装运行时依赖(别把训练库都带上)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt \
&& pip install --no-cache-dir onnxruntime
COPY model.onnx ./model.onnx
COPY app.py ./
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
GPU 支持(用官方 CUDA 镜像):
FROM nvidia/cuda:12.1-runtime-ubuntu22.04
# 装 Python 与 onnxruntime-gpu
RUN pip install --no-cache-dir onnxruntime-gpu
# docker-compose 配 GPU
services:
ml:
build: .
ports: ["8000:8000"]
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
镜像瘦身:只装运行时依赖(onnxruntime)、多阶段构建、模型放镜像或挂载卷。
6. 推理优化:量化与缓存
量化(Quantization):把权重从 FP32 降到 FP16/INT8——模型更小、推理更快:
import onnxruntime as ort
from onnxruntime.quantization import quantize_dynamic
quantize_dynamic('model.onnx', 'model_int8.onnx', weight_type='QUInt8')
# 或 PyTorch:model.half()(FP16 在 GPU)
| 优化 | 效果 | 代价 |
|---|---|---|
| FP16 | GPU 快 ~2× | 极小精度损失 |
| INT8 量化 | 快 + 体积小 4× | 需校准、可能精度降 |
| 批处理 | 吞吐提升 | 延迟增加 |
| 缓存 | 相同输入秒回 | 内存占用 |
结果缓存(重复请求去重):
from functools import lru_cache
@lru_cache(maxsize=1000)
def cached_predict(features_key: str):
# 转回张量推理
...
记忆:线上优化三板斧——量化提速、批处理增吞吐、缓存去重复。
7. 模型版本管理与回滚
模型 = 制品,必须可回滚:
模型版本命名:v1 / v2 / v3(或 git hash + 训练时间)
存储:模型注册表(MLflow 模型注册 / 对象存储路径)
部署:服务配置指向「当前版本」→ 随时切回
MLflow 模型注册:
import mlflow
mlflow.set_tracking_uri('http://mlflow:5000')
with mlflow.start_run():
mlflow.log_param('lr', 1e-3)
mlflow.log_metric('accuracy', 0.92)
mlflow.pytorch.log_model(model, 'model')
mlflow.register_model('runs:/<run_id>/model', 'churn-model')
回滚策略:
- 服务配置里记录
model_version,异常时一键切旧版 - 新模型灰度(见下节)再全量
- 数据分布变了回滚旧模型不一定有用——要重训
铁律:模型必须有版本号 + 训练参数记录——否则出问题不知道哪个模型、怎么训的。
8. A/B 测试与金丝雀发布
A/B 测试:把流量分两组,对比新旧模型效果:
# 简化:按 user_id hash 分桶
import hashlib
def ab_bucket(user_id: str) -> str:
h = int(hashlib.md5(user_id.encode()).hexdigest(), 16)
return 'A' if h % 100 < 50 else 'B' # 50/50 分流
# 服务里按桶选模型
model = models['v2'] if ab_bucket(uid) == 'B' else models['v1']
金丝雀(Canary):先放 5% 流量观察,无异常再逐步全量。
评估对比:
| 指标 | 说明 |
|---|---|
| 业务指标 | 点击率、转化、收入(最真实) |
| 离线指标 | 与线上对比漂移 |
| 延迟/错误率 | 工程质量 |
| 用户反馈 | 人工审核抽样 |
记忆:A/B 验证「新模型是否更好」——别只信离线测试集,线上真实用户说了算。
9. 监控与告警
ML 模型监控 ≠ 普通服务监控——除了延迟/错误,还要看模型质量:
服务指标(通用):延迟分位数、错误率、QPS、GPU 利用率、内存。
模型指标(ML 特有):
| 指标 | 监控什么 | 告警信号 |
|---|---|---|
| 预测分布漂移 | 输出分布变化 | 均值/分位数突变 |
| 输入特征漂移 | 特征分布变化 | PSI/KL 增大 |
| 线上 vs 离线 | 数据不一致 | 准确率下降 |
| 标签延迟 | 真实反馈难获取 | 用代理指标 |
| 空值/异常输入 | 上游数据问题 | 异常率上升 |
落地工具:
指标:Prometheus(服务)+ 自采模型指标
漂移:evidently / whylogs(特征/预测漂移检测)
日志:推理日志 + traceId 贯穿
告警:Alertmanager → 飞书/钉钉
# 简易漂移检测(PSI,用 evidently 更专业)
def psi(expected, actual, bins=10):
# 分段密度对比,PSI > 0.25 视为显著漂移
...
记忆:普通监控看「服务病没病」,ML 监控看「模型还准不准」——特征漂移往往先于业务掉量。
10. 速查表
| 需求 | 做法 |
|---|---|
| 模型导出 | ONNX(跨框架/硬件) |
| 在线服务 | FastAPI + onnxruntime |
| 批处理 | 任务队列 + 调度 |
| 打包 | Docker(GPU 用 CUDA 镜像) |
| 加速 | 量化(FP16/INT8)+ 批处理 |
| 去重 | 结果缓存 |
| 版本 | MLflow 注册 + 服务配置可回滚 |
| 验证 | A/B 测试 / 金丝雀 |
| 监控 | 服务指标 + 特征/预测漂移 |
| 告警 | Prometheus + 通知 |
一句话记忆:训练导出 ONNX,FastAPI 起服务、Docker 打包;在线走 API、离线走批处理;量化提速、缓存去重、版本可回滚、A/B 验证效果;监控除了延迟还要看特征漂移——MLOps 一条龙。
延伸阅读
- /ml-deep-learning-advanced/ — 待部署模型的训练质量
- /ml-model-evaluation/ — 评估指标与阈值选择
- /ml-time-series/ — 批处理场景的时序模型
- [[devops]] — CI/CD 与容器编排
- [[hpc]] — GPU 推理加速
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。