前阵子有朋友问我ai-engineering 到底要怎么 from scratch 学起。他刚跑通一个图像分类模型也看过不少教程但真到了要把模型交给业务方用的阶段整个人是懵的模型文件扔给后端就完事了吗数据变了怎么办训练和推理环境不一致怎么办这些都是典型的“从零开始做AI工程”会撞上的墙。我按自己踩过的坑把整个链路从头到尾梳理了一遍希望能帮你少走几个月的弯路。很多人把“AI工程师”理解成“会训练模型的人”这其实只占了一小半。真正值钱的另一半是让模型在真实系统里稳定、可复现、可观测、可迭代地跑起来。这套能力不是靠堆模型结构学来的而是要一步步从数据版本、代码结构、训练流程、部署方案和监控机制里磨出来。这篇文章适合两种读者一种是想转行做AI工程但还没找到抓手的新手另一种是已经能把模型跑通但总觉得生产环境处处是坑的开发。1. 先想清楚AI工程到底在解决什么问题1.1 从“跑通一个模型”到“上线一套系统”我见过太多人把“训练完模型”当成项目终点。模型在 Notebook 里跑出 0.95 的准确率欢天喜地地保存成model_v1.pth结果到了线上立刻原形毕露。问题往往不在模型本身而在它周围那圈看不见的工程系统。举个例子。你做一个垃圾图片识别模型离线测试表现很好但上线后准确率暴跌。原因可能是线上图片的拍摄角度、压缩比例、光线条件和你训练时完全不一样。这不是模型结构的问题而是数据分布变了。要发现这个问题你需要记录线上图片的分辨率分布、颜色直方图还要有监控系统在生产环境里持续对比这些指标。这就是AI工程的核心让模型在一个真实、动态、非理想的环境里依然能稳定发挥。所以 AI 工程本质上是在解决三类问题第一怎么让训练过程可复现不会因为换一台机器、换一个环境就“薛定谔的精度”第二怎么让模型可以重复、可靠地部署到生产环境并对外提供接口第三怎么让模型上线后可以被观测、被诊断、被回滚出了问题能快速定位是代码问题、数据问题还是环境问题。从零开始搭这套东西比单纯调参要复杂但它是从“算法demo”走向“可用产品”的唯一路径。1.2 工程化与科研 prototype 的区别不少从学校或竞赛出来的人习惯用科研原型的方式写代码一个 Notebook 从上到下跑完变量随意覆盖数据集直接读固定路径超参写在代码里。这种方式在探索阶段没问题但交付给生产环境就是灾难。科研原型的目标通常是“在固定数据集上拿到最好的指标”而工程化的目标是“长期、稳定、低成本地支撑业务”。两者的差异体现在很多细节上。科研代码只要跑一次拿到结果工程代码要反复跑、别人也能跑科研数据是静态的工程数据是每天增长的科研评估是一次性的工程评估是持续性的科研不关心接口延迟工程要关注 99 分位耗时科研代码只要作者能看懂工程代码要团队所有人都敢改。维度科研原型AI工程代码组织Notebook 贯穿逻辑耦合模块化数据/训练/部署分离数据管理固定文件手动拷贝版本化、可回滚、有Schema校验环境依赖装到当前环境即可依赖锁定环境可重建评估方式一次性离线指标离线指标在线指标持续监控失败处理重新跑一遍有日志、告警、自动回滚机制我个人的体会是如果你的模型只是给自己看看效果那怎么折腾都行。但只要别人要基于你的结果做决策或者模型要接受真实流量就必须切换成工程化思维。这也是“from scratch”的真正含义把整个技术栈的每一个环节都亲手搭一遍理解它们各自的边界和配合方式而不是复制一个模板了事。2. 从零搭建AI工程的基础设施2.1 项目目录怎么组织才不失控工程化的第一课是给项目建一个清晰的目录结构。很多“模型训练代码”让人崩溃就是因为所有东西都堆在一起.py文件不好好分层数据和脚本混在一个目录配置文件塞在utils里。我推荐一个长期实践下来比较好用的结构project/ ├── configs/ # 实验配置YAML或JSON ├── data/ │ ├── raw/ # 原始数据只读 │ ├── processed/ # 清洗后的数据 │ └── external/ # 外部导入数据 ├── notebooks/ # 探索性分析不做核心逻辑 ├── src/ │ ├── data/ # 数据处理代码 │ ├── features/ # 特征工程 │ ├── models/ # 模型定义、训练逻辑 │ └── deploy/ # 服务化、预测接口 ├── models/ # 模型产物按版本存放 ├── experiments/ # 实验记录、指标结果 └── tests/ # 单元测试和集成测试关键点是notebooks只用来做探索和可视化核心数据处理逻辑一定要放进src写成可导入、可测试的 Python 模块。配置不要散落在代码里统一放configs用 YAML 或 JSON 管理。这样每次实验只需改配置不用动代码。另外我强烈建议从第一天就用src布局而不是随手建目录。因为它强迫你思考“这段代码的职责是什么”。数据处理是数据处理训练是训练部署是部署各司其职。哪怕刚开始多写几个文件也比三个月后面对一坨混乱的代码要好。2.2 数据版本管理与实验追踪数据在AI工程里同样是“代码”。业务数据每天都在变你训练时用的数据集是 3 月 1 号的等到 3 月 15 号想复查实验原始文件可能已经被覆盖了。这时候你就只能靠记忆和聊天记录去追溯非常痛苦。我现在的做法是用 DVCData Version Control管理数据版本。它的用法和 Git 很像先把数据文件加入 DVC再提交版本然后可以随时切换回任意一次实验所对应的数据快照。基本操作dvc init dvc add data/raw dvc push配合 Git把每次实验的代码、配置、数据版本全部锁定在一个 commit 里。这样你就能精准地回答“这个模型是用哪份数据、哪段代码、哪个超参训练出来的”这个问题。实验追踪则推荐 MLflow。每次训练开始前用mlflow.log_param记录超参训练过程中用mlflow.log_metric记录指标训练结束用mlflow.log_artifact保存模型文件。一套下来所有实验自动汇总到一个面板里不同超参、不同数据版本的对比一目了然。很多新人觉得这些记录很麻烦但实际上它是救命的没有实验记录你根本不知道线上这个模型当初是怎么调出来的。2.3 环境与依赖锁定“在我机器上能跑”是AI工程里最恐怖的一句话。Python 版本、CUDA 版本、深度学习框架的小版本差异都可能让模型加载后表现异常。所以环境必须锁死不能靠“记得装什么包”来保证。我推荐用 Poetry 或 pip-tools 管理 Python 依赖把直接依赖和传递依赖都锁定到具体版本。比如[tool.poetry.dependencies] python 3.10,3.12 torch 2.1.2 transformers 4.36.2然后在 Dockerfile 里指定基础镜像的精确版本不要用latest标签。训练和推理都跑在同一个镜像里才能保证从训练到部署环境一致。基础镜像里的 CUDA、cuDNN 也要和本机测试用的版本一致否则模型在线上的前向传播结果会和离线测试有细微差异。踩过的一个典型坑是本地 CUDA 11.8线上容器 CUDA 12.1同样一份 PyTorch 模型前几层输出完全一致到后面开始有微小浮点误差最终精度掉了好几个点。排查了一整天最后发现就是环境版本不一致。从那以后我所有项目第一件事就是写 Dockerfile把环境和依赖固化成镜像人和机器都只认这个镜像。3. 核心环节数据处理、训练与评估的工程化实现3.1 数据管线的设计与清洗数据清洗看起来只是写几个dropna、fillna但工程化之后你要面对的是“怎么让清洗过程可复现、可测试、可重跑”。最基础的要求是原始数据一旦进入data/raw就不可修改。后续所有清洗和特征工程都应该写成独立的脚本从 raw 生成 processed新的清洗逻辑不应该覆盖 raw。一条标准的数据管线通常包括几个步骤数据校验、清洗、特征变换、切分。每一步都要有输出并且每一步结束都应该做一次数据质量检查。比如def validate_raw(df): assert not df[user_id].isnull().any(), user_id 不能为空 assert df[price].min() 0, 价格不能为负 assert df[timestamp].is_monotonic_increasing, 时间戳必须递增不要小看这种简单的断言它们能在你拿到脏数据的第一时间报警而不是让错误一路蔓延到训练阶段。更完善的做法是用 Great Expectations 这类工具定义数据期望每次 pipeline 跑完自动生成数据质量报告。我自己一般先用简单的assert和小型测试起步等数据源多了再上 Great Expectations避免一开始就被工具链压垮。数据切分也有讲究。做机器学习竞赛时大家都随机打乱数据但在真实业务里模型处理的是未来的数据所以验证集和测试集应该按时间切分而不是随机切分。否则你评估出来的指标会过于乐观上线后遇到分布变化的真实数据表现立刻打折扣。3.2 训练脚本的可复现性设计训练脚本是AI工程的“心脏”但也是最容易被写成一次性脚本的地方。为了让训练可复现我坚持几个原则所有超参数从配置读取不硬编码在代码里所有随机种子显式设置所有输入输出路径通过参数传入所有实验结果自动落盘。一个比较实用的训练脚本骨架长这样click.command() click.option(--config, defaultconfigs/base.yaml, help训练配置文件) def main(config): cfg load_config(config) set_seed(cfg.seed) train_df load_data(cfg.data.train_path) model build_model(cfg.model) trainer Trainer(cfg.train) trainer.fit(model, train_df)配置示例data: train_path: data/processed/train.csv valid_path: data/processed/valid.csv model: name: resnet18 pretrained: true train: epochs: 20 batch_size: 64 learning_rate: 1e-4 seed: 42这样每个实验对应一份配置文件整个实验状态就是配置文件的快照。想复现实验只需要切到对应的 Git commit再传入同一个 config 文件即可。另一个很容易被忽略的点是 checkpoint。训练过程中每隔几个 epoch 就保存一次模型权重并保留最好和最新的两份。我经历过一次跑了 30 小时的训练在第 28 小时断电checkpoint 只留了最后一版并且已经损坏的情况。从那以后我习惯每 2 个 epoch 保存一次并且保留最近 3 个 checkpoint磁盘多花一点但换来的是安全感。3.3 评估指标与离线测试评估是连接“模型开发”和“业务价值”的桥梁。很多工程师只看准确率但准确率在多数业务场景里根本不够用。垃圾短信识别里你把正常短信误判成垃圾短信带来的体验伤害远大于漏掉一条垃圾短信。所以要把离线指标拆细精确率、召回率、F1、AUC、混淆矩阵还有业务侧的覆盖率、平均精度等。测试集的划分标准也要提前定好。我不建议每次训练前现场切分这样不同模型的评估基础不一致没法公平对比。正确做法是在数据管线里固定一个时间节点用一个统一的测试集所有实验都在同一份测试集上评估。评估不仅要看指标数值还要看“失败案例”。我每次训练完都会从验证集里挑出预测错误的样本肉眼过一遍看是数据标注错了还是模型没有学到关键特征。这一步很像调试普通软件时的“打印日志”能帮你快速定位问题。如果你只是盯着准确率数字上下浮动可能好几轮实验都在原地打转却始终不知道模型哪里做得不对。4. 部署与上线让模型真正跑在生产环境4.1 模型服务化的两种常见方式模型部署有两种典型模式在线推理和离线批处理。在线推理适合实时性要求高的场景比如推荐系统、风控审核用户请求来了要立刻返回结果通常用 FastAPI 或 Triton 这类服务框架封装模型提供 HTTP/gRPC 接口。一个最简的 FastAPI 推理服务from fastapi import FastAPI from pydantic import BaseModel import joblib app FastAPI() model joblib.load(models/model_v1.pkl) class PredictRequest(BaseModel): features: list[float] app.post(/predict) def predict(req: PredictRequest): pred model.predict([req.features])[0] return {prediction: int(pred)}这个例子虽然简单但它体现了工程化的两个要点一是输入输出要有明确的 schema 校验避免脏数据直接传给模型二是服务启动时就把模型加载到内存避免每次请求都重新加载模型造成不必要的延迟。离线批处理则适合那些不需要秒级返回的场景比如每天凌晨跑一遍用户分群、周期性生成推荐候选集。这种模式相对简单用 Celery、Airflow 或 K8s CronJob 定时调度即可。选哪种模式核心看业务延迟要求和成本。我见过不少团队一上来就追求微服务化、GPU 推理集群结果流量小到根本撑不住资源开销白白浪费成本。在线推理如果延迟敏感还要考虑模型优化。像 ONNX Runtime 或 TensorRT 能把模型推理速度提升不少但代价是转换过程可能引入精度损失。我的建议是先直接用 PyTorch 的torch.jit或 ONNX 导出用测试集确认输出一致再考虑更激进的量化手段。不要一开始就上 TensorRT除非你已经确认普通方式满足不了性能要求。4.2 CI/CD for ML 的落地传统软件有 CI/CDAI项目同样需要有“机器学习流水线”的门禁机制。你不可能每次训练完都手动记录一下结果、手动比较一下指标、手动部署模型。这个流程必须自动化。我在实际项目里把 CI/CD 分成几个阶段代码提交后先跑单元测试和静态检查确保数据处理、特征工程、训练逻辑没有被改坏。用一个小批量数据跑一次冒烟训练确认代码可以端到端跑通。如果通过了再拉取全量数据跑正式训练产出一个模型候选。把模型候选放到固定的评估集上和当前线上模型做对比只有离线指标“不降反升”才允许发布。发布到测试环境做集成测试再灰度到线上。这里最难的是第4步。因为模型不是传统代码做不了“功能对不对”这种确定性验证。我常用的做法是写一个评估脚本自动把候选模型和线上模型在同一个测试集上跑出来对比关键指标如果候选模型的加权得分低于线上模型流水线直接失败不允许发布。这一步能拦下绝大多数回归。具体工具上用 GitHub Actions 或 GitLab CI 都能做。刚开始不需要搞太复杂的平台先写几个简单的 shell 或 Python 脚本把这些步骤串起来等团队大了再引入像 Kubeflow、MLflow Pipelines 这类专职工具。4.3 监控与告警模型也会“生病”模型上线不等于结束恰恰是运维的开始。很多同学以为监控只看 CPU、内存、GPU 利用率就够了但AI系统的核心风险不在资源而在数据和模型行为的变化。最基础也最隐蔽的问题是数据漂移。线上数据的特征分布会随着时间改变比如一个电商推荐模型双十一前后的商品特征分布完全不同。如果数据漂移了模型精度必然下降但你可能毫无感知。我的做法是双管齐下。基础设施层用 Prometheus 采集在线推理的延迟、吞吐、请求量等指标模型行为层用 Evidently 这类工具监控预测分布、特征分布和数据漂移指数。重点盯几个代理指标预测均值的波动、特征分位数的变化、预测类别的分布变化。比如一个二分类模型平时预测正类的概率均值在 0.2 左右某天开始飙升到 0.8即使还没有用户投诉你也应该收到告警。告警阈值怎么定不要拍脑袋。我建议先用线上模型跑两周把指标的正常波动范围记录下来然后取均值加减三倍标准差作为告警线。这样能避免指标自然波动带来的误报。一旦触发告警第一件事不是重新训练而是找原因数据是不是变了特征是不是没对上规则是不是改了确诊后再决定要不要重新训练模型。5. 常见坑与排查心得5.1 数据漂移为什么最隐蔽我遇到过一次非常头疼的线上事故一个用户意图识别模型上线时各项指标都正常一个月后业务方反馈效果变差但我看监控面板上的准确率根本没办法实时统计也没有任何异常告警。后来排查了半天才发现用户输入的口语化表达越来越多模型的训练语料还是几个月前的根本没见过这些新说法。这就是典型的数据漂移特征和标签的关系变了但代码和模型都没有动。离线评估精度很高线上却一塌糊涂。从那以后我把“数据分布监控”和“模型指标监控”放到了同等优先级。训练时保存一份特征统计信息比如均值、方差、分位数、类别频率然后上线后用同样的统计逻辑处理线上样本定期算 PSIPopulation Stability Index或 KL 散度超过阈值就报警。对这种问题最好的解决方式就是提前预防。在数据管线里就把特征分布统计输出成文件随模型一起注册进模型仓库。这样线上监控就有了一份“基准数据”随时可以和当前分布对比。没有基准的数据漂移检测就像没有参照物的测量毫无意义。5.2 版本不对齐导致的灵异事件另一个高频坑是版本不对齐。代码、数据、模型、配置这四个东西一旦有一个对不上线上就很可能出现“看起来还正常但细节就是不对”的灵异问题。有一次特征工程代码更新了我重新训练并部署了模型但线上的推理服务代码没有同步更新导致线上推理用的特征和模型训练时用的特征不是一套组合结果模型输出变得非常奇怪。更气人的是这个错误不会导致崩溃它只会默默让你损失业务收益。所以我现在强制要求每个模型发布都带上完整的血缘信息发布项版本信息模型文件model_v3.pklMD5:a1b2...训练代码Git commit8a3f21c数据版本DVCdata/raw.dvc对应快照特征配置configs/features_v3.yaml推理服务镜像rag-api:20240601在推理服务启动时把模型的期望特征列表和线上实际输入特征列表做一次 diff不匹配就直接拒绝启动。用这种硬校验杜绝“我以为对齐了”这种侥幸心理。5.3 资源与成本控制做AI工程另一件容易被忽视的事是资源和成本。GPU 很贵数据存储也不便宜。我看到很多团队不管什么任务都开 8 卡训练其实数据量小的话单卡也能跑白白浪费成本。这里分享几个我的习惯。第一正式训练前先在一个小而代表性子集上把代码调通再放大规模避免浪费整批资源后才发现代码有 bug。第二训练任务都加到队列里统一管理不要每个人手动抢占 GPU否则容易冲突。第三模型上线后定期检查调用量如果业务低峰期调用量很低可以把推理实例缩容到最小规模用弹性伸缩应对高峰。第四定期清理中间产物和旧版本的模型文件磁盘空间不是无限的。成本核算是AI工程里少有人提、但非常影响职业发展的一项。在老板眼里你既要能做出一个高性能模型也要能说明白它花了多少成本、带来多少收益。把这些账算清楚你和业务方沟通的时候才有底气。6. 给从零开始的人几条路线建议6.1 先做一个“最小闭环”如果你刚开始接触 AI 工程最快的学习路径不是去读重平台源码而是亲手做一个端到端的小项目。只要它覆盖这些环节数据获取、数据版本管理、模型训练、模型评估、接口部署、基础监控。我个人建议复刻一个“垃圾短信分类器”或者“房价预测模型”因为数据容易获取业务逻辑也简单你可以把精力全部放在工程组件上。先用 DVC 管理数据用 MLflow 记录实验把训练脚本模块化再用 FastAPI 部署最后加一个简单的数据漂移报警。六个环节全部走通你会对 AI 工程有个非常具体的体感。很多新手会卡在一个环节总想着把模型精度刷到最高再去做工程化。这是本末倒置。模型是一个既有答案的组件工程化才是真正考验你的部分。哪怕用一个简单的逻辑回归穿插上数据、训练、部署、监控全链路也比花一个月调一个生产环境跑不起来的 Transformer 更有价值。6.2 不要一上来就上重平台现在市面上的MLOps平台很多Kubeflow、Feast、DataRobot等等看着很强大。但我的建议是如果你的团队只有几个人、几个模型不要急着上重的平台。平台带来的通用性和易用性与它引入的概念负担、运维成本是成正比的。我个人的实践顺序是先用脚本和开源工具解决眼前问题比如用 Git DVC 做版本管理用 MLflow 做实验跟踪用 Docker 做环境管理。等实验数量多到手工维护确实吃力再评估要不要上更完整的平台。一上来就搞全功能平台很容易被工具链本身拖死陷入“配置几天、跑一个模型、然后维护工具”的循环。路线总结成一句话先做最小闭环再逐步加东西。优先级大概是数据版本管理 实验追踪 环境锁定 CI/CD 模型监控 全平台化。每个阶段都一定要有实际的痛点驱动不要为了技术而技术。最后再分享一个小技巧做 AI 工程别把模型看得太重数据、代码、环境、监控每一环都可能是杯具的源头。从零开始搭一次你会对“什么是AI系统”有完全不一样的理解。这条路没有捷径但每踩一个坑都会变成你未来面试、答辩、带团队时最宝贵的谈资。