1. AI工程到底是什么:先理清几个容易混淆的概念
很多人看到"ai-engineering-from-scratch"这个项目名,第一反应是"又要从零手写神经网络了"或者"是不是得先把线性代数啃完"。说实话,这两个理解都不算错,但都跑偏了。我见过太多人在这个路口走岔道:要么一头扎进理论推导里拔不出来,要么急着调API却始终摸不到门道。
AI工程,英文叫AI Engineering,它和传统的软件工程、数据科学、机器学习研究都不一样。它不是某个单一学科的延伸,而是把这几个领域的能力揉在一起,解决一个更具体的问题:怎么让AI模型稳定、可靠、可维护地跑在真实业务里。
我在带团队和带新人的过程中观察到,绝大多数人缺的不是某一块知识,而是缺少一个完整的、从零到一的工程视角。你让他写个Python脚本调一下OpenAI的接口,他半天就能搞定;你让他做一个完整的OCR识别服务,需要考虑并发、容错、精度评估、模型迭代,他就开始乱了。这就是典型的"知道怎么调库,但不知道怎么工程化"。
所谓from scratch,在三五年前可能真意味着自己从反向传播开始写代码。但现在不是了,现在的from scratch指的是:不依赖现成的端到端方案,从数据收集、模型选型、训练调优、部署监控这条完整链路自己走一遍。你可以用现成的PyTorch、HuggingFace的预训练模型,但你要真正理解每个环节在干什么、为什么这么干、出了问题怎么排查。
这篇文章面向三种人:想转行做AI工程的开发者、已经在做但总觉得零散不成体系的初级AI工程师、还有带团队时不知道怎么给新人设计成长路径的技术leader。我不打算给你画一张包罗万象的知识地图,那玩意儿看着唬人,实际没用。我更想拆解的是:一个AI工程师从零开始到底应该怎么想、怎么学、怎么做,踩过哪些坑,哪些弯路是完全可以避免的。
2. 底层能力建设:数学、编程与机器学习基础的实际取舍
2.1 数学基础:别被"四大数学"吓退,分清主次
聊AI工程绕不开数学,但很多人的学习方式是错的。上来就把《统计学习方法》从头啃到尾,或者把3Blue1Brown的线性代数系列全刷完,然后发现:学的时候挺爽,用的时候全忘。
我的建议是,按"用到什么学什么"的原则来,而不是按教材顺序来。如果你的目标是AI工程而不是算法研究,优先级应该是这样排的:
线性代数是最值得花时间的,因为深度学习里90%的操作本质都是矩阵运算。你不需要会证明奇异值分解的数学定理,但你需要理解张量的shape变化意味着什么、矩阵乘法为什么这么定义、embedding矩阵到底是什么。这部分直接决定了你读PyTorch代码时是"看不懂报错"还是"一眼就知道哪维对不上"。
概率统计排在第二,但不是贝叶斯推导那种纯理论。你更需要的是:分布的概念(因为数据预处理经常要做标准化和归一化)、期望和方差(评估指标里全是这俩)、最大似然估计的思想(交叉熵损失函数就是从这里来的)。中心极限定理和各类假设检验,说实话日常工程里用得不多,可以先放一放。
微积分最实用的是链式法则,因为反向传播就是链式法则的工程实现。你不需要手推梯度公式,但当你看到梯度消失、梯度爆炸这类问题的时候,脑子里必须能浮现出"多层复合函数求导会累积小数值"这个画面,才能理解为什么会有残差连接、为什么激活函数要选ReLU。
这么说吧,数学是给你提供"直觉"和"判断力"的,不是给你考试用的。我面试过一个候选人,手推Softmax梯度推导毫无压力,但让他解释为什么训练时Loss曲线震荡不收敛,他完全没有头绪。这就是典型的数学能力和工程直觉脱节。
2.2 编程能力的三层进阶:从"能跑"到"能扛"
AI工程的编程能力和普通后端开发不太一样。它要求的不是CRUD的熟练度,而是三个方面:数据处理能力、模型训练代码的组织能力、和系统工程的对接能力。
第一层是数据处理。真实项目里你花在数据清洗上的时间远比模型训练多。pandas的groupby和merge、numpy的向量化操作、以及怎么高效处理大规模数据集(比如用datasets库或者arrow格式而不是硬读CSV),这些是基本功中的基本功。我见过一个很典型的场面:同事用for循环一条条处理几百万条文本数据,跑了整整一晚上还没结束,换成向量化操作和并行处理后十分钟搞定。这个差距就是工程效率的差距。
第二层是训练代码的组织能力。新手喜欢把数据加载、模型定义、训练循环、评估逻辑全写在一个Python文件里,临时跑通没问题,但一旦要调参、换模型、加日志,就会变成一团乱麻。稍微成熟一点的做法是用PyTorch Lightning这类框架把训练逻辑结构化,或者至少要自己规划好config、dataset、model、trainer、evaluate这几个模块的边界。
第三层是系统工程能力,这是AI工程师区别于算法工程师的关键。你要懂基本的Linux操作和shell脚本,因为模型训练基本都在服务器或云主机上进行;你要会Docker,因为部署模型必须做环境隔离;你要理解基本的API设计和并发模型,因为模型服务是要被别人调用的。具体怎么把这些组合起来,我在第4节会展开讲。
2.3 机器学习核心概念:用直觉而不是公式来理解
在动手写代码前,有几个核心概念必须建立直觉。第一个是过拟合和欠拟合,用生活化的方式理解:欠拟合就是学习不够,考试没复习到位,训练集上的表现就很差;过拟合就是死记硬背了训练题的标准答案,平时模拟考满分,一上考场换了题目就懵。对应的解法也自然就有了:欠拟合就加模型容量、加特征;过拟合就加正则、加数据增强、用早停。
第二个是偏差和方差的权衡。模型太简单,高偏差低方差,预测稳定但系统性偏差大,怎么调都差一口气;模型太复杂,低偏差高方差,训练集上神挡杀神,换数据就翻车。做AI工程其实很大程度上就是在这两者之间找平衡点。用单个模型扛不住的,就用集成学习或者加验证集做模型选择。
第三个是训练集、验证集、测试集的三权分立。很多新手只分训练集和测试集,调了几次参之后发现测试集分数虚高,一看就是用测试集做过模型选择,信息泄漏了。正确做法是训练集上训练、验证集上调参和早停、测试集只在最后一刻用一次。这里还有个容易被忽略的坑:如果你的数据存在时间相关性,不能随机切分,要按时间切分,否则就是用"未来"预测"过去",线上的表现一定会崩。
3. 动手实操:从零搭建一个完整的AI工程项目
3.1 项目选型:为什么第一个完整项目这么关键
理论学得再多,不落地都是空中楼阁。但第一个项目的选题非常讲究,选大了容易半途而废,选小了练不到工程能力。我推荐的标准是:任务类型要经典、数据要好获取、评测要客观、复杂度要可控。
四个条件都满足的最佳选项是文本分类任务,比如情感分析或者垃圾邮件识别。它任务逻辑简单,不涉及复杂的生成式模型;数据集公开好找,IMDb影评、THUCNews新闻分类都有现成的;评测指标直接用准确率、F1就行,不需要你设计复杂的评测方案;同时它又能覆盖AI工程的完整链路:数据清洗、文本预处理、模型选型(从词袋模型一路做到BERT微调)、训练调优、模型部署。
你也可以选择图像分类或者简单的结构化数据预测,但文本分类我是最推荐的。原因很实在:纯文本数据的处理门槛最低,不用处理图像解码、增强这类杂活,可以把大部分精力放在理解AI工程的主线上。项目名虽然叫from scratch,但实践路径可以先用现成的模型框架把主链路跑通,再逐步替换成自己实现的模块。
3.2 数据环节:预处理、切分与质量控制的完整流程
数据是整个AI工程的地基,地基不稳,上面盖什么都白搭。我在实际项目中见过太多"模型效果差"的案例,最后排查下来都是数据问题。所以这个环节一定不能糊弄。
第一步是数据探查。拿到数据先别急着训练,花时间做几件事:统计类别分布,看有没有严重的类别不平衡;抽样几十条数据人工看一下内容质量,有没有乱码、重复、标签错误;统计文本长度分布,确定序列长度的裁剪策略。这一步看似简单,但对后续模型效果影响巨大。
以IMDb情感分类为例,经典做法是把标签0-1映射到负面和正面,文本做小写化、去除HTML标签、处理缩略语。但注意,不要做过度的文本清洗,比如把标点符号全删掉或者做词干提取,在深度学习时代这些操作往往得不偿失——因为BERT这类预训练模型本身对原始文本更友好。传统机器学习方法吃一套清洗逻辑,深度学习吃另一套,你得明白自己在哪条路上。
第二步是数据切分。除了前面提到的训练/验证/测试划分,还要注意stratified抽样。在类别不平衡的情况下,随机切分可能导致某个小类别在训练集中只有零星几个样本。用sklearn的train_test_split时记得传stratify参数,或者直接用StratifiedKFold做交叉验证。
第三步是数据质量控制。这块是最容易被忽略的。我做情感分析时遇到过标签和文本明显不匹配的情况,比如一条写着"I love this movie, it is amazing"的评论被标为0。遇到这种情况,如果是小数据集就人工修正,大数据集通常的做法是写规则清洗加抽样复核。还有一个窍门:训练完之后分析错误样本,如果发现大量错误集中在某些特定模式上,比如特定语气词、特定标点,反过来说明数据里有系统性噪声。这时候清洗数据比调模型参数有效得多。
3.3 模型选型与训练:从baseline到优化的完整链路
很多新手一上来就追求最先进的模型,这是个大误区。AI工程讲究的是"先让链路跑通,再逐步优化"。正确的方法论是从一个足够简单的baseline开始。
Baseline可以是什么?对于文本分类,最简单的就是用TF-IDF特征加逻辑回归。别小看这个组合,在很多任务上它的效果能超过一些深度模型,而且训练只要几秒钟。它的意义不在于最终效果,而在于给你建立第一参考线:后续你做的所有优化,都要能说明白比这个baseline好在哪里、为什么好。
baseline跑通之后,再切换到一个预训练模型,比如bert-base-chinese(中文场景)或者distilbert-base-uncased(英文场景)。这个阶段有两个关键点。第一个是数据加载器要用DataLoader配合Dataset类,注意设置shuffle=True用于训练集,验证集不需要shuffle。第二个是优化器和学习率的选择,预训练模型微调通常用AdamW配合transformers库里的get_linear_schedule_with_warmup,学习率一般在2e-5到5e-5之间。这个和从零训练的模型完全不同,因为预训练模型已经收敛过一个阶段了,学习率太大会灾难性遗忘。
训练过程中必须要监控的指标有四个:训练损失、验证损失、准确率、F1分数。我看训练曲线有个习惯:前几个epoch训练损失下降明显而验证损失基本不动,这是正常的;如果验证损失先降后升,而训练损失还在降,恭喜,你找到了过拟合的起始点。这个点就是早停的最佳时机。用PyTorch Lightning的话,可以配置ModelCheckpoint回调来保存验证集上表现最好的checkpoint,而不是最后一个epoch的模型——这是新手最容易犯的错误,用最后一轮的权重做评估,但其实早停点附近的表现往往更好。
评估阶段,不要只看整体准确率。对于情感分析这种二分类任务,至少要看混淆矩阵,区分到底是把负面误判成了正面还是反过来。我还建议在测试集上抽样一部分预测错误的样本做人工分析,写进项目的README里作为已知问题的记录。这样做有几个好处:一是方便你下次迭代时有历史参考;二是面试或写技术总结时有第一手材料;三是能帮你发现模型在哪些语义模式上存在盲区。
3.4 部署与服务化:工程能力的试金石
模型在本地测试集上跑出90%准确率,只完成了AI工程的一半。怎么把它变成一个可以被业务调用的服务,才是工程能力的分水岭。我见过太多项目死在部署这最后一公里上。
最简单可靠的方案是用FastAPI封装模型推理接口,然后用Docker打包部署。整个流程我建议你这么走:
第一步,把训练好的模型和tokenizer保存到本地。用HuggingFace的save_pretrained方法,同时把模型配置也存下来。这里注意,不要只存模型权重不存tokenizer,没有tokenizer你根本没法把文本转换成模型需要的输入格式。
第二步,写一个推理类,负责加载模型、做推理、返回结果。核心点是把模型加载放在接口服务启动的时候,而不是每个请求都加载一遍——模型加载几秒钟,推理只要几十毫秒,加载放请求里会导致超时。如果追求性能还可以把模型推理放到GPU上,配合批处理来做并发优化。
第三步,用FastAPI写HTTP接口。输入是JSON格式的文本,输出是预测的标签和置信度。记得处理异常,比如空文本、超长文本,要在接口层做截断或报错处理,不能直接让模型崩溃。
第四步,写Dockerfile。基础镜像选pytorch/pytorch官方镜像或者python:3.9-slim自己装依赖都行,关键是requirements.txt要锁定版本。我踩过的坑是:本地环境能跑但容器里跑不起来,十有八九是某个依赖版本不一致。所以依赖锁定这个事一定要做。另外,镜像里要记得把模型文件拷进去,很多人漏了这一步,容器启动后模型路径找不到直接报错。
部署完之后,还要做两步收尾工作。一是写一个简单的压测脚本,用locust或wrk看看接口的QPS大概在什么水平、延迟是不是能接受。不追求高性能,但至少要知道自己服务的上限。二是写一个health端点,返回服务存活状态。这看起来是小事,但上生产后探活、监控全靠它。
4. 常见问题与排查技巧实录
4.1 新手最容易犯的7个错误
我这些年带过不少新人,也看了大量开源项目的代码,总结出七个高频错误,每一个都值得单独说。
第一个是数据集切分时的信息泄漏。最常见的形态是用全体数据进行标准化或归一化之后再切分。正确的做法是先切分,再用训练集的数据统计量去转换验证集和测试集。用sklearn的Pipeline配合fit和transform的分离就能避免这个问题。第二种泄漏形态是用测试集做调参,这前面已经说过了,根源是缺少独立的验证集。
第二个是类别不平衡却不处理。比如正负样本比例是9比1,模型只要全预测为正就拿到90%准确率,看起来好得不行,实际上是个废物模型。面对不平衡数据,除了用F1、AUC这类指标外,还要在损失函数上做文章,比如给少数类更高的权重,或者用WeightedRandomSampler重采样。
第三个是超参数全部使用默认值。默认值是在通用场景下调的,你的任务有自己的数据分布,学习率、batch size、序列长度、隐藏层维度都应该基于实验调整。哪怕是batch size这种看起来不重要的参数,从16调到32都可能让训练结果发生明显变化。
第四个是只存最后一个epoch的模型。训练早期可能过拟合、中期效果最好、后期又退化,这种情况太常见了。务必用checkpoint机制存验证集上最好的模型,而不是最后一个。
第五个是没有固定随机种子。深度学习到处都是随机性,数据加载顺序、权重初始化、dropout都会带来差异。不固定种子,你连自己改了一行代码之后效果是变好还是变坏都判断不了,因为你根本不知道这个变化是来自代码还是随机波动。所以实操时,数据加载的worker、模型初始化、NumPy和PyTorch的种子,都要固定下来。
第六个是GPU和CPU的代码路径不一致。很多人在本地CPU上开发,在服务器GPU上训练,一个没注意就把.cuda()或者.to(device)写死了。好的习惯是让device从配置读取,代码里对CPU和GPU都能兼容,这样本地调试和线上训练才不会出问题。
第七个是忽略实验记录。不做实验记录,等于你的经验没有沉淀。调了学习率、换了模型结构、改了数据预处理,过两个月你全忘了。简单的做法是每个实验建一个目录,里面存config文件、训练日志、测试集评价结果,文件名用实验描述加时间戳。有条件的直接上wandb或者mlflow,一劳永逸。
4.2 排查AI系统问题的体系化方法
模型训练出问题的时候,最忌讳的就是病急乱投医。我总结了一套排查流程,按顺序走,大多数问题都能找到根因。
第一步看数据。Loss变成NaN,先查是不是数据里有NaN或者无穷大值;模型输出全是一个类别,先查类别分布是不是严重失衡;训练集上表现极差,先查数据标签是不是错了。数据排查往往能解决一半以上的"模型不收敛"问题。
第二步看训练曲线。我前面说过,训练损失降了验证损失不降,这是过拟合;两边都高位震荡,多半是学习率太大;训练损失几乎不降,要么是学习率太小,要么梯度消失了。把损失曲线画出来,比到处问人要快得多。
第三步看具体错误样本。模型说这个结果是错的,你要去看它到底在哪些输入上犯了错误,错误的模式是什么。比如情感分析里模型老是把含"not"的句子判错,那就说明模型还没有理解否定结构。这一步给你优化指明方向:要不要加数据、要不要换模型、要不要改预处理。
第四步看部署环节。模型推理结果和离线评测不一致?先检查是不是预处理环节不一致,训练时做了小写化和去标点,部署时没做,结果肯定对不上。再检查是不是模型的训练模式和推理模式弄混了,比如dropout和batch normalization在训练和推理时行为完全不同,PyTorch的model.eval()必须调用。
这套排查流程看着简单,但真的能帮你省下大量时间。我接手过很多"跑不动"的项目,按这个顺序排查,大概率能定位到具体环节。
4.3 工程化与实验管理的经验谈
做AI工程和做算法研究有一个很大的区别:算法研究追求SOTA,工程追求的是可控和可复现。从这一点出发,实验管理就不是"锦上添花",而是"刚需"。
我个人的习惯是每个项目维护三个文件:README.md记录项目背景和数据说明、config.yaml保存所有实验参数、experiments.md记录每次实验的动机、改动、结果和结论。这个做法看起来很朴素,但在项目跑了几个月之后价值极大。尤其是当你同时做了五六个方向的尝试,有了实验记录才能说清楚"哪条路是死路,哪条路有效"。
版本管理同样重要。模型文件有个特点:同样一份代码,训练出来的模型可能因为随机性、数据版本不同而产生差异。所以光有代码的Git版本不够,还要管住数据和模型。数据用dvc(Data Version Control)管理,模型用dvc搭配云存储或者简单的文件命名规范都行。原则只有一个:任何一份模型产物,都要能对应到它的代码版本和数据版本。
最后说两句关于协作的事。AI项目的协作难度通常比普通软件开发高,因为除了代码还有数据、模型、实验结果这些资产。如果你在团队里,建议尽早约定好这些资产的存放位置、命名规范、交接方式。这些看起来是小事,但数据文件叫data_final_v2.csv还是train_20240115.csv,直接决定了一个星期后别人找不找得到你要用的数据。AI工程本质上是人、机器、数据三者的协作工程,这条心得是我踩过很多坑之后才真正理解的。
5. 持续进阶:从完成项目到成为真正的AI工程师
5.1 学习资源怎么选:信息过载时代的筛选原则
现在网上的AI学习资源多到令人窒息,从Coursera的课程到GitHub的万星仓库,从B站的实战视频到各大厂的博客,真要用起来很容易陷入"收藏了等于学会了"的幻觉。我自己的筛选原则有三条,供你参考。
第一条是优先看官方文档和源码。PyTorch的官方教程、HuggingFace的文档、FastAPI的文档,这些一手材料信息密度高、更新及时,而且没有二手转述的损耗。遇到报错查Stack Overflow没问题,但系统学习一定要用官方材料打底。学会读源码是AI工程师拉开差距的关键能力,比如transformers库里的Trainer源码,读懂了你就对训练流程的理解比只会调用的人深一个层次。
第二条是实践导向的资源优先。一个课程好不好,不看它讲了多少概念,而看它有没有带你做完整项目。B站和YouTube上有很多hands-on类型的教程,跟着敲代码比自己只看书效率高好几倍。但要注意,跟着做项目不代表你学会了,你要能脱离教程、自己换一个数据集、重做一遍整个流程,才算真的吸收了。
第三条是紧跟一个小圈子。AI领域变化太快,去年火的框架今年可能就过时了。关注几个靠谱的社区或技术博客,比如HuggingFace的Blog、PyTorch的官方博客、以及一些优质的中文技术公众号,定期浏览,保持对技术趋势的敏感度就够了。不要试图什么都追,核心知识体系稳定下来,新框架都是纸老虎。
5.2 从单点项目到系统能力:一份可持续的进化路线
做完一个完整的文本分类项目之后,接下来怎么进阶?我见的很多人的做法是再做一个类似的项目,换个数据集、换个模型,本质上能力没有跃迁。我建议的路径是用四个方向来扩展你的能力边界。
第一个方向是挑战更复杂的任务类型。从文本分类到命名实体识别、到问答系统、到文本生成,每一步都在引入新的技术点。比如从分类到生成,你要理解自回归模型、解码策略(贪心、束搜索、采样)、以及评估生成质量的指标(BLEU、ROUGE),这套知识体系和判别式模型完全不同。
第二个方向是深入性能优化。同样的任务和数据,怎么把推理速度提上去、怎么把模型压缩到原来的十分之一还能保持效果,这是工业界的真需求。你可以学习模型量化、蒸馏、剪枝这些技术,尝试用ONNX或者TensorRT部署加速,把QPS从几十做到几百。
第三个方向是扩展到多模态。从纯文本到图文结合,比如做图片描述生成、视觉问答,这会让你接触视觉编码器和跨模态对齐的技术。多模态是目前AI工程的热门方向,提前布局没有坏处。
第四个方向是补系统工程能力。学一学消息队列、向量数据库、Kubernetes的基本用法。大一点的AI系统从来不是单个模型的服务,而是多个组件编排在一起:你需要知道数据从哪来、流到哪里、模型如何被调用、结果如何反馈。这个层面已经超出"AI"本身,进入了"AI系统架构"的范畴,但也正是高级AI工程师和普通算法工程师的分水岭。
这四个方向不用同时发力,按兴趣和业务需求选一两个深耕就够了。关键是要保持"完成闭环"的习惯:每学一个新方向,都要从数据集准备到部署运行走一遍完整链路,而不是只看教程和论文。
做AI工程最大的感受是,这个领域的知识是永远学不完的,但核心方法论就那么几条:数据先行、实验驱动、工程兜底。把这几条贯彻到每个项目里,无论技术栈怎么变,你都不会慌。从零开始不是一句口号,它意味着你每一个环节都亲手走过、亲手踩过坑、亲手填平了。这种经验,才是任何课程和教程都给不了你的东西。