
ai-engineering-from-scratch懂行的人看到这个标题会先笑一下又要从零开始撸AI了。但新朋友别误会这里的from scratch跟那个图形化编程工具Scratch半毛钱关系都没有它是英文“从零开始”的意思——不拿现成的黑盒大模型API拼一个demo而是把数据、模型、训练、部署、评测这条AI工程链路自己动手完整走一遍。这篇文章就是用我的实际经验把这套从0到1的路径拆开揉碎告诉你每一步该做什么、为什么这么做、会踩哪些坑适合那些不想只会调包、想真正理解AI系统是怎么工作的开发者。1. 先想清楚从零开始的AI工程到底在搭什么1.1 别上来就写代码先定义“从零开始”的边界很多朋友一听到“from scratch”第一反应是连numpy、PyTorch都要自己造轮子。真不是这个意思。我们做AI工程说的是不直接拿现成的黑盒模型接口来拼业务而是自己掌握模型训练、调优、部署和迭代的完整能力。这就好比你要开一家餐厅你说“从零开始做菜”不意味着你要自己种菜、自己铸铁锅但至少调味、火候、食材处理这些核心环节你得自己说了算而不是直接买料理包加热端上来。我见过最典型的翻车案例一个团队花了一周调API、搭Promptdemo跑得贼好结果一上生产就废了。为什么因为请求延迟不稳定、成本飙升、bad case没人能改。问题的根子就在于——他们根本没碰过模型训练遇到模型行为不符合预期时只能靠改提示词试运气没有任何可诊断、可干预的手段。所以真正的ai-engineering-from-scratch目标是让你拥有“打开模型黑盒”的能力。1.2 一个最小可用AI工程包含的五个层级我做了这些年AI项目踩过无数坑之后总结出一个最小可用工程必须包含的五个层面缺一不可。这五个层面不是按时间顺序执行的而是像一座房子得同步考虑层级核心职责常用工具/手段最常犯的错数据层采集、清洗、标注、版本管理Pandas、DVC、标注平台不管理数据版本改完数据集说不清改了什么模型层架构选型、参数配置、训练策略PyTorch、HuggingFace、DeepSpeed盲目追SOTA模型过大导致根本训不动训练层超参调节、日志监控、稳定性维护WanDB、TensorBoard、混合精度只看train loss漏看eval loss和grad norm服务层推理加速、接口封装、性能调优ONNX Runtime、vLLM、Docker直接裸上PyTorch推理QPS上不去评测层离线指标、在线AB、数据回流sklearn、AB平台、标注抽检只用Accuracy一锤子定生死这五个层级每一层都能写一本手册。但在从零起步时我建议你的精力分配是数据占40%、训练占25%、部署和评测各占15%、架构选型占5%。为什么数据要占这么多因为后面所有问题八成都能追溯到数据头上。我后面会细讲。2. 数据地基喂给模型的每一口都要干净2.1 数据量和质量哪个更要命直接给结论对AI工程来说脏数据比缺数据可怕得多。我自己做过一个文本分类项目原始数据里有大概1%的错标标签。一开始没在意结果模型F1怎么调都卡在0.82上不去。后来把错标样本捞出来重标之后同样的训练配置F1直接跳到0.89。1%的脏数据能拉低五六个点这个代价比很多人想象的大得多。倒过来说数据量在深度学习时代数据量没有一个绝对的门槛但可以给你一个经验参考。分类任务每个类别起步建议1000到5000条太少的话模型学不到类内差异。生成类任务比如文本摘要、代码生成需要的不是条数多而是“多样性”大同样一句话换个说法、换个场景对模型来说就是新样本。你如果只有2万条高度重复的新闻稿效果大概率不如8000条五花八门的口语对话。那么质量怎么判断我一般分三步走第一步机器粗筛。去重用simhash相似度超过0.85的标出来人工看格式用正则清理比如HTML标签、乱码字符、全半角混乱。第二步规则过滤。根据你的业务场景写黑名单比如广告词、辱骂词、隐私信息身份证、手机号、银行卡命中直接删。第三步人工抽检。随机抽200条按干净/可用/垃圾三类打标垃圾比例超过5%就回到第一步重新清洗。2.2 标注一致性两个标注员吵起来怎么办有监督任务绕不开标注。很多项目死在“标注质量不稳定”这件事上。最专业的做法是让两个标注员独立标注同一批样本然后算Cohen‘s Kappa一致性系数。公式是κ (P₀ - Pₑ) / (1 - Pₑ)其中P₀是实际一致率Pₑ是随机一致率。κ大于0.7说明标注规则清楚可用低于0.5别急着训练先回炉改标注规范。举一个我踩过的具体例子。做一个情感分类项目A标注员把“手机掉地上屏幕碎了”标为“差评”B标注员标为“中性描述”。两个人吵得不可开交。后来一查规范发现规范里只定义了“对产品和服务的评价”没有定义“事实描述算不算情感表达”。这就是标注规范不完整导致的。最后我们加了例子集规定“有明确情感倾向词的才计入情感类纯事实描述算中性”kappa从0.51涨到了0.82。数据增强这块我的建议是先把干净的原始数据、一致性达标的标注搞定再考虑增强。图像任务可以做随机裁剪、翻转、色彩抖动文本任务可以搞同义词替换、回译、随机删除。但有一条铁律——增强一定不能改变标签语义。曾经有团队做文本分类把“这部电影太烂了”回译成“这部电影太棒了”标签还没变模型直接学歪了。这种低级错误得靠抽检拦截。2.3 用DVC管数据版本别让数据集失控很多人把代码用Git管得好好的数据集却靠复制粘贴改名存网盘。结果就是训练跑完、模型上线三个月之后想复盘已经说不清当时用的数据是哪一版了。我的做法是用DVCData Version Control管数据。核心思路是代码进Git数据进对象存储DVC把数据和代码的版本关系记下来。具体操作很简单dvc init 初始化dvc add data/ 把数据目录纳入管理它会生成一个 .dvc 文件其实是一个指向数据的指针git add data.dvc git commit把指针提交进Gitdvc remote add myremote s3://your-bucket配置数据存储的远程位置dvc push 把数据推到远程。之后任何人checkout某个commit只需要 dvc pull就能把这个commit对应的数据版本完整拉下来。实测下来这套流程让团队协作和问题回溯的难度降了大半。你不需要记住什么“0715_final_v3清洗版”这种反人类的文件名因为你根本不会出现这种文件。3. 模型选型不追SOTA选对才省事3.1 任务类型决定模型家族模型选型这件事我见过太多人一上来就上大模型仿佛不用百亿参数就显示不出自己的水平。结果显存爆了、训练时间按周算、效果还不如一个小模型。记住一句话模型是工具的集合不是炫技的奖杯。我们按任务类型来选文本分类、情感分析、命名实体识别这类“理解型任务”用Encoder架构比如BERT、RoBERTa系列。模型小、速度快、效果稳定。文本生成、对话、代码补全这类“生成型任务”用Decoder架构比如GPT系列、LLaMA系列。它一次吐一个token天然适合续写。翻译、摘要这类“序列到序列任务”传统上用Encoder-Decoder架构比如T5但现在很多场景也被大模型替代了。多模态任务另说但核心思路是一样的先明确任务本质再选结构。我自己做一个商品评论分类器就是从中文RoBERTa-wwm-ext-small开始的。93M参数单卡就能训练线上推理CPU都能扛。你说它为啥不用GPT-4级别的模型没那个必要。杀鸡用牛刀烧钱还不一定好用。3.2 从0训练还是微调这个问题很贵要想清楚从零预训练一个模型和拿开源模型做微调是两个完全不同的工程路线。判断标准我总结成三条数据量少于100万条又属于通用领域直接微调开源模型省时省力。数据量几百万到上千万且数据分布非常独特比如医疗病历、法律文书、代码仓库可以考虑从零预训练一个小规模基座再在下游任务上微调。数据量上亿级或者你有明确的长期成本优势才值得认真考虑大规模预训练。成本怎么算拿1亿参数100M的模型举例。它用fp16存权重占200MB。看起来不大对吧但混合精度训练时光优化器状态就远超这个数。AdamW优化器需要为每个参数保存两份动量momentum和variance一份fp32是4字节两份就是8字节。再加上主权重fp32的4字节梯度fp16的2字节算下来每个参数约14字节。1亿参数就是1.4GB。就是说一个100M参数的模型训练时显存需求大概是1.4GB起步还没算激活值。激活值更夸张它跟batch size、序列长度成正比。想做从零训练这个账要先算明白。我在本地用4张RTX 3090每张24GB训过一个100M参数的GPT风格模型数据大约800万条中英文混合文本。跑了大概一周loss降到了2.8左右。效果能看但绝对算不上惊艳。这个经历给我的教训是从零训练是重资产游戏没有明确理由别轻易入场。3.3 架构配置里的隐藏成本选好了模型家族还有很多细枝末节的配置要关注。比如位置编码用绝对还是相对归一化层放在attention前后激活函数用GELU还是ReGLU。这些听起来只是“细节”但对训练稳定性和最终效果影响不小。我的建议是新手阶段别自己发明架构直接抄成熟开源项目的默认配置。还有一个隐藏成本特别容易被忽略——序列长度。Transformer的注意力计算量是O(n²)序列长度翻倍计算量涨四倍。如果你要做长文档分类优先考虑“先检索关键片段再让模型看摘要”的工程方案而不是无脑把序列长度拉大到8192。我见过有人为了让模型看完整篇文章序列长度设成16384结果训练速度慢到让人怀疑人生。这完全可以通过工程手段绕开。4. 训练与调优盯住这几个信号别瞎试参数4.1 一份可以直接抄的初始超参清单超参数调节是个无底洞我分享一份自己试过很多次、实测稳定的初始配置。适用于大多数Transformer模型的微调和中小规模预训练参数名推荐初始值说明优化器AdamW比SGD稳定带权重衰减学习率3e-4Transformer的标配起点太高容易炸学习率调度warmup cosine decaywarmup比例0.03到0.1Batch Size32到128受显存限制不行就用梯度累积权重衰减0.01AdamW默认常用值Grad Clip1.0限制梯度范数防梯度爆炸训练轮数3到10看eval loss拐点混合精度fp16或bf16显存减半速度翻倍有几个关键理解要展开说。第一为什么学习率用3e-4而不是1e-2Transformer的loss landscape非常崎岖学习率太大训练几步就会发散你会看到loss直接变成NaN。我在一个文本生成项目里把lr设成5e-3训练到第120步loss从2.1跳到NaN整个实验白跑。教训就是宁可小不可大。第二Batch Size为什么不是一个固定的值因为它是“梯度估计的精度”。batch太小梯度噪声大训练不稳batch太大收敛方向太死容易陷进尖锐极小值。一个折中方法是使用梯度累积如果你显存只能放下batch为8的数据但你想要batch为32的效果那就连续算4个batch的梯度累加之后再更新一次参数。注意这是累加而不是平均等价于放大了学习率所以如果用梯度累积学习率也要相应调整。4.2 训练日志里最值得看的四条曲线训练的时候不要盯着一堆指标发呆。真正重要的曲线就四条train loss、eval loss、grad norm、learning rate。train loss下降说明模型在拟合训练集。如果它一直不降或者降得太慢先检查数据、再检查lr。eval loss是模型在没见过的数据上的表现。当train loss还在降、eval loss开始回升——恭喜你过拟合了。这时要么加数据增强要么加dropout要么提前停止。grad norm监控梯度范数。正常训练中它的值应该在某个范围内稳定波动。如果突然飙升到几千几万说明梯度爆炸了赶紧查lr和grad clip。learning rate曲线是配合查看进度用的。warmup阶段像预热引擎cosine decay阶段像缓慢降温理解它你才知道模型在哪个阶段。我习惯把WanDB或TensorBoard的日志挂在训练全程每隔几百步瞟一眼。特别是eval loss一旦出现连续5个评估点不降反升我就会手动停掉实验调低学习率或者调整正则化而不是放任它继续跑几个小时。4.3 过拟合、欠拟合处理的优先级不能搞反先说结论先解决欠拟合再解决过拟合。很多新手一上来就加dropout、加正则化但模型本身连训练集都学不好这些正则化措施只会雪上加霜。欠拟合的信号很明确train loss和eval loss都高都下不去。处理方法按优先级排加大模型容量换更大模型、加层数、加宽度。增加训练轮数确认它只是没学够。调高学习率如果当前lr过小导致更新太慢。过拟合的信号是train loss低、eval loss高。处理顺序增加数据多样性数据增强、收集更多真实数据。加正则化dropout、权重衰减调大。提前停止在eval loss开始上升的附近截断。降低模型容量很多时候小模型反而泛化更好。我在做中文评论情感分类时模型用的是RoBERTa-wwm-ext-base训练5轮后train loss已经降到0.05但eval loss在0.4附近死活下不去。后来加了dropout到0.1eval loss降到了0.35又做了一版数据增强同义词替换降到了0.31。这种组合拳比单纯堆数据量省事得多。5. 推理部署把模型从训练机搬到生产环境5.1 模型压缩先把量化这一课补上训练好的模型不能直接抱着PyTorch环境上生产。原因有两个一是PyTorch推理框架太重启动慢、占用高二是模型动辄几百MB推理速度和延迟都不达标。我的标准流程是先做模型压缩再部署。量化是目前性价比最高的压缩手段。核心思路是把模型的浮点权重从fp32压到int8甚至int4模型体积直接缩小4到8倍推理速度翻倍而精度损失通常可控制在1到2个点以内。做量化的两种主流方式是PTQ训练后量化不需要重新训练拿一批校准数据跑一遍统计出每层的动态范围然后做定点转换。简单快捷适合分类、匹配这类任务。QAT量化感知训练在训练过程中模拟量化的误差让模型适应低精度表示。效果好但成本高适合对精度极度敏感的任务。我自己在一个BERT分类模型上做过PTQ从fp32转int8F1从0.89掉到0.87模型大小从400MB减到105MBGPU推理延迟从12ms降到5ms。这个性价比完全值得做。如果你用CPU部署差距会更明显int8模型配合AVX512指令集速度提升接近3倍。5.2 动态Batching和缓存QPS上不去的解药很多人把模型部署上线后发现QPS就是上不去。为什么因为模型推理是“请求进来才计算”每次请求都占用全部GPU资源算一个结果这是巨大的浪费。好比一个厨师每来一桌客人就从头点火开炒不管客人点的是不是同样的菜。解决方案是动态BatchingContinuous Batching。思路是把多个并发请求攒在一起凑够一个batch再喂给模型。这样GPU资源被充分利用吞吐量成倍上涨。如果模型需要处理大量重复或相似请求加一层缓存Cache效果立竿见影。我做过一个FAQ问答系统大概30%的问题是重复出现的。在数据库里加了一张cache表旧问题命中后直接返回历史答案不经过模型。整个服务的实际QPS提升了几乎一半而模型本身的负载还降了30%。关于KV Cache也得说两句。生成模型推理时每生成一个新token都需要重新计算前面所有token的注意力。KV Cache的作用是把之前已经算好的key和value缓存下来只计算新token对应的部分。这个技术是开源框架vLLM的核心优化点能带来数量级级别的生成加速。如果你做大模型服务选型时一定要确认框架支持这个特性。5.3 部署框架选型按场景选别跟风部署框架五花八门我的建议是分场景选择场景推荐方案原因小模型、CPU部署ONNX Runtime OpenVINO轻量、生态成熟、CPU优化好大模型、GPU部署vLLM / TensorRT-LLM支持连续batching、PagedAttention边缘设备TensorRT / ONNX Runtime能跑int8/int4量化模型即可快速原型PyTorch TorchServe部署快但性能一般给一个明确的实操建议中小模型先导出ONNX用ONNX Runtime跑通再考虑TensorRT。很多项目卡在“ONNX导出失败”这一步原因多半是模型里有动态控制流或者自定义算子。遇到这种问题不要硬刚用torch.onnx.export的dynamic_axes参数指定动态维度或者用onnxsimplifier做简化九成的情况能解决。关于Docker我的铁律是镜像里只装推理所需的最小环境。不要为了省事把整个训练环境打包进去。我见过有人把一个带CUDA、cuDNN、全套PyTorch训练库的镜像推到生产结果镜像8个GB冷启动要一分钟。精简后的推理镜像控制在1GB以内冷启动10秒级这才是生产级做法。6. 评测与迭代上线只是开始数据闭环才是护城河6.1 离线指标别只看Accuracy要看切片很多团队上线前只看一个“准确率95%”就觉得万事大吉。结果产品一上线用户反馈稀烂。为什么因为全局指标会掩盖局部问题。假设你的模型对99%的简单样本都做对了剩下1%的困难样本全错准确率还是99%但这个模型在困难样本上就是不可用的。正确的做法是做“切片评测”按数据维度拆开看指标。比如按文本长度分桶短文本准确率98%长文本准确率60%那问题就很明显了。按类别看召回率某些小众类别F1只有0.3全局均值0.85根本反映不出来。这个表一定要在训练报告里写清楚否则上线后出了问题你连数据依据都没有。对于生成类任务BLEU、ROUGE这些自动指标只能做粗筛它们跟人类判断的相关性有限。我自己的习惯是必须配人工评测。找2到3个人打乱模型输出顺序做盲评哪怕只看200条样本也能给你远比BLEU靠谱的信号。6.2 上线后立刻把数据闭环建起来模型上线不是终点而是下一轮迭代的起点。我见过太多团队“上线就撒手”完全不看模型在真实环境里的表现等到用户投诉攒了一堆才回头修周期长、效率低。数据闭环要做三件事第一件在线日志。把每次请求的输入、模型输出、用户反馈点赞/点踩/跳过存下来这是你最珍贵的真实数据源。第二件bad case回流。定期把线上表现差的样本捞出来人工标注加进训练集。这个流程不能停哪怕一周只加200条效果也远超闭门造车。第三件AB测试。想改模型先在线上开一个小流量实验对比新老模型的点击率、转化率、满意度。不要拿“我感觉新模型更好”这种话去说服别人要用数据说话。我做推荐排序项目时靠的就是这个闭环每周从线上抽3000条点击数据人工标注正负样本合并到训练集里重新训练效果周周都在涨。半年后模型CTR从3.1%涨到4.2%涨幅远超一开始“一把梭优化模型结构”那段时间。6.3 模型版本管理与回滚是保命符很多人做AI工程不重视版本管理觉得那是纯软件团队的活。直到线上出了问题才手忙脚乱想回滚结果发现“上一个模型”文件早不知道被谁覆盖了。我的建议是模型的每一次训练产出都要带上三样东西——模型文件本身、训练数据的版本DVC commit id、训练代码的版本Git commit id、训练超参配置WanDB截图或yaml文件。这些信息互相绑定任何一个出问题都能回溯到源头。用模型注册表管理MLflow或自定义目录结构都可以每次上线前记录passed/failed的评测结果。有回滚方案是底线。上线前把目前线上的模型备份到指定目录万一线上新模型出事能在一分钟内切回去。回滚不是无能的象征而是成熟的标志。7. 大实话我踩过的坑和给你的建议7.1 训练loss不降按这个顺序去查这是新手遇到最多的问题我的排查顺序是固定的检查数据。用一个小batch直接打印输入输出看数据是不是乱的。比如label和输入对不上、文本是空的、padding位mask没弄对。这一步能排除80%的问题。检查学习率。lr设得太小会卡死不降太大直接NaN。尝试调到3e-4或者1e-4。检查模型结构。最后几层的维度对不对有没有搞错分类头。检查loss scale。混合精度训练时如果loss scale一直很小且梯度全被截断数值精度就有问题。换成bf16试试。我印象最深的一次一个小型文本分类项目loss前200步纹丝不动。我把所有配置翻了个底朝天最后发现是数据加载时batch的sample默认开了shuffleTrue但我的label和feature没对齐。就这一个细节浪费了我整整一天。所以第一条“先看数据”永远放在首位。7.2 显存OOM很多人不知道梯度累积这招显存不够是家常便饭。当你看到CUDA out of memory时第一反应不要是“换更小的模型”而是按顺序尝试调小batch size。最简单但注意调小之后梯度噪声变大训练可能会不稳。开梯度累积。保持batch size在所有维度的“等效值”不变把一个大batch拆成多个小batch分步累加梯度。开激活值检查点activation checkpointing。训练时不保存中间激活值反向传播时重新计算。这招能让显存占用降30%-50%代价是训练速度降低20%-30%。必要时换小模型或者用DeepSpeed ZeRO Stage 2/3把优化器状态切分到多卡。我自己的经验是在2张24GB卡上训一个350M的模型如果只用标准配置会OOM但把batch size降到16、gradient accumulation设为4、打开activation checkpointing之后不仅不OOM训练效果还比原来更稳。原因很简单等效batch size大了梯度估计更准确。7.3 指标好看线上效果却崩了多半是数据泄漏这是一个很隐蔽的坑但一旦踩中后果极其严重。数据泄漏的核心特征是训练时模型偷偷看到了“不该看的信息”导致评估结果虚高上线后打回原形。最常见的三类泄漏时间泄漏。按时间划分训练集和测试集而不是随机划分。比如用1-10月数据训练11-12月数据测试。特征泄漏。模型的输入特征里混入了未来信息。比如做电商推荐时把一个用户在“点击后”才产生的行为当成特征。组泄漏。同一个用户的不同样本被分到了训练集和测试集两侧。模型记住的是用户ID而不是行为规律。我做过一个线上效果惨败的项目离线AUC高达0.92上线后不到0.70。最后定位到问题特征工程里把“当天最终购买记录”当成特征了离线评估时模型自然神勇。这就是典型特征泄漏。验证方法也很简单在线上随机抽取200条带真实label的样本离线推理对比如果准确率远低于测试集指标要么泄漏要么分布漂移。7.4 给新手的几条忠告都是我拿时间换来的第一条先做小项目闭环。选一个小的分类任务从数据到部署完整跑通再去做大模型。很多人一上来就想训练GPT结果连loss都不认更别提定位问题了。第二条所有实验必须有日志。一开始觉得麻烦后来你会发现这是最划算的投资。WanDB免费版够用每个实验自动记录超参、指标和模型文件复盘的时候节省的时间远超你记录的时间。第三条不要迷信“别人说好用的模型”。模型的效果高度依赖数据分布和任务类型。你必须在自己的数据上做一个baseline实验用数据说服自己。我做项目时永远先跑一个小模型baseline哪怕效果差它也能给你一个“最低水位线”后续所有模型才有比较基准。第四条注意身体。训练大模型跑几天几夜盯着loss曲线失眠是常态。现在我的原则是晚上睡觉前启动训练早上起来看曲线只要不是NaN就让它跑着该睡觉睡觉。AI工程是持久战不是熬夜战。第五条不要试图一次性解决所有问题。从数据、到模型、到部署、到评测你可以选择任何一个方向深挖但作为起步先拿一个端到端闭环练手比什么都重要。等你把这条链路走通了再回头看那些复杂的分布式训练、模型并行会发现它们不过是在这条主线上的优化分支而已。我个人这些年最大的体会是AI工程的能力不是来自你调过多少个模型而是来自你把一条端到端链路走通过多少遍。走通一遍你就知道问题会出现在哪里走通十遍你就知道哪些方案在什么场景下一定会踩坑。这个过程没有捷径只有亲手做一遍才算数。最后再分享一个小技巧把每个实验的config文件和WanDB日志放在同一个Git标签下。这个习惯已经帮我节省了无数次的回溯时间。当你三个月后想回答“这个模型当时是用什么数据、什么参数跑出来的”时你会感谢这个动作。