机器学习部署不是“跑通就行”:一个Vim党在杭州实验室的血泪总结

分布式背锅侠
2025-12-24 07:29
阅读 800

大家好,我是某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?两个原因:

  1. 我们模型不大(<100MB),没必要上重型方案;
  2. 实验室服务器资源有限,连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系统必须支持动态配置。于是我们在服务层加了三层“逃生舱”:

  1. 特征开关:通过配置中心(我们用Apollo)控制哪些特征参与计算;
  2. 模型热更新:监听本地模型文件变化,自动reload(注意加锁!);
  3. 降级策略:当推理超时或失败,返回兜底规则(比如按热门度排序)。
# 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

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝