从零构建推理模型:AI工程from scratch全流程拆解
发布时间:2026/10/2 5:43:39 锦皓数字建站

“AI工程”这个词这两年已经被说烂了但“from scratch”依然是那个能快速区分“会用别人的系统”和“能造系统”的试金石。我不止一次在技术群里看到有人问我想学AI是不是该先从读PyTorch源码开始是不是要先把Transformer论文背下来我的回答通常是别绕直接动手搭一个小而完整的推理模型从数据清洗到训练再到部署走一遍比啃十篇教程都有用。这篇文章要聊的就是“ai-engineering-from-scratch”这个主题下我实际做过的完整路径如何从零开始构建一个能用的推理模型需要的知识、工具、步骤以及那些文档里不会写但你必须知道的坑。不论你是有一定编程基础想切入AI工程的新人还是已经用过开源模型、想搞懂底层细节的工程师这个拆解都适合你。1. 为什么要把“AI工程”从零过一遍1.1 from scratch 到底割掉什么很多人觉得“from scratch”意味着从数学原理开始推导那是一个误区。AI工程里的“从头”说的是不直接使用现成的、封装好的预训练大模型API也不直接把Hugging Face上的checkpoint下载下来就完事。你要自己决定数据怎么处理、tokenizer怎么训练、模型架构怎么搭、预训练怎么跑、后训练怎么调这一整套链路都处在掌控之下。但要注意这不是“拒绝所有轮子”。真正的AI from scratch是复用PyTorch、Hugging Face生态、CUDA这些底层工具把模型本身、训练流程、数据管线当作自己的核心零部件来设计和实现。你可以不写CUDA内核但你必须知道显存为什么爆你可以不自己实现Attention但你得知道多头机械记忆和KV Cache的关系。从工程上看from scratch的实质是被迫面对每一个黑盒。当你直接把一个开源语言模型拿来微调时出了问题你只能猜是数据不对是学习率太高是填充没处理好而当你自己把构建流程走一遍每层都清楚排查问题的路径就缩短了一大截。这就是为什么我一直建议哪怕最后你工作里就是调别人的模型也值得亲手从头搭一次。1.2 为什么这是一条可复现的路径“可复现”听起来像论文里的概念但放到工程里它的意思是你可以在一个普通配置的环境里按这套路径稳定拿到结果。我自己在做这个项目时一开始也怀疑是不是必须要有8张A100才能跑神经网络后来我发现一个参数不到1亿的小语言模型用一张消费级显卡也能完成预训练全流程只是速度慢一些效果差一些但学到的机制是一样的。从零建模型的路径是清晰的先做数据再做词表再搭结构再写训练循环再评估最后部署。每一步都有明确的输入、输出和验证标准。你不用在每个环节等待“整个系统跑通”才能看到成果而是能随时调小规模训练快速形成反馈。所以与其说from scratch难不如说它需要的是工程纪律。如果你习惯了“数据集一丢训练脚本一跑等出结果”的流水线操作会非常不适应。因为在from scratch的每一步你都要做判断和取舍这正是工程能力的核心修炼。2. 动手前的技术选型与准备2.1 硬件、基础环境和工具链先说硬件。如果只是想验证推理模型的基础能力目标参数量在1亿以下一张显存16GB左右的显卡就够用。我在做实验时用的一张24GB显存的卡训练一个参数量约8000万的小模型序列长度512batch size到8剩余的显存还能跑评估。如果你只有8GB显存也有应对思路把序列缩短到256梯度累积开起来照样能跑通。关于环境我的建议是装好Python 3.10以上版本PyTorch用稳定版。尽量不要在Windows裸环境上折腾CUDA容易踩版本兼容的坑最省心的做法是直接用Linux环境或者WSL2。这里有一个实操习惯值得养成所有依赖版本固定下来用requirements.txt或者conda环境单独管理因为AI生态里“昨天能跑今天更新个依赖就报错”的情况太常见了。2.2 核心框架与生态选择框架层面主流选择就是PyTorch不推荐新手从零去写CUDA或直接用JAX。PyTorch的生态最全资料最多Debug时社区能帮上忙的也最多。Hugging Face的库建议作为工具箱使用用它的tokenizer工具加速开发用它的dataset库做数据预处理但不要用它的Trainer直接把模型训练流程包掉那就失去意义了。在“from scratch”的精神下我建议以下分工模型结构自己写用PyTorch的Module实现。数据加载可以用HF的Dataset库但要自己理解binpacking、padding、截断的逻辑。Tokenizer训练可以用HF的tokenizers库直观高效。训练循环自己写控制梯度、学习率调度、日志。分布式前期不碰单卡先跑通再说。这套选型背后实际上是“能控制的都自己控制纯基建的复用”这么个原则。你不需要重新发明文件存储但必须自己决定数据布局不需要重写矩阵乘法但必须知道你的模型结构和显存是怎么对应的。3. 从零构建推理模型核心细节与实现3.1 数据从哪里来怎么造训练集构建推理模型第一步是数据而且数据质量直接决定模型能力的上限。很多人以为数据量越大越好实际训练一个小模型数据质量比规模重要得多。如果语料里到处都是重复段落、HTML碎片、乱码模型学到的都是噪声loss到后面怎么都压不下去。数据获取渠道有很多开源数据集、自己的业务数据、公开语料等等。我个人的建议是如果你在做一个用于学习的项目先别追求多大规模而是收集一个主题相对集中的、干净的中文或者英文语料规模在几百万到几千万token之间就够了。预处理这个环节极其关键。你需要做以下操作去重去掉一模一样的段落。过滤删除过长或过短的样本过滤含乱码的文本。清洗统一换行符转半角全角处理unicode异常。切分保留完整的语义单元不要粗暴按固定字符数切割。做完初清洗后用tiktoken或者自己训练好的tokenizer计算一遍token数你会发现原始字符数和实际token数差距很大。这个数字是后续训练batch设置和步数估计的基础。3.2 分词器是第一个真正的工程节点分词器是很多初做from scratch的人最容易忽视、却最容易埋雷的地方。它的本质是把文本转成id序列但它的质量影响两个地方一个是词表大小直接跟Embedding矩阵的显存挂钩另一个是分词粒度决定模型看到文本的最小单位。训练一个自己的BPE分词器并不复杂tokenizers库几行代码就能做到。但你要做几个决定词表大小是8000、32000还是50000要不要在词表里加入特殊控制符比如|endoftext|英文和中文的处理策略是什么中文场景下我建议别把整词都塞进词表中文常用字其实在几千到一两万之间具体可以结合分词算法字符和字节级的混合策略效果通常更稳。词表选太大Embedding和输出层会占掉大量显存对一个小模型来说是很大的负担。我的常用做法是先在目标语料上跑一个20000词表的BPE观察它在验证集上的压缩率和oov情况再决定要不要调整。你需要随时能回答一个问题“一个句子进来经过tokenizer变成id再decode回去能还原吗”如果中间出现乱码一定是预处理哪里出了问题。3.3 模型结构实现从多头注意力和KV Cache开始到了模型结构这一步很多人的思路开始散掉实际上一个标准的自回归语言模型核心就是三块Token Embedding 位置编码层、多头注意力模块、前馈网络。你可以不追求绝对工业级但要理解每一层的数据形状和维度流动。我自己在实现时最重视的是注意力部分的数据维度。很多新手在写多头注意力时都容易在“把序列长度、特征维度、头数”这三个维度中绕晕。这里分享一个我经常用的调试技巧在每一次view和permute之后打印一次张量shape对照自己手算的预期shape。界面清晰了维度错误一眼就能看出来。KV Cache是一个稍高级但绕不开的细节。在训练阶段不需要它但在推理阶段它会决定你的生成速度。如果不做缓存每生成一个token整个历史序列都要重新计算一遍注意力耗时随生成长度线性增长。加上KV Cache以后已算过的Key和Value被存下来新token只需要计算和增量部分做交互速度快很多。这也是我对“推理模型”工程化的理解不光是能生成文本而是能高效生成。位置编码也有讲究。经典Transformer用的是三角函数式固定位置编码现在很多模型用可学习的RoPE旋转位置编码。为图省事你完全可以从可学习的绝对位置编码做起它简单直观对小模型足够。如果想往更先进的架构靠拢再升级到RoPE。3.4 训练循环loss曲线、学习率、梯度累积写训练循环是让“from scratch”落地的一块硬骨头。很多人写出来的训练循环能跑但效果极不稳定问题往往出在几个细节上。先说说初始化。PyTorch里Module的默认初始化在多数场景够用但如果你的模型比较深可以考虑给残差分支的输出层做特殊缩放初始化这能显著提升训练的稳定性。这一招在训练早期特别重要不然你可能会在第一个step就看到loss变成NaN。再聊聊学习率。Transformer类模型通常需要先用warmup把小学习率跑起来再进入高峰之后逐步衰减。我常用的做法是先warmup 100到500步峰值为3e-4的Adam优化器配上余弦退火。你不需要一开始就调很多超参数先把这一组跑稳再逐步调。梯度累积是一个躲不开的实操技术。显存不够时不能硬上大batch标准做法是小batch跑几步把梯度累积起来再走一步优化器。代码实现很简单但要注意真实batch size 单卡batch × 累积步数学习率要按这个真实值去匹配不是说累积了就随便改。我见过有人梯度累积开了16步结果学习率没调直接失控。Loss曲线怎么看训练初期loss会快速下降之后进入缓慢长尾。如果训练中期loss还在跌说明模型还没学完可以继续加步数。如果loss近似水平、甚至震荡增大就要检查学习率是不是太高数据里是不是有重复噪声。评估集上的loss同样重要如果训练loss降但验证loss不降说明过拟合了需要加数据、加dropout或者把模型变小。4. 实操记录一个可落地的完整流程4.1 小规模验证用100M参数跑通全流程在实际动手时我强烈建议先跑一个“迷你版”的流水线把整个链路验证通畅再上大规模。我的迷你实验配置大概是这样语料量约5000万token词表BPE词表大小20000模型参数量约5000万到8000万序列长度512训练步数10000步左右单卡batch size8配合梯度累积数据管线用HF的Dataset加载语料后先做分词、截断、padding然后组装成一个tensor化的数据集。这里有个细节值得注意不必把整个数据集全部padding到固定长度可以做一个collator在取batch时动态补齐到这个batch内的最大长度。这样省显存也减少了很多无谓的计算。训练代码用PyTorch裸写一个典型的循环是取batch、前向、算loss、反向、梯度裁剪、优化器step、调学习率、定期打日志。我一般会每一百步打印一次loss每500到1000步在验证集上做一次评估。跑完这套迷你流程观察到的现象很有意思一开始模型只能蹦出一些高频的碎词后来能学会标点符号的停顿感再后来能产生一些语法上完整的短句。虽然生成的内容还很蠢但你能感受到语言建模的几个阶段变化这比单纯看论文直观太多。4.2 扩展到更大规模数据工程和训练时长如果迷你版跑通了接下来就是放大。放大并不是一个无脑加数据、加参数的过程。首先你要评估现有数据规模够不够模型参数量如果翻倍数据量最好是原来的两倍以上否则效果提升会非常有限。我在扩展过程中体会最深的是数据并行和序列长度调整这两件事要一起思考。序列长度从512提升到1024时自注意力计算量是平方级增长显存压力增大很多。如果你只是想把模型做得更好不一定非要加长序列也可以在相同长度下提升批次大小、增加训练步数或者用两阶段的训练先在短序列上训得快再在长序列上精调。训练时间是另一个容易被低估的问题。在我那张24GB显存的卡上5000万token的实验大约需要几小时如果想扩大到5亿token预计训练时间就是几十小时。一定要做好断点续传。我建议每1000步保存一次checkpoint保留optimizer状态和tokenizer配置不然中断一次就得从头再来。关于规模扩展后的评估就不能只看loss了。你必须准备几个固定的提示词让模型做生成自己肉眼观察输出是否符合语料的语言风格。数字指标和人工观察要结合这种“看输出”的习惯越早养成越好。5. 常见问题与排查技巧实录5.1 训练不收敛先猜模型再猜数据训练不收敛是from scratch里最高频的坑也是最容易让人崩溃的。loss一直是几百万、直接变成NaN、或者一开始就能感到学习率爆炸这些问题我都遇到过。排查顺序有讲究先检查数据再检查模型最后检查超参数。数据方面大概率是语料里混入了非UTF编码的乱码字节或者label没对齐token-level的偏移会导致模型学到错误映射。模型方面可以先用极小的语料比如几万token去过拟合测试如果小数据都不能把loss降到很低那问题大概率出在结构实现上比如注意力mask写错了、残差连接没对齐维度。超参数方面最常见的翻车点就是学习率。Transformer模型里3e-4是常见起点但如果你用了大batch而没有加大学习率模型学得慢反过来小batch却用了高学习率loss就会震荡。我的建议是先把warmup加大前几百步别让模型“跑太快”。5.2 显存不够从计算图到KV Cache的系统性省法显存不足OOM是另一个绕不开的坎。别只盯着batch size这一个旋钮其实有很多组合方案。最简单的降序列长度、降batch size、开梯度累积都是常规手段。进阶一点的方案是使用梯度检查点用一部分计算换显存训练速度会慢一些但在极限情况下能继续跑。如果你是推理阶段显存不够重点在于优化模型本身。先把fp16的精度打开显存立刻少一半。然后把推理的Embedding、FFN层做量化把FP16降到INT8。深度学习模型在推理阶段的精度冗余其实很常见量化以后输出质量影响很小速度还有提升。还有一个体验检查你模型里是不是有意想不到的大中间张量。比如有些实现会把整个batch的attention矩阵全部返回而实际上只需要最后一层的输出这就会白白吃掉大量显存。写模型时注意及时释放中间变量该detach的地方就detach。5.3 生成效果差问题多半在采样策略模型训练已经收敛了loss也降得挺低但生成出来的文本依然奇怪这时问题往往出在采样策略而不是模型本身。首先看温度系数温度太高会乱蹦词温度太低会重复。从0.7到0.9之间一般是比较稳的区间。再看top-k和top-ptop-p设为0.9左右通常比top-k更平滑。还有一个常见但容易被忽视的点重复惩罚。自回归模型在生成时很容易陷入同一句话的自我循环特别是序列比较长的时候。设置重复惩罚要谨慎设得太大模型会强行回避正常重复的词汇输出反而更奇怪。我通常从1.1开始试。如果你生成的是结构化文本比如JSON或者代码不要用普通贪心解码。贪心解码会让输出陷入局部最优建议直接用约束解码让每个token的生成被限制在合理的语法边界内。6. 一些实在的建议和体会把“ai-engineering-from-scratch”这条路完整走完以后我的一个强烈感受是真正难的不是神经网络结构而是工程系统的每个小环节环环相扣。一个干净的数据集可以让你省掉大量的Debug时间一个好的tokenizer词表可以让你用同样的算力跑出更好的效果一个稳定的训练循环可以让你安心去调其他参数。如果你也想尝试我的建议是先“小到不能再小地起步”。别一开始就铁了心训练一个几B参数的大模型先用一个小规模的语料搭一个几十M参数的模型哪怕生成出来的句子很幼稚也没关系。跑通全流程以后你会对整个AI工程有一个比任何课程都扎实的认知框架。最后分享一个实际操作里很管用的小技巧每次调整任何配置都记录下来包括数据变化的版本、超参数的值、对应的loss曲线变化和生成样例。等到你回头看的时候会发现这份实验日志比代码本身更值钱。我踩过的多数坑总结下来都是因为某些改动没有记录导致出了问题根本不知道是哪一步引起的。用版本控制管理代码用实验笔记管理每次训练记录这个习惯越早建立后面越省心。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。