
1. 从零搭建AI工程体系为什么我劝你别急着调包ai-engineering-from-scratch这个标题第一次看到的时候我愣了一下。不是因为陌生恰恰相反是因为太熟悉了——过去两年里我见过太多人抱着从零开始学AI工程的念头冲进来结果三天之后就开始复制粘贴别人的notebook两周之后连自己跑的是什么都说不清楚。这个项目标题背后藏着的其实是一个很朴素但极少有人真正做到的问题当你把所有的库、框架、预训练权重全部拿掉之后你还剩下什么我自己是从传统后端转过来的最早接触AI那会儿上来就是pip install transformers然后from_pretrained一把梭。模型能跑demo能出但一旦线上出了bad case我连从哪儿查起都不知道。tokenizer到底做了什么attention的显存为什么是平方级增长batch size调大之后为什么loss反而炸了这些问题在调包的路径下永远得不到答案。所以当我看到ai-engineering-from-scratch这个方向的时候我是真心觉得它值得认真写一写——它不是教你造轮子而是让你在拆开轮子之后再装回去的时候手不抖。这篇文章适合谁看三类人。第一类是有一定编程基础、但AI工程经验为零的开发者你想知道一个完整的AI系统从数据到上线到底经过哪些环节第二类是用过框架但心里没底的中级工程师你想把那些黑盒打开看看里面到底怎么回事第三类是做技术管理或者面试官的朋友你需要一套判断候选人是否真正理解AI工程的标准。全文我会围绕从零构建这个核心把数据管线、模型训练、推理服务、评估迭代这几个环节拆开讲每个环节都告诉你为什么这么设计、坑在哪里、怎么验证。需要提前说明的是from scratch不等于不用任何工具。真正的从零是指你理解每一层的职责边界知道什么时候该自己写、什么时候该用现成的。盲目造轮子是另一种形式的偷懒。下面我按实际项目推进的顺序来展开你可以当成一份施工图来看。2. 整体架构设计先想清楚边界再动手写代码2.1 为什么从零的第一步是画数据流图而不是建模型很多人一上来就想着搭网络结构这是典型的本末倒置。我在实际项目里踩过最大的坑就是模型训到一半发现数据格式不对回头改数据管线结果之前训的全废了。所以ai-engineering-from-scratch的第一课应该是先把数据从产生到消费的完整路径画出来。一个最小可用的AI工程系统数据流大致是这样的原始数据采集 → 清洗与去重 → 标注或弱标注 → 特征/分词处理 → 数据集切分 → 训练时动态加载 → 模型消费 → 输出后处理 → 评估回流。这条链上任何一环出问题最终表现都是模型效果不好但根因可能跟模型一点关系都没有。我见过一个团队调了两个月超参最后发现是训练集和验证集有重叠样本这种问题不画数据流图根本发现不了。画图的时候有个技巧每个节点标注三样东西——数据量级、更新频率、责任人。数据量级决定了你后面选什么存储和加载方式更新频率决定了你是批处理还是流处理责任人决定了出问题找谁。这三样东西写清楚架构就成型了一半。2.2 技术选型的取舍逻辑什么该自己写什么该用库from scratch最容易被误解的地方就是有人真的从矩阵乘法开始手写。我的观点很明确底层算子用成熟库工程骨架自己搭。原因很简单矩阵运算、卷积、attention这些底层实现成熟库经过大量优化和验证你自己写的版本在数值稳定性和性能上大概率更差这是重复造轮子。但数据管线、训练循环、服务封装这些工程骨架恰恰是体现你理解深度的地方也是出问题最多的地方必须自己掌控。具体来说我的选型原则是这样的层级建议理由数值计算用成熟张量库性能与数值稳定性经过验证自动求导用成熟框架手写反向传播极易出错数据加载自己封装业务逻辑强通用库难覆盖训练循环自己写需要精细控制日志、断点、恢复推理服务自己封装涉及并发、批处理、超时等业务需求评估体系自己建指标定义与业务强相关这张表的核心逻辑是越靠近业务、越需要定制的部分越应该自己写越靠近数学、越通用的部分越应该用库。很多人搞反了用现成的训练框架却自己写数据加载结果两头不讨好。2.3 目录结构一个能撑住半年迭代的工程骨架工程骨架最直观的体现就是目录结构。我试过很多种组织方式最后稳定下来的结构是这样的project/ data/ raw/ # 原始数据只读 interim/ # 中间产物可重建 processed/ # 最终训练数据 src/ data/ # 数据管线代码 model/ # 模型定义 train/ # 训练循环 eval/ # 评估逻辑 serve/ # 推理服务 configs/ # 配置文件 scripts/ # 入口脚本 tests/ # 测试 artifacts/ # 模型、日志等产物这个结构的关键在于data目录的三层划分。raw只读保证原始数据永远可追溯interim放中间产物随时可以删掉重建processed是训练直接消费的必须可复现。我见过太多项目把处理后的数据覆盖原始数据出了问题连回滚都做不到。另外configs单独抽出来是为了让实验可复现——同一个代码配不同配置跑出来的结果必须能对上。提示目录结构定下来之后写一个Makefile或者justfile把常用命令固化下来比如make data、make train、make eval。新人接手的时候看命令比看文档快得多。3. 数据管线AI工程里最脏最累但最值钱的活3.1 数据清洗的四个层次别想着一步到位数据清洗这件事新手容易走两个极端要么完全不洗要么想一次洗到完美。我的经验是分四个层次逐步推进每一层解决一类问题。第一层是格式清洗处理编码错误、字段缺失、类型不一致。这一层用脚本批量跑就行重点是记录清洗前后的数量变化任何一步清洗导致数据量骤降超过10%都要停下来查原因。第二层是内容清洗去重、去噪、过滤明显无意义的样本。去重我推荐用minhash或者simhash做近似去重精确去重会漏掉大量改写样本。第三层是质量清洗这一步需要定义质量信号比如文本长度、语言置信度、困惑度等用规则或者小模型打分过滤。第四层是分布清洗检查各类别、各来源的分布是否合理避免某一类样本占比过高导致模型偏置。这四层不要一次性全上而是每加一层就重新训一次baseline观察指标变化。我踩过的坑是一次性加了所有清洗规则结果指标掉了根本不知道是哪条规则的问题。分层推进虽然慢但每一步都可归因。3.2 分词器自己训一个还是用现成的分词器这个环节特别能体现from scratch的价值。用现成的分词器当然省事但你会遇到两个问题一是词表跟你的领域不匹配大量专业术语被切成碎片二是你完全不知道分词器对特殊字符、数字、多语言的处理逻辑出了问题无从下手。我的建议是通用场景用现成分词器垂直领域自己训一个。自己训分词器其实不难核心是准备好语料、选好算法BPE、WordPiece、Unigram各有适用场景、定好词表大小。词表大小这个参数很关键太小会导致序列过长太大则embedding矩阵浪费显存。经验值是中文场景3万到5万英文场景3万左右多语言场景10万以上。训完之后一定要做覆盖率检查随机抽1000条真实样本统计有多少token是词表外的OOVOOV比例超过1%就说明词表不合适。另外还要检查压缩率也就是原始字符数除以token数这个值太低说明分词太碎会拖慢训练和推理。3.3 数据集切分的三个陷阱数据集切分看起来简单实际上坑最多。第一个陷阱是随机切分导致数据泄漏。如果你的数据里有同一来源的多个样本随机切分会让训练集和验证集出现高度相似的样本验证指标虚高。正确做法是按来源、按时间、按用户等维度做分组切分。第二个陷阱是验证集太小导致指标波动大。验证集至少要保证每个类别有足够的样本量一般建议验证集占总数据的10%到20%但类别不平衡的时候要按类别分层采样。第三个陷阱是测试集被反复使用。测试集只能用一次用多了就变成了验证集失去了评估意义。我见过一个团队把测试集当验证集用了半年最后上线效果和测试指标差了十几个点。注意切分完之后把三个集合的样本ID列表存下来写进版本控制。任何一次实验都要能追溯到用的是哪个版本的切分。3.4 数据加载的性能优化别让GPU等数据训练的时候GPU利用率上不去十有八九是数据加载拖了后腿。优化数据加载有几个层次最基础的是预取用多进程或者多线程提前把下一批数据准备好进阶的是缓存把处理好的数据缓存到内存或者本地磁盘再进一步是格式优化把数据存成列式格式或者二进制格式读取速度比文本快一个数量级。我实测下来一个中等规模的数据集百万级样本从文本格式换成二进制格式加载速度能提升5到10倍。另外要注意shuffle的代价全局shuffle需要把整个数据集读进内存大数据集下不现实通常用shuffle buffer做局部打乱buffer大小设成batch size的100到1000倍比较合适。4. 模型训练把黑盒拆开看清楚每一步在干什么4.1 训练循环的骨架五个必须自己控制的环节用现成的训练框架一个trainer.train()就完事了。但from scratch要求你自己写训练循环因为只有自己写你才能控制这五个环节梯度累积、混合精度、梯度裁剪、学习率调度、检查点保存。梯度累积是为了在小显存上模拟大batch核心是每累积N步才更新一次参数注意loss要除以N。混合精度能省显存加速训练但要注意loss scaling否则梯度会下溢。梯度裁剪防止梯度爆炸通常按范数裁剪阈值设1.0是常见起点。学习率调度里warmup特别重要前几百步用线性warmup能显著稳定训练。检查点保存要同时存模型参数、优化器状态、学习率调度器状态和随机种子少一样都没法精确恢复。这五个环节每一个都有坑。比如梯度累积和batch norm一起用的时候统计量是按micro-batch算的跟真实大batch不一致这时候要么改用layer norm要么调整momentum。这些细节不自己写一遍是体会不到的。4.2 超参选择的经验法则超参调优是玄学重灾区但有一些经验法则能帮你少走弯路。学习率是最重要的超参一般从1e-4到1e-3之间试大模型用小学习率小模型用大学习率。batch size和学习率要联动batch size翻倍学习率通常也要相应增大但不是线性关系平方根关系更常见。权重衰减一般设0.01到0.1太小没效果太大欠拟合。dropout在数据量大的时候可以关掉数据量小的时候0.1到0.3比较合适。我的实操流程是先用一组保守的超参跑通全流程确认没有bug然后固定其他参数单独调学习率找到loss下降最快的值再调batch size观察显存和收敛速度的平衡最后微调正则化参数。整个过程用配置文件管理每次实验记录配置和结果方便对比。4.3 训练过程的监控看什么指标什么时候该停训练监控不是看个loss曲线就完事了。我通常同时盯四个指标训练loss、验证loss、学习率、梯度范数。训练loss下降但验证loss上升是过拟合两个都不降是欠拟合或者学习率太小loss突然飙升多半是梯度爆炸梯度范数长期接近0说明梯度消失。早停策略也很关键。我一般设两个条件验证指标连续N个epoch不提升就停或者验证指标提升幅度小于阈值就停。N通常设3到5阈值看具体任务。早停能省大量算力但要注意早停的耐心值跟学习率调度要配合如果学习率还在下降阶段就早停可能错过后面的提升。4.4 显存不够怎么办六个层次的优化手段显存不够是训练时的常见问题从易到难有六个层次的优化手段。第一层是减小batch size最直接但影响训练稳定性。第二层是梯度累积用小batch模拟大batch。第三层是混合精度能省30%到50%显存。第四层是梯度检查点用计算换显存能省大量激活值显存但训练变慢。第五层是模型并行把模型切到多卡上。第六层是offload把部分参数或优化器状态放到CPU内存。这六层不是越往后越好而是按需选择。大部分场景前四层就够了模型并行和offload会引入额外的通信开销和复杂度非必要不用。我个人的经验是先把前四层用满实在不够再考虑后面两层。5. 推理服务从能跑到能扛住线上流量5.1 推理服务的三个核心指标模型训完只是开始推理服务才是真正面对用户的环节。评估一个推理服务核心看三个指标延迟、吞吐、资源占用。延迟是单个请求的响应时间通常看P50、P95、P99三个分位数P99特别重要因为它决定了最差体验。吞吐是单位时间能处理的请求数跟批处理策略强相关。资源占用包括显存、内存、CPU决定了你的部署成本。这三个指标是相互制约的。批处理能提升吞吐但会增加延迟量化能降低资源占用但可能损失精度。所以推理服务的核心工作是在这三者之间找平衡点而这个平衡点取决于你的业务场景。实时交互场景优先延迟离线批处理场景优先吞吐。5.2 批处理策略动态批处理怎么实现动态批处理是提升吞吐的关键技术。核心思想是不固定batch size而是攒够一定数量或者等够一定时间就发一批。实现上有两个参数最大batch size和最大等待时间。最大batch size受显存限制最大等待时间受延迟要求限制。我实现过一个简单的动态批处理调度器逻辑是这样的请求进来先进队列调度器每隔一小段时间检查队列如果队列长度达到阈值或者最早请求的等待时间超过阈值就取出一批请求组成batch送进模型。这个逻辑用生产者-消费者模式实现注意要处理好超时和异常避免请求卡死。5.3 模型量化与加速精度和速度的权衡量化是推理加速的常用手段把浮点参数转成低精度整数能显著降低显存占用和加速计算。常见的有动态量化、静态量化和量化感知训练三种。动态量化最简单训练后直接转但加速效果有限静态量化需要校准数据加速效果更好量化感知训练在训练时就模拟量化误差精度损失最小但需要重新训练。我的建议是先试动态量化精度损失可接受就用不行再试静态量化还不行才考虑量化感知训练。量化后一定要做精度对比在验证集上跑一遍指标掉超过1%就要谨慎。另外要注意不是所有层都适合量化embedding层和最后的输出层通常保持高精度。5.4 服务封装接口设计、错误处理和限流推理服务的接口设计要遵循几个原则输入校验前置、错误信息明确、超时可控。输入校验前置是指在进入模型之前就把格式不对的请求拦掉避免浪费算力。错误信息明确是指出错时要告诉调用方是参数问题还是服务问题方便排查。超时可控是指每个请求都要有超时时间避免慢请求拖垮整个服务。限流是保护服务的最后一道防线。常见的限流算法有令牌桶和漏桶令牌桶允许突发流量漏桶则更平滑。我一般用令牌桶配置上根据服务的实际承载能力设定速率和桶容量。限流触发时要返回明确的错误码让调用方知道是限流而不是服务挂了。6. 评估与迭代让系统持续变好的闭环6.1 离线评估和在线评估的分工评估体系分离线评估和在线评估两部分。离线评估在测试集上跑快速迭代但跟真实效果有差距。在线评估用真实流量做A/B测试结果可信但成本高、周期长。两者的分工是离线评估做筛选在线评估做决策。离线评估的指标要跟业务目标对齐。分类任务看准确率、召回率、F1生成任务看BLEU、ROUGE或者人工评估排序任务看NDCG、MRR。但要注意离线指标高不代表线上效果好因为线上数据分布跟测试集可能不一样。所以离线评估通过之后一定要小流量上线验证。6.2 构建评估集的三个原则评估集的质量直接决定评估结果的可信度。构建评估集有三个原则代表性、独立性、稳定性。代表性是指评估集要覆盖真实场景的各种情况包括长尾case。独立性是指评估集不能跟训练集有重叠也不能被反复用来调参。稳定性是指评估集要固定下来不能随意增删样本否则指标没法横向对比。我通常会把评估集分成两部分公开评估集用于日常迭代私有评估集用于最终验收。公开评估集可以频繁使用私有评估集只在关键节点用避免过拟合。6.3 从bad case到改进一个可复用的分析流程线上出bad case是常态关键是怎么从bad case里挖出改进点。我的分析流程是四步收集、归类、归因、验证。收集是把bad case集中起来归类是按问题类型分组归因是分析每类问题的根因验证是针对根因提出改进方案并验证效果。归类这一步特别重要因为bad case往往是长尾分布不归类根本看不出规律。我一般会定义几个维度输入类型、错误类型、影响程度。归因的时候要区分是数据问题、模型问题还是工程问题不同问题解法完全不同。验证的时候要控制变量一次只改一个地方否则不知道是哪个改动起了作用。6.4 版本管理与实验追踪别让实验变成一团乱麻做AI工程实验数量会快速增长没有好的版本管理和实验追踪很快就会乱套。我的做法是代码用git管理数据用版本号管理实验用配置文件管理。每次实验记录配置、代码commit、数据版本、评估结果存到一个统一的实验记录系统里。实验追踪工具市面上有不少但我建议至少自己实现一个最小版本一个表格记录实验ID、配置、指标、备注。这个表格用csv或者数据库存都行关键是坚持记录。我见过太多团队实验做了一堆最后要复现某个结果的时候找不到配置只能重跑浪费大量时间。7. 我踩过的坑和给你的实操建议7.1 那些文档里不会写的教训第一个教训永远不要相信数据已经处理好了这句话。我接手过一个项目前任说数据已经清洗过了结果我抽查了100条发现里面有20条是重复的。从那以后我接手任何项目第一件事就是自己抽查数据不看不放心。第二个教训训练日志要记全尤其是随机种子。有一次我训出一个效果特别好的模型想复现的时候发现没记种子怎么跑都跑不出那个结果。后来我强制要求所有训练脚本必须记录种子并且支持从种子恢复。第三个教训推理服务的性能测试要在真实数据上做。我用合成数据测出来延迟很低上线之后发现真实请求的输入长度分布跟合成数据完全不同延迟翻了三倍。性能测试一定要用真实流量采样。7.2 新手最容易犯的五个错误第一个错误是跳过baseline直接上复杂模型。没有baseline你根本不知道复杂模型带来的提升是真的还是运气。第二个错误是在验证集上反复调参。调多了验证集就失效了必须留一个从没碰过的测试集。第三个错误是忽略数据质量只关注模型。数据是地基地基不稳模型再好也白搭。第四个错误是不做错误分析只看总体指标。总体指标掩盖了大量细节问题必须做分类分析。第五个错误是过早优化性能。先跑通再优化过早优化会让你陷入细节出不来。7.3 工具链推荐少而精的选择工具不在多在于用熟。我的核心工具链是这样的版本控制用git环境管理用conda或者venv实验追踪用自己搭的表格加tensorboard配置管理用yaml测试用pytest。这些工具都足够成熟学习成本低组合起来能覆盖大部分需求。不要盲目追新工具。我见过团队每出一个新框架就换一次结果每个都只懂皮毛出了问题都不知道怎么查。工具链稳定下来之后把精力放在业务和算法上这才是正道。7.4 从零到一的路线图给不同阶段的你如果你是完全的新手我的建议是先跑通一个最小闭环用一个小数据集训一个小模型做一个简单的推理接口走完整个流程。这个闭环可能很粗糙但能让你理解各个环节的衔接。如果你已经能跑通闭环下一步是优化每个环节数据管线加缓存训练加混合精度推理加批处理评估加错误分析。每优化一个环节记录前后的指标变化积累经验。如果你已经能优化各个环节再下一步是建立体系把流程标准化把经验文档化把监控自动化。这时候你的重点从技术转向工程管理怎么让团队高效协作怎么让系统稳定运行。这个路线图不是线性的很多环节会反复迭代。但大方向是这样从点到线到面从能跑到跑好到跑稳。AI工程这件事急不得但也停不得每天进步一点半年之后回头看你会发现自己已经走了很远。