从测开转后端三年,我踩过的机器学习部署坑比代码还多
大家好,我是老王(不是那个卖瓜的),坐标杭州未来科技城,白天在某大厂写后端,晚上在出租屋里折腾模型。三年前我还是个测试开发,天天和 CI/CD、接口自动化打交道,结果一不小心被算法组拉去“支援”,从此走上了一条不归路——既要写 API,又要搞模型部署,偶尔还得帮产品解释为什么“AI 不能直接预测用户明天会不会分手”。
上周五凌晨两点,我又一次坐在电脑前,盯着 Prometheus 上飙升的 CPU 使用率,心里默默问候产品经理全家。事情起因很简单:业务方想上线一个用户流失预警模型,要求实时推理、低延迟、高可用。听起来人畜无害对吧?但当你发现这个模型是用 PyTorch 写的,依赖一堆 torch==1.12.0+cu113 这种玄学版本,而生产环境只认 Python 3.8 + CPU 的时候……你就知道什么叫“理想很丰满,现实要重装系统”了。
部署不是 dump 模型就完事了
很多算法同学(没有冒犯的意思)觉得:“我把 .pt 文件给你,你 load 一下不就行了?” 哥,那是 Jupyter Notebook 里!生产环境可不吃这套。
我们一开始也天真地用了 Flask + TorchScript 的方案,本地跑得飞起,一上 K8s 就 OOM。为啥?因为 TorchScript 虽然能序列化模型,但推理时的内存占用和训练时差不多,而且没法做 batch 推理优化。更惨的是,有次半夜报警,日志里全是:
RuntimeError: CUDA out of memory. Tried to allocate 2.00 GiB...
问题是……我们的 Pod 根本没挂 GPU!后来才发现是模型里残留了 .cuda() 调用,算法同事说“本地调试方便”,结果线上直接炸穿。
架构设计:别让算法绑架后端
痛定思痛,我们开始重新思考整个部署链路。核心原则就一条:算法负责效果,后端负责稳定。具体拆解成几个关键点:
模型必须与服务解耦
别再把模型塞进主应用了!我们用独立的 inference service,通过 gRPC 对接后端。这样模型升级不用动主站代码,运维也敢重启了(以前改个模型参数,后端兄弟手都在抖)。输入输出标准化
算法给的 feature 工程代码五花八门:有的用 pandas,有的用 numpy,还有的直接传 JSON 字符串。我们强制要求:所有输入必须是 Protobuf 定义的结构体,输出也一样。这样既能做 schema 校验,又能避免“字段突然少了一个”的灵异事件。资源隔离与弹性伸缩
模型推理是 CPU 密集型,而我们的主站是 I/O 密集型。混部署?达咩!我们单独开了 inference 的 deployment,HPA 基于requests_per_second自动扩缩容。双11期间流量涨了5倍,自动扩容到20个副本,老板看了直呼内行。
折腾新技术?先过稳定性这关
说到新技术,去年我们团队一度想上 TensorRT 加速,据说能提速3倍。结果折腾一周,发现我们的模型用了 torch.nn.functional.interpolate,TensorRT 不支持动态 shape……最后还是乖乖回退到 ONNX + ONNX Runtime。
ONNX 真香警告:
- 支持跨框架(PyTorch/TensorFlow 通吃)
- 可以做常量折叠、算子融合等图优化
- ONNX Runtime 提供 CPU/GPU 统一接口,还能开 quantization
贴一段我们导出 ONNX 的脚本(带血泪注释):
import torch
import onnxruntime as ort
# ⚠️ 必须用 eval 模式!不然 BatchNorm 会崩
model.eval()
# ⚠️ 输入必须是固定 shape!动态 axis 要显式声明
dummy_input = torch.randn(1, 128, dtype=torch.float32)
torch.onnx.export(
model,
dummy_input,
"churn_model.onnx",
export_params=True,
opset_version=13, # 别用最新版!14 以上有些算子 runtime 不支持
do_constant_folding=True,
input_names=["input"],
output_names=["output"],
dynamic_axes={
"input": {0: "batch_size"}, # 批量推理的关键
"output": {0: "batch_size"}
}
)
# 验证 ONNX 模型是否和 PyTorch 一致
ort_session = ort.InferenceSession("churn_model.onnx")
ort_inputs = {"input": dummy_input.numpy()}
ort_outs = ort_session.run(None, ort_inputs)
print("ONNX 输出:", ort_outs[0][:5])
上线后,P99 延迟从 320ms 降到 95ms,内存占用砍半。运维小哥终于不用半夜被叫起来扩容了(他请我喝了杯瑞幸,值了)。
区块链?别笑,真有用上场景
你可能会问:标题里的 区块链 是来凑数的?还真不是。
我们有个 B2B 场景:给合作银行提供风控模型。银行要求模型推理过程可审计、不可篡改。这时候,传统日志根本不够用——谁知道你是不是偷偷换了模型权重?
于是我们搞了个骚操作:每次推理请求的输入、输出、模型版本、时间戳,都生成一个 hash,写入私有区块链(Hyperledger Fabric)。虽然性能有损耗(加了 15ms 延迟),但满足了合规要求。产品经理拿着这个去跟客户吹“AI+区块链双驱动”,当场签了百万合同。
冷知识:区块链在这里不是为了去中心化,而是为了可信存证。别被 buzzword 吓到,技术选型永远服务于业务。
综合对比:几种部署方案实测数据
为了选型,我们横向测了主流方案(4核8G Pod,batch_size=32):
| 方案 | P99 延迟 (ms) | 内存 (MB) | 吞吐 (QPS) | 稳定性 | 上手难度 |
|---|---|---|---|---|---|
| Flask + PyTorch | 320 | 1200 | 45 | ⭐⭐ | ⭐ |
| FastAPI + TorchServe | 210 | 950 | 70 | ⭐⭐⭐ | ⭐⭐ |
| ONNX Runtime | 95 | 600 | 180 | ⭐⭐⭐⭐ | ⭐⭐ |
| Triton Inference Server | 80 | 550 | 210 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
结论很清晰:中小团队闭眼选 ONNX Runtime。Triton 虽强,但运维成本高(要管 model repository、metrics、GPU pooling),适合阿里/字节这种有专职 infra 团队的。
最后一点真心话
从测开转开发这三年,我最大的感悟是:机器学习部署的本质,不是炫技,而是工程妥协的艺术。
算法追求 AUC 0.01 的提升,而我们要保证 99.99% 的可用性;产品经理想要“明天就上线”,而我们知道光是模型压测就得跑三天。
但正是这些撕扯,让工作变得有意思。上周模型成功扛住双11流量高峰后,我凌晨三点走出公司,西湖边的空气都是甜的——虽然第二天还得改需求。
如果你也在杭州,搞后端或者 MLOps,欢迎约 coffee。顺便帮我看看有没有跳槽机会?网易和阿里的 HC 还放吗?(狗头保命)
彩蛋:我们开源了内部用的模型部署脚手架,包含 ONNX 导出、gRPC 服务、Prometheus metrics,GitHub 搜
ml-deploy-kit就能找到。Star 了记得回来夸我!

评论 0