市面上讲“AI工程”的文章太多了但九成都在教你调API、写Prompt、套RAG框架。真正到了模型训练这一步很多人是懵的分词怎么做数据怎么组织Loss曲线不对劲怎么办Reasoning能力到底是怎么“训”出来的我最近完整跑了一遍从零构建LLM再到训练推理模型的流程中间参考了《Build a Large Language Model From Scratch》那套思路也踩了不少文档里不会写的坑。这篇想把整个链路拆开讲清楚——从数据准备、分词器训练、预训练到SFT和RL训练reasoning model的完整实操路径以及每一步背后的“为什么”。适合已经跑通过一些开源大模型推理、但没亲手训练过模型的读者也适合准备从“调包侠”转向“造轮子”的工程同学。先说个反直觉的结论从零构建AI模型最难的不是模型结构本身而是对“数据流”和“目标函数”的把控。模型结构大方向今天已经非常成熟但数据清洗规则、序列组织方式、训练阶段的切换时机才是决定模型最终像不像样的关键。1. 为什么值得从零构建一次模型先别急着上LoRA很多人的第一反应是现在开源模型那么多直接拿Llama、Qwen微调不就行了吗何必从零开始。这个质疑很合理我也犹豫过。但真正动手做完一轮之后我意识到这件事的价值根本不在“训练出一个能用的模型”而在于让你把AI工程里最容易被跳过的几层地基补上。第一层是数据视角。使用现成模型的时候你只关心“输出好不好”但训练时你关心的是“token怎么切、样本怎么拼、批次怎么组织”。同一个句话不同分词器会切成完全不同的token序列这直接影响训练效率和效果。当你亲手训练自己的分词器并观察它在多语言文本上的切分表现你对预训练模型“为什么有语言偏好”的理解就完全不一样了。第二层是Loss的直觉。调API的人永远不知道一个模型的参数是怎么一步步从随机初始化变成能说人话的。我自己跑预训练的时候盯着Loss从7.2往下降到1.8那种“每一步梯度都在做实事”的感觉和看别人贴出来的训练曲线完全是两码事。你会对学习率、batch size、序列长度这些超参产生真正的敏感度而不是照抄别人的配置。第三层是Reasoning模型的构建门槛。最近“from scratch构建reasoning model”很热但很多人不知道的是reasoning能力不是预训练里自然涌现的它需要专门的后训练阶段。这个阶段对算力的要求相对低但对数据设计和训练策略的要求极高。从零构建一次模型之后你再去理解DeepSeek R1那套GRPO训练Group Relative Policy Optimization一种通过组内相对奖励来更新策略的强化学习算法可有效压低显存占用路线会顺很多。所以我的建议是如果你有至少一块24G显存的显卡3090/4090都可以A100更好并且手里有几万到几十万条文本数据完全可以跑一轮小规模的从零训练。不是复现ChatGPT级别的是找手感、找门道。2. 从零构建LLM的四块基石结构、分词、数据组织和训练目标2.1 模型结构选对基线别“发明创造”从零构建不意味着从零设计网络。现代LLM的Transformer结构已经非常收敛除非你在做科研探索否则不要自己改注意力机制的数学形式。我这次用的是标准的decoder-only Transformer层数12层、隐藏维度768、8个注意力头整体参数量约1.2亿——略小于GPT-2 medium但足以验证一条完整的训练链路。在实现层面关键点其实集中在几个容易被忽视的地方RoPE位置编码旋转位置编码比绝对位置编码更平滑外推能力更好绝大多数现代模型都在用。我第一次实现时把旋转角度的计算搞反了维度顺序结果训练到第2000步Loss就开始震荡。那个现象特别迷惑人乍一看像学习率太大实际上是位置信息混乱导致token间的相对距离没有意义。Pre-Norm还是Post-Norm选Pre-Norm先LayerNorm再进Attention/FFN。它训练更稳定对学习率不那么敏感对从小项目入手的场景极其友好。FFN的激活函数SwiGLU即门控线性单元配合SiLU激活是当前的主流选择效果比GELU要好。我直接用PyTorch从零写了一遍模型定义没有用HuggingFace的AutoModel训练。这不是为了炫技而是这么操作之后你才能看到每一个参数矩阵进出的shape变化才能理解什么是隐藏维度、为什么KV Cache是那个大小、为什么梯度检查点能省显存。2.2 分词器训练被严重低估的第一个坑分词器是所有人都觉得“先跳过、以后再说”的东西但它实际上是整个训练链路里最影响“成本”的组件——因为你的序列长度和vocab大小直接决定了Embedding矩阵的规模和注意力计算的复杂度。我用的是SentencePiece训练BPE模型字节对编码。几个实际经验Vocab大小选定我选了16K16512左右留了特殊token的位置。对于纯中文代码英文混合的语料16K够用但不算宽裕。vocab太小会导致每句话的token数偏长训练和推理都会变慢vocab太大则Embedding矩阵暴涨。Special token必须预留pad、bos、eos、unk这四个是底线如果后面要做SFT和RL还要加|user|、|assistant|、|reason|这类角色分隔符。不要后面再改词表否则要重训整个Embedding层。中英混合语料的切分观察BPE在英文上切得挺准但中文上经常出现一个字拆一个token的情况这是正常的。你要是想把中文切得更紧凑可以考虑加Unigram方案或者用更大的“字符片”候选集——但训练速度会变慢。训练完分词器之后一定要做一个覆盖测试拿一份没有出现在训练语料里的文本走一遍“encode→decode”流程确认没有大量unk出现。这个测试很便宜但能救你后面整条训练流程。2.3 数据组织从原始文本到训练样本的关键工程很多人刚学LLM的时候以为“训练数据就是一篇文章一行”这是个巨大的误解。预训练的目标是让模型学会“预测下一个token”所以你提供的每个训练样本本质上都是“一段连续的token序列”模型需要在每个位置上做next-token的预测。这就决定了数据组织有三个硬性要求样本必须是完整的语义片段不能把一个句子的后半截截断扔到下一个样本开头那样模型学到的next-token预测规律会被打断。我处理时按预定的sequence length比如2048切分文本超长的文档按段落优先切分再把切出来的每个chunk单独作为一个样本。样本之间要有连接逻辑如果你的语料是问答、对话或者文档集合尽量让同一个来源的连续片段保持在同一个batch里group by text而不是全局随机打散。这能让监督信号更加连贯模型的困惑度下降更快。Batch的组织我用了可变的全局batchglobal batch。单个GPU上micro batch设16梯度累积4步global batch就是64。对于小模型来说这个规模够用损失曲线的震荡幅度也比较理想。数据清洗比模型搭建更耗时。我把原始语料过了几层规则去HTML标签、去掉重复率过高的垃圾段落、去掉包含异常字符的文档、长度过滤短于100字的直接丢弃。这一套流程跑完原始数据大概丢掉了30%剩下的部分才是能喂给模型的。2.4 训练目标cross-entropy的“标准答案”长什么样预训练阶段的目标函数非常简单——对每个token位置预测下一个token然后计算cross-entropy loss。但你需要在代码里面明确两件事:Label的构造输入序列是token_ids[0:-1]标签是token_ids[1:]每个位置预测下一位。这个错位如果不处理好Loss会居高不下模型怎么train都学不会。Loss计算范围预训练阶段对整个序列做next-token预测。但在SFT阶段你只对|assistant|回答部分的token算loss用户问题部分的token要mask掉。这是我见过最多的初学者错误——SFT时对着用户问题也算loss模型为了优化loss会开始“复述问题”回答质量大幅下降。3. 实操路线数据准备到预训练收敛的完整记录3.1 硬件与训练配置我的方案是单张RTX 409024GB显存模型参数约1.2亿序列长度1024收费的峰值显存大约在15GB左右余量足够跑一些推理验证。如果你预算更充裕两张显卡可以启动梯度累积和DDP分布式数据并行训练周期能缩短一半。超参数选择上我调试了几轮后的最终配置如下参数值说明模型参数量~120M12层768维8头序列长度1024先小一点容易排错Vocab size16512含特殊tokenBatch size6416×4梯度累积学习率3e-4Warmup 2000步余弦衰减到3e-5训练步数约60000数据总量约15亿token优化器AdamWweight decay 0.1关于学习率我必须在分享里强调3e-4是相对激进的。小模型能扛住但如果你把模型规模放大到1B以上这个学习率大概率会直接Loss爆炸。经验法则是模型参数量每增大10倍学习率大约要降为原来的1/2到1/3——这不是精确公式是工程经验。3.2 训练过程中的三条Loss曲线判断法训练前中期大约前5000步Loss会从最初的7.x快速降到4.x这是模型正在学习“基础语言能力”的阶段——词语搭配、语法结构、基本事实关系。这时候你要关注的不是Loss绝对值而是它的下降斜率是否稳定。进入中期约15000步以后Loss降到2.2左右这时候开始出现“量变引起质变”的现象模型生成的短句变得通顺了能产出一些连词和基本从句。到了后期40000步以后Loss降到1.6左右生成质量有了明显飞跃但你要注意观察验证集上的Loss——如果验证Loss开始回升而训练Loss还在降那就是过拟合了。我的训练在56000步左右出现了轻微的验证Loss回升果断提前终止并保存checkpoint。预训练收敛的判定标准不是“训完设定的步数”而是“验证Loss最低的那个点”。3.3 生成验证别只看Loss要学会“听模型说废话”Loss只是训练过程的函数不能直接告诉你模型“像不像人话”。我每隔5000步就做一次生成测试——给定同一个prompt看模型续写出来的内容在语法、语义、常识三个维度上的表现。比如我给模型喂“为什么天空是蓝色的”预训练早期它会输出一堆完全无关的token序列中期变成“天空是蓝色的但是是吗”这类有语法但没逻辑的句子后期能给出带有解释性质的句子。这个过程中你会真正体会到语言能力是先于世界知识涌现的。模型先学会了“怎样说话像人”然后才学会“怎样说得有道理”。4. 从通用LLM到推理模型SFT和RL的完整链路预训练结束后模型只是一个“知识丰富但不会交流”的大模型。它确实会续写但你问它问题它不会以问答格式回应因为它从来没有见过“问题和答案”这种对话结构。这是个非常关键的分界线预训练训练的是“语言模型”后训练SFTRL训练的是“助手模型”或者说“对话模型”。4.1 SFT阶段为模型注入对话和基础推理的“形状”SFT全称是Supervised Fine-Tuning核心目标是用人工标注的高质量问答对让模型学会“回答问题”这个动作。数据格式是对话模板|user|问题|assistant|回答。这里重点讲一下SFT数据的构建方式和数据量级。对于一个小规模从零项目SFT数据不需要几十万条三五万高精度对话就够了。关键在于覆盖面普通知识问答让模型学会陈述事实。多轮对话让模型学会引用前文、指代消解。代码补全和解释如果目标领域包含代码。逐步推理问答为后续RL阶段打底。例如“小明有3个苹果给了小红1个又买了2个最后有几个”这种需要分步骤回答的case模型要暴露如何一步步得出答案的路径。SFT训练学习率要低通常在1e-5到2e-5epoch数控制在1-3轮。我见过有人直接按预训练的学习率微调模型把对话格式学得七扭八歪回答极其冗长。SFT的核心在于“激活已有知识到正确输出格式”而不是让模型去记忆新知识——学习率过高很容易导致灾难性遗忘。训练完成之后必须检查一个东西模型会不会把问题部分的loss也算进去。我把user部分的token在loss计算里直接置为-100忽略。如果你省了这步模型会学会“一边重复问题一边回答”。4.2 RL阶段构造“推理过程”并引入奖励信号训练完SFT的模型能回答问题了但它的回答往往是“直给答案” ——碰到需要逻辑推导的题目要么胡说八道要么只给结论不说过程。Reasoning model的核心能力就是“把解题的中间推理步骤展现出来”这是RL阶段才真正开始塑造的。我实现了一套类GRPO的reinforcement learning训练流程。核心思想并不复杂对同一个prompt让当前模型采样多组答案比如8组。对这8组答案分别计算奖励reward。将每个答案与这8组答案的平均奖励比较高于均值的答案获得正加权低于均值的答案获得负加权。用这些加权值更新策略同时让旧策略和新策略的KL散度不要差太多防止模型突然“性格大变”。整套流程里奖励设计是最大的修罗场。奖励信号本质上告诉模型“什么行为是好的”。如果奖励只基于“最终答案是否正确”模型会沉迷于输出“答案是4”这种极短路径——它不去写推导了因为它发现直接抄答案的reward更稳定后续我会重点讲这个“reward hacking”问题。我用的奖励函数是两部分加权答案正确性奖励对structured数学类问题程序化校验最终结果是否与标准答案一致正确1.0错误/无法校验0。过程质量奖励检测回答中是否包含明确的推理步骤关键词和公式结构比如“因此”“因为”“逐步”等同时惩罚过度冗长和无意义复述。这部分给0到0.5的浮点奖励。这个组合奖励能在早期把模型引向“既要想到正确答案又要展示步骤”的方向。4.3 RL训练超参的取舍与稳定性控制GRPO这类强化学习方法非常容易不稳定。我踩过的最大一次坑是KL系数设得太大结果模型为了保证“和旧策略不要太不同”干脆把学到的推理过程丢弃变成了复读机。后来把KL系数调到0.01左右对每个token的logits差异加惩罚并且用动态clip效果才稳定下来。另外一个重要细节是参考模型的冻结。我在RL训练时冻结了一个SFT版本的参数作为参考每次更新都要计算新策略和参考策略在该prompt上的KL散度控制两者差异。这种方法能有效防止奖励模型钻空子也保证了RL训练结束后模型的语言能力不会衰退。RL训练的训练步数并不长我大概训练了4000步就达到了推理效果明显的收益——这个数字和预训练的6万步完全不是一个量级。这也反映出reasoning model训练的边际成本其实远低于预训练最关键的是奖励设计和数据质量。5. 工程化落地的关键训练可复现性、资源规划与数据管线从零构建AI工程模型训练只是中间环节真正让人头疼的是如何让整个流程可复现、可调试、可观测。5.1 实验追踪从一开始就把wandb或tensorboard接进去这是我吃了两次亏之后的血泪教训。第一次训练时我只在终端打印Loss没有曲线视图结果Loss在第8000步轻微震荡了我完全没注意到浪费了整整两天的算力。第二次训练时我把每一个阶段的Loss、学习率、梯度范数、分词器覆盖率全部记录到wandb上不但能及时发现异常还能做不同超参组合的对比分析。梯度范数grad norm是个特别管用的指标如果grad norm突然升高到训练初期的10倍以上往往意味着数值不稳定应该第一时间降学习率。不要盲目相信“训练Loss在降就是没问题”——很多灾难性遗忘和模式坍塌酝酿的时候Loss曲线确实是平稳下行的。5.2 训练脚本要写成可恢复任务而不是一次性脚本训练是个长周期过程网络断了、显存OOM了、推理时电闸跳了……任何一个中断都会让你损失算力。所以从一开始就必须把训