Skip to main content

从零训练一个小 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」。如果你算力无限,那当然是模型越大、数据越多越好。正因为算力有限,才有这个最优分配问题。

参数量 NChinchilla 最优 token 数 D
0.1 B2 B
0.5 B10 B
1 B20 B
7 B140 B

然后有个挺巧的事。HuggingFace 的 HuggingFaceFW/fineweb-edu 官方就切好了一个叫 sample-10BT 的子集:

configrowsparquet 体积
sample-10BT9,672,10128.5 GB
sample-100BT97,270,686286.4 GB
sample-350BT339,347,842998.1 GB
default1,525,223,0564,522.7 GB

(2026-08-19 实测自 datasets-server 的 /size 接口。最后那个全量的别碰。)

0.5B 乘 20 就是 10B tokens,正好一个 sample-10BT。28.5 GB 下得动,一块盘放得下。规模就这么定下来了。

3.2 第二步:算要跑多久

训练一次要多少 FLOP,有个很好记的估算式:

C6×N×DC \approx 6 \times N \times D

这个 6 是怎么来的,值得说一下,因为后面估 MFU 要反复用。对一个线性层 y=Wxy = Wx,前向做一次矩阵乘,每个参数贡献 2 次浮点运算(一次乘一次加)。反向要算两个梯度:对输入的梯度和对权重的梯度,各是一次同样规模的矩阵乘,所以是 4 次。加起来每个参数每个 token 就是 6 次。

代进去:

C=6×5×108×1×1010=3×1019 FLOPsC = 6 \times 5\times10^8 \times 1\times10^{10} = 3\times10^{19}\ \text{FLOPs}

A100 80G 的 BF16 稠密峰值是 312 TFLOPS,但真实训练摸不到峰值。差距用 MFU(Model FLOPs Utilization)描述,它的定义就是「实际有效算力 / 理论峰值算力」。小模型能跑到 35%~45% 算正常,大模型调好了能到 50% 以上。按 0.40 算,单卡有效算力:

312×1012×0.40=1.25×1014 FLOPS312\times10^{12} \times 0.40 = 1.25\times10^{14}\ \text{FLOPS}
卡数有效算力预训练耗时(估算)
1 × A1000.125 PFLOPS约 67 小时
2 × A1000.25 PFLOPS约 33 小时
4 × A1000.5 PFLOPS约 17 小时
8 × A1001.0 PFLOPS约 8.3 小时

上面这张表假设多卡是线性加速的,实际肯定拿不到。通信要花时间,4 卡拿不到 4 倍。到底差多少,是 04 篇要实测的事。

我选 4 卡。晚上开跑,睡一觉起来差不多完事,而且卡数够多,能真看出通信瓶颈。

3.3 第三步:核对显存装不装得下

不少教程会让人以为,多卡是因为单卡装不下。0.5B 这个规模装得下,而且富余不少。

显存占用分两部分,一部分是静态的(跟 batch size 无关),一部分是动态的(激活值,跟 batch size 和序列长度成正比)。先算静态的,用 BF16 混合精度加 AdamW:

项目每参数字节0.5B 合计为什么是这个数
BF16 权重21.0 GB前向反向用的就是它
BF16 梯度21.0 GB反向算出来的
FP32 master weights42.0 GBBF16 精度不够做参数更新,得留一份高精度的
AdamW 一阶动量 m42.0 GB梯度的滑动平均
AdamW 二阶动量 v42.0 GB梯度平方的滑动平均
小计168.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 第四步:算钱

设卡时单价 pp(元 / 卡 / 小时)、卡数 nn、耗时 tt 小时:

Cost=p×n×t\text{Cost} = p \times n \times t

4 卡跑 17 小时是 68 卡时,这是顺利跑通一次的量。但基本不可能一次就顺,调参和翻车都要重来,我按 150~200 卡时做整体预算。

各家平台的挂牌价一直在变,这里不写死。开跑前按当天单价代进去算,05 篇会记这轮实际烧了多少。

租卡有几个坑,04 篇细说,先记在这。数据先下到持久化云盘再开卡,别开着 4 张 A100 在那下 28.5 GB,那是拿 GPU 的价钱买下载时间。checkpoint 一定要落到持久化存储,租的实例说回收就回收,跑了 12 小时的结果丢掉是真的会想撞墙。先用 1% 的数据在单卡上把整条流程走通,再开多卡,多卡调试的每一分钟都是 4 倍的钱。

四、最终的模型配置

02 篇会一行一行把它写出来,这里先把结论摆着,方便对照。

超参取值定它的理由
vocab_size32,000对齐 Llama;且小于 65536,token id 能塞进 uint16
d_model1,536配合层数凑到 0.5B
n_layers18同上
n_heads12head_dim = 1536/12 = 128,对齐主流
n_kv_heads4GQA,3 个 Q 头共享 1 个 KV 头
ffn_dim4,096正好等于 8/3 × d_model,Llama 的惯例
max_seq_len2,048显存和长程依赖的折中
权重共享embedding 与 lm_head 共享省 49M 参数

按这个配置实算,总参数量是 502,193,664,正好 0.5B。拆开看:

部分参数量占比
Embedding(与输出层共享)49,152,0009.8%
18 层 Transformer Block453,040,12890.2%
最后的 RMSNorm1,536~0%
合计502,193,664100%

每一层 Block 内部再拆:

组件参数量占比
Attention(q/k/v/o 四个投影)6,291,45625.0%
SwiGLU MLP(gate/up/down 三个投影)18,874,36875.0%
两个 RMSNorm3,072~0%

MLP 占了四分之三的参数,这是 Transformer 的常态,也是为什么做量化和 MoE 都优先动 MLP。

五、篇章规划

#标题这一篇的产出状态
01数据工程清洗、去重、训 tokenizer、打包成 train.bin🚧
02模型结构从零实现model.py,能前向、参数量对得上
03预训练主循环train.py,单卡能跑,loss 会降
04从单卡到多卡DDP 和 FSDP 实测,显存账、通信瓶颈、租卡实操
05第一次完整预训练与复盘真实 loss 曲线、MFU、耗时、花费、翻车记录
06SFT指令数据怎么造、loss mask、全参和 LoRA 对比
07偏好对齐以 DPO 为主,讲清 GRPO 跟 PPO 差在哪
08评测与接回推理框架跑评测集,权重喂给自制推理框架

六、参考资料

这个专题不是凭空写的,下面这些是我实际会翻的东西。star 数是 2026-08-19 用 gh api 查的。

先说中文的系统教程,这几个的完成度和体系性比大多数英文博客高。

项目Star协议我拿它干什么
datawhalechina/happy-llm33,055见仓库《从零开始构建大模型》,跟本专题重合度最高。第五章手搓 LLaMA2、第六章训练流程、第八章强化学习,是我主要的对照物
datawhalechina/self-llm31,758Apache-2.0《开源大模型食用指南》,偏部署和微调实操,06 篇 LoRA 部分对照它
datawhalechina/tiny-universe5,015见仓库《大模型白盒子构建指南》,全手搓,思路跟本专题一致
Infrasys-AI/AIInfra7,970Apache-2.0AI 基础设施全栈,04 篇讲分布式并行和集合通信时的主要参考,配套站点 aiinfra-docs
HuaizhengZhang/AI-Infra-from-Zero-to-Hero4,284MITAI System 论文和工业实践的索引,找某个主题的经典论文时查它

再是训练框架和参考实现。

仓库Star协议拿它干什么
karpathy/nanoGPT62,199MIT训练主循环的骨架,最干净的参考,03 篇主要照着它
jingyaogong/minimind54,820Apache-2.0中文小模型全流程,规模和本专题最接近
karpathy/llm.c30,827MIT想弄明白某个算子到底算了啥,翻它
hiyouga/LLaMA-Factory74,215Apache-2.006 篇 LoRA 的对照组
volcengine/verl23,027Apache-2.0RL 那块的工业级参考
huggingface/trl19,107Apache-2.007 篇 DPO 的基线
pytorch/torchtitan5,636BSD-3-Clause并行策略的官方参考实现,04 篇对照
huggingface/nanotron2,789Apache-2.03D 并行的极简实现,04 篇对照

七、语料候选

数据集行数体积许可用途
HuggingFaceFW/fineweb-edusample-10BT9,672,10128.5 GBODC-By英文主语料
opencsg/chinese-fineweb-edu1,200,0003.5 GBApache-2.0中文补充
roneneldan/TinyStories2,141,7091.0 GBCDLA-Sharing-1.0冒烟测试用,几分钟跑一轮
HuggingFaceTB/smollm-corpusODC-By参考它的配比方案
BAAI/CCI3-HQ中文备选

(行数和体积是 2026-08-19 实测的;标 — 的是 datasets-server 没返回体积,01 篇补上。)

别一上来就下 28.5 GB

先拿 TinyStories 把 01 到 05 整条链路完整走一遍。模型蠢成什么样都无所谓,目的是确认代码没 bug、checkpoint 能续上、评测能出数。都对了再换大语料开多卡烧钱。