如果你正在琢磨ai-engineering-from-scratch这几个字,我猜你多半是这两种情况之一:要么是算法出身,模型在 notebook 里跑得挺欢,一上生产环境就四处冒烟;要么是后端出身,想往 AI 方向走,但面对一堆框架、概念、工具链,有点不知道从哪里下口。
这个标题翻译过来就是“从零开始做 AI 工程”,它强调的不是从零手写一个 Transformer,也不是重新发明深度学习框架,而是指:把一个 AI 项目从“能跑通的实验”变成“能上线、能维护、能迭代的工程系统”的完整过程。这篇文章就是围绕这件事来写的,我会把从环境搭建、目录设计、数据处理、模型训练,到服务化部署、上线监控、问题排查的整条链路,按我实际做过的项目经验拆开讲清楚。
适合谁来读?如果你是刚接触 AI 工程的后端开发、想补工程能力的算法工程师、或者正在带一个 AI 项目从 POC 往生产走的同学,这篇内容能给你一个相对完整的参考框架。不预设你有深厚的机器学习基础,但需要你写过代码、装过环境、被依赖地狱折磨过,下面这些内容你读起来会特别有感觉。
1. 先搞清楚 AI 工程到底在解决什么问题,别急着写代码
很多人栽跟头不是因为代码写得烂,而是脑子里对“AI 工程”这四个字的理解太窄。以为模型训练完就结束了,实际上训练完成只是整个系统生命周期的起点。
1.1 “模型训练成功”和“系统上线能用”之间隔着一整条河
我见过太多这样的场景:算法同学在 Jupyter Notebook 里把模型调到了 95% 的准确率,兴冲冲地交给工程团队,结果上线之后发现——请求延迟 800 毫秒,并发一高就超时;模型每天吞进去的数据格式和训练时完全不一样;用户群体变了,模型预测的结果越来越偏;出了坏模型,没有回滚机制,只能紧急下掉整个功能。
这些问题没有一个是“模型训练”环节能解决的,它们全部属于AI 工程的范畴。
简单来说,AI 工程要处理的是模型之外的所有“脏活累活”:
- 数据怎么稳定地流入系统,怎么验证合法性
- 训练流程怎么可复现、可追踪、可回滚
- 模型怎么封装成服务,怎么扛住线上流量
- 线上的模型表现怎么监控,什么时候需要重新训练
- 整个链路怎么自动化,怎么降低出错的概率
用个生活化的类比:训练模型就像是在厨房里研究出一道新菜,AI 工程则是你要把这道菜开成连锁餐厅。后者要解决的问题包括供应链、后厨流程、出菜速度、食品安全、员工培训、顾客投诉处理。会做菜和会开餐厅,完全不是一回事。
很多团队做 AI 项目翻车,本质上是把“研究出一道菜”当成了“开一家餐厅”的全部。
1.2 什么样的项目适合“从零开始”做 AI 工程
不是所有 AI 项目都要从零开始搭一套完整工程体系,做之前先判断一下自己属于哪种情况。
适合从零动手做的情况有这么几类:
- 个人或小团队想完整掌握 AI 工程链路,不是为了应付某个具体业务,而是为了建立体系化的能力;
- 项目还处于 POC(概念验证)阶段,没有历史包袱,可以在工程结构上打好地基;
- 业务对稳定性、可迭代性有要求,不只是“能跑就行”,而是要长期维护和发展;
- 你正在做一个端到端的 AI 应用,比如一个推荐系统、一个智能客服、一个内容审核服务,需要处理真实的数据流和用户请求。
不适合从零做全套的情况也明显:如果你的团队已经有成熟的机器学习平台,直接用平台能力就好,没必要重复造轮子;或者你只是想快速验证一个算法的效果,那在 notebook 里跑就足够了,不用为了工程而工程。
我个人的建议是:即便只是做一个小项目,也值得用工程化的方式走一遍全流程。因为很多问题只有在你真正经历一次“从数据到上线”的完整闭环之后才会浮现出来。
1.3 能力模型与常见认知误区
既然是从零开始,就要清楚哪些能力是硬门槛,哪些是伪需求。
硬门槛是这些:
- 扎实的 Python 工程能力:函数模块化设计、异常处理、日志、测试,这些基本功决定你的代码能不能被别人维护,能不能在半年后还被自己看懂;
- 数据的敏感度:能发现数据里的异常、缺失、偏移,懂得验证数据质量的必要性;
- 基础的算法直觉:不要求你会手推公式,但要理解模型输入输出是什么、损失函数在优化什么、过拟合和欠拟合长什么样;
- 部署和排障能力:会写 Dockerfile、会看日志、会排查线上问题,这是 AI 工程和算法研究最大的区别。
伪需求和误区也很多,我挑常见的说:
- “必须从零手写模型才能叫 from scratch”——恰恰相反,工程上要尽可能复用成熟的模型架构和工具,你的精力应该花在数据、系统、稳定性上;
- “一定要精通所有深度学习框架”——不需要,把一种框架吃透,其他框架的概念是相通的;
- “等模型效果完美了再上线”——真实业务里,模型效果只要达到基线就可以上线,然后在反馈中迭代,追求完美只会让你永远无法上线。
记住这条主线:AI 工程的核心矛盾不是你模型的精度不够,而是整个系统的稳定性、可维护性、可迭代性不足。后面所有章节,都是围绕解决这些矛盾展开的。
2. 从零搭建工程骨架:目录、环境、数据版本管理
确定了方向之后,第一件具体的事就是搭骨架。这一步做得好,后面的开发和维护都会顺畅很多;做得随意,后面每改一步都像在拆一颗随时会炸的雷。
2.1 一个能落地的项目目录结构
我在经历过几次“代码和模型、数据混成一锅粥”的惨痛教训之后,形成了一个固定的目录结构,你可以直接参考:
ai-engineering-from-scratch/ ├── data/ │ ├── raw/ # 原始数据,只读,不修改 │ ├── processed/ # 清洗后的数据 │ └── features/ # 特征数据 ├── notebooks/ # 探索性分析的 notebook,编号命名 ├── src/ │ ├── data/ # 数据下载、清洗、验证 │ ├── features/ # 特征工程 │ ├── models/ # 模型定义、训练、评估 │ ├── serving/ # 模型服务化、API │ └── utils/ # 通用工具 ├── configs/ # 所有配置文件 ├── tests/ # 单元测试和集成测试 ├── scripts/ # 命令行脚本入口 ├── Dockerfile ├── pyproject.toml └── README.md这个结构不是拍脑袋定的,每一条都有它的逻辑。
data/下面分raw、processed、features三层,是为了管住数据流。原始数据永远只读不修改,所有清洗逻辑都通过代码生成 processed 和 features 数据,这样出了问题你能知道是在哪一步坏的,而不是对着一个被覆盖的原始文件欲哭无泪。
notebooks/只用来做探索性分析,不适合承载核心逻辑。原因很简单:notebook 的 cell 执行顺序可以乱跳,很容易出现“这个变量是哪来的”的诡异问题。我的习惯是:notebook 里验证完思路,把逻辑整理成src/下面的正式模块,notebook 里只留分析过程,不依赖它跑生产任务。
src/下面按数据、特征、模型、服务分模块,边界清晰。我见过把训练、预测、数据清洗全写在一个巨型文件里的项目,改一个功能要滚屏半天,还容易误伤别的逻辑。拆开之后,每个模块只负责一件事,测试也好写。
configs/单独拎出来,是为了做到“配置与代码分离”。超参数、路径、环境变量都放在配置文件里,不要硬编码在代码中。这样同一个代码库可以针对不同场景切换配置,而且不会因为改参数产生代码 diff。
2.2 环境与依赖管理:告别“在我电脑上是好的”
环境问题是我见过最多的新坑,而且它通常不是在开发阶段爆发的,是在别人要复现你的项目时爆发的。
“在我电脑上是好的”这句话,是 AI 工程里最危险的话之一。你用了某个依赖的某个版本,你的机器恰好配套,别人的机器不行;你昨天跑通了训练,今天更新了一个库,结果整个流程挂了。这些问题的根源都是依赖没有被锁定。
我现在的做法是:
- 使用
uv或conda管理 Python 环境,新建项目时第一件事就是创建虚拟环境,绝不直接用全局 Python; - 用
pyproject.toml管理项目依赖,并且锁定完整版本,不只是顶层依赖,传递依赖也要锁定; - GPU 环境下特别注意 CUDA、cuDNN、PyTorch 的版本匹配。这是整个环境搭建里最容易出问题的环节。
举个例子,你用 PyTorch,如果安装时不指定版本,很可能装到一个和你显卡驱动不匹配的 CUDA 版本,然后运行时直接报显存不可用。解决的办法是安装前先查清楚自己显卡支持的 CUDA 版本,再到 PyTorch 官网选择对应的安装命令。
另外一个容易被忽视的点:Docker 里的环境和本机环境要尽量保持一致。我见过本地跑得好好的代码,进了 Docker 就崩,原因就是 Docker 镜像里的系统库版本不对。你在做环境管理的第一个阶段,就要把“Docker 也是运行环境”这个意识刻进脑子里。
2.3 数据和模型的版本管理:光靠 Git 不够
代码可以用 Git 管,但数据文件和模型权重文件动辄几个 GB,用 Git 管会把仓库撑爆,而且 Git 本身也不适合管理二进制大文件的变更历史。
这里需要引入专门的数据版本管理工具,我用得比较多的是 DVC(Data Version Control)。
DVC 的核心逻辑很简单:它不把数据存在 Git 里,而是把数据的元信息和一个哈希值存在 Git 里,真实数据存在本地目录或远程存储(比如 S3、OSS、NAS)上。每次数据变更,DVC 会生成一个新的哈希版本,你在 Git 里看到的是一个指向这个哈希的指针。
实际操作大概是这样的:
# 初始化 DVC dvc init # 跟踪数据目录 dvc add data/processed # 关联远程存储 dvc remote add myremote /mnt/dvcstore # 推送数据到远程 dvc push # 数据变更后再次 add,Git 会记录新版本 dvc add data/processed git add data/processed.dvc git commit -m "update processed data"这样做的价值在于:代码、模型、数据三者的版本可以对应起来。你回滚到某个 Git commit,对应的数据和模型版本也跟着回滚,整个实验的可复现性大大增强。
模型也是一样,训练产生的权重文件用 DVC 管理,不要直接塞进 Git。有些团队用 Git LFS 管模型文件,也可以,但我个人觉得 DVC 和实验记录的配合更顺畅。
这里要强调一点:版本管理不是为了“多一个工具显得专业”,而是为了让你在几个月后还能知道“这个模型是什么数据、什么参数训练出来的”。没有版本管理,你对模型的每一次更新都是一次赌博。
3. 数据管道与模型训练:把“实验”变成“流程”
骨架搭好之后,开始往里面填核心内容。这部分我按数据管道和训练流程分开讲,因为它们在 AI 工程里遵循不同的逻辑。
3.1 数据处理:清洗、样本构造、切分中的安全红线
数据是整个 AI 工程里最不能想当然的环节。模型不行可以换架构重训,数据不行,模型再强也白搭。
数据处理的第一步是清洗:处理缺失值、去除重复样本、修正错误格式。但这里有个经验教训:不要在一开始就把清洗规则定得太死。真实数据里永远有你没见过的脏数据,所以清洗脚本要留好日志,记录每条规则删除了多少样本,这样上线后数据分布变化时你能快速定位原因。
第二步是样本构造。这一步的坑主要在特征泄漏——你在构造特征时,不小心把未来信息或者标签信息泄漏进了训练数据。我见过一个真实案例:一个预测用户流失的模型,特征里包含了“用户是否已经提交退订工单”这个字段,而退订工单本来就是流失的结果。模型上线后准确率奇高,但实际上是“用结果预测结果”,根本没有任何业务价值。
第三步是数据切分。常规的分类问题随机切分训练集、验证集、测试集没问题,但如果你的数据是时间序列,或者样本之间存在关联(比如同一个用户的多条行为记录),就必须谨慎切分。时间序列数据要按时间切分,不能用未来的数据训练过去的样本;用户维度的数据,要确保同一个用户的所有记录只出现在一个集合里,否则会严重高估模型效果。
我在处理这类问题时的标准做法是:
- 切分前先检查是否有 ID 列需要分组建组;
- 时间序列用时间戳排序后按前 80% / 后 20% 切分;
- 对验证集和测试集做一次特征分布检查,确认切分没有引入系统性偏差。
3.2 训练流程工程化:配置化、可复现、自动记录
训练流程的工程化,核心目标就三个词:可复现、可追溯、可自动。
可复现的第一步是固定随机种子。模型训练里有很多随机性来源:参数初始化、数据打乱顺序、dropout 等。固定种子至少保证同一个代码和同一次数据能跑出同样的结果。
第二步是把超参数放入配置文件。我习惯用 YAML 文件来管理,比如:
model: name: resnet50 pretrained: true training: batch_size: 32 learning_rate: 0.001 epochs: 50 optimizer: adam seed: 42 data: train_path: data/processed/train.parquet val_path: data/processed/val.parquet代码里只读取配置文件,不硬编码任何超参数。这样每次实验的配置本身就是一个可记录的实体,而不是散落在代码里的十几个魔法数字。
第三步是实验记录。我用 MLflow 来记录每次实验的配置、指标、模型产物。每次训练跑完,自动记录 accuracy、loss 等指标,并保存模型文件。这样做最大的好处是:你可以横向比较几十次实验,而不是靠记忆“上次那个参数好像是 0.001 还是 0.0001?”没有实验记录,调参就是盲人摸象,而有了记录,你才能发现“学习率 0.001 的时候模型收敛最好”这类真正有价值的结论。
训练过程的代码层面,有几个工程习惯特别值得养成:
- 训练循环中定期保存检查点(checkpoint),防止中途挂了要从头来过;
- 使用早停机制(early stopping),验证集指标连续多轮不提升就停止训练,既省时间又防止过拟合;
- 训练日志要包含关键指标和耗时信息,方便判断训练是否正常。
3.3 评估不只是看准确率:上线前要过的几道关
很多人对模型评估的理解只有一个指标:准确率。但在真实业务里,准确率几乎是误导性最强的指标之一。
一个直观的例子:如果你的业务里 95% 的样本是负样本,一个“永远预测为负”的模型准确率就有 95%。看起来很高,但这个模型毫无价值。
所以我评估模型时会同时看多个维度:
- 基础指标:对分类看精确率、召回率、F1;对回归看 MAE、RMSE;对排序看 NDCG、MAP;
- 业务指标:比如这个模型的预测结果如果落入“高置信且高错误”的区间,对业务的影响是什么;
- 切片分析:按用户群体、时间段、地区等维度拆分评估指标。整体指标再好,某个子群体崩溃也不行;
- 边界测试:输入一些异常值、空值、格式错误的数据,看模型输出是否合理。
做切片分析是很多团队忽略的重头戏。我印象很深的一个案例:一个内容推荐模型整体指标不错,但按内容品类一看,新发布的内容几乎得不到曝光,因为模型在新内容上缺乏历史行为数据,预测置信度低,系统就不敢推它。这个问题的发现完全依赖切片分析,整体指标是发现不了的。
上线前的最后一道关,是做一个“影子测试”或者“离线回放”:把历史请求日志重新喂给新模型,对比新旧模型的输出差异。差异过大说明行为变化剧烈,要再仔细检查,而不是直接上线替换。
4. 部署上线:模型从“实验品”变成“服务”
训练评估完,模型还是一个躺在磁盘上的文件,离“能被用户用到”还差一大截。部署这一步,是把模型变成服务的关键。
4.1 模型服务化的选型与取舍
模型服务化,最简单的说法就是“把模型包成一个 API,让业务方可以调用”。但实现方式有讲究,选择取决于你的场景。
如果只是一个内部小规模调用、模型推理延迟要求不苛刻,直接用 FastAPI 写一个接口,加载模型做预测就够了。这种方式胜在简单直接,代码少,排障方便。
如果面对高并发、多模型共用一套 GPU、或者需要动态批处理等高级能力,就别自己硬造轮子了,用专门的推理服务框架更稳。我自己在实际项目里接触过的有 NVIDIA Triton Inference Server、TorchServe、Ray Serve,各自有各自的特点:
- Triton 支持多种框架、动态批处理、并发模型管理,性能优化空间大,适合复杂场景;
- TorchServe 和 PyTorch 生态结合紧密,适合以 PyTorch 为主的团队;
- Ray Serve 更适合模型和业务逻辑混合编排的场景,能结合 Ray 的分布式能力。
从零开始做 AI 工程,我的建议是:第一版先用 FastAPI 跑通,后续遇到性能瓶颈再迁移到专门推理框架。不要一上来就上重框架,否则调试成本和心智负担会把你拖死在部署阶段。
无论用哪种方式,API 设计都有几个共性注意点:
- 输入输出用 JSON 结构,并定义好 schema,配合 Pydantic 做校验,非法请求在入口处就拦截掉;
- 设置合理的超时时间和重试策略,模型推理超时不能无脑重试,否则雪崩时会把系统打挂;
- 接口要有优雅的错误返回,不要直接把模型报错堆栈抛给调用方。
4.2 推理优化:延迟、吞吐与资源消耗的平衡
部署上线后,你很快会面对一个新问题:模型推理太慢,或者太吃资源。
要优化推理,先得知道瓶颈在哪。常规手段是测:打个接口,看 P50/P95/P99 延迟,看错了多少,看 GPU 利用率。没有数据支撑的优化都是瞎猜。
实际中比较常用的推理优化手段有这么几类:
- 量化:把模型权重从 FP32 降到 FP16 或 INT8,可以大幅减少显存占用、加快推理速度。代价是精度会有轻微损失,需要在优化后重新评估指标,确保损失在可接受范围内;
- 批处理(batching):把多个请求攒在一起,一次推理处理一批,充分利用 GPU 的并行能力。这个手段在单请求延迟不敏感的场景下效果显著;
- 模型裁剪与蒸馏:用更小的模型结构或用大模型蒸馏出小模型,减小计算量,保留大部分效果;
- 缓存:如果存在重复请求,比如同一张图片多次请求分类结果,可以把结果缓存起来,直接命中缓存,极大降低压力。
我自己在项目里最常用的组合是“FP16 量化 + 动态批处理”,这两个手段堆上去,通常能把吞吐提升 2 到 4 倍,而且实现成本不算太高。
这里要泼一盆冷水:不要为了优化而优化。如果当前延迟已经满足业务要求,资源消耗也正常,就不必折腾。优化是有成本的,每一次量化、裁剪都可能带来精度变化,都需要重新回归验证。我把“尽量用最朴素的方式满足需求”当成工程信仰,在 AI 工程里也一样。
4.3 容器化与发布流程
模型服务化之后,接着要做的是把它容器化,并设计一套可靠的发布流程。
Dockerfile 的编写有几个关键点,直接影响生产环境的稳定性:
第一,用多阶段构建。第一阶段装全量依赖、编译或下载模型;第二阶段只复制运行所需的文件和依赖,这样最终镜像体积小,启动快,攻击面也小。
第二,容器内用非 root 用户运行。默认 root 运行服务,万一被攻破,攻击者直接拿到容器最高权限,这是非常危险的事。用普通用户跑服务是基本安全素养。
第三,依赖安装要利用缓存层。把pyproject.toml先复制进镜像,安装完依赖再把代码复制进去,这样每次代码变更只需要重建上层,构建速度快不少。
第四,模型文件不要打进镜像。模型文件做成了几个 GB,如果打进镜像,每次构建和推送都是折磨。我的一般做法是把模型文件放在共享存储或者对象存储中,容器启动时通过 DVC pull 或直接从远程下载到本地缓存目录。
发布流程上,模型服务不是一个“上线就不管”的静态系统。我强烈建议上线时做蓝绿部署或金丝雀发布:
- 蓝绿部署:新版本和旧版本同时存在,流量全部切到新版本前,先验证新版本的健康;
- 金丝雀发布:先把小部分流量导到新版本,观察一段时间的错误率和延迟,确认无误再全量切换。
这套流程的核心目的是:保留一条退路。模型更新是有风险的,你永远不知道新模型在真实流量上会表现出什么超出预期的行为。有了回滚机制,出问题就能秒级恢复,不会演变成事故。
健康检查也必须有。K8s 里配置 liveness 和 readiness 探针,分别判断进程是否存活以及是否能服务。如果模型没有加载成功,进程还活着但不 ready,这时候负载均衡就不该往这个实例发流量。
5. MLOps:上线不是终点,监控与迭代才是常态
模型上线只是另一个开始。我相信很多同学听过“模型会腐烂”这句话,意思是随着时间和环境变化,模型的效果会逐渐下降。MLOps 的职责,就是对抗这种腐烂。
5.1 监控体系:系统指标和模型指标一个都不能少
上线后的监控,至少要分两个层面。
第一个层面是系统监控:接口延迟、错误率、QPS、CPU/GPU 利用率、显存占用。这些指标决定服务是否健康。我会配置告警规则,比如 P99 延迟超过阈值持续 5 分钟就告警,错误率超过 1% 就告警,GPU 利用率接近 100% 也要告警以免过载。系统挂了还蒙在鼓里是最糟糕的状态。
第二个层面是模型监控,这才是 AI 工程和普通后端的区别所在。系统还活着不代表模型还好使。模型监控通常关注两个东西:
- 预测分布漂移:比如一个分类模型预测“正类”的概率均值从 0.3 慢慢涨到了 0.7,这说明模型的行为在变化,可能输入数据变了,也可能模型本身有问题;
- 特征分布漂移:入模特征的均值、方差、缺失率、取值分布和训练时差别过大,模型效果必然受影响。常见检测手段是 PSI(群体稳定性指标,Population Stability Index),也可以用 KS 检验。
我自己在监控平台上的数据,通常会包括这几个维度:输入数据量、特征缺失率、特征均值、预测分数分位数。这些指标按天聚合,设定基线,一旦偏离过多就触发告警。
日志和链路追踪也要做。每次推理请求记录输入摘要、预测结果、耗时、模型版本,这样当业务方来问“为什么这个用户被推荐了这条内容”,你可以拉出当时的请求日志,而不是一脸茫然。
5.2 模型迭代机制:数据回流、重训练与自动评估
监控发现问题之后怎么办?这就进入模型迭代的环节。
第一步是数据回流。线上服务积累的推理日志、用户反馈、业务结果数据,要定期回流到数据管道,成为下一轮训练的原料。很多模型的提升空间不在算法,而在数据——你有了更多真实场景下的样本,效果自然会涨。
第二步是判断是否需要重训练。触发重训练的条件可以是:
- 监控发现特征分布或预测分布发生显著漂移;
- 业务指标下降,比如转化率下滑、用户投诉增加;
- 有时间和周期性规律,比如某些业务每周或每月周期性更新;
- 离线评估新模型指标显著优于当前在用模型。
第三步是设计安全的替换机制。我在团队里推动的方式是:新模型训练完成后,先在离线数据集上评估,再进入 shadow 模式(影子模式)——把新模型的预测结果和旧模型并行跑,但不影响线上决策,只记录差异。观察几天,确认新模型没有明显异常,再通过金丝雀逐步放量。
这个流程看起来繁琐,但它的价值是让每一次模型更新都变得可预期、可回滚。我见过太多团队用“直接替换”的方式更新模型,结果新模型上线第二天出了大问题,想回滚都找不到旧模型的文件。模型版本管理(前面 DVC 的部分)在此时会救你的命。
这里还要提一个容易忽略的点:重训练不能盲目使用所有回流数据。线上数据的分布本身可能存在偏差,比如某些极端样本被过度采样或欠采样。直接拿回流数据训练,模型可能越训越偏。我一般会在训练前做一次数据分布对比,确认新增数据和原有训练集分布差异不大,再用到训练中。
6. 常见问题与排查技巧实录
AI 工程整个链路长、环节多,出问题的概率非常高。这一节我把自己实际踩过的坑整理成速查表,每一类问题的描述、排查思路、常用处理手段都有,相信你能直接用上。
6.1 环境与依赖问题速查
| 问题场景 | 可能原因 | 排查思路与处理建议 |
|---|---|---|
| 模型加载报 CUDA error | CUDA/PyTorch/显卡驱动版本不匹配 | 先查nvidia-smi看驱动支持的 CUDA 版本,再按对应版本安装 PyTorch |
| 本地能跑,容器里跑不起来 | 镜像缺系统依赖或环境变量未设置 | 逐项检查依赖库,Dockerfile 中显式安装,日志里定位到具体报错的库 |
| 训练一半内存溢出 | 数据加载方式不当或 batch size 过大 | 改用 DataLoader 分批次加载,或调小 batch size,检查是否有多余的大对象占内存 |
| 依赖冲突,装 A 把 B 搞坏了 | 没有锁定版本或直接改了全局环境 | 用虚拟环境隔离,锁定完整依赖版本,不要用全局 Python 跑项目 |
| 别人的机器上复现不了你的结果 | 随机种子没固定,或依赖版本不一致 | 固定种子,锁定依赖,记录完整实验配置 |
补充一个技巧:不管什么问题,先看日志再查代码。AI 工程链路长,问题的根因常常不在你以为是的地方。比如模型推理慢,可能不是你模型太大,而是数据加载环节发生磁盘 I/O 争抢。日志能帮你缩小范围,而不是满世界找原因。
6.2 数据与训练问题速查
| 问题场景 | 可能原因 | 排查思路与处理建议 |
|---|---|---|
| 训练集效果很好,测试集一塌糊涂 | 过拟合或数据泄漏 | 先查特征里是否有泄漏,再考虑正则化、数据增强、模型简化 |
| 验证集指标不稳定,一次高一次低 | 数据切分不当或样本量太少 | 用分层采样或交叉验证;检查切分时是否有分组泄漏 |
| 模型收敛慢或不收敛 | 学习率不合适、数据未归一化、梯度异常 | 从学习率排查开始,尝试用 warmup 和余弦退火;检查特征尺度 |
| 特征分布上线前后差异大 | 上游数据发生变化 | 用 PSI 逐特征检测,定位变化特征,确认是否业务逻辑变更导致 |
| 类别不均衡导致模型全部预测多数类 | 分类阈值设置不当或损失函数不合适 | 评估时用 PR 曲线代替 ROC;调分类阈值;用加权损失或过采样/欠采样 |
关于数据泄漏,我再多说一句:它的隐蔽性和破坏性都很强。特征泄漏通常不是你在代码里主动创造的,而是不经意间把不该看到的信息放进了训练数据。一条经验是:每构造一个特征,都问一句“这个特征在预测时点能拿到吗?”,能拿到才合法,拿不到就是泄漏,哪怕它的预测力再强也要去掉。
6.3 上线与运维问题速查
| 问题场景 | 可能原因 | 排查思路与处理建议 |
|---|---|---|
| 上线后接口超时严重 | 模型推理慢或并发处理能力不足 | 先量化瓶颈,压测找最大 QPS,再上量化和批处理优化 |
| 冷启动慢,一扩容就超时 | 模型加载耗时过长没有预热 | 启动时主动加载模型 + 预热推理一次,准备就绪后再接流量 |
| GPU 利用率低但延迟高 | 请求过于稀疏,没有批处理 | 开启动态批处理,或者调整服务并发模型,让 GPU 有足够请求吃满 |
| 新模型上线后业务反馈变差 | 模型行为与旧模型差异大 | 检查新旧模型输出分布对比,确认是否异常;必要时回滚到旧版本 |
| 监控发现特征分布漂移 | 上游数据源变更或业务环境变化 | 定位漂移特征,同时确认是否触发重训练条件,进入迭代流程 |
运维经验里我想特别强调一件事:任何变更都要有回滚计划,包括数据管道变更。上线新模型有风险,改清洗逻辑、改特征计算方式同样有风险。我在负责一个系统之后,给自己定了一条铁律——凡是变更,先想“怎么退回去”,再想“怎么改上去”。这条铁律让我避免了好几次重大事故。
写在最后的小建议
从零开始做 AI 工程,这一年多我最大的体会是:这个领域拼的不是谁模型调得好,而是谁的系统更稳、迭代更快、问题定位更准。很多高深的理论在实际工程里反而不常用,真正消耗时间的是环境配置、数据处理、依赖管理、延迟优化这些“不性感”的工作,但正是这些工作的扎实程度,决定了一个 AI 项目能否长期稳定地跑下去。
最后分享一个我反复验证有效的方法:无论多复杂的 AI 项目,先别急着完善所有细节,用最快的方式跑通一个最简版本的端到端链路——哪怕模型效果很差、数据处理很粗糙,先让数据从源头流到模型输出,再流到接口返回。这个最小闭环跑通之后,你再回头逐个环节优化,每一步都有明确的验证方式,不会迷失在细节里。反之,如果你一上来就想把每个环节做到完美,大概率会在某个环节卡住很长时间,整体进度遥遥无期。
AI 工程是一条可以越走越宽的路径。把地基打牢了,后面无论是换更好的模型、接入更多数据,还是扩展新的业务场景,都会顺很多。希望这篇文章能给你一张还算清晰的地图,剩下的路,还是得自己一步一步走。