ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

从零构建AI推理模型:数据、训练、量化与部署全流程复盘

2026/10/3 18:24:43 拓冰建站 浏览量
从零构建AI推理模型:数据、训练、量化与部署全流程复盘 做AI工程这几年我心里一直有种不踏实感。用别人训练好的模型、调现成的接口、一条命令部署服务每一步都很顺可真到出了问题的时候我发现自己根本说不清内部到底发生了什么。年初我给自己定了个目标做一个“ai-engineering-from-scratch”项目从零开始不调现成大模型API、不用封装好的训练框架、不下载现成权重把数据、模型、训练、推理、部署整条链路亲手走一遍。这篇记录就是这个项目的完整复盘。这里说的“推理模型”不是泛指语言模型而是具备思维链CoT能力、会“先想后答”的reasoning model。我选它作为目标是因为推理模型的构建几乎覆盖了当代AI工程的完整技术栈数据合成、预训练、监督微调、强化学习、量化部署哪一个环节都绕不开。如果你能亲手从零搭出一个1B量级、能推理、能部署的模型你对AI工程的整体理解会和只会调包完全不同。这篇记录适合三类人想做AI应用但不想停留在“调API”层面的工程师、正在入门大模型训练的研究生以及所有对黑盒内部好奇的实践者。1. 从零构建AI推理模型为什么非要走最远的路1.1 “from scratch”到底是从哪里开始很多人说要“从零写AI”但真动手会发现起点都定义不清楚。是从写Attention机制开始是从清洗语料开始还是从数学上推一遍反向传播我这次给自己划的边界非常明确任何环节都不调用现成的“大模型成品能力”不下载别人训练好的权重做微调不用封装好的Transformer Trainer。数据由公开语料和合成脚本搭建模型结构用PyTorch底层函数自建训练循环自己写推理服务和量化也全部自实现。划这个边界不是为了标榜“能造轮子”而是为了理解每个环节的真实成本。用现成框架训练loss一掉万事大吉但我不知道数据配比、学习率、batch size这些参数在链路里扮演什么角色。自己动手之后每一个异常现象都得自己解释——这种被迫思考的机制反而成了学习速度最快的推进器。1.2 为什么把目标定为“推理模型”而不是普通文本模型如果只是验证“从零能搭起流程”做个文本续写模型就够了。但那是半途而废。真正的reasoning model背后涉及三块关键能力基础语言能力预训练阶段获得、遵循推理格式的能力监督微调阶段获得、自我纠错与探索的能力强化学习阶段获得。这三块恰好串起了当今AI工程里最热门的三类技术路线。还有一个非常实际的工程原因推理行为在小模型上也能观察到明显效果。1B左右参数量单卡就能训练推理延迟低迭代速度快特别适合作为从零实践的载体。如果你一上来就选100B模型大概率连数据都装不下项目会在中途夭折。做工程和做研究不一样能持续看到反馈比什么都重要。1.3 哪些人适合参考这份实践记录我估计下面三类人能从中得到价值。第一类是AI应用开发者他们每天调接口却不知道训练和推理之间的依赖关系。第二类是论文复现入门者读了很多理论但不知道工程上从哪里下刀。第三类是已经在用现成框架、但遇到问题只会“重启再试”的实践者。需要的基础其实不高Python语法、PyTorch的Tensor基础、基本的机器学习概念——知道损失函数、梯度、过拟合是什么意思就够了。不需要你会手推完整的反向传播公式。但如果你连Tensor的shape变换都不熟练建议先花两周补一下基础否则下面涉及的数据处理和模型实现细节会有些吃力。2. 数据与架构先行两块绕不开的基石2.1 推理数据的构造一条思维链如何变成训练样本推理模型训练的核心数据是“问题思维链答案”三元组。所谓思维链就是模型在给出最终答案之前展示的完整推理过程。为什么必须用这种结构的数据因为推理模型最重要的行为特征就是在回答前先输出推理路径SFT阶段如果看不到这种结构模型能学会流畅说话却学不会“先想后答”的行为模式。数据来源我用了两条腿走路。一部分来自公开的数学题集和逻辑题集挑选标准是有标准答案、干扰项少、表述清晰另一部分是自己写脚本合成的问题——基于数学题模板随机生成数值变体再用符号计算脚本自动生成标准答案和分步推导。公开数据集的规模有限且模型容易“背题”合成数据可以无限扩展且天然带标准答案两者互补是最稳的组合。关于数据量的经验给大家一个参考1B模型预训练我用了约20B token的通用语料SFT阶段用了约800万条三元组样本。这个量在单卡A100 80G上大约一周能训完一个可用模型。如果显存小数据量要相应压缩但SFT数据不要低于百万级否则指令跟随能力会非常弱。数据清洗我踩了不小的坑。公开数据里答案标注错误的比例看着只有1%到2%但在小模型上这点噪声可能带来评测分数明显下降。我的处理是合成数据全部用计算脚本重新推导一遍公开数据做一次投票过滤用一个小模型对推理过程和答案分别打分分数过低的直接剔除。这套预处理看起来费时但它省掉了训练后期排查bad case的大量时间属于典型的“先省后花”。2.2 模型架构参数怎么定以1B模型为例我选择的基础架构是标准的decoder-only transformer参数量约1B。具体配置如下参数项数值说明隐藏维度 hidden size15361B量级常用配置层数 num layers18深度适中单卡可训注意力头数 num heads18与隐藏维度对齐head dim96前馈网络维度4096采用SwiGLU结构上下文长度4096支撑较长思维链输出词表大小32kBPE兼顾中英文总参数量约0.9B含embedding参数这里专门说说为什么用SwiGLU而不用传统ReLU全连接。SwiGLU在相同参数量下能带来稳定的下游能力提升特别是在需要分布式推理的任务上信息在门控结构里更容易被分段保留。代价是实现稍微多几行代码但完全值得。对工程实践来说SwiGLU和RoPE位置编码已经是现代LLM的标配别自己再走一遍ReLU加绝对位置编码的回头路。参数计算有个容易忽略的点embedding矩阵占比很高。以hidden size为1536、词表32k为例仅embedding就有1536×32000约4900万参数占总参数的5%以上。这就是为什么词表不能盲目扩大的原因——每增一万词参数就增加d_model×10000。我最终选32k词表是在中英文覆盖度和参数效率之间取的平衡。2.3 预训练阶段就要为推理能力做铺垫一个常见想法是先把通用语料训完再在SFT阶段教推理。这个思路没错但有隐患——如果预训练语料里推理相关内容太少SFT阶段需要学的东西过于集中模型容易学成“背模板”而不是真正掌握推理。我的做法是在预训练语料里混入约5%的代码和数学文本再加约2%的带分步推导的科学文本让模型在预训练阶段就见过“逐步推导”这种文本模式。这7%是经验值不是铁律。通用语料质量高可以适当调低质量一般就调高。实测下来推理类语料占比低于8%时通用语言能力几乎没有损失超过10%模型回复会明显变生硬频繁出现“第一步、第二步”的机械化表达。找到这个阈值的过程本身就是理解数据配比如何影响模型行为的最好课堂。3. 训练全流程实录从预训练到强化学习3.1 预训练先让模型获得语言基础预训练阶段的目标不是让模型“会做题”而是获得扎实的语言建模能力。损失函数就是最标准的下一个token交叉熵。配置AdamW优化器学习率峰值3e-4warmup占比1%之后线性衰减到1e-5batch size为512个序列每序列4096个token相当每次迭代约200万token梯度裁剪1.0。有个容易被忽视的细节是数据顺序打乱。预训练语料里大量文本来自同一来源如果按来源顺序喂数据训练初期loss曲线会出现周期性的锯齿模型会在不同领域间“切换状态”。我在数据加载器里严格按来源做了混洗每个epoch内再重新打乱一次。调整后loss曲线平滑很多最终能力也更好。预训练在单卡A100 80G上跑了约7天20B token大约见10个epoch。有人会问为什么要让模型反复看这些数据小模型没有能力一遍记住全部数据多epoch是合理的但一般不要超过15个epoch否则会开始记忆语料噪声表现为生成时出现与原文高度重合的段落。8到12个epoch是我试下来最稳定的区间。3.2 监督微调用思维链数据教模型“先想后答”预训练结束后模型能流畅生成文本但不会答题。SFT阶段要做的就是教会它面对问题时先输出推理过程再输出最终答案。训练样本的格式分三段系统提示“请先逐步推理再给出最终答案”、用户问题、模型回复思维链加答案。训练时系统提示和用户问题部分不计算loss只在模型回复部分算。这样梯度信号全部集中在“如何作答”上模型不会在“如何读题”上浪费能力。SFT学习率比预训练低一个数量级我设为2e-5batch size 128训练4个epoch。这里有个关键经验一定不要训练过久。SFT loss下降到一定程度后继续训练模型多样性会下降表现为所有回答长得一样。这在推理任务里特别致命——模型一旦只会套模板遇到没见过的问题就彻底失效。我试过训到8个epoch评测分数反而下降是典型的过拟合信号。另一个容易踩的坑是输出长度设置。思维链数据里有些推理过程特别长训练时如果直接截断过长样本模型会学到坏习惯推理到一半就停下。我的解法是动态长度组合短样本和长样本分开组batch长样本单独训练。虽然训练效率略有下降思维链的完整性明显更好。3.3 强化学习阶段让模型学会自我纠错SFT做完的模型已经能输出推理链但它有个明显问题遇到不会的题会硬编一个推理过程答案明明错了也毫不动摇。强化学习阶段解决的就是这个——让模型在推理过程中发现错误、修正方向最终给出正确结果。我用的是GRPOGroup Relative Policy Optimization。思路可以通俗解释让模型对同一个问题生成多组回答给每组回答打分然后用组内相对优势更新策略——比组内平均表现好的回答被强化差的被抑制。这样不用训练额外的价值网络工程实现简单很多也更省显存。奖励函数我设计了三个维度。一是格式奖励模型必须完整输出思维链和答案缺一部分就扣分二是答案正确性奖励用脚本计算最终答案是否正确三是推理过程奖励检查关键步骤是否与标准推导一致。最初只用前两个维度结果模型学会了“跳步”——答案正确但完全不展示推理过程。加上第三维后模型才逐步生成完整可验证的推理链。GRPO训练里我给每组生成8个回答RL学习率3e-6KL惩罚系数0.01。这个KL系数是控制模型偏离SFT模型的“松紧带”。系数太小模型会乱跑太大又学不到新东西。我试过0.05、0.03、0.01最终0.01效果最好训练稳定性和最终得分达到平衡。3.4 训练稳定性的三个关键参数RL训练最痛的是稳定性差。我经历了几次loss爆炸和策略崩溃最后沉淀出三个关键参数。第一是学习率。RL学习率必须比SFT低一个数量级以上我用3e-6而不是SFT的2e-5。如果学习率过高策略会在几轮内大幅偏离SFT模型输出直接变乱码。第二是KL惩罚系数它不能为0也不能太大。第三是梯度裁剪RL阶段有效batch因为每组生成8个而变大梯度方向本身更平滑但裁剪依然必须开我设1.0防止个别异常样本把参数推出稳定区域。还有一个容易被忽略的细节RL问题集的加载顺序。应该预先打乱并且不要让每个epoch的顺序完全一致。固定顺序会导致模型记住问题顺序看似loss在降实际泛化能力在恶化。4. 把模型真正用起来推理部署与性能优化4.1 INT8量化精度、吞吐与显存的三角权衡训练完成后模型还是FP16权重1B参数约占2GB显存。单用户场景没问题但并发一高显存和带宽就成了瓶颈。我做了INT8量化权重显存降到约1GB吞吐提升约30%。量化方案选了weight-only INT8只量化权重矩阵激活保持FP16。实现简单且对推理质量影响小。具体做法是对每个权重矩阵按行计算缩放系数用对称量化映射到[-127, 127]。实测1B模型在数学推理评测集上的分数下降不超过1%。一个反直觉的经验量化前一定要做校准不能直接把权重一股脑转成INT8。校准集要贴近实际使用场景——问题加思维链的数据而不是随便抽几段通用文本。我用500条评测集样本做校准效果比用通用语料好很多。校准本身只花十几分钟但对最终精度影响极大。4.2 批处理与KV Cache延迟和吞吐的取舍逻辑推理服务的核心矛盾是延迟和吞吐。单请求进来直接计算延迟最低但显存利用率极差多个请求拼成batch吞吐上去最慢的请求会拖累所有人。我的处理是连续批处理思路不等到batch里所有请求结束再释放资源而是每完成一个请求就把它移出去立刻加新请求进来。这样长请求不会卡死短请求。实现上最重的部分是按请求长度动态管理KV Cache。一开始我写静态KV Cache池后来改成动态分配延迟抖动明显减小。KV Cache分配也有讲究。1B模型4096上下文每个请求的KV Cache大约占几十MB。如果服务要支撑同时30个请求建议预留至少2倍空间因为连续批处理下实际生成长度可能比预估值长得多。我在这个上面吃过亏一开始只留1.5倍并发一高显存不够请求开始排队延迟暴涨。调到2倍后一切恢复正常。4.3 自写推理服务比套框架多学到的几件事这次项目我特意自己写推理服务没有直接套现成框架目的是把推理循环每个环节吃透。服务主体是一个Python异步HTTP接口内部就是生成循环接收请求、拼接prompt、查KV Cache、逐token采样、拼接输出。采样环节我设了两档温度普通问答0.7推理场景0.5。温度越低生成越稳定但过低会损失推理多样性导致模型在同一个错误方向上反复打转。实测数学推理场景0.5比0.7准确率高约2个点但遇到难题时“卡壳”概率也更高。这个取舍必须根据场景调整不能一刀切。服务里还要把max_new_tokens留足。推理模型的思维链经常很长我设2048。低于这个值模型会被截断表现为“推理到一半突然结束”。如果问题特别复杂要同步加大上下文长度和max_new_tokens但记得KV Cache占用也会成比例上涨。4.4 三层评测体系怎么证明模型真的会推理服务上线后真正的问题是它到底会不会推理我建了三层评测。第一层是数学推理评测集约500道带标准答案的数学题看答案正确率第二层是逻辑推理自建集约200道条件推理和排序推理题用规则脚本判断结果是否符合逻辑结构第三层是通用对话评测看自由对话流畅度防止推理能力增强后把对话能力搞崩。三层评测贯穿训练过程每个epoch结束都完整跑一遍。一个值得记录的经验是评测batch不要太大。模型在推理时如果同批次问题难度差异过大容易互相干扰难问题回答质量会下降。我把评测集每个batch控制在8条以内结果更稳定也更能反映真实水平。5. 踩坑实录训练和部署中的典型问题排查5.1 预训练loss震荡不收敛第一次遇到的严重问题是预训练中期loss呈锯齿状震荡一降一升不停。排查思路三步走先看数据加载顺序是否按来源混洗再看学习率峰值是否过高最后检查梯度范数如果频繁超过1说明裁剪没生效。这次问题的根源是我把梯度裁剪的位置写错了——只裁剪了主参数梯度没有裁剪优化器的动量状态个别异常batch的梯度被放大导致参数在稳定区域附近来回震荡。修正后loss重新平滑下降。这个坑提醒我梯度裁剪必须验证它真正生效的位置而不是代码里出现一行就以为万事大吉。5.2 输出死循环与思维链截断SFT完成后测试发现模型频繁输出“第一步第一步第一步……”的重复循环。最开始还以为是采样温度设置问题降到0.1依然复现才意识到是训练数据的问题。排查后发现SFT数据里有约2%的样本其思维链本身就是重复文本被模型当成了正常模式。解法分两层。数据层面写脚本检测思维链中连续重复的n-gram超过阈值直接剔除。推理层面加一条运行时规则当检测到相同三元组重复出现两次以上强制中断当前生成对最后一段做高温度重新采样。这个“重采样兜底”策略在实际使用中几乎杜绝了死循环现象。5.3 RL阶段策略崩溃奖励量纲不匹配的教训GRPO训练到第20个epoch左右模型突然开始输出大量无意义符号。追查发现是奖励尺度出了问题三个奖励维度的量级差别太大格式奖励占了绝对主导模型为了拿格式分直接放弃了推理内容。解法是重新设计奖励融合方式对每个维度的奖励先做归一化再乘各自的权重系数后相加。格式权重降到0.1正确性0.5推理过程0.4。调整后训练曲线恢复模型开始在正确性奖励引导下重新学习推理。这个教训非常关键多目标奖励不能裸加必须先统一尺度再融合否则梯度会被单一目标绑架。5.4 INT8量化后效果下降部署阶段发现INT8量化在简单题上表现几乎不掉点但难题正确率掉了约5%。排查定位到精度损失主要集中在attention层的大型权重矩阵。修复方式是改用per-group量化group size设为128替代per-row量化。per-row在小矩阵上够用但在大矩阵上舍入误差会累积。改进后难题正确率下降从5%收窄到1%左右。如果你的量化模型效果下降明显优先检查attention和mlp大矩阵的量化粒度这个方向能解决大部分问题。5.5 排查方法论小结把这些坑串起来看我会把排查经验归纳成一句话异常现象先归类归类后从数据、模型、参数三层分别切入。数据问题会表现为模型学会某类错误模式、loss形态奇怪模型实现问题多表现为分布极端异常、tensor形状报错参数问题通常表现为loss震荡、训练不收敛。按这个框架排查比顺手“改改学习率再跑一次”要高效得多。6. 复用这套工程能力它不止做了一个模型这次从零实践做完之后最大的收获不是模型分数高了几个点而是把链路每个环节都串了起来。以前用现成框架训练出问题只会重启现在看到loss异常我能快速判断是数据问题、学习率问题还是奖励配置问题。这种“定位能力”和“掌控感”是任何现成工具都给不了的。做完这套项目后这套能力可以迁移到几个方向一是给团队搭建内部的模型评测和诊断流水线二是做垂直领域的推理优化时知道改数据配比、改奖励函数、改量化粒度分别会带来什么效果三是从零复现一篇新论文时会从容很多不会对着论文里的公式发愁“这到底怎么实现”。如果只保留一条经验我会选“奖励归一化”。它帮我避开了RL阶段最大的坑也真正理解了多目标优化的本质——不同量纲的目标不能直接相加必须先统一尺度。这是AI工程里放之四海皆准的原则以后做任何多目标优化的任务都绕不开它。最后给也想动手做类似项目的朋友一句建议别追求一步到位复现大模型。先用自建数据、自写代码把一个小模型的完整链路跑通再一步步扩规模。这条路看起来绕实际是理解AI工程最扎实的路径。工程能力不是看会了多少工具而是看你在没有工具的时候能自己造出多少可用的东西。