
1. From Scratch 到底在说哪一层先别急着打开 IDE过去这一年社区里有个特别明显的风向变化。Sebastian Raschka 那本《Build a Large Language Model from Scratch》被大量从业者当成案头书GitHub 上 build a reasoning model from scratch 这类项目也频繁爬上热榜。我自己的实践经历也在印证这件事单纯会调用训练好的模型、会写几个 prompt、会搭一条 RAG 链路在今天已经算不上 AI engineering 了——大家真正缺的是把模型从头造出来的那种掌控感。但我想先说清楚一个常见的误解。所谓 from scratch不是让你丢掉 PyTorch、从零写 CUDA也不是逼你徒手实现整个 Transformer 才能谈 AI 工程。它指的是另一种能力标准从数据构造、模型架构、训练循环、损失函数、评估指标到部署形态链条上的每一环你都亲手碰过、理解过而不是停留在某个框架帮我搞定了的层面。换句话说from scratch 是一种心智模型和排错能力不是一种具体的工具选择。这篇文章我会结合自己从传统后端开发转向 AI 工程以及后来用极低成本复刻一个轻量级推理模型reasoning model的完整经历把 AI engineering 从 0 到 1 的关键路径拆开讲。它适合三类人想转行 AI 的软件工程师、只会调框架但觉得底层是黑盒的初级算法工程师以及那种永远想自己造一遍的折腾型开发者。2. 动手造模型之前先看清 AI Engineering 的完整能力拼图2.1 它和算法研究、传统软件开发的边界在哪里我在做技术分享时经常被问一个问题AI 工程师到底跟算法工程师有什么区别 我的理解是算法工程师的核心产出是某个任务上效果更好的模型所以重心在模型结构、损失函数、实验对比上而 AI 工程师的核心产出是一个可持续运行、可迭代、效果可保障的 AI 系统所以除了模型本身还必须覆盖数据管线、训练基础设施、评测体系、部署推理和线上监控。这个边界决定了 from scratch 的路线图不该一上来就扑到 Transformer 上。诚然模型结构是核心但如果你完全没有数据清洗和采样策略的概念训练集里混入大量重复文本模型表现会神秘地差一截如果你没有评测集设计和指标选择的意识训练完你根本不知道它是好是坏。这些都是 AI engineering 的一等公民不是做完模型之后的事。2.2 一个完整的从零项目至少要覆盖七个层面我把自己的复刻项目拆成了七层后来带人也基本按这个地图走层级核心问题我的实操落点数据层模型吃什么、不吃什么去重、过滤、配比、采样策略词元层文本怎么变成模型能处理的序号BPE 分词、词表大小、特殊标记架构层参数在层间如何流转Decoder-only、注意力、显存估算训练层参数如何被更新前向/反向、优化器、学习率、混合精度评估层怎么判断模型变好了困惑度、任务准确率、人工抽样部署层模型如何对外服务推理引擎、KV Cache、批处理迭代层如何持续改进数据回流、增量训练、回归测试这七层没有哪一层是可以跳过的。很多人从架构层开始训练完才发现数据烂得没法看也有人从部署层开始把服务搭好了模型效果却始终不达标。正确的姿势是先做一个极小的端到端闭环让每一层都跑起来再逐层放大。这也正是 from scratch 项目的价值所在——它逼你把整条链路亲手通一遍。3. 第一块被低估的敲门砖数据、分词与采样策略3.1 数据质量的决定性权重我见过太多人花两周调模型结构却不愿意花两天看数据。这里我可以说个反直觉的结论在大多数中小规模的训练任务里数据清洗带来的收益远大于模型结构微调。重复数据会让模型产生记忆偏差低质量文本会拉低生成流畅度而语料配比失衡会让模型在某个领域偏科。我的做法是先建立一套可复用的清洗管线按固定顺序执行HTML 标签剥离、乱码过滤、长度过滤、去重MinHash 或简单哈希、高质量段落打分。规则不复杂但每条规则背后都有明确的动机。比如去掉过短文本是因为短文本往往没有完整语义训练进去只会增加噪音去重是因为模型会把高频重复片段当先验知识背下来直接影响生成多样性。提示如果你只是想做 1B 以下的小模型预训练不需要追求超大语料。我当时用了几十 GB 的清洗后文本做第一版已经能训练出表意连贯的小模型。先把管线跑通再考虑规模。3.2 分词器模型认识的字母表分词器Tokenizer是新手最容易忽略、但影响无处不在的组件。它决定了你的词表大小、序列长度利用率、甚至模型对某些语言的表现。业界默认的 BPE 方案原理不难从字符开始不断合并出现频率最高的相邻字节对直到达到目标词表大小。实际操作中我推荐直接使用成熟的 tokenizer 训练工具比如 Hugging Face 的 tokenizers 库不要自己从零实现字节对编码细节——这属于没必要重复造轮子的部分。但词表大小这个参数值得自己实验词表太大嵌入层参数爆炸小模型根本学不过来词表太小长文本变成长词元序列训练开销上升。对 1B 以下的模型32K 到 64K 是比较常见的区间。另外记得在训练前检查特殊标记bos、eos、pad是否在词表里并且固定好它们的 id否则后面加载 checkpoint 时动不动就 index out of range。3.3 采样策略同样一堆数据喂法不同结果不同同样的数据不同的采样顺序和混配比例训练效果可以差出好几个百分点。我在小模型上做过的两组对照实验里一组按通用语料为主、代码语料为辅的 7:3 配比训练一组把代码语料提到 5:5结果后者在推理类任务上的表现明显更好但通用知识问答略微下滑。所以别迷信某个最佳配比要针对你的目标下游任务做配比实验。采样层面还有几个容易被忽略的细节一是 shuffle 时要固定全局随机种子保证实验可复现二是要做数据去重后的重新混排避免相似样本扎堆成块导致模型在局部过拟合三是训练后期可以适当提高高难度数据的上采样倍率相当于给模型做奥数冲刺。这些技巧都是我从复刻推理模型的过程里一个个验证过来的。4. 训练循环不是 fit()亲手写一遍你才懂 Loss 和收敛4.1 一个最小训练循环里到底发生了什么用 PyTorch 生态时model.fit()这类高层 API 把太多东西藏起来了。我强烈建议你至少在项目里手写一次训练循环哪怕最终跑生产时还是会换回框架封装这个理解过程无可替代for step, batch in enumerate(train_loader): input_ids batch[input_ids].to(device) labels batch[labels].to(device) logits model(input_ids) # (batch, seq_len, vocab_size) loss cross_entropy(logits.view(-1, vocab_size), labels.view(-1)) optimizer.zero_grad() loss.backward() grad_norm clip_grad_norm_(model.parameters(), max_norm1.0) if grad_norm threshold: skip_step_or_reduce_lr(...) # 极端训练不稳定的保护 optimizer.step() lr_scheduler.step() if step % 100 0: record({loss: loss.item(), grad_norm: grad_norm.item(), lr: lr_scheduler.get_last_lr()[0], tokens_seen: tokens_seen})这段代码看着简单但它把你和真正的问题拉到同一个平面上loss 为什么是交叉熵因为它衡量的是下一个词元预测分布和真实分布的差距而语言模型的本质就是一个极其复杂的条件概率估计器。梯度裁剪为什么是 1.0因为大模型训练后期容易出现梯度范数突刺不裁剪就直接把参数推向深渊。为什么记录 tokens_seen 而不是只看 step因为不同批次序列长度不同只有按累计 token 数画 loss 曲线不同实验之间才有可比性。4.2 损失曲线不下降到底先查什么新手最慌的一件事就是 loss 不降。按我的排查顺序大概率能定位到问题先确认数据是不是真被加载进来了打印 batch 的 shape 和内容片段再确认 labels 是否对齐很多 loss 异常都出在 shift 错位然后看学习率和优化器配置小模型常见的是学习率过大导致震荡最后看数值稳定性混合精度下出现 NaN 一般先关 AMP 再定位。这里分享一个我自己的笨办法训练启动后的前几百步我会故意用一个很小的 batch 跑一次过拟合测试目标是让 loss 降到接近 0。如果这个小 batch 都过拟合不了说明模型代码或数据流有 bug先修代码再做大规模训练。这套先过拟合、再谈泛化的检查思路能帮你省掉大量在无效训练上的时间。4.3 可复现性没有实验追踪所有调参都是玄学复刻推理模型的过程里我最大的教训之一是没有一开始就建立实验追踪。前几版训练跑完我完全说不清是这个数据配比让结果变好的还是这次随机种子撞了大运。后来我把每个实验都固定记录九项内容数据版本、数据配比、tokenizer 版本、模型结构、batch size、学习率及调度器、梯度裁剪阈值、随机种子、总训练 token 数。任何一次改动如果这九项里有任意一项不同我就不允许自己做效果归因。日志和 checkpoint 策略也要提前定好每固定步数保存一次完整 checkpoint 之外我还会额外保存一个当前最优副本防止训练中途崩溃把整个多天的进度带走。这些听起来像工程常识但在我见过的从零项目里恰恰是这些常识最先崩塌。5. 从零复刻一个轻量级推理模型我的 100M 参数实践5.1 规模与架构选择为什么先从 100M 开始build a reasoning model from scratch 听起来很唬人但我不建议任何人一上来就冲几十亿参数。推理能力reasoning的本质是模型在生成过程中进行多步计算和回溯修正这个能力在小模型上虽然弱但特征是可观察的。我选择从约 100M 参数的 decoder-only 模型开始原因是单卡 A100 或 4090 就能跑训练单次实验周期短迭代速度快而且足够验证数据管线、训练策略和评测方法是否成立。架构上我直接沿用了 Llama 风格RMSNorm、SwiGLU 激活、旋转位置编码RoPE、GQA分组查询注意力。这里有个判断既然我的目标是学习 AI engineering架构部分应该能复述原理但不重复造轮子——注意力公式我能手推但实现可以用开源代码库比如用 tinyllama 或 nanoGPT 改把省下来的精力留给数据、训练和评测这些更缺经验值的环节。5.2 三段式训练预训练到指令微调再到强化阶段我完整复刻推理模型时采用了三段式路线这也是目前开源社区里做小模型最主流的方案第一段是预训练pretraining目标是让模型学会语言本身。我用了混合语料训练约 1000 亿 token 的等价效果在小模型上不现实所以我压缩到约 20 亿 token先保证语法和知识底子。重点观察的是验证集困惑度perplexity是否随训练稳定下降以及生成样本是否通顺。第二段是监督微调SFT目标是把模型从会说话变成会回答。我构造了一批带推理链的问答数据包括我自己标注的高质量思维链样本和开源数据集中筛选的一部分让模型学会先推理、后作答的输出格式。这一段的 loss 收敛速度会比预训练快很多但不能过度训练否则模型会遗忘预训练阶段的通用能力出现典型的灾难性遗忘。第三段是强化学习微调RL常用方法包括 PPO 或 GRPO目标是用奖励信号进一步引导模型输出可验证的正确推理过程。对小模型来说奖励模型不宜做太复杂我直接基于答案正确性做规则奖励正确给正分、错误给负分格式不规范再扣一点配合 GRPO 这类不依赖单独 critic 的算法资源占用友好。实测下来第三段确实能在推理类任务上再拉高几个点但收益在 100M 这个规模上边际效益递减得很快——这也是我后来把实验重心转向数据质量的原因。5.3 我在这个过程中踩过的三个坑第一个坑是上下文长度。推理模型的思维链往往很长而小模型由于长度外推能力弱在短上下文训练数据上训练出来的模型生成到一半就断片了。我的解决办法是刻意加入一部分长序列样本并在训练后半段逐步提高序列长度上限。第二个坑是格式惩罚过度。RL 阶段我为了追求严格输出格式给格式错误的样本扣了太多奖励结果模型学会了用最短路径满足格式思维链变成一句话完全丧失了推理过程。后来我大幅降低格式惩罚权重只保留对答案正确性的强奖励模型行为才恢复正常。第三个坑是评测集污染。我一开始用的推理评测集里有部分题目和训练数据同源导致模型在评测上的分数虚高换一套没见过的题立刻露馅。从那以后我专门留出一个隔离评测集训练脚本里禁止任何方式触碰这个集合。6. 训练只是前半场评测、部署与线上监控同样决定项目成败6.1 评测体系决定了你迭代的方向一个小模型训练三周你到底怎么知道它变聪明了光看 loss 不行loss 下降只能说明拟合程度提高不代表推理能力增强。我给自己搭建了三层评测体系评测层内容作用基础指标验证集困惑度、生成样本人工阅读快速发现训练是否正常标准任务集数学题、代码补全、常识问答等公开小规模测试集横向对比、追踪提升定制任务集自己设计的带明确答案的长链条推理题检验本项目核心目标这三层分别对应诊断训练问题、对比外部基线、验证核心能力。我的经验是每一层都要固定版本换任何一道题都要记录在案不然两周后你根本没法判断某个数据改动到底带来的是真实提升还是评测集波动。6.2 部署推理模型再小服务化也要讲究很多人都觉得部署是把模型 load 起来起个 HTTP 服务就完事。实际上推理侧有不少细节直接决定你项目能不能落地。第一是 KV Cache注意力层的历史键值如果每步都重新计算生成速度会慢几十倍工程上必须做缓存。第二是连续批处理continuous batching不同请求的生成长度不同动态插队比静态批次能显著提高吞吐。第三是量化小模型本身不大但如果你要部署到 CPU 或边缘设备int8 量化几乎是必然选择。我用 vLLM 做过部署也自己写过一次简化版推理脚本两者的对比很有意思框架带来的吞吐提升非常可观但当你遇到奇怪的输出截断或显存抖动问题时理解 KV Cache 和调度队列的底层逻辑才是真正的排错钥匙。这也是 from scratch 路线在工程端的价值——你会用框架但你不依赖框架。6.3 线上监控迭代的闭环入口模型上线之后真正的工程挑战才刚开始。我的监控面板固定盯四类指标请求延迟P50/P95、token 吞吐、错误率、以及不太常规的输出关键词分布漂移。最后这个指标很有用比如上线一周后模型突然在某个话题上生成频率异常往往是数据回流或 prompt 变化引起的趁早发现能避免线上用户帮忙找 bug。监控数据要定期回流成新的训练数据这才能形成迭代闭环。我的习惯是每天抽取线上低质量回答样本周维度做一次人工标注再进入下一轮 SFT 训练。很多团队把模型训练当一次性项目实际上从 scratch 到可持续运行的 AI 系统中间差的就是这一套回流机制。7. 到底什么时候该 from scratch成本、边界与我的建议7.1 两个场景的决策标准不是所有项目都适合 from scratch。我的判断标准很简单如果目标是快速上线、效果由大模型能力兜底那就用现成模型加 RAG 或微调别自己训练如果目标是理解全链路、解决特定垂直问题、或者你手头有别人没有的数据优势那 from scratch 就是值得的。复刻推理模型这件事坦白说产出的模型本身能力远不如现成大模型但它的工程收益非常高。通过亲手做完七层链路我再去看市面上的训练框架、推理引擎、评测工具视角完全不一样了——我清楚每一层在解决什么问题出了问题知道去哪里查这是纯用 API 永远换不来的。7.2 给后来者的路线图如果让我重新走一遍我会把这个路径压得更短先花一周写一个极小的字符级语言模型比如莎士比亚文本生成那种规模完全跑通训练循环再花两周把小模型升级到 10M-100M 参数加入真正的 tokenizer 和标准数据集然后开始做推理模型复刻同时补评测和部署。每一阶段都坚持全链路闭环而不是只训练、不部署。我自己踩过的坑都写在上面了但每次我带新人时最想传递的还是那个核心认知from scratch 不是一种炫耀姿态而是一种让你对系统拥有解释权的训练方式。当模型输出不如预期时你不再只能干瞪眼或者改 prompt而是能沿着数据、训练、评测、部署这条链路一路排查下去——这种能力才是 AI engineering 真正值钱的地方。最后再分享一个小技巧每次训练开始时我会把当次的完整配置连同数据版本、代码 commit 记录写在一个固定的实验卡片里训练结束后第一时间对比卡片和实际结果。这个习惯一度救了我好几次——很多灵光一闪的调参后来都被证明是配置记录不全造成的假象。你如果也想从零开始走一遍我建议你从这个实验卡片做起它花不了五分钟但会是整个项目最值得的投入之一。