前阵子好几个朋友都在问我同一个问题:AI工程到底从哪里开始?网上的资料倒是一大堆,但要么是纯算法理论推导,要么是某个工具的使用手册,中间那条“从零到能落地”的路径反而没人系统性地讲清楚。我干脆把自己从只会调参,到能把一套完整AI应用推上线,再到能稳定维护这套系统的经验,整理成了一个项目,名字就叫 ai-engineering-from-scratch。这个项目不是又一个教程合集,而是一份从零开始做AI工程的路线图加实操手册,里面完整记录了我在数据准备、模型训练、部署上线、线上监控这一整条链路上踩过的坑、验证过的方案和觉得值得反复看的细节。今天这篇,就是把这个项目的设计思路、核心模块、实操细节和常见问题一次性拆开聊聊。
1. 项目定位与整体思路
1.1 为什么叫“from scratch”
“from scratch”不是说让你从高等数学第一页开始学起,而是要求你亲手把一条完整的AI应用链路拉通。很多时候我们习惯拿来一个模型直接fit,数据是现成的,环境是配置好的,代码是别人写好的,运行一下就能出结果,但这中间其实隔着一层纱。一旦遇到真实问题,比如数据格式变化、模型版本回滚、线上推理延迟上涨,很多人就卡住了,因为从来没有亲手处理过这些环节。
这个项目把AI工程拆成了几个大块:数据、训练、评估、部署、监控、迭代。每一块都要求学习者亲手做一遍,而不是只看代码。我的判断标准很简单:如果换一台完全干净的机器,你能否仅仅凭项目里的文档,就把整个系统重新拉起来。能做到这一点,才算真正跨越了“只会跑别人代码”的阶段。
还有一个考虑是,很多公开的教程喜欢跳过数据工程,直接拿现成数据集训练模型,但真实生产项目里数据问题占掉的时间往往超过70%。所以这个项目从第一版开始就把数据工程放在非常靠前的位置,而不是让它作为“补充阅读”存在。
1.2 这个项目解决什么问题
我总结了一下,身边想转AI工程的开发者,最常被三件事卡住。
第一是知识断层。会训练模型但不会部署,会写Python但不懂机器学习生命周期,懂一点算法但不知道如何设计一个可评估、可回滚、可监控的AI系统。断层导致的结果是,模型训练完了就结束了,根本到不了业务侧。
第二是工具碎片化。很多人用过TensorBoard、MLflow、Docker、Kubernetes,但不知道它们各自应该在什么阶段出现,更不知道如何串成一条流水线。工具不在多,关键在于它们能不能衔接起来。这个项目里我把每一个工具放在它真正发挥作用的位置去讲,而不是单独罗列功能介绍。
第三是复现困难。每次实验改了点参数,下次想找回上次的结果,却发现环境和数据都变了,代码也没记录,模型文件不知道存哪了。这是非常可怕的问题。项目里特意设计了一套实验记录机制,哪怕是个人小项目,也要把版本管理、数据追踪、指标记录当成规范动作。
1.3 适合谁,不适合谁
适合已经懂一点机器学习基础,想往工程方向深入,但被“模型训练”和“系统落地”之间那道墙卡住的工程师。也适合刚入门,但目标明确,想直接做AI应用开发的初学者。项目里每个模块我都尽量从零讲起,但希望你至少有Python基础、知道常见机器学习概念。
不适合谁呢?不适合想一周内学会所有算法的人,也不适合想不写代码、只靠拖拽工具做AI的人。这个项目默认你会动手写代码,而且愿意反复折腾。
2. AI工程的核心知识模块
2.1 底子:不是背公式,而是能推演
很多人一听说AI工程,就觉得数学门槛高不可攀。实际上,工程实践需要的数学知识并不是那种钻研到论文级别的深度,而是要能理解每一步操作背后的原理。我的建议是,别从教科书第一页啃起,而是从问题反推。比如做文本分类时,为什么TF-IDF之后向量维度那么大,还能算相似度?这里用的就是线性代数里的矩阵运算和稀疏表示。再比如训练神经网络时,梯度经常出现爆炸或消失,这背后的核心就是链式法则和激活函数导数的性质。
编程方面,Python绕不开,但不是会写个for循环就行了。你需要熟练使用NumPy、Pandas、Matplotlib这些库,能独立完成数据清洗,能通过可视化发现数据分布问题,能写一个可复用的小工具类。这个项目里我把这些基础内容都压缩成“最小可运行”的示例放在前面,每个示例都标注了阅读顺序和动手练习建议。
我自己在实际陪跑过程中发现,真正让初学者崩溃的并不是数学公式,而是“看着代码能跑,但不知道每一步在干什么”。所以我给每个基础示例都加了“为什么要这样写”的注释,而不是只贴代码。这是这个项目一个比较核心的特点。
2.2 数据工程:动手改数据比调参更重要
数据质量决定模型上限,这句话我说一百遍都不腻。从零开始的AI工程,必须把数据工程当成第一优先级。这个项目里,数据这块我拆成了几层。
数据获取与聚合。真实场景的数据很少会是一个完美的CSV,更多是分散在数据库、日志、API甚至外部爬虫里的碎片。你需要写代码把这些数据聚合起来,并且处理接口限流、字段不一致、格式混乱这些脏活累活。
数据清洗。缺失值怎么处理,是直接删、填充还是建模预测?异常值是保留还是剔除?重复记录怎么去重?这些听起来很简单,但每一条都需要结合业务场景去判断。例如预测用户流失时,那些从来没有真正活跃过的账号,算不算真实用户?如果算,很可能把模型带偏。
标签设计。监督学习必须有标签,但标签怎么定义、由谁标注、标注一致性怎么验证,很多人第一次做的时候都会懵。项目里我分享了一个小技巧:在正式标注前,先做一次小范围的试标,然后计算标注员之间的一致性,如果一致性太低,那一定是对标签定义理解有偏差,需要先修正标准再扩大标注规模。
切分策略。训练集、验证集、测试集不能简单用随机切分。时间序列类数据必须按照时间切分,防止未来信息泄漏。如果正负样本不均衡,还要考虑分层采样。我见过不止一个人因为切分不当,导致模型在测试集上看起来效果很好,一上线就崩。
还有一个特别容易被忽略的点:数据版本。训练样本是动态变化的,如果不给数据集打版本标记,那么模型版本和数据集版本之间就永远对不上。这个项目里我强制要求使用数据版本工具,至少在每个数据集目录下保存一个hash值、创建时间和数据描述,这是一个成本极低但收益极高的习惯。
2.3 模型训练与评估:一切以可验证为前提
模型训练这块,最容易犯的错误是一上来就想调一个SOTA模型。我的建议是:先跑通,再优化。先做一个最简单的baseline,哪怕只是一个线性模型,也要保证全流程能走通。然后再去尝试更复杂的模型、加特征、做超参数搜索。每一次改动都要有记录,否则你怎么知道哪个改动真正带来了提升?
评估指标不能只看一个数字。分类任务要看准确率、召回率、F1,但在业务场景里还要关心最差样本的表现。回归任务除了MAE、RMSE,还要看误差分布,是否有极端离群点被模型严重预测错误。做一个好的评估集非常重要,而且评估集要尽量贴近线上真实分布,否则离线结果很漂亮,线上完全不是一回事。
这个项目里推荐使用实验跟踪工具,比如MLflow或者W&B。每次实验的代码版本、参数配置、训练数据版本、评估指标、模型文件都要记录下来。原因很简单:当你有上百次实验后,你不可能记住所有细节,实验跟踪是你唯一可信的记忆系统。它不是一个可选项,而是必需品。
3. 工程化落地:从Notebook到生产系统
3.1 部署形态与推理优化
模型训练完成后,被低估得最严重的就是部署环节。很多人觉得把模型保存下来,写个Python脚本调用预测就算完事了。但在真实业务中,这远远不够。
部署的第一个选择是形态。最轻量的是把模型包成一个REST API服务,适合内部工具、离线分析或者实时性要求不高的场景。如果要求低延迟,例如互联网产品里的实时推荐,那么就要考虑用模型量化、剪枝,或者用更高效的语言重写推理路径,又或者直接上推理服务框架。这个项目里不会一上来就让你上K8s,而是先把单机的部署和性能调明白。
我自己常用的做法是:先用FastAPI把一个模型包成最小服务,把输入输出格式用Pydantic定义清楚,然后做一轮压测。压测一般会暴露很多问题,比如GPU显存占用过高、并发请求时排队延迟剧增、模型加载时间太长导致超时。这个时候再针对性地做优化,而不是盲目追求高级架构。
优化时要先确认瓶颈在哪里。如果瓶颈是模型推理本身,可以考虑TensorRT、ONNX Runtime等加速推理引擎;如果瓶颈是网络IO,可以调整服务并发模型或使用连接池;如果瓶颈是数据预处理,可以把它放到模型输入之前做批处理。很多时候,问题比你想的更朴素。
3.2 MLOps基础:版本、CI/CD、监控
不少人听到MLOps就头大,觉得这是大厂才需要的东西。其实它本质上是把软件工程的最佳实践搬进机器学习生命周期。哪怕你只是一个人开发,也应该具备下面这些意识。
代码版本管理。所有训练、评估、部署代码都进Git仓库,不能有“这一版训练代码没问题,先放桌面”这种操作。模型版本管理。一个模型文件应该对应一个明确的版本号,记录它的结构、参数、训练数据和指标。数据版本管理。数据一旦被用于训练或测试,就应该被固定下来,不能事后偷偷修改。CI/CD不是只给Web应用用的,训练流程、评估流程、部署流程都可以做成自动化的流水线,测试通过后自动发布。监控则是必须一开始就考虑的事情。线上预测结果要记录分布、延迟、错误率,还要定期跟训练时的分布对比。
我见过很多人把MLOps当作一个“以后再说”的事情,结果模型上线后出了漂移问题,既不知道什么时候开始的,也没有数据可以追溯,只能回滚到“看起来没问题的版本”,非常被动。这个项目里特意设计了一个很简单的监控模板,用Grafana展示预测分布和延迟指标,让新手也能建立最基本的可观测性。
3.3 一个最小可落地的端到端案例
为了把前面所有模块串起来,这个项目里内置了一个端到端案例:从一份原始日志数据出发,构建用户流失预测系统。整个链路包括数据清洗、特征构造、模型训练、评估筛选、打包API、容器化部署,再加一个简单的监控页面。案例用docker-compose拉起来,换一台干净的机器也能一键启动。
这个案例的业务很朴素,但价值在于完整。你会看到数据定义和模型代码如何组织、模型文件如何挂载、API服务如何读取模型、监控如何采集线上指标。很多人在这一步会突然意识到,原来训练一个模型只是整个系统里很小的一部分,前面有数据管线,后面有服务架构和运维。
做完这个案例,你基本能够理解AI工程的全貌了,后面再遇到更复杂的框架,也不会觉得无从下手。
4. 实操过程与工具选型
4.1 工具链选型对照
很多初学者会陷入“工具选择恐惧症”,生怕选错了浪费时间。我的建议是:最小可行组合,够用就行,后面再按需替换。下面是我在这个项目里常用的一套组合。
| 环节 | 常用工具 | 我的选择 | 备注 |
|---|---|---|---|
| 实验跟踪 | MLflow、W&B | MLflow | 可以本地一条命令跑起来,支持模型注册 |
| 数据版本 | DVC、dvc、自带脚本 | DVC | 习惯后能省很大的心 |
| 训练代码 | PyTorch、TensorFlow | PyTorch | 个人项目调试更灵活 |
| API服务 | FastAPI、Flask | FastAPI | 自带请求参数校验和文档,省事 |
| 容器化 | Docker、docker-compose | docker-compose | 单机开发完全够用 |
| 监控 | Prometheus + Grafana | 同一套 | 云原生标配,理解一次通用 |
这套组合的好处是每个环节都有清晰的边界,且相互之间可以通过标准接口连接。比如MLflow记录模型产物,DVC记录数据,FastAPI读取模型并对外提供接口,Prometheus采集服务指标。不需要学习一大堆新概念,核心是把每个工具的职责搞清楚。
4.2 几个必须养成的实操习惯
这个项目里我反复强调几个习惯,它们看起来琐碎,但能帮你躲掉大部分返工。
第一,每次实验固定随机种子。如果随机种子不固定,两次相同参数训练的结果可能相差很大,你很难判断改动到底是有效还是随机波动。至少保证数据切分和模型初始化是可复现的。
第二,不修改历史实验记录。实验记录是随时间累积的,如果发现某次实验的参数设置错了,应该新建一条实验去修正,而不是偷偷改掉旧记录,否则之后的对比就全乱套了。
第三,代码和配置分离。训练脚本不要硬编码数据路径、超参数、模型保存位置,外部传参或读取配置文件。这样方便切换环境,也方便后面做超参数搜索。
第四,定期清理无用实验,但保留一张结论摘要表。实验多了之后,UI列表会非常乱。我会把有价值的结论写进项目的notes文件,方便回顾,然后清理掉历史垃圾实体。
4.3 成本与效率的平衡
个人开发者做AI工程,经常遇到GPU资源不够。项目里我专门分享了一套成本控制方法论。核心思想是:先用小数据验证,再上全量。任何新想法都先在千分之一的数据上跑通,确认有效后,再花全量资源做正式实验。
训练过程中可以开启混合精度,速度提升明显,显存占用也低很多。设置合理的早停策略,loss不再下降就自动停止,能省下一大笔开销。云服务器尽量选抢占式实例,用完即关,不要一直开着什么都没跑。
另外一个很容易被忽略的浪费是“疯狂堆参数”。超参数搜索当然有用,但不要让搜索空间无限扩大。先固定一批不敏感参数,只对两三个关键参数做搜索,性价比最高。
5. 常见问题与排查技巧
5.1 训练不收敛怎么办
训练不收敛是最常见的问题。我一般按这样的顺序排查:先看数据是否有异常,特征是否归一化,标签是否正确;再看学习率是否过大或过小,过大loss震荡甚至发散,过小收敛太慢;然后用一个小样本做“过拟合测试”,比如只拿几十条数据,看模型能不能把训练集背下来,如果连这个都做不到,那大概率是代码bug,而不是模型问题。
loss曲线的形态也很重要。如果loss一上来就是nan,可能是梯度爆炸或存在NaN数据;如果loss缓慢下降到某个平台就不动了,可能卡在局部最优,可以尝试换学习率策略或加动量。遇到问题不要急着换模型,先用这些方法定位。
5.2 离线指标很好,线上效果差
这是模型上线后最常见的翻车场景。首要嫌疑是数据分布漂移。线上实时特征分布和训练时的分布可能完全不一样,最简单的检测方式是每天统计关键特征的均值和方差,画趋势图。其次是训练测试切分泄漏,比如对时间序列数据做了随机切分,导致模型“偷偷”看到了未来信息,离线表现虚高。再次是特征一致性问题,线上请求中有某个特征缺失,代码里用了默认值填充,而这个默认值在训练时不存在,模型输出自然就偏了。
解决这些问题的前提是有监控和数据记录。如果一开始没有记录线上特征,后面根本无从分析。所以我在项目里特意要求大家上线第一天就要记录预测分布,而不是等出问题之后才想着补数据。
5.3 环境依赖与复现问题
“在我电脑上是好的”,这句话在AI工程里同样危险。换一台机器跑训练代码,可能因为Python版本、CUDA版本、依赖库版本差异,结果完全不同。为了减少这类问题,项目里要求使用虚拟环境或容器,固定依赖版本并生成锁文件。镜像构建的时候要把训练、评估、部署用到的依赖都写清楚。
即使不发布给别人,你自己过三个月后再跑原来的项目,也一样会遇到环境问题。所以,把环境配置当成代码一样管理,这是AI工程最基本也最容易被忽略的规范动作。
6. 个人体会与扩展建议
6.1 最想放弃的时刻
按照这个项目从零走一遍,大多数人会在“第一次端到端跑通之前”最想放弃。也许是docker-compose起不来,也许是某个依赖版本冲突,也许是模型接口返回的数据格式跟预期不一致。我当时也是这样,反复折腾了两三天,一度怀疑自己是不是不适合做AI工程。
后来我总结出一个心法:先别追求完美。用最简单的方式跑通一个完整流程,哪怕只是一个极小的模型、一个很简陋的API、一个只能看一两个指标的页面,先让整条链动起来。动起来之后再一点一点优化,难度会小很多。这个心法也是我后来教很多新人时最常强调的一点。
6.2 后续可以怎么扩展
如果你走完这个项目,会发现在这套流程之上,还有很多可以深挖的方向。比如现在很火的LLM应用工程化,同样离不开数据版本、评估集、可观测性和持续迭代这些底子。RAG系统里怎么评估检索效果?token成本怎么监控?这些本质上还是AI工程的问题。
再往上走,可以做特征平台、全链路可观测性、自动化模型评测,甚至让整个AI系统具备自动修复和自愈能力。但不管方向怎么延展,我在 ai-engineering-from-scratch 里搭建的那套骨架都还能复用。这也是我后来最欣慰的地方。