经常有人拿着训练好的模型跑过来跟我说:“代码能跑,准确率也还行,帮我部署上线吧”。结果我一看,没有版本管理,没有数据记录,代码里的随机种子没固定,推理时的数据预处理跟训练时用的完全是两套逻辑。这种状态离“上线”其实还有十万八千里。
“ai-engineering”这个词这两年被反复提及,但多数人理解的AI工程还是“训练模型”。真正的AI工程,是从需求定义开始,到数据建设、模型训练、部署上线、监控迭代的完整闭环。我做了几年AI落地相关的工作,最大的体感就是:算法能力只占项目成功的很小一部分,更多精力都耗在了那些“看起来不起眼”的工程环节上。
这篇文章,就围绕“ai-engineering-from-scratch”这条主线,把从零开始落地一个AI项目最常遇到的环节、最容易踩的坑、以及我实际验证过的做法,完整地过一遍。适合正在从“会训练模型”走向“能交付系统”的开发者,也适合刚立项、准备带AI项目的技术负责人参考。
1. 先想清楚一件事:AI工程和做算法根本不是同一件事
很多从零开始的项目,第一步就错了——错在把“AI工程”理解成了“算法调优”。等到模型训练出来才发现,周围配套的东西一样都没有:数据没有版本,评估指标说不清,上线方案没想好。这不是个案,是普遍现象。
1.1 两种“从零开始”:自学者与项目负责人的路完全不同
我见过两类人都在聊“从零开始学AI工程”。一类是自学入门者,他们理解的从零是“从线性代数、Python语法开始”;另一类是已经能跑通训练脚本、正要往系统化方向走的人,他们需要的从零是“把项目从头到尾做扎实”。
这篇文章主要面向第二类人,但第一类人同样能从中找到完整的路径感。原因很简单:AI工程不是算法研究的下半场,而是一条独立的知识线,只是恰好用到了一些算法知识而已。
举个例子。训练阶段你关心的是“这个batch的loss有没有降”,工程阶段你关心的是“模型上线后,凌晨三点的推理请求失败率为什么突然飙升”。前者是实验思维,后者是系统思维。这两个思维模式差异巨大,但学校也好、大多数教程也好,只教你前者,后者全靠项目实战自己攒。
1.2 需求澄清比模型选型重要十倍
从零开始的项目,最容易出现的情况是:需求方说“我要一个人脸识别系统”,等做完了才发现他要的其实是“对特定角度的闸机摄像头画面做人员身份核验”,压力、角度、遮挡、光线,每个维度都跟“通用人脸识别”不一样。
我在每个项目启动前,都会花大量时间做需求澄清。不只是问“你想解决什么问题”,还要问:
- 这个问题的“坏结果”是怎么定义的?漏报更严重还是误报更严重?
- 当前人工处理流程是什么?AI介入后想替代哪一部分、保留哪一部分?
- 可用的数据长什么样?有多少?谁标注?标注标准谁说了算?
- 系统跑在什么环境里?算力、网络、并发量有没有预期?
这里有一条我的经验:凡是需求方说“先做出来看看”的,一定要追问一句“看什么、用什么标准看”。很多人不是不想说清楚,是没想过这个问题。你帮他澄清的过程,本身就是工程交付的一部分。
1.3 定义“什么算成功”时,要同时定义“什么算失败”
工程和科研还有一个重要区别:科研看的是“在某些 benchmark 上刷到多少分”,工程看的是“在真实约束下,能不能稳定产生价值”。所以,项目启动阶段就要把成功条件量化。比如:
- 识别准确率不低于某个值,同时在指定场景下漏报率不超过多少;
- 单次推理延迟不超过多少毫秒,达到这个延迟的并发上限又是多少;
- 标注数据达到什么规模、覆盖哪些边界情况才能算“可上线”;
- 系统的“失败模式”是什么——是偏向保守(宁可不识别也不要错),还是偏向激进(偶尔错一次可以接受)。
这些定义越细,后面的弯路越少。我见过太多项目死在“准确率90%”这种模糊目标上:90%是离线测试的90%,还是线上实时的90%?包不包括那5%根本无法预处理的脏数据?这些不定义清楚,模型训练得再好,也走不到交付阶段。
2. 数据先行:从零项目最耗时、也最容易被低估的环节
如果说AI工程有一个“新手不重视、老手天天提醒”的环节,那一定是数据工程。模型训练你可能两周就能跑通一个baseline,但一份能支撑上线的数据集,从采集、清洗、标注到质检,往往要花掉整个项目三分之二的时间。这不是说算法不重要,而是说算法的上限从第一天起就被数据锁死了。
2.1 数据获取:不要迷信“爬数据”,先想清楚业务闭环
很多从零项目起步时,第一反应是去网上爬数据。但实际做过一次就知道,爬来的公开数据在真实场景里往往水土不服——domain gap大得离谱。我做过一个工业质检的项目,公开数据集上效果不错的模型,到了产线实测,准确率直接掉下来二十多个点。原因就是公开数据的拍摄角度、光照、缺陷形态跟产线真实情况完全不一致。
正确的做法是先跑通业务闭环,再谈扩充数据。具体来说:
- 第一版数据必须来自真实使用场景,哪怕只有几百条,先把流程走通;
- 找领域专家一起确定“哪些是典型样本、哪些是边界样本”,而不是让不懂业务的人盲目采集;
- 给数据打上“来源标签”,线上跟线下的数据分开管理,方便后面做domain adaptation时溯源。
我自己的习惯是,数据采集阶段就在脚本里把采集环境、设备信息、时间戳写进文件命名或者元数据里。这看起来麻烦,但后面做数据分析和模型调优时,你会发现这些信息是救命稻草。
2.2 标注规范:漏标和错标,是两种完全不同的毒药
标注这件事,表面上考验的是“人海战术”,实际上考验的是“标准设计”。我见过最典型的问题就是:标注人员凭个人理解标数据,同一个概念,三个标了三种样子。等模型训出来,学到的全是一堆互相矛盾的模式,性能拉不上去还找不到原因。
好的标注规范,至少要包含三个部分:
- 定义明确:每个类别的边界是什么、包含什么、不包含什么,要给出正例和反例;
- 边界情况处理规则:模糊样本怎么标、不确定时怎么办、是否设置“无法判断”选项;
- 抽检与仲裁机制:一定比例的标注结果要经过二次审核,争议样本要有仲裁人拍板。
另外一个容易被忽略的点是:标注规范一定要跟模型评估指标联动。如果生产环境里“漏报”代价更大,标注时就应该对“难样本”更敏感,宁可多标一些模糊的,也不要为了图省事一刀切。我通常会让标注组跟算法组坐在一起过一轮数据,双方对齐“什么算正例、什么算负例”的口径,后面可以省掉大量返工。
2.3 数据质检:用统计手段,而不是靠肉眼
数据量小的时候,人眼检查没问题;数据量一旦上来,“凭感觉判断数据质量”就是天方夜谭了。我常用的方法是,在每个批次的数据集上跑一遍基础统计:
- 类别的数量分布,确认有没有个别类别少得离谱;
- 标签的信度一致性,抽样让两个人标同一批数据,算一下一致性系数(比如Cohen‘s Kappa),低于阈值的标注员需要回炉培训;
- 特征维度的分布,比如图像的平均亮度、对比度、分辨率,文本的平均长度、词汇覆盖,一旦发现分布异常,优先怀疑采集或者预处理环节出了问题。
踩过坑之后我还养成了一个习惯:给每个版本的数据集写一份“数据报告”。里面记录该版本的数据量、类别分布、来源分布、已知问题等。这看起来是额外工作量,但后面做实验对比,你根本想不起三个月前某个版本的数据里多了一批重复样本,报告会帮你避掉大量“为什么效果不如从前”的灵异事件。
3. 基线优先:第一版模型不是用来刷分的,是用来建立评测基线的
在AI项目里,我见过最多的时间浪费发生在同一个地方:拿到数据后,团队直接扎进模型调参的深海,一调就是几周甚至几个月。而真正的工程节奏应该是——第一天就有一个端到端可运行的baseline,用最快的速度把整个链路打通,让后续所有实验都有明确的对比基准。
3.1 第一版模型:丑不重要,跑通才重要
我做项目有一条铁律:第一版模型绝对不上什么高大上的结构,能跑通就行。甚至可以直接先用一个简单的逻辑回归或者线性模型来摸底,再换到稍微强一点的结构。目的是检验数据管线是否通、评估代码是否对、训练和推理路径是否一致。
不要小看这个看起来“没有技术含量”的baseline。它的价值在于,把“项目重难点”从“算法创新”拉回到“工程打通”。等你发现数据读入、预处理、eval脚本、日志记录这些环节全都顺了,再逐步提升模型复杂度,这时候每一点提升都能稳定复现,才是真正有效率的工作方式。
我实际跑过的一个项目里,第一版视模型大概用了不到两天时间:一个预训练模型加一个简单的分类头,离线指标在当时看来平平无奇。但正是这个“平平无奇”的版本,暴露了整个推理管线里大部分问题:数据归一化不对、batch size对结果的影响、eval时shuffle没关导致指标虚高……这些问题如果直接从“高性能模型”开始调,你根本分不清模型问题跟工程问题。
3.2 评测集怎么建,决定了你将来的“性能天花板”
评测集的重要性,怎么强调都不过分。现实情况是:很多项目把所有的数据揉在一起训,再用同一批数据评估,自己骗自己。正确的做法是,在项目初期就划好三块数据:训练集、验证集、测试集。划分的原则只有一个:测试集要尽可能模拟真实上线后的数据分布。
这里有三个具体建议:
- 测试集一定要留到最后一刻才用,绝不要用测试集做调参依据;
- 划分时尽量按“来源”划分,而不是随机打散——比如用户A的数据进训练集,用户B的数据进测试集,这样能检验模型的泛化能力;
- 测试集里要有计划地加入一部分“脏数据”:低分辨率、模糊、遮挡、极端光照等。这些才是真实场景里的常态,同时也是最容易让模型翻车的地方。
有人会问:数据本来就不多,再划出一块测试集,训练数据更少了怎么办?我的答案是:宁可用少一点数据训练,也一定要有独立的评测。因为数据少还有过拟合之外的办法可以缓解,但评测集垃圾,那整个项目就是盲人摸象,所有后续迭代都是白费。
3.3 指标体系的坑:单点指标会骗人
很多项目只盯着准确率(Accuracy)这一个指标,这是最容易被“虚假繁荣”蒙骗的做法。分类不平衡时,准确率会给出完全误导的结论:99%的负样本、1%的正样本,你全预测成负样本,准确率也有99%。
我通常会同时盯着一组指标:对分类任务,除了准确率还看查准率(Precision)、查全率(Recall)、F1值,严重不平衡的场景还要看PR曲线而不是ROC曲线;对检测任务,不同IoU阈值下的mAP分开看;对比试任务,看的是不同阈值下的误检率和漏检率曲线。核心思路是:把“这个模型好不好”拆成“它擅长什么、不擅长什么”。这样后面的模型改进才有明确方向。
以及,所有指标一定要跟业务成本挂钩。漏报率高的模型跟误报率高的模型,对业务的影响完全不同。搞清楚业务更接受哪种失败,再回头决定优化目标,这才是工程决策,而不是论文决策。
4. 训练调优的工程化手段:让每一次实验都有迹可循
模型训练这件事,入门者觉得是“炼丹”,做久了就会明白:真正的效率提升来自实验管理。你有没有经历过这种崩溃——三周前跑出来的那个效果不错的模型,代码还在,但当时的超参、数据版本、预处理细节全都想不起来了?我经历过,从那之后才开始老老实实做实验追踪。
4.1 从第一步就引入实验追踪工具,而不是等“项目复杂了再说”
现在的MLOps工具已经非常成熟。轻量一点可以用MLflow,团队协作性强一点可以用W&B或Neptune,不想自建基础设施的话,这些SaaS方案都是不错的选择。不管选哪个,核心要记录的内容是一致的:
- 代码版本(git commit hash);
- 数据版本(数据集编号或hash值);
- 超参数(学习率、batch size、优化器设置等);
- 模型结构信息(结构名称、关键配置);
- 训练日志(loss曲线、评估指标每轮的数值);
- 环境信息(Python版本、依赖包版本、CUDA版本)。
我自己用得多的是MLflow,因为它开源、轻量、本地部署也方便。关键是,从第一天跑第一个baseline开始就记录,不要等到项目复杂了再补。补记录这件事,基本只有第一次和永远不做的区别。
4.2 超参数调优:别用“手感调参”,用网格或贝叶斯做个小的搜索
说句不客气的话,大部分“手感调参”在统计效率上是极低的。因为人脑处理高维参数之间的关系本来就困难,再加上代码改动和数据变动混在一起,你根本不知道指标提升到底来自哪个变化。
我推荐的流程是:
- 先固定一个结构比较简单的baseline,确认数据管线是健康的;
- 用一个小规模的随机搜索或网格搜索,对学习率、batch size、优化器这几个最重要的超参做一轮快速扫描;
- 扫描的结果记录下来,找到相对最优的区间;
- 再在这个最优区间附近做精细搜索,同时逐步放开模型规模。
这里有一个重要提醒:超参搜索时,要在完全相同的训练数据、完全相同的预处理逻辑下进行。否则搜索结果根本没有可比性,浪费的计算量倒是很可观。
4.3 随机种子、可复现性,以及“看似相同实则不同”的代码陷阱
“可复现”是AI工程的基本素质,但很多人不以为意。随机种子的设置、数据加载器的shuffle逻辑、数据增强的随机性,甚至GPU的浮点运算顺序,都会导致结果差一点点。提升模型性能时,一点点波动可能让你做出错误判断。
我的做法是:
- 代码里统一固定随机种子(PyTorch里包括torch.manual_seed、numpy.random.seed,以及cudnn相关设置);
- 每次训练前记录数据集的校验和或行数,确保用的数据是同一个版本;
- 所有数据增强操作,在训练和推理时严格区分,推理阶段不进行任何随机增强;
- eval时关闭shuffle和drop_last,防止最后一批数据丢失或顺序影响指标。
这些都不是什么高深技术,但真的能让人少掉很多头发。我一度以为某个模型隔了一天复现出的结果不同是因为“玄学”,最后发现是eval脚本里忘了关shuffle。这种坑极其隐蔽,一旦出现,非常打击团队士气。
4.4 训练失败排查清单:先怀疑数据,再怀疑代码,最后怀疑模型
训练过程中loss不降、指标报错,很多人的第一反应是“模型结构设计错了”,于是开始改结构。我的经验恰恰相反:绝大多数训练异常,根源都在数据和代码。我总结了一张简单的排查清单,按顺序快速过:
- 数据标签是否错位?拿几个样本出来人工看看,确认图像和标签对得上;
- 预处理是否一致?训练时的归一化参数、resize尺寸,跟eval时是否用的是同一套代码;
- 数据加载是否正常?shuffle、batch大小、采样逻辑是否会引入bias;
- 数值稳定性是否存在?loss是否出现NaN,梯度是否爆炸,学习率是否过大;
- 模型是否有bug?把模型输出和期望输出做一遍等值或一致性检查。
只有在第5步真正发现问题时,才值得去动模型结构。否则,你在模型上打转的时间,大概率是在给数据管线的bug陪跑。
5. 部署只是开始:从“模型能跑”到“系统能扛”的跨越
模型训练好了,指标也好看了,接下来才是真正决定项目成败的环节——部署上线。这个阶段,考验的不再是你的算法能力,而是将模型嵌入一个完整技术体系的系统能力。
5.1 部署方式的选择:单体服务还是pipeline?
不少从零开始的项目,第一版部署形态就是一个单体的HTTP服务:模型加载进内存,接收请求、返回预测结果。这在简单场景下完全够用。但一旦任务变复杂,比如视频流要抽帧、多模型要级联、结果要规则后处理,单体服务会迅速变得难以维护。
我推荐的做法是:从第一版开始,就把推理链路拆成可独立测试的步骤。哪怕暂时部署在一起,代码结构也要按pipeline的方式组织。一个典型的推理pipeline可能包含:
- 输入校验与预处理模块;
- 模型推理模块(可能不止一个模型);
- 后处理与业务规则模块(把模型的原始输出映射到业务可用的结果,比如过滤掉低置信度的预测、合并重复检测框等);
- 结果落库与反馈模块。
这样拆分的好处是,任何一个模块出问题,都能快速定位和单独回归。上线初期可能觉得繁琐,但一旦系统进入迭代期,这种结构会带来极大的省心。
5.2 延迟、吞吐和成本:三个要一起看的指标
模型部署上线,除了离线评估指标,还要盯线上运行指标。我见过一个典型的翻车现场:模型离线指标很不错,但在线推理时,单次推理延迟达到了业务不能接受的2秒。原因很简单——离线测试时用的是批处理,线上是单条实时请求,负载差异巨大。
所以,联调阶段就必须明确以下几件事:
- 单次推理的P50/P95/P99延迟分别是多少,对应业务可接受的SLA是什么;
- 当前部署形态能支持的并发请求数是多少,压测到极限时是CPU先跑满还是GPU先跑满;
- 按业务预估的调用量,一月的推理成本大概是多少,是在预算范围内还是在失控边缘。
这些数据之间经常是此消彼长的关系:降延迟可能牺牲吞吐,升吞吐可能增加成本。项目负责人要做的不是“全都最好”,而是基于业务需求和成本预算,找一个可以接受的平衡点。
5.3 灰度发布和回滚:没有退路的系统,迟早会出事
上线这件事,最怕的不是出错,而是出了问题没有办法快速恢复。从零开始的项目很容易忽略这一点——一开始先保证“能跑”,出问题再说。但实际上,你只需要提前做两件小事,就能省掉很大的风险:
- 灰度发布:新模型可以先切一小部分流量过去,比如5%到10%,观察一段时间,确认指标稳定后再逐步放量;
- 版本回滚:保留上一个版本的模型文件和配置文件,一旦发现新版本出问题,可以一键切回老版本。
另外,强烈建议把模型文件跟代码仓库分开管理。模型文件动辄几个GB,塞进Git里会让仓库迅速膨胀,每次拉代码都是一种折磨。比较合理的做法是用模型仓库(比如简单一点,就放在对象存储里)管理模型版本,代码里只记录需要加载的模型版本号。这样配合灰度发布,你就能在“新模型有问题”和“代码回滚”之间做精准的操作。
5.4 推理接口的健壮性:输入校验不是小事
模型服务化之后,暴露给上游系统的就是一个API接口。你有没有想过这个问题:上游系统传入的数据,跟训练数据分布不一致时会发生什么?
我见过不少线上事故,就是上游传了一张空白图、一个超长文本或者一个异常格式的文件给模型服务,然后模型直接崩溃或者返回一个莫名其妙的结果。要避免这种情况,必须做三层防护:
- 请求入口做格式校验,不合法直接拒绝;
- 模型服务内部对输入内容做size、类型、范围校验;
- 后处理阶段对模型输出做兜底,给出“无法识别”之类的默认结果,而不是抛异常。
做这些防护,你不是在防御“敌人”,而是在防御真实系统里永远存在的“意外”。我的经验是,任何一个部署超过一个月的模型服务,最终遇到的最多的问题,不是模型能力不足,而是没想过输入会这么脏。
6. 上线之后的AI工程:反馈闭环比算法优化更重要
模型上线只是第一步,严格来说,只是AI工程长期迭代的起点。很多项目死在“上线即结束”的状态里——模型发布一次,后就再也没人管了。然后过了三个月,业务说“好像效果变差了”,你回头一看,线上数据已经和训练数据相差十万八千里,但没有任何机制提醒过你。
6.1 数据漂移:最安静的“效果杀手”
现实世界的数据分布一直在变化。用户的偏好会变,拍摄的设备会变,业务规则的调整也会让输入数据发生变化。这些变化单看每一天都很小,但累积起来,模型效果会肉眼可见地退化。
监控数据漂移不需要很复杂的系统,从最基础的统计开始就够了。我常用的做法是:
- 对关键特征做每日或每周的分布统计,跟训练集的分布做对比;
- 设置朴素的漂移预警规则,比如某特征的均值偏移超过几倍标准差就告警;
- 对线上预测结果做抽样人工复核,统计“模型高置信度但实际错误”的比例。
这套机制一旦跑起来,你就能在“效果已经明显变差”之前收到提醒,给自己留出从容的迭代时间窗。而不是等一线业务人员来投诉,才被动地排查问题。
6.2 坏样本回流:把线上错误变成训练燃料
AI系统跟传统软件最大的不同是,它可以通过“吃更多的数据”来变强。而最有效的数据来源,恰恰是线上产生的“坏样本”——那些模型预测错、用户反馈了问题的数据。
项目上线之后,我建议建一个**“坏样本回收管道”**:
- 线上系统自动捕获那些“低置信度”或者“人工纠偏后结果不一致”的样本;
- 定期人工审核这些样本,决定是加入训练集、加入增强集,还是判定为噪声丢弃;
- 新版本的数据集生成后,带着这批新增样本重新训练、重评估、灰度发布。
这个循环跑起来之后,系统才会产生复利效应:用得越久,数据越多,效果越好。反过来,如果不建设这个回路,那你的模型永远只能依赖项目初期的静态数据,效果只会越来越僵硬。
6.3 团队协作:AI工程是一个多角色配合的长期项目
最后想聊一下团队配置。AI工程跟“一个人炼丹”最大的区别在于,它要求多个角色之间稳定协作。最精简的配置,也需要有人懂业务(能定义问题和验收标准)、有人懂数据(能处理标注和质量)、有人懂算法(能建模调优)以及有人懂工程(能承载部署和运维)。
实际项目中,这四个角色往往有重合,一个人身兼数职也正常。但即便是一个人包办,也要在心里清晰地划分这些职责,因为它们的思维方式完全不同。业务分析师定义什么重要,数据工程师保证喂给模型的料是干净的,算法工程师找到用这些料做到最优的方式,平台工程师让这一切稳定地跑100天而不掉链子。
从零开始做一个AI项目,最大的体会就是:AI工程不是一个词、一个职位,而是一整套围绕模型生命周期的工程纪律。它要求你同时具备科学家的度量能力、工程师的系统能力,还要有一点点产品经理的业务嗅觉。这条路没有捷径,但每走一段,你对“模型怎么变成产品”的理解就会透一层。