MLOps 是什么
这一课你将能做到
- 说清楚 MLOps 与传统 DevOps 的边界:多出数据、模型、监控三块新增职责
- 画出 ML 系统从数据到监控的完整闭环图,并说明每一环的产物
- 以本站「判题超时预测模型」为例,跑通「训练 → 保存 → API 服务」的最窄闭环
为什么要学
在 Jupyter 里跑出 95% 准确率,和把模型稳定跑在生产环境每天处理几万次请求,是两件完全不同的事。前者是「实验」,后者是「系统」。隔开它们的不是算法,而是工程:数据版本、实验追踪、自动重训、灰度发布、漂移监控。
MLOps 就是把 DevOps 的自动化思想搬到机器学习上,并补上三块 DevOps 不用管的东西:数据会变、模型会衰减、效果无法靠单元测试证明。
人话:Web 开发里代码 push 后 CI 跑测试、CD 自动部署——这条路 ML 工程师也要走,只是他们的交付物里多了一个从数据里学出来的黑盒,而且这个黑盒会随用户行为变化悄悄失灵。MLOps 解决的就是「怎么让一个会过期的黑盒持续可靠地服务用户」。
本课程贯穿两个真实场景:判题超时预测模型(根据提交特征预测这次判题会不会 TLE,用于动态放宽超时阈值)和题目难度评分模型(根据通过率等数据自动给题目打难度分,替代人工标注)。
先建立图像
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ 数据管道 │ → │ 训练实验 │ → │ 模型注册 │ → │ 服务部署 │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
↑ ↑ ↑ │
数据校验 DVC+MLflow MLflow Docker/K8s
│ ↓
┌───────────────────────────────────────────────────┐
│ 监控(延迟/错误率/数据漂移/业务指标) │
└───────────────────────────────────────────────────┘
│ 漂移或指标衰减触发
└──→ 回到数据管道,重新训练(闭环)
对应到判题超时预测模型:
| 环节 | 做什么 | 工具 |
|---|---|---|
| 数据 | 提交记录 + 判题日志 → 清洗特征 | pandas / SQL |
| 训练 | 梯度提升树分类「是否 TLE」 | scikit-learn |
| 追踪 | 每次实验的超参、指标、模型文件 | MLflow |
| 版本 | 数据集快照与管道可重放 | DVC |
| 部署 | FastAPI 打包成容器 | Docker + K8s |
| 监控 | 预测分布漂移、延迟分位 | Evidently + Prometheus |
跟着做
步骤 1:搭建贯穿全课程的项目骨架
Python 3.11+,先建环境:
mkdir mlops-lab && cd mlops-lab
python -m venv .venv
# Windows: .venv\Scripts\activate
# macOS/Linux:
source .venv/bin/activate
python --version # 确认 3.11+
pip install numpy pandas scikit-learn joblib fastapi "uvicorn[standard]"
建立目录结构,后续所有课都在这里扩展:
mlops-lab/
├── data/ # 数据集(第 3 课用 DVC 接管)
├── src/
│ ├── train.py # 训练脚本
│ └── serve.py # 推理服务
├── models/ # 模型 artifact
└── requirements.txt
步骤 2:写出判题超时预测的最小模型
# src/train.py
import numpy as np
import pandas as pd
import joblib
from pathlib import Path
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score
FEATURES = ["code_lines", "loop_depth", "lang_java", "problem_rank"]
def make_fake_data(n: int = 5000) -> pd.DataFrame:
"""模拟本站提交记录;真实场景替换为读判题日志表。"""
return pd.DataFrame({
"code_lines": np.random.randint(5, 300, n),
"loop_depth": np.random.randint(1, 5, n),
"lang_java": np.random.randint(0, 2, n),
"problem_rank": np.random.randint(1, 6, n), # 题目难度 1-5
})
def build_label(df: pd.DataFrame) -> pd.Series:
"""经验规则造标签:代码长 + 循环深 + 题目难 → 容易 TLE。"""
score = df["code_lines"]/300 + df["loop_depth"]/4 + df["problem_rank"]/5
return (score > 1.1).astype(int)
if __name__ == "__main__":
df = make_fake_data()
X, y = df[FEATURES], build_label(df)
X_tr, X_te, y_tr, y_te = train_test_split(
X, y, test_size=0.2, random_state=42)
model = GradientBoostingClassifier(random_state=42)
model.fit(X_tr, y_tr)
print("F1 =", round(f1_score(y_te, model.predict(X_te)), 4))
Path("models").mkdir(exist_ok=True)
joblib.dump(model, "models/timeout-v1.joblib")
python src/train.py
# F1 = 0.97 左右。多跑几次,结果一致吗?为什么?(答案:所有随机处都固定了 seed)
步骤 3:包一个推理接口
# src/serve.py
import joblib
import pandas as pd
from fastapi import FastAPI
app = FastAPI(title="判题超时预测服务")
model = joblib.load("models/timeout-v1.joblib")
@app.get("/health")
def health():
return {"status": "ok"}
@app.post("/predict")
def predict(code_lines: int, loop_depth: int,
lang_java: int, problem_rank: int):
X = pd.DataFrame([dict(code_lines=code_lines, loop_depth=loop_depth,
lang_java=lang_java, problem_rank=problem_rank)])
proba = model.predict_proba(X)[0, 1]
return {"timeout_probability": round(float(proba), 4)}
uvicorn src.serve:app --port 8000
# 另开一个终端:
curl -X POST "http://127.0.0.1:8000/predict?code_lines=200&loop_depth=4&lang_java=1&problem_rank=5"
# {"timeout_probability": 0.93}
恭喜,你走完了「训练 → 保存 → 服务」的窄版闭环。但现在重新跑一遍 train.py,立刻冒出三个问题:这次用的数据从哪来?实验参数记在哪?线上该加载哪个版本的模型?——这正是后面课程逐个解决的问题。
讲清楚要点
- MLOps 的核心是闭环与可复现:同样的数据 + 代码 + 参数必须得到同样的模型;漂移或衰减必须能触发重训。工具只是这两个目标的载体。
- 实验代码与生产代码要分开写:notebook 追求「能跑出来」,生产追求「可重复、可回滚、可监控」。上生产要重写成脚本/模块,不是把 notebook 原样推上去。
- 上线是监控的起点而非终点:本站若新增 Rust 语言提交,特征分布就变了,模型准确率自然下滑。没有监控的模型上线等于裸奔。
- 先手动跑通,再引入工具:一上来就堆 K8s + 全托管平台是最常见的失败路径。本课程的顺序就是先窄闭环、后逐环自动化。
- 小团队也要做轻量 MLOps:Git + DVC + MLflow 三件套一个人就能维护;规模上来再谈平台。
你可能会懵的地方
- MLOps 和 DevOps 到底差在哪? DevOps 管「代码」的构建、测试、部署、回滚;MLOps 在此之上多了:数据版本管理、实验追踪、模型注册与发布门禁、数据/概念漂移监控。CI 也从「跑单测」扩展到「数据校验 + 训练冒烟 + 指标门禁」。
- 模型准确率在测试集很高,为什么上线后感觉不准? 三个常见原因:训练数据与线上分布不一致(采样偏差)、train/serve 特征口径不一致(skew)、上线后数据分布漂移。逐一排查的方法在监控课展开。
- 判题超时预测用规则写死不行吗? 初期完全可以规则先行。当规则分支超过几十条、难以维护时,数据驱动模型的维护成本才开始低于规则。这也是「先基线后模型」的工程常识。
- AutoML / 端到端平台能替代 MLOps 吗? 平台省掉脚手架,但漂移监控阈值、特征口径对齐、成本与发布策略仍要人决定。理解原理后再用平台,才不会被平台绑架。
- 为什么要用 joblib 存模型而不是每次现场重训? 训练要分钟级,推理要毫秒级。模型就是「编译产物」,保存后服务端只做加载与计算;重训由离线管道按节奏触发。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| notebook 改完直接上生产 | 无法复现、无版本、无人敢动 | 拆成 src/ 下脚本与函数,走版本控制 |
| 只保存模型文件,不记训练细节 | 三个月后不知道它是什么数据训的 | 数据、代码、参数、指标全部入库 |
| 准确率高就上线,不接监控 | 衰减了没人知道,业务先爆炸 | 上线同时接系统指标 + 漂移检测 |
| 数据集靠复制文件命名 v2_final_最终版.csv | 文件名即混乱,回滚无从谈起 | 用 DVC 给数据打版本(第 3 课) |
| 上来就搭 K8s + GPU 集群 | 复杂度爆炸,运维拖垮迭代 | 从 pip + 单机 Docker 起步 |
卡点急救
python src/train.py报 ModuleNotFoundError:在项目根目录用python -m src.train运行,并确认已激活 .venv。- uvicorn 启动后接口 404:路径与方法要匹配,POST 用
curl -X POST;直接访问/docs用交互页面验证最稳妥。 - 每次运行结果不一样:所有随机来源显式传
random_state/np.random.seed,这是可复现性的第一课。
面试常问
- 说一下 MLOps 的完整生命周期。 数据采集与校验 → 特征工程 → 训练与实验追踪 → 评估与模型注册 → 部署(在线/批量/边缘)→ 监控(服务质量 + 数据/概念漂移)→ 触发重训形成闭环。每一环都要求可复现、可回滚。
- 线上模型效果变差,你的排查思路? 先看系统指标(延迟/错误率/流量)排除工程故障;再查数据漂移(特征分布对比训练时);再查概念漂移(P(y|X) 变化,如判题规则更新);最后确认上游数据管道是否断流。定位后决定修复数据、重训或回滚版本。
- ML 项目的 CI 和普通后端有什么不同? 除单测与静态检查外,还要做数据 schema 校验、小数据集上的训练冒烟测试、模型评估门禁(指标不达标阻止发布)。
自测三问
- MLOps 比 DevOps 多出的三类问题是什么?
- 画出判题超时预测模型从日志到监控的链路,标出每环工具。
- 为什么说「模型上线是监控的起点而非终点」?
小结一句话
MLOps = DevOps 的自动化闭环 + 数据版本 + 实验追踪 + 漂移监控,目标是把一次性实验变成持续可靠的系统。
延伸
- Google Cloud:MLOps 白皮书(Continuous delivery and automation pipelines in machine learning)——业界引用最多的 MLOps 成熟度分级框架,建议通读。
- Made With ML——与本课程思路一致的实战教程,可对照加深理解。
- 下一课:数据工程基础——模型吃进去的数据从哪来、怎么清洗、怎么变成特征表。