14. 从 CPU 走到多卡 GPU
前 13 章讲的是“代码怎样工作”。这一页讲“怎样做自己的实验”,以及在不同硬件上应该期待什么。
先选硬件档位
| 档位 | 典型环境 | 适合做什么 | 不适合做什么 |
|---|---|---|---|
| 入门 | 笔记本 CPU、Apple Silicon | 跑通流程、观察张量、改小参数 | 训练高质量模型 |
| 单卡 | 1 张 A100 / H100 / 4090 | 小规模实验、调试训练 | 完整复现 GPT-2 速度 |
| 多卡 | 8 张 H100 80GB | 完整 speedrun、缩放实验 | 没有预算时长期占用 |
nanochat 的 README 给出的参考是:在 8 张 H100 上,runs/speedrun.sh 训练一个 d24 模型并达到 GPT-2 附近的能力,约 1.5 小时,成本约 48 美元。实际时间和价格会随云厂商、网络和数据下载变化。
CPU 路线
CPU 路线的目标不是效果,而是把每一步都走通。
官方脚本
bash runs/runcpu.sh它会:
- 下载 8 个训练分片和 1 个验证分片。
- 训练一个 6 层、512 上下文长度的模型。
- 训练 5000 步。
- 做一次小规模 SFT。
作者在 M3 Max 上的参考时间是约 30 分钟预训练加 10 分钟 SFT。普通 CPU 可能慢很多,笔记本电脑风扇会持续高速运转。
更短的实验
只想确认链路时,可以使用上一章的 tiny 配置,把层数降到 4、序列长度降到 256、训练步数降到 200。
CPU 上值得做的观察
- 改变
--depth,看参数量和训练时间怎样变化。 - 改变
--max-seq-len,看内存和速度怎样变化。 - 用
Engine和GPT.generate()比较有无键值缓存的生成时间。 - 打印张量形状,确认
B/T/C/H/D的含义。
CPU 不适合训练出好模型,但非常适合建立“数据形状”和“调用顺序”的直觉。
多卡 GPU 路线
完整 speedrun
bash runs/speedrun.sh它会下载约 170 个数据分片,训练 d24 模型,做 SFT 和评估。仓库注释建议在 screen 或 tmux 中运行,避免 SSH 断开导致训练中止:
screen -L -Logfile runs/speedrun.log -S speedrun bash runs/speedrun.sh数据下载量约为 17 GB。确认磁盘空间充足,并尽量在网络稳定的机器上运行。
快速实验:d12
README 推荐用 d12 做快速迭代,预训练大约几分钟。核心命令是:
OMP_NUM_THREADS=1 torchrun --standalone --nproc_per_node=8 -m scripts.base_train -- \
--depth=12 \
--run="d12" \
--model-tag="d12" \
--core-metric-every=999999 \
--sample-every=-1 \
--save-every=-12
3
4
5
6
7
这个配置关闭了中间采样和定期保存,只在最后评估和保存,适合快速判断一个改动是否有效。
单卡 GPU
把 torchrun 去掉即可:
python -m scripts.base_train --depth=12 --run="d12-single"nanochat 会自动使用梯度累积补足总批量。单卡速度约为 8 卡的八分之一,但结果应当接近。显存不足时,降低 --device-batch-size。
显存不够时怎样调
按下面的顺序尝试,不要一次改多个:
- 降低
--device-batch-size,例如 32 → 16 → 8 → 4 → 2 → 1。 - 降低
--max-seq-len。注意这会影响模型能看到的上下文长度。 - 关闭
--fp8,或者反过来,在支持 FP8 的 H100 上开启它以减少显存。 - 减少
--eval-tokens,避免评估阶段一次性占用过多显存。 - 如果仍然不够,换更小的
--depth。
不要先改模型结构。绝大多数“显存不足”来自批量或序列长度,而不是模型结构本身有问题。
值得做的五个实验
实验 1:深度与参数量
分别训练 d4、d8、d12,记录:
- 总参数量。
- 训练词元数。
- 训练时间。
- 验证 BPB。
你会看到模型宽度、头数和训练预算都随深度变化,不只是层数增加。
实验 2:序列长度
固定其他参数,把 --max-seq-len 从 256 改成 512、1024。
观察:
- 每条样本的
inputs形状。 - 显存占用。
- 每秒处理词元数。
- 验证 BPB。
序列长度增加会让注意力计算更快增长,不只是线性增加。
实验 3:滑动窗口
比较:
--window-pattern=L
--window-pattern=SSSL2
短窗口层能减少计算,但可能损失长距离信息。不要只看训练速度,也要看验证 BPB 和实际生成效果。
实验 4:跳过 RL
用同一个 SFT 模型和 RL 模型分别跑 GSM8K 评估:
python -m scripts.chat_eval -i sft -a GSM8K -x 50
python -m scripts.chat_eval -i rl -a GSM8K -x 502
比较 pass@1 和 pass@8。注意 RL 模型只在 GSM8K 上训练过,其他任务不一定会变好。
实验 5:中文数据
用一小批中文对话替换或混合 SmolTalk,重新做 SFT。观察:
- 中文回答是否更连贯。
- 英文能力是否下降。
- 词元数量怎样变化。
- 评测集是否真的覆盖中文。
只换 SFT 数据通常不够。中文能力需要中文预训练语料,否则模型只是学会用中文格式输出已有的英文知识。
怎样记录实验
一个可复现的实验记录至少包含:
日期:
代码提交:92d63d4
硬件:1×H100 80GB
数据分片数量:8
命令:完整启动命令
关键参数:depth / seq_len / batch / lr
训练时间:
验证 BPB:
CORE / ChatCORE:
观察与结论:2
3
4
5
6
7
8
9
10
不要只记“效果好像变好了”。没有基线、没有固定评估样本,结论很容易被随机波动误导。
调试顺序
训练出问题时,按下面的顺序排查:
- 命令是否和上次成功时一致?
- 数据分片和分词器是否完整?
- 检查点路径是否正确?
- 模型词表和分词器词表是否一致?
- 显存是否足够?
- 学习率是否过大,导致损失震荡或变成
nan? - 评估样本数是否太少,导致结果波动?
先排除配置和路径问题,再怀疑模型结构。大多数失败不是“模型不够好”,而是“加载错了文件”或“少了一个参数”。
本章要记住的三件事
- CPU 用于理解流程,GPU 用于训练效果;不要在 CPU 上追求 GPT-2 水平。
- 显存不足时先降批量,再降序列长度,不要先改模型结构。
- 实验要记录提交、硬件、数据和完整命令,否则结果无法复现。