☰
从零搭建AI工程:模型部署、监控与迭代的完整实践
2026/11/3 20:25:46 网站建设 项目流程

两年前我第一次把一个“调好的模型”交给部署组的同事时,被连问了三个问题:模型文件在哪个环境里跑?接口的输入输出长什么样,出错怎么返回?线上推理万一挂了,回滚方案是什么?当时我只能把训练好的权重文件递过去,然后干瞪眼。那一次我才彻底意识到,会训练模型和能做AI工程(ai-engineering)是两码事。这个认知转变,就是我眼里“from scratch”的真正含义——不是你从零学会了某个框架,而是你从零把一个模型变成了一个可运行、可监控、可迭代的系统。

这篇文章写给三类人:已经用PyTorch或TensorFlow跑通过几个模型,但不知道下一步该干嘛的;在算法岗做了两年,想往工程方向转型的;以及刚入门AI、想系统了解工程全貌,不想一上来就被各种工具绕晕的。我会把我个人从零搭建AI工程的全过程、选型理由、踩坑记录和学习路径建议都摊开来讲。没有玄学,全是实操出来的经验。

1. 先搞清楚:AI工程和“能跑通模型”根本不是一回事

1.1 三个角色的分工差异

我先说一个可能让很多人不太舒服但非常现实的观点:算法研究、数据分析、AI工程是三套完全不同的能力体系。

算法研究员的目标是验证新方法的有效性。模型在标准数据集上把准确率提升了,实验记录写清楚,事情就基本结束了。他们可以不用关心推理成本、不用关心用户会不会乱说话、不用关心那条数据为什么读取失败。

数据分析师的目标是发现业务规律。他们交付的是洞察和报表,核心指标是结论靠不靠谱、能不能指导业务动作。他们的工作对象是历史数据,不是在线流量。

AI工程师的目标完全不同:让模型在一个真实的业务系统里常年稳定运行。交付物不是模型文件,而是一个包含数据管线、训练流程、评估体系、部署服务、监控告警、迭代机制的完整系统。核心指标是可用性、延迟、成本、数据质量、迭代周期。

我见过很多从算法转工程的人,第一个月特别难受。因为在学校里,模型训练完就是终点;在公司里,模型训练完只是起点。工程师要做的事情听起来一点都不性感:写数据校验、做压测、处理容器日志、设计回滚机制,甚至要和运维吵一晚上“这个镜像到底能不能起”。但这些事恰好决定了模型能不能被真实世界使用。

1.2 为什么准确率高不等于能用

举个我实际见过的例子。一个客服意图识别模型,离线测试准确率92%,上线第一周就跌到70%左右。团队第一反应是模型不行,要换更大的预训练模型。结果排查下来,问题根本不在模型结构,而是离线测试数据来自历史工单,线上真实用户的路子完全不同——他们会打错字、会有情绪、会在一句话里塞两个意图、会说一半突然问“刚才那个能换吗”。数据分布变了,模型表现得就变了。这在工程上叫作“分布漂移”。如果系统里没有数据监控和回流机制,你甚至不会发现它已经悄悄变差,直到业务方来投诉。

再比如推理延迟问题。模型在离线评测环境里跑一次只要几十毫秒,上线后发现高并发下平均要200毫秒,P99甚至超过一秒。原因并不玄学:没有做batch推理、显存分配不合理、模型没有加载预热、日志把CPU打满了。这些问题没有一个是算法问题,全是工程问题。

所以我的结论一直是:一个模型再好,如果没人保证它的数据、部署、监控、迭代链路,它只能躺在训练日志里当PPT素材。AI工程的核心就是把“偶尔能跑通”变成“持续稳定可用”。

2. 从零搭建AI项目的骨架:先定边界再写代码

2.1 技术栈选型的取舍逻辑

我从零开始跑通一个AI项目,技术栈基本是固定的,不是因为别的不好,而是这套组合踩坑成本最低,最适合一个人或小团队从零走到上线。

  • Python 3.11+,不用多解释,AI生态的中枢。
  • PyTorch。不是TensorFlow不好,而是PyTorch的调试体验更接近普通Python,报错信息相对友好,从研究到部署之间的沟也更浅。团队如果有新人,PyTorch上手更快。
  • FastAPI做服务化封装。自带请求校验、自动文档,天然支持异步,把模型包成一个HTTP服务非常顺手。性能也够用,真正扛不住的时候再换更重的推理框架。
  • PostgreSQL存元数据,Redis做缓存和轻量队列。
  • Docker + Docker Compose统一环境。

每次有人问我“为什么一定要用Docker”,我都会反问一句:你有没有经历过“在我机器上是好的”?AI项目里这个问题尤其致命,因为除了代码,还有Python版本、CUDA版本、PyTorch版本、依赖库版本,任何一个不一致都可能让结果微妙地变化。Docker把这一切锁在一个镜像里,是我目前遇到的环境问题里性价比最高的解法。本地能跑,线上基本也能跑,如果跑了不对,至少锅不在环境差异上。

2.2 项目目录结构与配置管理

项目骨架我建议一开始就按模块拆好,不要所有代码堆在一个文件夹里。我常用的结构是这样的:

my-ai-project/ ├── configs/ # 所有配置集中管理,YAML文件 │ ├── data.yaml # 数据路径、校验规则 │ ├── train.yaml # 超参数、训练参数 │ └── serve.yaml # 服务端口、模型路径、批处理参数 ├── data/ │ ├── raw/ # 原始数据,只读 │ ├── processed/ # 清洗后的数据 │ └── golden/ # 黄金集,只用于评估 ├── src/ │ ├── data/ # 数据下载、清洗、校验 │ ├── features/ # 特征工程,与模型解耦 │ ├── models/ # 模型定义与训练脚本 │ └── serving/ # 推理封装、API、监控 ├── tests/ # 测试从第一天就存在 ├── scripts/ # 杂项脚本 └── Makefile # 统一入口,make train / make serve

这样分的核心原因是:数据和模型解耦,特征工程独立,配置全部落到YAML而不是散落在代码里。很多人嫌配置管理麻烦,喜欢在代码里硬编码参数。短期是快,但一旦实验数量上来,你会发现自己根本记不住哪份数据配了哪个参数。把配置集中到YAML,用pydantic或者Hydra做类型校验和加载,训练脚本就只需要一行命令。

还有一件事我强调很多次:tests目录从第一天就要建。AI项目里写测试的比例低得吓人,但数据清洗函数、特征转换函数这些逻辑一旦出错,整个模型的结果都不可信。你没看错,模型训练前,先把清洗和特征的测试写了。

3. 数据管线:AI项目里最容易被低估的工程环节

3.1 数据校验与版本管理

AI行业有句老话:垃圾进,垃圾出。模型代码重构一百次都能跑,但数据一旦被污染,模型学到的就是垃圾,而且这种污染很隐蔽,通常要到上线后业务侧出问题才会暴露。

我在每个项目里固定做三件事。

第一件,schema校验。字段类型、取值范围、缺失率、唯一性,都要在数据进入训练流程前跑一遍检查。我常用pandera定义DataFrame的schema规则,跑一个校验函数就能发现大部分异常。比如字段“状态”本来是整数1和0,如果上游有一天改成字符串“已完结/处理中”,schema校验会立刻报错,而不是让模型在脏数据上默默训练。

第二件,黄金集固定。准备一份经过人工校验、能代表真实线上分布的数据集,任何实验都不能用它训练,只能用来评估。这个集合的意义有两层:一是防止训练集“穿越”导致评估虚高,二是防止你为了刷指标反复调评估集。黄金集是模型上线前的最后一道质检线。

第三件,数据版本管理。我用DVC对数据集做版本控制,像管代码一样管数据。每个模型训练时记录数据版本的哈希,几个月后可以精确回答“这个模型是用哪份数据训练的”。没有数据版本管理,溯源就是一笔烂账。

我踩过一个特别典型的坑:有一次上游业务改了字段含义,工单的“状态”字段从数字标记变成了状态字符串,我的清洗代码没适配,也没人发现。结果训练出来的模型在线上表现极其诡异,查了三天才定位到是数据语义变了。从那以后,所有进入训练流程的数据必须过schema校验,没有例外。

3.2 数据漂移与线上分布的预防机制

静态检查只能保证数据格式正确,保证不了线上输入分布和训练分布一致。用户的行为会变,业务的规则会变,模型面对的输入也会慢慢漂移。

常用的预防机制是算PSI(群体稳定性指数)。简单来说,就是把训练分布和线上最近一段时间的分布分桶,然后量化两者的差异程度。用生活类比:你经营一家奶茶店,平时顾客点珍珠奶茶的比例稳定在60%,某天开始这个比例突然变成20%,你肯定要调整备货和推荐策略。PSI就是那把量化“变化有多大”的尺子。

我的一般判断标准是:PSI小于0.1,分布基本稳定,不用管;0.1到0.25之间,开始留意,去排查可能的原因;超过0.25,必须告警,并且要手动检查线上数据质量。计算PSI不需要复杂系统,一个脚本每天跑一次,把每个重要特征的分布变化写进报表就够了。不要一上来就搭一套“实时特征监控平台”,先把日级别统计做出来,价值立刻就有了。

特征层面也要做差异化监控,不能眉毛胡子一把抓。重点监控对模型结果影响大的特征,或者业务上意义重大的特征。非重要特征的漂移可以记录但不告警,否则告警列表会被无意义信息刷爆,真正的异常反而被淹没。

4. 训练管理:让实验可复现、可追溯

4.1 实验追踪怎么选怎么用

人脑记不住十个以上实验之间的差别。我见过有人把超参数写在文件名里,还在微信群里发“谁记得上周那个结果最好的batch size是多少”。正规做法是上实验追踪工具。

我用MLflow,因为它自托管、免费、表格界面够用,数据在自己手里。Weights & Biases的界面更漂亮,但如果公司对数据出网有要求,商用SaaS会受限。选型就一句话:看你们团队的隐私要求和预算,不要为了界面好看牺牲可控性。

每个实验必须记录四样东西:超参数、代码版本的git commit id、数据版本、关键指标。训练完成后再把最佳模型的权重路径一并记进去。目标是两周之后,你根据记录能完整复现当时的实验条件。这一点怎么强调都不过分。

实验可复现有三个典型的坑。第一,随机种子没固定,模型结果每次跑都不一样,实验对比全是噪声。第二,依赖版本没锁死,requirements.txt里写“numpy>=1.2”这种写法迟早害人,要用精确版本,最好配合poetry或pip-tools。第三,CUDA环境不一致,换一张显卡结果可能微妙地变化,用Docker把CUDA和PyTorch版本一起锁住。

4.2 从单卡到多卡:什么时候才需要分布式

很多人一开始就想上分布式训练,觉得单卡太慢。我的建议是:先搞清瓶颈在哪,再决定要不要上。用nvidia-smi和profile工具看一眼GPU利用率。如果利用率不到80%,问题不在卡少,而在数据加载和预处理太慢。这时候该优化的是DataLoader、缓存和预处理逻辑,不是引入分布式。

分布式训练能带来什么?多卡并行确实能大幅缩短训练时间,但代价是工程复杂度直线上升:代码要改成DistributedDataParallel,数据队列要换分布式采样器,调试难度翻倍,还要学集群资源管理和任务调度。对于绝大多数业务场景,单机多卡已经绰绰有余。我见过有人花一周时间搭分布式训练环境,就为把原本只需两小时的训练缩短到四十分钟,完全不划算。

先把单机流程彻底跑通,把数据和模型做对,再谈分布式。这不是懒惰,是控制工程复杂度。分布式训练只有在模型大到单卡放不下、或者训练时间长到严重拖累迭代节奏时才真正需要。

5. 部署上线:模型真正干活的地方

5.1 模型格式转换与推理优化

训练完得到的.pt权重文件,不能直接扔给生产服务。原因有两个:推理框架加载PyTorch模型需要一整套PyTorch环境,又重又容易版本冲突;而且PyTorch自带的推理性能一般,没有针对线上场景做算子融合等优化。

我的标准流程是先转成ONNX。使用torch.onnx.export导出,把固定输入尺寸配置好,batch维度设为动态轴。ONNX最重要的价值在于它是通用中间格式,服务端可以用ONNX Runtime加载,CPU上也能跑得很快,不用依赖完整的PyTorch环境。

如果ONNX Runtime的性能还不够,再考虑TensorRT。TensorRT需要NVIDIA显卡,转换耗时也不短,但推理延迟通常能再降一半左右。我只有在流量大、延迟敏感的实时场景下才用它,比如实时反欺诈、交易风控这类。大多数业务用ONNX Runtime就够了。

不管转成什么格式,转换后必须做“输出对齐”测试:喂同样一组输入给原始模型和转换后的模型,比较输出张量的最大误差,超过阈值说明转换过程中精度受损,要重新调整。这一步不能省。

我踩过一个经典坑:导出ONNX时没注意固定了序列长度,结果线上来了一条特别长的文本,服务直接报错。模型本身没问题,纯粹是转换参数没配好。这类问题文档里都不会写,只有真实流量里才能逼得你改掉。

5.2 服务化封装与性能测试

模型本身只是一个函数,要变成服务得包一层API。我用FastAPI,理由很实在:自带pydantic数据校验,输入输出结构清晰,出错时能返回结构化错误信息;自动生成接口文档,联调效率高;性能在同类型框架里也算能打。

如果追求极致性能,可以考虑Triton Inference Server。它支持动态batch、模型并发、多模型管理,性能上限很高,但部署和运维都复杂。我的建议永远是把FastAPI先跑通,评估真实流量和性能瓶颈,不够了再换Triton,不要一开始就引入重型武器。

性能测试必须做,而且要做规范。我用locust或wrk做压测,重点看四个指标:QPS、P50和P99延迟、CPU和GPU利用率、错误率。压测场景也不要只测单请求,要覆盖并发请求和极端长度输入。每次模型更新后都要重新压,防止性能回归。

上线前还有一个经常被忽略的环节:模型加载预热。很多推理库在第一次推理时会做初始化或编译优化,如果服务刚启动就迎来一波大流量,会被第一次请求的慢延迟打趴。解决方法是启动时发几个假请求“暖机”,把该做的初始化都做掉,再对外开放流量。这个小操作真的救过我好几次。

6. 线上监控与迭代:上线只是开始

6.1 监控指标与告警设计

模型上线是开始,不是结束。很多团队最大的误区就是部署完就没人管了,直到业务方反馈效果变差才想起来看数据。

监控分三层。

第一层是业务层:这个模型服务的业务指标有没有变化。比如是推荐系统就看点击率和转化率,是客服工单分类就看解决率和人工介入率。业务指标是模型的最终裁判。

第二层是数据层:线上输入分布有没有漂移,PSI、缺失率、异常类型占比这些。数据层异常通常是业务问题的前兆。

第三层是系统层:延迟、内存、GPU显存、错误率、QPS。系统层异常会影响可用性,是最直接的警报源。

告警不能乱设,否则会变成“狼来了”的剧本,真正出问题时反而没人看。我的原则是:系统层告警必须即时,服务挂了马上要响;数据层告警按天汇总,每天看一眼分布变化;业务层告警看周粒度趋势,因为单天的波动往往不是模型问题,而是业务周期性。

举一个真实案例:工单分类模型上线第三周,P99延迟突然从80ms涨到300ms,用户倒没明显感知,但监控亮黄灯了。查下来不是模型问题,是日志打得太频繁,把CPU几乎占满了。把日志级别调低、去掉无意义字段后,延迟立刻降回去。不监控系统层,这种小问题可能要等业务方骂上门才发现。

6.2 模型更新与回滚策略

模型不可能一劳永逸。用户行为变了,业务规则变了,模型就得跟着迭代。但模型更新不能像开发小功能那样“改完就发”,因为模型更新的风险往往不是程序崩溃,而是静默变差。

我的标准流程分四步。

第一步,离线评估。新模型在黄金集上跑一遍,和旧模型对比所有指标。没有明确提升就不过,这一步是门槛最低也最有效的筛选。

第二步,影子部署。新模型和旧模型同时接收线上请求,但新模型的结果只记录、不返回。用一小段真实流量数据评估新模型的表现。这个步骤最容易被跳过,但也是最值得做的——它能在不打扰任何用户的前提下,用真实数据回答“新模型到底行不行”。

第三步,灰度发布。先切5%流量到新模型,观察一段时间内的监控指标。没有异常再逐步扩大到20%、50%、100%。每次切量之间留观察窗口,出了问题损失可控。

第四步,保证能快速回滚。发布前确保旧模型镜像还在,回滚就是把流量切回旧版本。用Docker镜像tag或Kubernetes的Deployment回滚都能实现,关键是“随时能回”。我见过有人发布新模型后旧权重文件被覆盖,回滚只能重新训练,那个痛苦谁经历谁知道。

7. 一次真实项目的从零复盘:36小时跑通工单自动分类

7.1 需求澄清与数据摸底

光讲理论不过瘾,我复盘一次真实的从零到上线经历。周五下午接到需求:市场部每周有几千封用户邮件要人工分到对应部门,希望做一个自动分类。数据方面有过去三个月的历史邮件,人工标注过的部门信息可以当标签。

第一件事不是写代码,是需求澄清。我确认了三件事:分类体系一共8类;预估流量峰值是每秒5封邮件左右;业务上“宁可不归类也不能乱归”,因为错误分类比不分类更影响用户体验。最后一条直接决定了评估指标的选择,精确率优先级高于召回率。

数据摸底发现的问题比预想多:大约15%的邮件是转发件、退信、空内容,根本不应该进模型训练范围。另外抽样人工核对了100条历史标注数据,发现人工打标本身也有错误,有些邮件被分配到了错误的部门。和业务确认后,我们把低置信度的样本剔除,保持了一版相对干净的数据集。数据清洗这一步花了整个项目将近三分之一的时间,我早就习惯了。

7.2 部署上线的执行顺序

当天晚上8点半,我开始写训练脚本。基线模型用的是TF-IDF加逻辑回归,不是深度模型。原因很简单:样本量几千条,深度学习在这么小的数据上不会比简单模型好多少,但训练、调参、部署的成本高一个数量级。工程上先跑通链路,再谈模型提升,这是我一直以来的原则。

次日早上9点,模型训练完成,离线F1约0.83,和业务方确认可接受。然后做工程化:转ONNX、封装FastAPI、写schema校验、全部塞进Docker。本地跑通后,我把服务部署到一个影子环境,将周末两天到达的新邮件同时发给影子服务,但不返回任何结果,只做预测记录。

下午1点做压测,模拟峰值5倍的流量,结果是P99延迟21毫秒,CPU利用率30%,性能余量充足。下午4点开始灰度:先5%,观察一小时,再20%,再100%。周一早上人工抽查了一批线上预测结果,准确率在可接受范围内,模型全量上线。接好监控和告警,项目收尾。

7.3 复盘:哪些步骤不能省

这个项目的模型很简单,但工程环节一个不少。复盘的时候我很确定,最不能省的是四件事:需求边界确认、数据清洗校验、影子部署、灰度发布。

需求边界确认决定了“预测错”的定义,直接影响评估指标和数据筛选方式。数据清洗校验决定了模型学到的东西靠不靠谱,这一步乱来后面全是白搭。影子部署让你敢放心用真实流量验证模型,不打扰用户。灰度发布把上线这件事从“一次赌博”变成了“一个流程”。

这个案例也说明了AI工程里大部分时间到底花在哪:不是调模型,而是清洗数据、确认需求、部署验证、设计监控。模型本身用最朴素的方法就够了。我特别想让刚入门的同学记住这一点:先把一条最朴素的链路从数据到服务完整跑通,再考虑花里胡哨的模型结构。

8. 关于学习路径,我的几条实在建议

8.1 先跑通最小链路,而不是囤课程

很多人学AI工程的姿势是囤课:收藏一堆教程、买一堆课程、关注一堆博主,然后就没有然后了。我的建议非常简单粗暴:选定一个小项目,目标是以“从数据到服务的所有环节都经过你的手”为终点,哪怕只是一个玩具级的情感分类模型。

这个过程中你会被迫去碰数据清洗、特征处理、训练脚本、模型转换、API封装、Docker镜像、压测、监控,这些全部碰一遍之后,你才对AI工程有真正的体感。课程只是地图,上路走一遭才是真的认识路。

8.2 先理解朴素方法,再上复杂模型

如果你刚开始,不要一上来就追Transformer、大模型。先用TF-IDF加逻辑回归、随机森林这类朴素方法做一个项目。为什么?因为它们边界清晰,参数少,行为容易理解,能帮你快速建立对数据、特征、评估指标的直觉。

有了这套直觉,再去上深度学习,你会知道什么时候该加复杂模型,什么时候加了也没用。我见过太多人一上来就用BERT做分类,模型表现和TF-IDF差不多,但推理成本高了几十倍。而他们根本不知道对比基线是什么。朴素方法的另一个好处是训练快、迭代快,你可以在一天内试错十次,这种手感在复杂模型上很难获得。

8.3 学会读代码,亲手部署一次

至少要完整读一遍开源项目的核心代码。找一个简单模型的训练脚本,逐行搞懂DataLoader是怎么采样、epoch循环怎么组织、optimizer.step()和scheduler.step()的调用顺序有什么影响。调API能让你“能用”,读代码才能让你“会改”。

然后一定要亲手部署一次。哪怕是个玩具模型,也要走一遍Docker化、FastAPI封装、压测、监控告警的流程。没部署过的人总觉得部署很简单,部署过的人知道这里有多少细碎但致命的坑。我认识不少研究能力很强的人,卡在部署这关过不去,其实就是动手太少。

8.4 心态上接受“大部分时间不是在训练模型”

最后一条是心态层面的。AI工程工作日常里,真正在训练模型的时间比例其实不高。更多时间花在数据清洗、写校验、看监控、排查版本冲突、调部署配置上。这不是某种“退步”,这就是工程本身。

我现在衡量一个AI工程师的标准,不是看他会多少模型,而是看他能不能把一个朴素的模型做到多可靠。能把一条最简单链路稳定跑上一年、遇到数据漂移能提前发现、发布新模型能轻松回滚,这些能力比论文里的SOTA数字值钱得多。这个认知也是我从零做AI工程以来最大的收获。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询