机器学习部署不是“跑通就行”:一个Vim党在杭州实验室的血泪总结
大家好,我是某211软工研二在读,目前在实验室蹲着做项目,坐标杭州——没错,就是阿里网易扎堆、租房价格堪比北上广、但食堂真的香的那个杭州。最近半年被导师“委以重任”(其实是没人愿意搞),负责把我们组训好的CTR预估模型从Jupyter Notebook里捞出来,塞进后端服务跑起来。说白了,就是让算法真正上线,能扛住运营同学天天改活动、产品半夜提需求、以及双11那种流量洪峰。
一开始我天真地以为:“不就是model.predict()加个Flask API嘛?”结果现实狠狠打了我三巴掌,还顺手关掉了我的咖啡续杯权限。
从Notebook到生产环境:理想很丰满,线上很骨感
我们的模型最初是用PyTorch写的,数据来自公司内部用户行为日志,特征工程搞了两周,调参调到怀疑人生,AUC终于上了0.85。导师一拍大腿:“效果不错,下周上线!”
我当时心里咯噔一下——上线?怎么上?谁来接?
后端同学一脸无辜:“你不是有模型吗?打包成API发我们就行。”
运维大哥冷笑一声:“别又来个单线程Flask,上次那个服务崩了,我通宵修的。”
产品经理还在群里@我:“能不能支持实时AB测试?我们要对比新老策略!”
那一刻我突然意识到:算法工程师的终点,才是部署工程师的起点。
架构设计不是炫技,是为“活着”服务
在实验室写代码可以随心所欲,但在生产环境,稳定性 > 性能 > 功能完整度。我们最终采用了一套轻量但可扩展的架构:
[前端请求] → [Nginx 负载均衡] → [FastAPI 微服务] → [ONNX Runtime 推理引擎] → [Redis 特征缓存]
为什么不用TensorFlow Serving或TorchServe?两个原因:
- 我们模型不大(<100MB),没必要上重型方案;
- 实验室服务器资源有限,连Docker Compose都要省着用。
关键决策点在于推理引擎的选择。PyTorch直接部署太重,依赖多,启动慢。于是我把模型转成了ONNX格式——这一步踩了个大坑:某些自定义算子不支持,最后不得不回退到用torch.jit.script导出TorchScript模型。教训:训练时就该考虑部署约束,别等到上线前才想起兼容性。
# 导出TorchScript模型(带trace)
example_input = torch.randn(1, 128)
traced_model = torch.jit.trace(model, example_input)
traced_model.save("ctr_model.pt")
加载时也别直接torch.load(),容易吃内存。用torch.jit.load + eval()模式,再加个简单的LRU缓存,QPS立马从50飙到1200。
运营要灵活,我们就得留“活口”
上周五晚上,运营小姐姐突然在钉钉上@我:“明天活动要用新特征,能不能临时加个开关?”
我当时正用Vim写配置文件(对,我就是那个被室友嘲笑“还在用hjkl移动光标”的人),差点把键盘砸了。
但冷静下来一想:好的ML系统必须支持动态配置。于是我们在服务层加了三层“逃生舱”:
- 特征开关:通过配置中心(我们用Apollo)控制哪些特征参与计算;
- 模型热更新:监听本地模型文件变化,自动reload(注意加锁!);
- 降级策略:当推理超时或失败,返回兜底规则(比如按热门度排序)。
# config.yaml
features:
user_age: true
item_category_emb: true
real_time_click_rate: false # 运营临时关掉这个
model_path: "/models/ctr_v3.pt"
timeout_ms: 50
fallback_strategy: "popular_first"
后端同学看到这个设计,终于没再说“你们算法能不能一次给全需求”。
算法、后端、运营:不是三角恋,是铁三角
很多人以为部署只是算法的事,其实不然。真正的ML系统是算法、后端、运营三方拉扯出来的产物。
- 算法关心指标和效果,希望模型越复杂越好;
- 后端关心吞吐和延迟,恨不能所有请求都在10ms内返回;
- 运营关心灵活性和迭代速度,今天要加特征,明天要切流量。
我们搞了个每周同步会,三方坐一块儿对齐:
- 算法提前告知模型改动范围;
- 后端评估接口改造成本;
- 运营明确实验排期和监控指标。
结果?上个月双11,我们的推荐模块零故障,点击率还涨了7%。运维大哥请我喝了杯瑞幸——虽然他说是因为“终于不用半夜爬起来救火了”。
性能不是玄学,是数字堆出来的
别信“微优化没用”这种鬼话。在线上,1ms的延迟可能意味着10%的流失。我们做了几件小事:
| 优化项 | QPS(单核) | P99延迟 |
|---|---|---|
| 原始Flask + PyTorch | 48 | 210ms |
| FastAPI + TorchScript | 820 | 62ms |
| 加特征缓存(Redis) | 1200 | 38ms |
| 批处理(batch_size=16) | 2100 | 29ms |
批处理特别有效——虽然用户请求是实时的,但我们用异步队列攒一批再推理,吞吐翻倍,延迟反而降了。当然,这对业务容忍度有要求,我们场景允许50ms内的延迟。
另外,日志必须结构化!别再用print("predict done")了。我们用structlog打日志,字段包括:request_id, model_version, features_used, inference_time。出了问题,运营同学都能自己查。
最后的碎碎念
写这篇文章的时候,窗外下着杭州典型的梅雨,实验室空调又坏了。但想到上周模型顺利灰度上线,心里还是有点小得意。
如果你也在实验室或小厂搞AI落地,记住几点:
- 别把部署当附属品,它值得和训练一样多的精力;
- 和后端搞好关系,一杯奶茶能省三天debug时间;
- 永远假设运营会在周五下午五点提需求;
- Vim虽好,但别在团队协作时强行安利(血泪教训)。
技术没有银弹,但有责任心的程序员,总能找到那条“能跑、稳住、还能迭代”的路。
对了,最近在看MLOps相关的东西,打算把CI/CD也串起来。要是阿里网易哪位大佬看到这篇,觉得这小伙还能抢救一下……简历已备好,求内推(狗头保命)。

评论 0