引言:训练完模型之后#
你在完整训练流程中完成了模型的训练,在CNN 消融研究:理解卷积神经网络各组件的作用中验证了各组件的贡献,在U-Net:图像分割的革命中用UNet处理了具体的图像分割任务——模型在测试集上跑出了不错的准确率,一切都很好。但现在有一个更现实的问题:这个模型怎么给别人用?
让模型真正产生价值,不是训练完就结束了。你需要考虑的事情比训练多得多:别人怎么调用它?它能同时处理多少个请求?如果图片不是28×28的怎么办?别人会不会把它搞崩溃?这些问题没有一个是训练的loss曲线能回答的。
从训练到服务的跨越#
想象一下,你训练了一个手写数字识别模型,想做一个"拍照识数"的App。这个场景下,模型需要:
接收一张照片,而不是训练时用的28×28张量
在几十毫秒内给出结果,用户没有耐心等
同时响应几百个人的请求,不是一个人调一次
对恶意调用有防护,不能被人搞崩溃
这就是从"训练模型"到"服务模型"的跨越。训练时你关注的是loss曲线和准确率,服务时你关注的是延迟、吞吐量、可用性和安全性。
维度 |
训练环境 |
生产环境 |
|---|---|---|
核心关注 |
准确率、loss |
延迟、吞吐量 |
输入来源 |
预设数据集 |
用户上传/数据流 |
并发量 |
单用户、顺序执行 |
多用户、高并发 |
运行时长 |
几小时到几天 |
7×24不间断 |
资源限制 |
GPU、大内存 |
未必有GPU |
安全要求 |
无(学术环境) |
鉴权、限流、审计 |
模型服务面临的三个核心挑战#
挑战一:格式兼容性#
PyTorch训练出来的模型是.pth或.pt文件,这些文件依赖PyTorch的运行时环境。如果部署环境没有PyTorch(或者版本不一致),模型就跑不起来。更麻烦的是,如果想让模型在移动端、浏览器或边缘设备上运行怎么办?某些场景下甚至可能要用C++或者JavaScript来加载模型。
ONNX(Open Neural Network Exchange)就是为了解决这个问题而出现的标准格式。它定义了一套统一的中间表示,让模型可以在不同框架之间无缝迁移。ONNX:模型的中立语言会详细讲解如何将PyTorch模型导出为ONNX。
挑战二:架构选择#
模型要做成API服务,第一个问题是"该用同步还是异步?"
同步推理就像打电话——你问一句,对方当场回答。适合轻量模型、低延迟场景。但是如果有100个人同时打电话呢?电话线不够用就会排队等待。
异步推理就像发邮件——你发出去,对方处理完再通知你。适合大模型、耗时长的推理任务。缺点是实现更复杂,需要任务队列和回调机制。
服务架构:从单机到分布式会详细分析这两种模式各自的适用场景和架构设计。
挑战三:运维复杂度#
模型服务上线后,你还需要:管理模型的版本(新模型上线怎么不中断旧服务?)、控制访问权限(不能让任何人都能调你的API)、监控推理延迟(模型变慢了怎么预警)、处理故障(服务挂了怎么恢复)——这些都是运维上的挑战。
部署实践:用Ferrinx服务模型会用Ferrinx这个具体的推理服务来展示如何应对这些挑战。
Ferrinx:一个简化的教学工具#
Ferrinx是一个用Rust编写的轻量级ONNX推理服务。它不像TensorFlow Serving或TorchServe那样功能丰富,但正因为简单,它更适合用来学习模型部署的核心概念。
Ferrinx采用axum作为Web框架、ort作为ONNX Runtime绑定,支持SQLite和PostgreSQL两种数据库后端。它提供两种运行模式:简单模式(单进程,无需任何外部依赖)和分布式模式(添加Redis后支持异步推理和多Worker扩展)。这种设计让读者可以循序渐进地理解模型服务的各个层次。
在部署实践:用Ferrinx服务模型中,我们将实际操作Ferrinx的部署流程,包括编译、配置、模型注册、推理调用等完整的生命周期管理。
本章路线图#
本章的目标不是让你成为模型部署专家,而是让你理解"从训练到生产"这个链路中的关键环节,建立完整的MLOps思维框架。当你以后训练出一个好模型时,你不仅知道"这个模型很准",还知道"怎么让它真正跑起来"。
下一步#
从ONNX:模型的中立语言开始,我们学习如何把PyTorch训练的模型导出为标准ONNX格式。这个过程需要处理一些细节:如何正确处理输入输出命名、如何处理动态batch维度、如何验证导出后的模型和原始模型输出一致——有意思的是,这些细节中的许多都跟实验设计中"控制变量"的思维方式相通:每次只改一个参数,确保结果可复现。