从零训练一个小 LLM
站里跟推理有关的内容已经有两块了:vLLM 专题讲怎么把别人的模型跑快,自制推理框架讲怎么自己写一个引擎。但模型本身是怎么来的,一直没写。这个专题补的就是这块。
数据快照 2026-08-19。下面所有数据集的行数、体积、仓库 star 数,都是当天用 HuggingFace 的 datasets-server API 和
gh api查的。训练耗时和显存占用等跑完再回填真实值,没实测过的数字我都标了「估算」。
一、先说清楚这个专题的定位
网上讲训练大模型的资料大致分三类,各有各的问题。
第一类是论文和综述,讲 scaling law、讲架构演进,看完知道了很多名词,但不知道第一行代码该写什么。
第二类是「一键微调」教程,pip install 一个库,改个配置文件,跑起来了。能出结果,但中间发生了什么完全是黑盒,出了问题只能换个参数再试一 次。
第三类是工业级框架的源码,Megatron、DeepSpeed,代码是对的,但一个类继承五层,看完两天还没找到 loss 在哪算的。
这个专题走的是第四条路:规模小到能跑完,代码浅到能看懂,但每一个部件都是真的。不用 Trainer,不用 from_pretrained,模型结构、训练循环、并行策略、数据流水线全部自己写一遍。最后训出来的权重,喂给自制推理框架加载,能正常生成文本。
先摆明白哪些事这个专题不做:不做架构创新,结构照抄 Llama;不追 SOTA,0.5B 的模型能力就那样,别指望它做数学题;不讲 MoE、不讲长上下文外推,那是另外的题目。
二、整条链路长什么样
四个阶段里,只有阶段二要烧 GPU 的钱。阶段一全在 CPU 上跑,一定要在租卡之前做完,这是省钱的第一原则。
三、规模怎么定
0.5B 这个数不是随手挑的。我是倒着算的:先看租得起多少卡、能忍多长时间,再反推能训多大的模型。这一节把整个推导过程走一遍,因为这套算法你换个规模还能再用一次。
3.1 第一步:定 token 数
Chinchilla 那篇论文(Hoffmann et al., 2022)给了个很好用的经验值:算力有限的时候,参数量 N 和训练 token 数 D 大概按 1:20 配,最划算。
这个结论的意思要理解对。它不是说「D = 20N 时模型最强」,而是说「给定一笔固定的算力预算,把它分配成 N 和 D 时,1:20 附近能得到最低的 loss」。如果你算力无限,那当然是模型越大、数据越多越好。正因为算力有限,才有这个最优分配问题。
| 参数量 N | Chinchilla 最优 token 数 D |
|---|---|
| 0.1 B | 2 B |
| 0.5 B | 10 B |
| 1 B | 20 B |
| 7 B | 140 B |
然后有个挺巧的事。HuggingFace 的 HuggingFaceFW/fineweb-edu 官方就切好了一个叫 sample-10BT 的子集:
| config | rows | parquet 体积 |
|---|---|---|
sample-10BT | 9,672,101 | 28.5 GB |
sample-100BT | 97,270,686 | 286.4 GB |
sample-350BT | 339,347,842 | 998.1 GB |
default | 1,525,223,056 | 4,522.7 GB |
(2026-08-19 实测自 datasets-server 的 /size 接口。最后那个全量的别碰。)
0.5B 乘 20 就是 10B tokens,正好一个 sample-10BT。28.5 GB 下得动,一块盘放得下。规模就这么定下来了。
3.2 第二步:算要跑多久
训练一次要多少 FLOP,有个很好记的估算式:
这个 6 是怎么来的,值得说一下,因为后面估 MFU 要反复用。对一个线性层 ,前向做一次矩阵乘,每个参数贡献 2 次浮点运算(一次乘一次加)。反向要算两个梯度:对输入的梯度和对权重的梯度,各是一次同样规模的矩阵乘,所以是 4 次。加起来每个 参数每个 token 就是 6 次。
代进去:
A100 80G 的 BF16 稠密峰值是 312 TFLOPS,但真实训 练摸不到峰值。差距用 MFU(Model FLOPs Utilization)描述,它的定义就是「实际有效算力 / 理论峰值算力」。小模型能跑到 35%~45% 算正常,大模型调好了能到 50% 以上。按 0.40 算,单卡有效算力:
| 卡数 | 有效算力 | 预训练耗时(估算) |
|---|---|---|
| 1 × A100 | 0.125 PFLOPS | 约 67 小时 |
| 2 × A100 | 0.25 PFLOPS | 约 33 小时 |
| 4 × A100 | 0.5 PFLOPS | 约 17 小时 |
| 8 × A100 | 1.0 PFLOPS | 约 8.3 小时 |
上面这张表假设多卡是线性加速的,实际肯定拿不到。通信要花时间,4 卡拿不到 4 倍。到底差多少,是 04 篇要实测的事。
选 4 卡。17 小时意味着晚上开跑、次日早上取结果,调参迭代的周期可以接受;同时卡数足够暴露通信瓶颈。
3.3 第三步:核对显存装不装得下
不少教程会让人以为,多卡是因为单卡 装不下。0.5B 这个规模装得下,而且富余不少。
显存占用分两部分,一部分是静态的(跟 batch size 无关),一部分是动态的(激活值,跟 batch size 和序列长度成正比)。先算静态的,用 BF16 混合精度加 AdamW:
| 项目 | 每参数字节 | 0.5B 合计 | 为什么是这个数 |
|---|---|---|---|
| BF16 权重 | 2 | 1.0 GB | 前向反向用的就是它 |
| BF16 梯度 | 2 | 1.0 GB | 反向算出来的 |
| FP32 master weights | 4 | 2.0 GB | BF16 精度不够做参数更新,得留一份高精度的 |
| AdamW 一阶动量 m | 4 | 2.0 GB | 梯度的滑动平均 |
| AdamW 二阶动量 v | 4 | 2.0 GB | 梯度平方的滑动平均 |
| 小计 | 16 | 8.0 GB |
「每参数 16 字节」这个数值得记住,它是 BF16 + AdamW 的标准开销。换个优化器就变了:用 SGD with momentum 只要 2+2+4+4 = 12 字节,用 8-bit Adam 能压到 2+2+4+1+1 = 10 字节。
剩下的才是激活值,靠 batch size 和重计算调。8 GB 静态占用,40G 的卡都够用,80G 更是随便放。
所以这个专题里用多卡,跟装不下没关系,就两个理由。一是省时间,67 小时压到 17 小时,才有可能反复调参。二是不上多卡就学不到并行,DDP 和 FSDP 的区别,一张卡上根本不存在。
也正因为装得下,FSDP 在 0.5B 上配了也看不出好处。所以 04 篇我打算故意把模型加大到单卡装不下,先看它 OOM,再用 FSDP 救回来。不这么干,那篇就只能纸上谈兵。
3.4 第四步:算钱
设卡时单价 (元 / 卡 / 小时)、卡数 、耗时 小时:
4 卡跑 17 小时是 68 卡时,这是顺利跑通一次的量。但基本不可能一次就顺,调参和翻车都要重来,我按 150~200 卡时做整体预算。
各家平台的挂牌价一直在变,这里不写死。开跑前按当天单价代进去算,05 篇会记这轮实际烧了多少。
租卡有几个坑,04 篇细说,先记在这。数据先下到持久化 云盘再开卡,别开着 4 张 A100 在那下 28.5 GB,那是拿 GPU 的价钱买下载时间。checkpoint 必须落到持久化存储。租用实例可能被随时回收,12 小时的训练结果没有第二份。先用 1% 的数据在单卡上把整条流程走通,再开多卡,多卡调试的每一分钟都是 4 倍的钱。
四、最终的模型配置
02 篇会一行一行把它写出来,这里先把结论摆着,方便对照。
| 超参 | 取值 | 定它的理由 |
|---|---|---|
vocab_size | 32,000 | 对齐 Llama;且小于 65536,token id 能塞进 uint16 |
d_model | 1,536 | 配合层数凑到 0.5B |
n_layers | 18 | 同上 |
n_heads | 12 | head_dim = 1536/12 = 128,对齐主流 |
n_kv_heads | 4 | GQA,3 个 Q 头共享 1 个 KV 头 |
ffn_dim | 4,096 | 正好等于 8/3 × d_model,Llama 的惯例 |
max_seq_len | 2,048 | 显存和长程依赖的折中 |
| 权重共享 | embedding 与 lm_head 共享 | 省 49M 参数 |
按这个配置实算,总参数量是 502,193,664,正好 0.5B。拆开看:
| 部分 | 参数量 | 占比 |
|---|---|---|
| Embedding(与输出层共享) | 49,152,000 | 9.8% |
| 18 层 Transformer Block | 453,040,128 | 90.2% |
| 最后的 RMSNorm | 1,536 | ~0% |
| 合计 | 502,193,664 | 100% |
每一层 Block 内部再拆:
| 组件 | 参数量 | 占比 |
|---|---|---|
| Attention(q/k/v/o 四个投影) | 6,291,456 | 25.0% |
| SwiGLU MLP(gate/up/down 三个投影) | 18,874,368 | 75.0% |
| 两个 RMSNorm | 3,072 | ~0% |
MLP 占了四分之三的参数,这是 Transformer 的常态,也是为什么做量化和 MoE 都优先动 MLP。
五、篇章规划
| # | 标题 | 这一篇的产出 | 状态 |
|---|---|---|---|
| 01 | 数据工程 | 清洗、去重、训 tokenizer、打包成 train.bin | 🚧 |
| 02 | 模型结构从零实现 | model.py,能前向、参数量对得上 | ⏳ |
| 03 | 预训练主循环 | train.py,单卡能跑,loss 会降 | ⏳ |
| 04 | 从单卡到多卡 | DDP 和 FSDP 实测,显存账、通信瓶颈、租卡实操 | ⏳ |
| 05 | 第一次完整预训练与复盘 | 复盘模板:检查清单、监控项、诊断表,跑完回填 | 🚧 |
| 06 | SFT | 指令数据怎么造、loss mask、全参和 LoRA 对比 | ⏳ |
| 07 | 偏好对齐 | 以 DPO 为主,讲清 GRPO 跟 PPO 差在哪 | ⏳ |
| 08 | 评测与接回推理框架 | 跑评测集,权重喂给自制推理框架 | ⏳ |
六、参考资料
这个专题不是凭空写的,下面这些是我实际会翻的东西。star 数是 2026-08-19 用 gh api 查的。
先说中文的系统教程,这几个的完成度和体系性比大多数英文博客高。
| 项目 | Star | 协议 | 我拿它干什么 |
|---|---|---|---|
datawhalechina/happy-llm | 33,055 | 见仓库 | 《从零开始构建大模型》,跟本专题重合度最高。第五章手搓 LLaMA2、第六章训练流程、第八章强化学习,是我主要的对照物 |
datawhalechina/self-llm | 31,758 | Apache-2.0 | 《开源大模型食用指南》,偏部署和微调实操,06 篇 LoRA 部分对照它 |
datawhalechina/tiny-universe | 5,015 | 见仓库 | 《大模型白盒子构建指南》,全手搓,思路跟本专题一致 |
Infrasys-AI/AIInfra | 7,970 | Apache-2.0 | AI 基础设施全栈,04 篇讲分布式并行和集合通信时的主要参考,配套站点 aiinfra-docs |
HuaizhengZhang/AI-Infra-from-Zero-to-Hero | 4,284 | MIT | AI System 论文和工业实践的索引,找某个主题的经典论文时查它 |
再是训练框架和参考实现。
| 仓库 | Star | 协议 | 拿它干什么 |
|---|---|---|---|
karpathy/nanoGPT | 62,199 | MIT | 训练主循环的骨架,最干净的参考,03 篇主要照着它 |
jingyaogong/minimind | 54,820 | Apache-2.0 | 中文小模型全流程,规模和本专题最接近 |
karpathy/llm.c | 30,827 | MIT | 想弄明白某个算子到底算了啥,翻它 |
hiyouga/LLaMA-Factory | 74,215 | Apache-2.0 | 06 篇 LoRA 的对照组 |
volcengine/verl | 23,027 | Apache-2.0 | RL 那块的工业级参考 |
huggingface/trl | 19,107 | Apache-2.0 | 07 篇 DPO 的基线 |
pytorch/torchtitan | 5,636 | BSD-3-Clause | 并行策略的官方参考实现,04 篇对照 |
huggingface/nanotron | 2,789 | Apache-2.0 | 3D 并行的极简实现,04 篇对照 |
七、语料候选
| 数据集 | 行数 | 体积 | 许可 | 用途 |
|---|---|---|---|---|
HuggingFaceFW/fineweb-edu(sample-10BT) | 9,672,101 | 28.5 GB | ODC-By | 英文主语料 |
opencsg/chinese-fineweb-edu | 1,200,000 | 3.5 GB | Apache-2.0 | 中文补充 |
roneneldan/TinyStories | 2,141,709 | 1.0 GB | CDLA-Sharing-1.0 | 冒烟测试用,几分钟跑一轮 |
HuggingFaceTB/smollm-corpus | — | — | ODC-By | 参考它的配比方案 |
BAAI/CCI3-HQ | — | — | — | 中文备选 |
(行数和体积是 2026-08-19 实测的;标 — 的是 datasets-server 没返回体积,01 篇补上。)
别一上来就下 28.5 GB
先拿 TinyStories 把 01 到 05 整条链路完整走一遍。模型蠢成什么样都无所谓,目的是确认代码没 bug、checkpoint 能续上、评测能出数。都对了再换大语料开多卡烧钱。