MLOps 流水线:模型训练、评估与发布的 CI/CD 实践

把 CI/CD 思想引入机器学习交付:训练/评估/注册/发布全链路流水线(Kubeflow/Airflow/MLflow)、模型与数据集版本化、CI 里的数据漂移测试、金丝雀与 A/B 发布、GPU 编排与模型监控回滚。

模型不是"写出来的代码",而是"数据喂出来的产物"——这是 MLOps 与传统 CI/CD 最大的分水岭。传统 CI/CD 保证"代码正确",MLOps 还要保证"数据新鲜、特征一致、模型没跑偏、线上达标"。本文按一条可落地的流水线展开:数据准备 → 训练 → 评估门禁 → 模型注册 → 金丝雀/A-B 发布 → 监控回滚,并给出 Kubeflow、Airflow、MLflow、DVC 的真实配置与坑。


目录


1. MLOps 与传统软件交付的差异

1.1 传统 CI/CD 的适用边界

传统交付:提交代码 → 测试 → 构建 → 部署 → 监控(确定性系统)
ML 交付:  数据变更 → 训练 → 评估 → 注册 → 发布 → 监控 → 再训练
每个环节都有不确定因素:数据分布、模型指标、线上行为都会变

1.2 ML 交付的四个特有挑战

挑战表现对策
数据即代码模型质量取决于训练数据DVC 数据集版本化 + 血缘
指标非布尔不是过/不过而是 AUC 涨跌评估门禁 + 阈值策略
试错成本高一次全量训练数小时缓存中间结果 + 增量训练
线上漂移生产分布随时间变化漂移监控 + 自动回滚

1.3 MLOps 成熟度分层

0 层 Notebook 手工训练 / 1 层训练脚本进 CI/CD / 2 层模型注册+自动发布 / 3 层监控+自动回滚
多数团队从第 1 层起步:训练/评估脚本进 Git 统一流水线触发,收益最大

2. 训练流水线:数据准备与特征工程

2.1 数据集版本化:DVC

模型复现的前提是"数据可回滚"。DVC 把数据指针存进 Git,数据本体存对象存储。

dvc init && dvc add data/train.parquet data/test.parquet
dvc remote add -d storage s3://ml-data-bucket/dvc
dvc push   # Git 只提交几十字节的 .dvc 指针
# 线上跑偏:git checkout <旧commit> && dvc checkout 拉回数据快照

2.2 特征存储消除训练-服务偏差

训练用特征工程与在线推理不一致会造成 Train-Serve Skew。把特征定义收敛到 Feature Store(Feast/Tecton),训练与在线共用同一特征视图,从源头消除偏差。

from feast import FeatureView, Field
from feast.types import Float32
view = FeatureView(name="user_features", entities=["user_id"],
    schema=[Field(name="ctr_7d", dtype=Float32)],
    online=True, source=parquet_source)

2.3 训练编排与触发

Airflow 管调度依赖(天级重训、跨系统协调),Kubeflow 管实验流水线,两者可并存。触发方式:定时(0 2 * * *)、数据分区新增、漂移超阈值(需人工确认)、手动。

# Kubeflow Pipelines(概念)
@dsl.pipeline(name="train-pipeline")
def pipe(data_uri, epochs):
    prep = data_prep_op(data_uri)
    train = train_op(prep, epochs)     # GPU 训练
    eval_ = evaluate_op(train)         # 离线评估
    register_op(eval_)                 # 写回 MLflow Registry

3. 评估流水线:离线指标与模型门禁

3.1 评估集分层

整体指标:AUC / F1(全局);分组指标:按地域/设备/年龄切片防"偏科"
鲁棒指标:新样本与对抗样本稳定性;业务指标:转化率、召回率(离线近似)

3.2 评估门禁设计

评估脚本输出指标如 {"auc":0.873,"group_auc_android":0.81,"drift_score":0.12},门禁规则任一不满足则流水线失败。

rules:
  auc: { min: 0.86 }
  group_auc_android: { min: 0.85 }   # 安卓端偏科 → 拦截
  drift_score: { max: 0.15 }

3.3 评估的两种形态

联内评估:训练完在流水线内评估(训练=构建,评估=测试)
持续评估:对已注册模型定时拉生产数据跑离线指标,发现劣化提前预警

💡 要点:评估必须可复现(固定代码、数据版本、随机种子),否则门禁失效。


4. 模型注册与版本化:MLflow 与 DVC

4.1 MLflow Model Registry

mlflow models register -m runs:/<run_id>/model -n recommendation_model
# 阶段流转:None → Staging → Production → Archived,评估门禁通过后自动置 Staging

4.2 模型与数据血缘

血缘链:数据版本(DVC) → 特征版本(Feast) → 训练 run(MLflow) → 模型版本(Registry) → 部署 revision → 线上流量。出问题顺着血缘反查"用哪份数据、哪个特征版本训的"。

# MLflow run 固化血缘标签
mlflow.start_run(run_name="daily_retrain_20260930")
mlflow.log_params({"data_version": "v42", "feast_view": "user_features@2026-09-29"})

4.3 版本策略

不可变:模型一经发布不可覆盖,只能新增版本
命名:<业务>-<日期>-<run_id前8位>,如 reco-20260930-a3f2c9d1
保留:生产版本 + 最近 N 个历史版本可回滚,其余归档清缓存

5. CI 里的模型测试:数据漂移与公平性

5.1 数据漂移检测

漂移指线上输入分布与训练时不一致,常用 PSI、KS 检验、分布距离衡量。

from evidently.report import Report
from evidently.metric_preset import DataDriftPreset
Report(metrics=[DataDriftPreset()]).run(
    reference_data=train_sample, current_data=production_sample)
# 输出每个特征的 PSI/drifted 标记,作为 CI 断言

门禁:>5% 特征漂移 → 黄灯;>15% → 红灯拦截并通知数据团队。

5.2 公平性与偏差测试

公平性=分组指标不塌方:按性别/年龄/地域分组计算指标,检查预测差异率是否超阈值(如转化差 >20% 视为有偏)。检测到偏差 → 阻止自动发布,走人工审查。

5.3 影子评估

用历史流量回放:过去 N 天线上请求喂给新模型打分,对比新旧模型预测分布与命中指标。
不切流量即可评估新模型线上表现

6. 模型发布与 A/B:金丝雀与影子流量

6.1 发布策略对比

策略风险流量回滚适用
蓝绿低100% 切换秒级模型无关流量
金丝雀低5%→50%分钟级在线模型服务
A/B可控按实验组随实验结束业务指标验证
影子零0% 全量打分无需新模型预评估

6.2 服务网格做流量切分

# Istio A/B 切分 90/10
http:
  - match: [{ headers: { ab-group: { exact: "B" } } }]
    route: [{ destination: { host: reco-svc, subset: v2 } }]
  - route:
      - destination: { host: reco-svc, subset: v1, weight: 90 }
      - destination: { host: reco-svc, subset: v2, weight: 10 }

6.3 自动回滚触发器

任一触发即回滚:线上 AUC/点击率环比下降超阈值 / P99 超 SLO 3 个周期 /
  预测失败率 >1%。回滚 = 把流量切回上一稳定模型版本

7. GPU 资源编排与 CI 成本

7.1 GPU 队列与调度

训练 Pod 声明独占一卡:resources: { limits: { nvidia.com/gpu: "1" }, requests: { nvidia.com/gpu: "1" } }。调度策略:按优先级分队列(生产 > 实验 > Notebook),Kueue/Volcano 做 quota 与抢占;MIG/时间片共享只给 Notebook,训练任务一律独占。

7.2 CI 触发 GPU 训练

train:
  stage: train
  image: pytorch/pytorch:2.3-cuda12.1-cudnn8
  tags: [gpu-runner]              # 专用 GPU Runner 池
  script:
    - dvc pull && python train.py --epochs 20
    - python evaluate.py
  artifacts: { paths: [metrics.json] }

7.3 成本控制三板斧

缓存:特征结果与预训练 checkpoint 存对象存储
并发:同机型训练全局最多 N 个并发
计量:按部门/项目打标签,FinOps 分摊 GPU 账单(nvidia-smi + gpu_utilization 看板)

8. 模型回滚与生产监控

8.1 监控指标分层

系统层:延迟/吞吐/错误率/GPU 利用率;模型层:预测分布、空预测率、置信度均值
业务层:点击率、转化率;漂移层:特征漂移、标签漂移

8.2 PromQL 监控示例

sum(rate(reco_click_total[1h])) / sum(rate(reco_impression_total[1h]))  # 点击率
histogram_quantile(0.99,
  sum(rate(reco_inference_duration_seconds_bucket[5m])) by (le))        # P99 延迟
rate(reco_prediction_errors_total[5m]) > 0.01                           # 错误率超阈值

8.3 回滚机制

回滚不是"删掉新模型"而是"指回上一稳定版本":
  拉 Registry 上一 Production 版本 → 更新服务镜像 tag(不可变)→
  切流量 + 观察 → 复盘(数据/特征/模型问题)再决定修复重训

ℹ️ 提示:把"模型+数据+特征版本 + 部署 revision"打成一条记录写审计日志,回滚复盘都靠它。


9. 案例与最佳实践

9.1 真实案例

某电商推荐团队(概念案例):每天凌晨 2 点重训,先手工跑脚本再手动部署,返工率高
改造:Airflow 定时触发 Kubeflow → MLflow 自动注册 → CI 门禁(AUC/分组/漂移)
  → 金丝雀 10% 观察 1 小时 → 全量
一次:监控发现召回率环比降 8%,自动回滚到上一版模型
效果:发布从"周"缩短到"日",回滚从小时级降到分钟级

9.2 最佳实践 Checklist

□ 数据用 DVC 版本化,代码/数据/模型三者对齐
□ 特征收敛到 Feature Store,消除训练-服务偏差
□ 评估门禁覆盖整体 + 分组 + 漂移,阈值可配
□ 模型进 Registry,阶段流转走审批,不可变
□ 血缘链完整:数据→特征→run→模型→部署 revision
□ 发布走金丝雀/A-B,配置自动回滚触发器
□ GPU 分队列调度,训练独占卡,缓存中间产物
□ 线上监控分层(系统/模型/业务/漂移)
□ 每次发布记录版本指纹,可随时追溯与回滚

9.3 常见坑

坑现象对策
只训不评上线才发现指标差评估门禁进流水线
评估不可复现每次 AUC 不同,门禁失效固定数据版本与随机种子
特征不一致线上与训练偏差大特征存储统一来源
直接全量发布跑偏影响全量用户金丝雀/A-B + 自动回滚
只监控系统层模型质量劣化看不见加模型层与业务层指标
版本覆盖回滚找不到旧模型不可变版本 + 归档策略

小结

MLOps 流水线 = 数据版本化(DVC)→ 训练编排(Airflow/Kubeflow)→ 评估门禁(整体+分组+漂移)→ 模型注册(MLflow)→ 金丝雀/A-B 发布 → 分层监控 → 自动回滚。核心三点:数据即代码要版本化、模型指标要门禁化、线上行为要监控回滚化。先做最小闭环(训练脚本进 CI + 评估门禁),再叠加发布策略与自动回滚,是性价比最高的路线。

继续阅读

探索更多技术文章

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

全部文章 返回首页

「DevOps」更多文章

  1. CI/CD 流水线安全:供应链攻击防御与硬编码凭证治理
  2. 多云与混合云工程:成本、身份与统一编排
  3. AI 辅助运维:GenAI 在事件响应与排障中的实践