有人问我“ai-engineering-from-scratch”到底是什么意思,我第一反应是:这说的根本不是一个具体项目,而是一条从零搭建AI工程能力的完整路线。你去看市面上大量课程和教程,大部分都在教算法、教模型结构、教怎么调loss,但真把一个模型从训练服务器搬到生产环境、让它稳定服役、能监控能回滚,这一整段路几乎没人系统讲过。我这次想聊的,就是这条从零到一的AI工程链路:数据怎么管、训练怎么复现、模型怎么部署、线上怎么监控、迭代怎么做。
这篇文章适合两类人:一类是刚入门机器学习、想搞明白“训练完模型之后到底还要做什么”的算法新人;另一类是后端工程师,想往AI基建方向转,但面对一堆MLOps工具不知道从哪下手。我会用一套完整的实践脉络来讲,全程围绕业务落地场景展开,不会给你堆名词。
1. AI工程不是调参:先把边界和路线搞清楚
很多人误以为AI工程就是把模型训练跑通,稍微调调超参数、把准确率提到90%就算完工。这个认知差得有点远。我见过太多模型在Notebook里表现神勇,一到线上就被打成筛子。AI工程真正要解决的,是“这个模型怎么变成一个稳定、可靠、可维护的系统组件”这件事。
1.1 算法工程师和AI工程师的差异
算法工程师的思考终点,是模型在评测集上的指标。AI工程师的思考起点,是模型在真实环境里的行为。后者要关心延迟、吞吐、异常输入、版本漂移、特征缺失、依赖冲突、资源成本,一大堆问题,任何一个都可能让一个“准率99%”的模型彻底瘫痪。
举个例子,你做一个文本分类器,离线测试准确率97%,很漂亮。上线以后,用户传进来的文本可能带有乱码、特殊符号、超长字符串、空值。这些数据在训练集里根本没出现过,模型第一次见到它们的时候,预测结果完全不可控。AI工程的第一个核心任务,就是提前假设“线上数据一定比训练集脏”,然后为这种脏做系统性防御。
这类问题光靠调参解决不了。你得在数据管道、特征校验、模型容错、降级策略等多个层面同时做设计。这也是为什么AI工程更像是一套“模型生命周期管理”体系,而不是某一个环节的单点技术。
1.2 从零到上线的最小路线图
我把自己做AI工程的习惯打法拆成六个环节:数据工程、实验管理、训练复现、模型部署、推理优化、线上监控。六个环节不是串行走一遍就完,而是围成一个闭环,每次模型更新都会完整跑一圈。
数据工程解决的是“喂给模型的东西靠不靠谱”;实验管理解决的是“每次实验改了什么,能不能追溯”;训练复现解决的是“换一台机器还能不能得到同样结果”;模型部署解决的是“模型文件怎么变成对外服务”;推理优化解决的是“服务能不能扛住真实流量”;线上监控解决的是“模型什么时候悄悄变坏了”。这六件事每件都不起眼,但连起来就是一个完整的AI工程体系。
新手最容易犯的错就是跳过前两步直接训练,最后模型调好了,却不知道用了哪份数据、跑了哪个版本,线上出问题根本没法排查。从零开始,务必先搭数据、再管实验、最后才谈训练。
2. 第一步不是训练模型,而是搭数据管道
数据管道的地位很容易被低估。学术实验里你可以用现成的公开数据集,清洗干净、格式统一。真实业务里数据从各个系统流过来,字段对不上、格式不一样、时间不同步,乱七八糟。如果数据管道不扎实,后面所有环节都像盖在沙子上的楼。
2.1 原始数据采集与清洗的坑
我接手过一个项目,业务方信誓旦旦说数据在数仓里都是“干净的”,结果一拉出来,光空值率就超过30%,部分字段还有明显逻辑矛盾。真实场景里这种情况极其常见。所以第一个必须做的事情,是给原始数据建立“入境校验”,就是在数据还没进入模型训练流程之前,用规则过滤掉明显异常。
校验规则至少包含几个维度:字段缺失率、值域范围、枚举合法性、时间戳有效性、文本长度边界。举一个具体的例子,如果模型特征是用户年龄,合理范围是0到120,那超过120的数据要么是爬虫抓错、要么是脏数据。你可以在管道里直接丢弃或者标记,不要让它们进入训练集。
清洗这块最容易踩的坑是“一刀切”。比如把缺失值全部填充为0,这会极大改变特征分布。我通常会分字段处理:数值型特征按分组中位数填充,类别型特征新增一个“unknown”编码,文本特征保留原样但记录长度异常标记。每种处理方式都要写进管道日志,保证事后能追溯。
2.2 数据版本化:这套东西为什么值得做
学术实验改来改去,数据集换来换去,出了结果你很难说清这个结果是用哪份数据训出来的。工程化要求你记录每一次实验使用的数据集版本。数据版本化就是给数据集加上类似Git的提交记录,每次清洗完就生成一个带哈希值的数据包,训练脚本引用这个哈希,不可变、可复现。
工具上我推荐两条路:轻量方案用DVC,重量方案用LakeFS。DVC的优点是和Git天然配合,把数据文件映射为Git指针,不影响原有开发流程。LakeFS更适合做大规模数据仓库的版本管理,支持分支和合并,适合有独立数据团队的组织。个人项目或者早期团队,DVC就够用。
实操中要记住一条铁律:数据管道输出的永远是一份不可变的数据快照,而不是一份会被反复覆盖的CSV。每次变更,哪怕只改了一个缺失值处理规则,都要生成新的版本。模型实验的完整复现,依赖的正是数据版本的完整闭环。
2.3 训练/验证/测试集划分的实操细节
数据划分看起来简单,实际上暗藏风险。最常见的问题是跨越时间的数据泄漏——你把一月份到九月份的数据按随机比例切割,训练集和验证集里都混有同一时间段的数据,模型学到的规律可能是“这段时间特有”的,而不是“长期稳定”的。
对于有时间序列特征的数据,我强烈建议按时间顺序划分:前80%的时间窗口做训练,后10%做验证,最后10%做测试。这能最大程度模拟线上“用过去预测未来”的真实场景。
还有一个细节:类别不平衡时,分层抽样要放到管道的最后一步做,而且分层维度不一定是标签本身。比如做用户行为预测,标签是“点击/未点击”,但不同用户群体点击率差别巨大,这时可以按用户群组分层切分,保证每个群里正负样本比例一致。这样切出来的验证结果才更接近线上表现。
3. 模型训练:从写代码到可复现
训练谁都会调,难点在于让训练过程可复现、可追踪、可回滚。我第一次独立跑项目时就吃过亏:一个模型效果不错,想复现之前的实验结果,结果发现训练代码已经改得面目全非,超参记录也不完整,最后只能凭记忆硬猜。从那以后我再也不用记事本管理实验,而是搭了一套标准化的实验管理流程。
3.1 训练脚本的工程化改造
训练脚本工程化的核心理念是“配置与代码分离”。训练代码里不应该硬编码任何超参数、数据路径或者模型结构配置。所有可变部分都放到外部配置文件里,代码只负责读取配置、执行逻辑、记录结果。
我习惯把训练脚本拆成四层:数据加载层、模型构建层、训练循环层、评估输出层。数据加载层读入配置中的数据集路径和批量大小;模型构建层根据配置实例化网络结构;训练循环层负责梯度更新、学习率调度、指标打印;评估输出层在训练结束后跑验证集评测并保存指标。四层只通过配置文件和函数接口交互,互不直接依赖。
改造完成后的第一份成果,是“一两行命令就能启动训练”。别人拿到你的代码,不用问你怎么跑,只要看配置文件,就能复现实验。工程化的模型训练,本质上就是让每个实验都有标准的启动方式和终止方式。
3.2 超参数管理:从记事本到配置文件
我曾经见过同事用一串很长的命令行参数管理超参数,改动一个值要从前到后找半天。更麻烦的是,命令行参数没法带注释,别人根本不知道你这个值为什么取0.001而不是0.01。
现在我的做法是:用YAML统一管理超参数,结构分四块。数据配置放在data段,模型配置放在model段,训练配置放在train段,输出配置放在save段。每个超参旁边写注释,说明取值范围和经验依据。训练脚本运行时读取YAML,用统一配置对象传给各模块。
这种方式的好处不仅仅是好维护。你可以在实验记录里保存完整的YAML配置,每次实验自动生成一份配置快照。出结果以后想归因,对照配置快照就能知道是哪个超参数起了决定作用。我见过很多团队用MLflow或者Weights & Biases做这件事,但你要是还没上这些工具,先把YAML配置用好,已经可以解决80%的复现问题。
3.3 训练过程中的监控和断点
训练周期短的话无所谓,但遇到大模型或者大数据集,跑一次十几个小时,中间显卡抖一下、机器重启一下,很可能前功尽弃。训练过程的监控和断点恢复,是这个阶段决不能省的投资。
断点检查点要覆盖三个信息:模型权重、优化器状态、当前epoch和step。只保存模型权重是不够的,因为优化器的动量信息一丢,恢复训练后学习率调度和梯度累积状态就错乱了。我用PyTorch的时候,习惯把checkpoint写成一个大字典,包含model_state_dict、optimizer_state_dict、scheduler_state_dict、epoch、step,原子写入,避免写一半文件损坏。
同时还要盯着显存、温度、loss曲线这些实时指标,用TensorBoard或者W&B可视化。这里有一个小经验:loss曲线不是只看下降趋势,还要看方差。如果loss在训练后期震荡剧烈,说明学习率偏高或者batch size太小,这时候应该在配置阶段就调好,而不是靠早停硬扛。
4. 部署上线:模型文件是怎么变成一个服务的
训练完成了,模型文件躺在磁盘上,接下来要做的是让它跑起来。这一步对后端工程师来说很熟悉,但对算法背景的同事来说可能有点懵。核心思路不复杂:把模型封装成HTTP接口,搭一个推理服务,处理好性能、容错和发布流程。
4.1 模型导出与封装
模型导出的第一步是搞清楚用什么运行时来跑推理。PyTorch模型推荐用TorchScript或者ONNX导出;TensorFlow模型用SavedModel;如果是LLM类模型,直接用Triton或者vLLM这类专用推理框架。关键是导出后要对比导出前后推理结果的一致性,误差不能超过预设阈值。
封装推理服务的时候有一个细节容易忽略:输入数据的预处理必须在服务端做完整。很多团队图省事,在客户端做特征工程,传过来的直接是特征向量。模型服务确实简单了,但客户端迭代和接口变更会变成一场灾难,而且线上特征生产逻辑和离线训练逻辑一旦不一致,模型效果就会跳水。正确做法是服务端接收原始请求,执行特征转换,再进入模型推理,直接复用训练时的特征代码。
另外,模型服务要返回的不只是预测结果,最好还带一个置信度字段。线上策略层可以依赖置信度做兜底,低置信度请求走人工或者默认答案,避免模型把低质量预测强行输出给用户。
4.2 推理服务的性能优化
性能优化的顺序是有讲究的。先看业务指标要求,再看当前瓶颈。通常顺序是:模型推理时间是瓶颈,就上批处理;请求吞吐是瓶颈,就加缓存;响应延迟是瓶颈,就优化网络和序列化。
我做过一个分类服务,初始延迟250ms,业务要求150ms内。排查发现模型本身推理只要80ms,剩下的全花在特征拼接和JSON序列化上。解决思路是:把特征拼接从纯Python改成向量化操作,JSON换成Protobuf传输,整体延迟直接降到110ms,没有动模型结构。
这里也要提醒一句:性能优化要按真实流量压测来验证。我见过有人把batch size调到64,本地测延迟漂亮得不行,一上生产,响应时间反而变差,因为batch攒得越久,第一批请求等得越久。最优batch size一定是个权衡点,需要在压测环境里实测,用P99延迟作为验收指标。
4.3 灰度发布与回滚
模型部署不能像改普通代码那样直接全量上。一个新模型,哪怕离线指标再好,线上也可能翻车。灰度发布是最基本的保护机制。
灰度发布的核心是流量切分:最初只放5%的线上流量给新模型,观测错误率和业务指标,没有问题再逐步放大,10%、30%、50%,直到100%。观测时间要有讲究,一般每个阶段至少观察数小时到一整天,要覆盖业务流量峰谷,不能只看高峰期的表现。
回滚这里有一个常见误区:回滚不只是把模型文件换回旧版,还要连带特征代码和配置一起回滚。因为新版模型可能依赖新版特征转换规则,旧版模型配新版特征,输出同样会乱。所以每一次发布,都要把模型、特征代码、配置文件打包成一个整体版本,回滚的时候一起回。
5. 上线不是结束:监控、评估与持续迭代
模型上线只是AI工程生命周期的开始。真实业务数据时刻在变,模型会过期、会漂移、会悄悄失灵。线上监控和持续评估,是保证模型长期可靠的最后一道防线,也是最容易被团队砍掉的一环。
5.1 线上推理监控要盯什么
监控分为两层:系统层和模型层。系统层看的是服务健康度,比如延迟、吞吐、错误率、GPU利用率,这些跟普通后端监控没什么区别,直接接Prometheus加Grafana就可以。模型层才是AI工程特有的,要盯三类东西:推理结果分布、请求特征分布、置信度分布。
推理结果分布非常直观。一个二分类模型,线上预测的正样本比例原来是20%,突然变成60%,这一定有问题。要么是业务场景变了,要么是模型已经过期。请求特征分布同理,输入文本的平均长度如果出现剧烈变化,模型效果大概率会受影响。置信度分布则更微妙——如果高置信度的请求占比骤降,说明模型对当下数据的把握在下降,哪怕准确率还没掉,也已经是个危险信号。
我强烈建议把这些模型层指标做成仪表盘,设好告警阈值。比如预测正例比例偏离历史均值超过30%触发告警,或者低置信度请求占比超过20%触发告警。告警宁多勿少,先建立感知,再慢慢收敛。
5.2 数据漂移检测是怎么一回事
数据漂移是模型性能下降的头号原因。业务分布变了,模型还是那个模型,输入空间却没对齐,输出自然偏离。数据漂移检测的目标,是在模型效果明显变差之前,提前发现信号。
统计方法上,数值特征可以用PSI这个指标来评估分布一致性。PSI的计算方式是把两个分布各自分箱,计算每个箱子里的占比差异,加权求和。PSI小于0.1说明分布稳定,0.1到0.25说明有轻度漂移,超过0.25则强烈提示需要更新数据或模型。分类特征可以用卡方检验或者Jensen-Shannon散度。
具体操作上,要把线上采样窗口的统计结果和训练集统计结果做定期的自动对比。我习惯每小时跑一次漂移检测任务,输出每个特征的漂移分,超过阈值就自动通知。记住一个经验:漂移检测要设置足够长的观察窗口,用一两天甚至一周的数据来对比,单小时数据偶然波动太强,容易产生误报。
5.3 模型更新的节奏
模型更新不是“新模型训出来了就赶紧换”,而是按照业务节奏走。常见做法是每两周到一个月做一次常规更新,中间发现明显问题,用灰度发布机制做紧急更新。常规更新讲究时空稳定性,尽量避开大促、特殊活动之类业务波动明显的时段。
模型更新还要配合特征版本一起管理。我在团队里推行一个原则:任何一次模型更新,发布说明里必须写清楚“数据版本从什么切到什么、特征逻辑有没有变化、模型结构有没有变化、评测指标变化是什么”。没有这个说明,更新就不允许上灰度。
长期运行下来,模型不是越更新越好的过程——现实是,有些更新在离线指标上确实提升,在线上不见得受益。所以我养成了一个心法:用线上业务指标(比如转化率、点击率、用户满意度)作为最终评判标准,而不是离线AUC或者F1。离线指标只是候选筛选器,线上指标才是真正的裁判。
6. 常见问题与排查技巧实录
AI工程落地过程中,我踩过的坑不少,有些问题靠资料根本查不到,只能靠现场一点点试。这里整理几个高频问题,都是真实遇到过并且有完整排查路径的,直接拿出来分享。
6.1 最容易被忽视的五个坑
第一个坑是环境依赖不一致。训练环境Python 3.9、PyTorch 2.0,部署环境Python 3.8、PyTorch 1.12,推理结果对不上,排查半天原来是版本差异。现在我的规矩是训练和部署必须用同一套容器镜像,镜像版本用哈希锁定,从根本上杜绝环境漂移。
第二个坑是特征顺序不一致。离线训练时特征A放前面,线上推理时特征B放前面,模型还是那个模型,输出全乱了,而且报错都不容易触发,因为数值型特征顺序错乱只是数值错位,模型照样能算。解决方案是特征处理代码统一用字典做输入,传到模型内部再显式排序。
第三个坑是未知类别值的处理。训练集里某个类别特征只有四个枚举值,线上突然出现一个训练集没见过的值,如果特征编码器不支持未知类别,直接崩掉。一定要在编码阶段预留unseen标签,凡是没见过的值统一映射到unseen。
第四个坑是长期不用的模型被遗忘。模型跑得好好的,半年没人管,突然数据漂移严重,业务指标崩了才有人发现。监控告警要年初就配好,定期演练,不是上线时才临时看的。
第五个坑是压测时没有模拟真实流量分布。压测脚本用的数据全是训练集里的干净样本,线上来的全是脏数据,压测成绩漂亮,线上直接跪。压测请求要混合线上真实日志的采样数据,才能暴露真实瓶颈。
6.2 排查思路与工具
排查线上模型问题,我的思路是自底向上:先确认服务和资源没有异常,再回放日志定位是哪些请求出了问题,接着对比线上特征与训练特征分布,最后才怀疑模型本身。
工具层面,日志系统我用ELK(Elasticsearch、Logstash、Kibana),链路追踪用Jaeger,指标监控用Prometheus加Grafana。专门面向机器学习的监测,可以加一套Evidently AI做漂移检测,或者用NannyML做无真值场景下的性能监控。
真值回传这件事值得一提。很多模型预测之后,真实结果要几天甚至几周才能拿到,比如推荐系统里用户是否会点进页面。这时候可以用“延迟真值”的方式做模型反馈,把预测结果和事件结果关联起来,定期计算准召。这套数据是模型迭代最宝贵的基础设施,一开始就要设计好埋点,等业务需要的时候再补,成本会高很多。
写在最后的一个实战建议
如果你现在正准备从零开始搭AI工程,不要一上来就买一堆昂贵平台。先把最朴素的数据版本管理、配置化训练、监控告警这套基本功做扎实,我的经验是,业务量没有起来之前,过于复杂的架构反而拖慢节奏。我的习惯是先用开源工具把全链路跑通,等确实遇到瓶颈,再有针对性地引入企业级方案。这套“从零到一”的路径,值得踩的坑我都替你踩过了,你上手时少走弯路,就值回这次阅读了。