模型部署与 MLOps:ONNX、FastAPI、Docker 与监控

系统覆盖 ML 模型上线:ONNX 导出跨框架、FastAPI 推理服务、Docker 容器化、批处理 vs 在线推理、A/B 测试、模型版本与回滚、监控告警、MLOps 全流程。

引言

训练出一个好模型只完成了一半——「能上线、可回滚、看得见」才是生产。本文讲透模型上线的工程链路:把 PyTorch 模型导出 ONNX(跨框架/硬件)、用 FastAPI 起推理服务、Docker 打包、批处理与在线推理的取舍、A/B 测试与版本管理,最后给监控告警——让 ML 项目从「notebook 里跑通」变成「生产里稳跑」。

前置:/ml-deep-learning-advanced/(模型训练)、/ml-model-evaluation/(评估指标)。服务部署见 [[devops]]。


目录


1. MLOps 全景:从 Notebook 到生产

MLOps = ML + DevOps:让模型「可重复训练、可上线、可监控」。

数据 → 训练实验(记录) → 模型注册(版本) → 部署(在线/批处理)
   ↑                                              ↓
   └──── 反馈/监控 ──────────────────── 推理结果

三大支柱:

支柱工具/实践
实验跟踪MLflow / W&B(记录参数、指标、产物)
模型注册版本号 + 元数据 + 审批
部署监控服务指标 + 模型漂移

心智:模型是「版本化的制品」——像代码一样管理:训练、打包、上线、回滚。


2. 在线推理 vs 批处理

维度在线(在线推理)批处理(离线)
延迟毫秒~秒级分钟~小时级
触发实时请求定时/事件
适用推荐、风控、实时翻译报表、离线打标、召回
部署HTTP 服务任务队列/调度
技术FastAPI + GPUSpark / 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跨框架/硬件标准生产首选
TorchScriptPyTorch 原生无外部依赖时
TFLite移动/嵌入式Android
OpenVINOIntel 硬件加速边缘

记忆: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)
优化效果代价
FP16GPU 快 ~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 推理加速

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「ml」更多文章

  1. 集成学习实战:Bagging、随机森林、梯度提升与 Stacking
  2. 迁移学习实战:预训练模型、特征提取与微调全流程
  3. 计算机视觉入门实战:图像处理与 CNN 图像分类