资讯动态

大模型From Scratch全链路实战:从数据到部署的工程指南

发布时间:2026/10/2 19:58:48 来源:尧图企业网站定制
1. 为什么“from scratch”这条路值得走一个工程判断力的自我修炼很多朋友看到“ai-engineering-from-scratch”这个标题第一反应是“又要造轮子”。说实话两年前我也是这么想的。当时工作里已经天天在用开源模型和推理框架觉得从零训一个模型既费钱又费力还不如把时间花在调 prompt、调参数、优化 RAG 上。直到有一次线上模型的输出出现了大面积重复同事排查了三天最后定位到是采样参数和重复惩罚配置冲突。那三天里我一直在想如果我们连模型生成的基本机制都只是停留在“好像是这样”的层面出了问题只能靠猜。后来我下决心完整走了一遍 from scratch 的流程包括数据清洗、分词器训练、模型实现、预训练、指令微调、偏好对齐再到部署评估。那个过程让我把过去工作中所有“知其然不知其所以然”的点全部补上了。这篇文章就围绕这条路线展开。它适合两类人一类是刚入门、想真正理解大语言模型原理的工程师或学生另一类是已经用开源模型做应用的开发者想补上底层认知、提升问题排查能力。我会把路线图、选型理由、关键步骤和踩坑经验都写出来尽量做到能直接照着走。1.1 三条做 AI 的路我为什么选了最“笨”的一条现在做 AI 应用基本有三条路。第一条是调 API。优点是快几天就能出原型。缺点是成本高、数据出境合规麻烦、有上下文长度限制而且核心机制完全黑盒。调 API 做应用本质上更像系统集成离“理解模型”其实很远。第二条是微调开源模型。比如拿一个 7B 的底座模型做 LoRA 或全参数微调让它适配自己的业务数据。这条路比调 API 深一截至少你能看到训练循环、损失曲线和 checkpoint。但它同样有局限——开源底座已经把数据、分词、预训练、对齐这些环节做完了你接触的只是最后一段底层仍然不透明。第三条就是 from scratch自己收集数据、自己训练分词器、自己写模型结构、自己跑预训练和后训练。这条路最费时间但对理解最彻底。我当时的判断是如果目标是成为一个合格的 AI 工程师而不是只会套模板的人这条笨路必须走一遍。1.2 一个反直觉的事实小模型走全链路比大模型做局部优化更有价值很多人觉得from scratch 就一定要做一个几十B参数的大模型动辄几千张显卡。实际上完全不是这样。我在完整走流程时先是跑了两个 19M 参数的 toy model 用来调试代码再上了一个 157M 的模型最后才跑到 1.4B。整个过程中学到的东西跟模型尺寸完全不成正比——即便只是 157M 的模型当你亲手把数据、分词、预训练、SFT、DPO 全流程跑通你会对所有大模型的新闻产生完全不同的感知。比如看到别人说“我们用了多少 T token 做预训练”你脑子里会自动换算这个规模的数据大概是多大体量的网页、清洗后剩多少、按我的 GPU 要跑多久。这个判断力是看论文和调 API 永远给不了的。论文给你的是一张地图from scratch 是让你亲手把这块地走一遍。1.3 这条路真正的产出不是模型而是正确的心智模型我做完整个项目复盘时最深的感受是兜里多了一个能跑的小模型但它远不是最重要的收获。重要的是三套心智模型。第一套是数据心智。现在我拿到任何数据集第一反应是看它的去重情况、语言分布、噪声水平。第二套是训练心智。看到 loss 曲线我能大概判断是学习率问题、数据问题还是梯度稳定性问题。第三套是评估心智。我不再轻信 benchmark 分数因为我知道那些测试集可能就在我自己的训练语料里——评估集的污染问题在 from scratch 场景下暴露得太明显了。这也是我把这篇文章的重点放在全链路而非单个点上的原因。每个环节单独拆开都有人写过教程但把它们串起来、用成本可接受的规模走通才是“from scratch”这个标题真正的价值所在。2. 一张完整的路线图从零到能跑的模型一共分几步在动手之前最重要的事情不是写代码而是把整个工程拆成清晰的阶段。AI engineering 本质上是复杂的系统工程如果你没有路线图很容易卡在某个环节出不来或者做到一半发现前面某个决策错了被迫重构。2.1 五阶段全景数据、预训练、后训练、评估、部署我最终把整个 from scratch 项目拆成五个阶段。这里用一张表格先把全貌列出来后面每一段我都会展开讲。阶段核心交付物关键决策点建议投入时间占比数据工程清洗后的训练语料、分词器数据源选择、去重策略、词表大小、混合配比30%模型实现可训练的 Transformer 源码架构选型、初始化、超参15%预训练checkpoint、loss 曲线学习率调度、稳定性、监控指标30%后训练指令跟随模型、偏好对齐模型SFT 数据、DPO/RL 策略15%评估与部署可调用的推理服务评测集、量化方案、推理引擎10%我特意把数据工程放在 30% 的投入这不是拍脑袋而是血泪教训。第一次做的时候我把 40% 时间花在了模型代码上结果预训练时发现语料里全是重复和低质量内容loss 死活降不下去最后回头重做数据等于白跑了两个礼拜。2.2 规模与成本估算一张消费级显卡能跑到什么程度先算账再动手。做 AI engineering 如果不算账很容易一开始就买车买炮然后发现打不起仗。模型规模的选择核心是理解两个数字参数量 N 和训练 token 数 T。业界有一个经验规律——一个大模型的总计算量大致是 6×N×T FLOPs6 倍的参数乘以 token 数前向反向各占一部分。所以你选定了模型大小再选定训练数据量基本就锁定了总计算量进而锁定 GPU 时长和成本。我建议的入门规模是这样三档19M 参数仅用于调试代码比如验证 forward、backward、数据 loader 有没有 bug。我用它在单张 4090 上跑了几百万 token十几分钟就完事。157M 参数这是我最推荐的学习主力规模。搭配 3B 左右的训练 token单张 A100 大概 2 到 3 天能跑完。没有 A100 的话用 4090 也能跑但时间翻倍。1.4B 参数这是“有点真实感”的规模能明显看到语言能力的涌现。搭配 10B 到 20B token大概需要 4 到 8 张 A100 跑一周。如果没有多卡资源这一档暂时可以先跳过。关于硬件我想纠正一个常见误区from scratch 并不一定要从 H100 起步。我自己在 4090 上跑过 157M 的模型24GB 显存完全够用。关键是用混合精度bf16别用 fp16后者在 LLM 训练里特别容易炸 loss。如果你只有一张 4090那就从 100M 到 300M 这个区间起步先把全链路走通以后再加规模。2.3 项目的最终目标是要一个模型还是要一项能力这一点很多教程不会讲但你的路线图应该由目标来倒推。我见过两种常见诉求。一种诉求是想做产品。比如要做垂直领域的客服助手。这种情况下我的建议是from scratch 只做学习闭环产品线仍然以开源底座模型为基础去做后训练这样可控性和效率都高得多。你不需要为了一个垂直场景投入巨额算力去训底座。另一种诉求是想吃透技术。这时候我建议你坚持把 from scratch 走到 1B 左右甚至更远因为只有到了这个规模很多在 157M 上观察不到的现象才会出现比如思维链长度的扩展、评估基准上的非线性提升。想清楚你要的是“一个能用的模型”还是“一套可迁移的工程能力”这决定了从零开始的真正路径。否则很容易做到一半发现方向错了。3. 数据与分词模型的一切在这里就已经决定大半Jim Fan 有句话我特别认同数据才是真正的护城河。做 from scratch 之后我对此体会更加深刻。模型架构是公开的、训练代码是公开的唯一让一个模型与众不同的是你喂给它的语料和喂料方式。3.1 数据收集与清洗从公开数据集到你真正能用的语料第一次做 from scratch 时最容易犯的错是直接下载一个巨型公开数据集就开始训练。实际上一份原始语料下载下来至少要经历三道处理。第一道是去重。公开语料里相同文本的变体非常多尤其是新闻和百科类内容。我用的方法是两层先做 exact match 去重再做 MinHash 近似去重。MinHash 的思路是把文本拆成 shingle连续片段集合然后算 Jaccard 相似度超过阈值就丢。这个做法成本低、效果好清洗后语料体积往往能减少 20% 到 30%。第二道是质量过滤。这里我建议至少跑以下规则语言检测只保留目标语言、长度过滤丢弃过短的和过长的、URL 过滤很多公开数据集直接提供了过滤模板、重复字符比例过滤。更进阶一点可以训练一个小型的 perplexity 分类器把生成文本和正常文本区分开。但这一步新手可以先跳过。第三道是内容安全过滤。这里不做展开但要记住你的模型会学到训练语料里的任何东西垃圾进垃圾出。公开数据里大量的成人内容、广告和机器生成文本都需要过滤掉。我用的方案是关键词黑名单加分类模型双层过滤这一步不要省。数据量的选择也很有意思。我最终给 157M 模型配的是 3B token。为什么是 3B一个简单的经验值是——数据量应该是参数量的 20 到 30 倍以上但这个比例在超大模型上并不成立。实际做的时候你可以先用一个小子集跑通流程再逐步增加数据观察 loss 下降的趋势有没有停。3.2 亲手训练一个分词器为什么它比模型结构更影响体感如果你的任务是中文和代码混合的语料分词器好坏对你的模型效果影响非常大。我一开始直接用了一个和别的开源模型完全相同的分词器结果生成中文时 token 效率奇低同样一段中文token 数是英文的两倍多。训练分词器我用的是 SentencePiece 里的 BPE 模式词表大小设成了 16k。这里有一个关键选择用 byte-level BPE还是 character-level 的我建议直接用 byte-level。这样做的最大好处是永远不会遇到 OOV词表外词任意一个 Unicode 字符都能被拆成字节并编码代价只是 token 序列会稍长一点但换来的是极强的泛化能力。训练分词器的一个经验是语料配比一定要和你预训练语料保持一致。如果你预训练语料 50% 是代码那分词器训练语料也应该给代码 50% 的权重这样分词器学到的是代码里常见的 token 分布而不是一堆自然语言里根本不出现的碎片字符。另外我强烈建议在词表里加入几个特殊 token除了标准的 bos、eos、pad、unk 之外至少再加一个|endoftext|。如果你要做对话模型还需要考虑 chat 模板的特殊 token比如|user|和|assistant|。这个决策要放在分词器阶段想清楚否则后面改 tokenizer 等于重训整个模型。3.3 数据混合配比一份可以直接抄的起点配置训练语料不是简单地拼在一起。我见过初学者把代码语料和自然语言语料直接 concat结果一个 batch 里全是代码下一个 batch 全是英文新闻模型的梯度更新像喝醉酒一样乱跳。正确的做法是设置采样权重也叫数据混合配比。我的起点配置是这样的通用自然语言网页、书籍、百科60%代码GitHub 类语料20%数学与科学文本10%多语言文本10%这个配比不是金科玉律但它有一个合理的内核通用语料是基础代码和数学能让模型获得更好的结构化能力多语言保证泛化性。实际执行时我会为每个数据源设置一个采样概率而不是简单拼接到一起。还有个细节数据集混入的顺序也可以做课程学习。初期多放通用语料后期逐步增加代码和数学比例。我实验下来这种渐进式混合有助于稳定起始阶段的训练但收益不算巨大属于有精力再做的优化项。4. 模型架构与实现从零手写 Transformer 的关键决策到了模型实现阶段最容易犯的错是上来就 import 一个大模型库调一个Trainer开始训练。用现成的库当然高效但那不是 from scratch。我建议在项目的第一版至少要把模型的前向传播和训练循环亲手写一遍。4.1 架构选型Decoder-only RoPE RMSNorm SwiGLU当前大语言模型的默认架构已经相当收敛我的选择和主流开源模型基本一致decoder-only 的因果语言模型。原因很简单自回归式生成是目前经过验证最稳定、最简单的范式。Encoder-decoder 在翻译任务上有优势但通用能力和训练稳定性不如 decoder-only。组件选型方面我也站在了主流一边归一化用 RMSNorm 而不是 LayerNorm。RMSNorm 去掉了均值中心化只保留均方根归一化计算量更小训练效果和 LayerNorm 几乎持平。位置编码用 RoPE旋转位置编码。它通过旋转矩阵把位置信息注入注意力计算天然支持相对位置表达。相比绝对位置编码RoPE 在推理时能更好地适应比训练更长的序列。激活函数用 SwiGLU。纯粹的 ReLU 在 Transformer 中表达力偏弱而 SwiGLU 是 gated 结构能带来稳定且可感的收益代价是多了一点参数量。我当时的参数表里FFN 的中间层维度和 hidden size 的关系是按 SwiGLU 的公式算的不是随便设的。4.2 一个最简可运行的 Transformer Block 骨架这里给一个极简的核心骨架它不是一个完整可训练的代码但能让你一眼看清模块结构。我用 PyTorch 写方便和nn.Transformer里的概念对照。import torch import torch.nn as nn import torch.nn.functional as F class RMSNorm(nn.Module): def __init__(self, dim, eps1e-6): super().__init__() self.weight nn.Parameter(torch.ones(dim)) self.eps eps def forward(self, x): rms torch.sqrt(x.pow(2).mean(-1, keepdimTrue) self.eps) return x / rms * self.weight class SwiGLU(nn.Module): def __init__(self, dim, hidden_dim): super().__init__() self.w1 nn.Linear(dim, hidden_dim, biasFalse) self.w2 nn.Linear(hidden_dim, dim, biasFalse) self.w3 nn.Linear(dim, hidden_dim, biasFalse) def forward(self, x): return self.w2(F.silu(self.w1(x)) * self.w3(x)) class TransformerBlock(nn.Module): def __init__(self, dim, n_heads, hidden_dim): super().__init__() self.attn_norm RMSNorm(dim) self.attn nn.MultiheadAttention(dim, n_heads, batch_firstTrue) self.ffn_norm RMSNorm(dim) self.ffn SwiGLU(dim, hidden_dim) def forward(self, x, maskNone): # 注意这里为了演示省略了 RoPE 的实现 # 实际应在 attention 的 Q、K 上注入旋转位置编码。 x x self.attn(self.attn_norm(x), self.attn_norm(x), self.attn_norm(x), attn_maskmask, need_weightsFalse)[0] x x self.ffn(self.ffn_norm(x)) return x看到这个骨架你应该能直观理解两件事。第一每个子层都是“残差连接 预归一化”的结构。归一化在残差分支之前这个顺序在现代 LLM 中几乎已经是标准。第二FFN 和 Attention 都被包裹在残差里所以整个网络的主干是一条恒等路径。这保证了深层网络的梯度可以顺畅回流是训练稳定性的结构基础。我自己第一版写的代码远比这个复杂还加了 attention mask、KV cache 的 switch以及 RoPE 的矩阵计算。但如果你把它拆到这个骨架的粒度去理解后续看任何开源模型的源码都会轻松很多。4.3 初始化和超参数论文里往往不写的那张表模型能跑起来之后决定训练顺不顺的往往是几个不太起眼的参数。我最开始就是随意初始化结果模型一度训不懂。先看初始化。我用了 GPT-2 风格的初始化方案所有 Linear 层权重用均值为 0、标准差为 0.02 的正态分布初始化残差分支的输出层用一个更小的标准差公式是0.02 / sqrt(2 * num_layers)。这样做的理由是层数越深残差累加的量越大如果每层都用同样的幅度到深层时梯度会过大。缩小残差分支的初始化幅度能保证整个网络前向传播的方差维持在某个稳定范围。再看优化器。LLM 训练的默认选项基本就是 AdamW。但一个细节是 AdamW 的 epsilon——很多框架默认是1e-8但在大模型训练里我实际更倾向于1e-5到1e-6。一个更小的 epsilon 会导致数值稳定性变差容易遇到 loss spike。学习率方面我的起点是峰值 3e-4配合 1% 的 warmup 步数然后 cosine 衰减到峰值的 1/10。这个设置对 157M 的模型非常稳但如果你发现 loss 波动剧烈可以降到 1e-4。另外权重衰减我设的是 0.1是针对非 bias 和 non-norm 参数的。最后是一个不写进论文的实战指标如果你的 loss 在训练初期完全不降先别折腾模型结构按“数据问题 → 学习率问题 → 代码 bug 问题 → 架构问题”的顺序排查。我第一次遇到 loss 不动花了两天检查 attention 实现最后发现只是数据 loader 里文本顺序写错了。5. 预训练实战让 loss 曲线的每一步都能被你理解预训练是整个 from scratch 项目里最磨人也最需要直觉的阶段。你面对的是一块黑屏唯一的反馈是每几百步打印一次的 loss 数字。一个好的工程师得能从这些数字里读出整个训练系统的健康状况。5.1 训练稳定的三件套调度、裁剪、精度我第一版训练脚本的三个关键配置直接决定了我后面能不能顺利睡觉。学习率调度上我采用 “warmup cosine decay” 的组合。warmup 让学习率从 0 缓慢升到峰值避免一开始就对随机初始化的参数施加过大更新。cosine decay 让训练后期步长逐渐变小有助于收敛到更平滑的极小值。warmup 比例我设为总步数的 1%峰值学习率 3e-4。梯度裁剪是第二道保险。我设max_grad_norm 1.0。不要听别人说“梯度裁剪会让训练变慢”实际不会。它只是把异常大的梯度拉回正常范围防止一步更新把参数甩到未知区域。遇到偶发 loss spike它就是你最好的防御。精度策略是第三道也是最容易踩坑的一环。在 NVIDIA 显卡上bf16 几乎总是优于 fp16。fp16 的数值范围窄loss 稍微大一点就会溢出成 NaN。bf16 牺牲了精度但保住了范围对大模型训练来说指数范围比尾数精度重要得多。我一开始用 fp16稳定踩了几天 loss spike换成 bf16 后世界清净了。5.2 怎么读 loss 曲线几种典型病态模式的判断普通的教程只告诉你“loss 下降就是好”。但实际上loss 曲线有几种典型形态每一种都对应不同的问题。正常形态loss 平滑下降斜率逐渐趋缓。训练初期前 10% 步数下降最快后面越来越慢。这表示数据、模型、调度器都在正常运作。平台形态loss 在某个值附近横盘不动。先别急着改模型检查数据循环是否在循环同一个文件。我遇到过数据 shuffle 写错导致每个 epoch 看到完全相同的顺序模型陷入局部循环。这种问题的特征是 loss 在学习率 warmup 结束后就不再变化。反弹形态loss 下降到某个点后突然上升。大概率是优化器状态被破坏或数据里混入了异常样本。checkpoint 后重新加载训练通常能解决。发散形态loss 变 NaN 或直接冲到几百。检查三件事——梯度裁剪有没有生效、bf16 有没有被意外关掉、输入数据里有没有大量非 UTF-8 字符。5.3 中间评估除了 loss人眼抽查才是最靠谱的评估到训练中期我开始每隔一定步数做一次“人眼抽测”。具体做法是从验证集里抽几条开头用当前 checkpoint 做 continuation肉眼观察输出是否连贯、有没有复读、有没有中文英文夹杂。这一步的灵感来自一次惨痛经历。当时 loss 已经降到 3.0 以下看起来一切正常但我抽测了几条中文样本发现模型在中文输入后回应的是英文。检查分词器才发现我的语料混合比例里英文占比过高中文 token 几乎被边缘化了。如果你的模型出现严重的复读现象几个常用的缓解手段是降低采样温度、增加重复惩罚、限制上下文长度。但如果是预训练阶段就严重复读更可能是训练数据里重复片段太多需要回到数据清洗阶段补一刀。6. 从“会续写”到“会推理”后训练与推理能力的打磨预训练结束你得到的模型本质上是“一个超强自动补全工具”。它会续写但不会回答问题。从“续写”到“对话”中间隔着后训练。而如果要走向“推理模型”还需要专门的力量。6.1 SFT 指令微调让模型学会“回答问题”SFT有监督微调的第一步是准备指令数据。一个好的初始配置是公开的 SFT 数据集比如 Alpaca 风格或 OpenOrca 风格加几十到几百条你自己写的领域数据。重点永远在质量不在数量——2000 条手工筛选的高质量数据效果远好于 20 万条爬虫数据。数据格式上我统一用 chat template。每条样本是 role 为 user 和 assistant 的对话SFT 时只对 assistant 部分的 token 计算 lossuser 部分只作为上下文输入不参与梯度更新。这一点非常关键否则模型会学会“复述用户问题”而不是学会回答。训练技巧上全参数微调在 157M 规模是可行的显存占用不算大。但如果你的 base 模型到了 1B 以上我更推荐 LoRA。LoRA 并不会让效果一定变差它的核心价值是省显存、省时间以及方便做多个领域的试验。6.2 让模型学会“思考再答”推理能力 from scratch如果你关注最近开源社区“build a reasoning model from scratch”的热点一定会好奇推理能力到底是怎么来的一个直觉而有效的做法是让模型在输出最终答案之前先输出一段推理过程。这就是 CoTChain of Thought。为什么它有效我的理解是它把原本必须在“隐空间”里完成的中间计算显式写到了 token 序列里。模型的每一次自回归生成本质上都是“计算一步”把中间步骤写出来相当于给模型扩容了计算深度。数据从哪里来常见做法有三种直接用公开的 CoT 数据集。这类数据集里有大量“先推理再作答”的样本。自己构造合成数据。你可以用既有的强模型生成带步骤的答案但要注意合规问题不能未经授权使用受保护的数据。半合成方法拿纯答案数据用小模型或规则先生成推理过程再人工筛选。我在做 from scratch 项目时用的是一种很轻量的“思考前缀”方案。做法是在推理数据中把模型生成强制分成两个阶段先用若干 token 写reasoning区域再写final answer区域。训练时模型被引导在回答前先“思考”。这不需要复杂的强化学习只是数据格式设计的改变效果却立竿见影。6.3 偏好对齐DPO 是门槛最低的一步再往下走就是偏好对齐。传统 RLHF 需要训练奖励模型、跑 PPO工程复杂度太高。DPO直接偏好优化的诞生把这一步简化到只需要正负样本对。DPO 的原理不复杂它不需要单独训练奖励模型而是直接用偏好数据在原有 SFT 模型上做一次对比优化。你只需要准备 chosen好回答和 rejected差回答的数据对。训练时模型被推动去提高 chosen 的概率、降低 rejected 的概率同时用一个参考模型约束住更新的范围防止模型被带偏。我的建议是SFT 做完后不要直接部署补一轮 DPO。我自己做过对照同样的 base 模型SFT 加 DPO 之后的回答“礼貌度”和“稳定性”都明显好于纯 SFT。如果之后你想做更接近 R1 风格的强化学习路线比如用 GRPOGroup Relative Policy Optimization做推理奖励优化那 DPO 这一步就是最好的热身。它让你先理解“偏好数据如何影响模型行为”然后再进入更复杂的强化学习循环。7. 部署与评估怎么证明你的模型不是“自我感觉良好”最后一个阶段也是所有 AI engineering 项目的终极拷问它真的可以吗这里需要把评估和部署分开看因为评估回答的是“模型能力”部署回答的是“能不能稳定提供服务”。7.1 评估集的选择别只用困惑度也别只信跑分预训练阶段看 loss后训练阶段如果还只看 loss那就容易被表面数字欺骗。损失函数下降并不等于任务能力提升这是 LLM 训练里最经典的一个陷阱。我的评估矩阵分了三个层次基础层语言建模困惑度。这是最弱的指标只能告诉你模型“有没有学会语料分布”不能告诉你“懂不懂”。任务层用开源评测工具跑 few-shot 基准比如 MMLU、HellaSwag、ARC 这类。对这个领域比较陌生的话先去了解lm-evaluation-harness这个工具它是目前生态里最主流的评估框架。应用层针对你的使用场景设计几十条真实测试题人肉逐个看回答质量。比如我的目标是对话助手就准备 50 条语气、知识、安全边界相关的测试题每条都人工打分。这里必须踩一个坑提醒评估基准的数据很可能在你预训练语料里出现过这叫“评估污染”。如果 benchmark 分数很高但真实对话常识错误百出先怀疑污染再怀疑评估方式。7.2 部署的最小闭环从 Python 推理到生产级服务的跳跃157M 的模型直接用 PyTorch 写推理也不是不行但一旦要考虑并发和服务化就要走标准路线。第一步是量化。我通常先用bitsandbytes做 4bit 量化或者用 GPTQ/AWQ 做离线量化。对 157M 模型来说收益不明显但到 1B 级别量化能把显存占用减半以上。第二步是推理引擎。如果你的服务是单用户、低并发直接写一个简单的 FastAPI 调用模型生成就够了。但如果是多用户、高并发强烈建议上 vLLM 或 TGI。它们实现了 PagedAttention 和 continuous batching吞吐量比朴素 PyTorch 推理高一个数量级。我实测同样一个模型裸推理每秒处理 5 个请求vLLM 可以到 50 个以上代价只是一个额外的依赖和几行启动代码。第三步是上线前的安全边界。包括输入长度限制、输出 token 数限制、敏感词过滤、以及拒绝服务时给用户的“安全回答”。这些属于工程细节但往往决定你的模型是“演示品”还是“产品”。7.3 我现在回头看这些坑是最值得提前知道的整个 from scratch 项目做完我复盘了那些最消耗时间的坑集中写在这里希望能帮你跳过。第一个坑是分词器语料和预训练语料不一致。我最初用纯英文语料训练分词器换到中英混合语料时发现中文 token 效率极低重头训练了分词器才好转。现在我的流程是先确定最终语料配比再用同样的配比训练分词器。第二个坑是 fp16 训练导致 loss spike。前面在精度策略里已经说过这里再强调一次直接使用 bf16 作为默认精度。第三个坑是评估集污染。我在 benchmark 上看到一个虚高的分数后来抽样测试才发现很多测试题在训练语料里有原文。从那以后我养成了“任何数据集进训练之前先和评估集做一次 overlap 检查”的习惯。第四个坑是贪多求大。第一次做 from scratch 的朋友往往一上来就想训 1B 模型结果连 loss 都不下降排查成本极高。我更推荐先用 19M 模型跑通整个 pipeline再逐步放大。如果你完整走一遍这条路——数据清洗、分词器、模型实现、预训练、SFT、DPO、评估、部署——你得到的不仅是一个模型文件和一份训练日志而是一套能迁移到任何模型项目上的工程直觉。以后再看到任何新模型发布你的第一反应会从“卧槽好强”变成“它的数据是什么训练配置是什么评测是怎么做的”。这种思维方式才是 from scratch 这个项目给一个工程师留下的真正资产。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑