v8 = capacity-axis A/B: freeze the v6/v7 2.255B FineWeb-edu subset, scale dim768→dim1024 (core 127M→226M, +78%) via bf16 + T13 activation recompute. 8-GPU DDP, 2.36B tok (1.05 ep), ~129K tok/s (recompute tax), ~5h. Result (same FineWeb val, v6/v7/v8 comparable): v6 3.0652 / v7 3.0149 / v8 2.9801. Capacity helps — v8 (1.05ep) beats v6 at the same ~1ep by 0.085 AND beats v7 (smaller model, 1.45ep more old data) by 0.035 ⇒ v6/v7 were partly capacity-limited, scaling capacity > repeating old data. But the gain is only ~3% (same magnitude as the data-axis single-step lever), and v8's val was still descending at the end (not saturated). Meta-finding: every single-axis lever (data-volume v5/v7, breadth v6, capacity v8) is now ~3%/lever ⇒ broad diminishing returns; to progress, scale capacity AND data together (Chinchilla, reproduced at toy scale). - docs/runs/08-v8-fineweb-edu-dim1024.md: full capacity experiment + v7-vs-v8 samples - docs/runs/README.md: +v8 row, v9 proposal - docs/evolution.md: +T13 infra row, +v8 scaling row, capacity-axis & diminishing-returns notes Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
14 KiB
Scaling Run v8: 容量轴 — dim1024/18L(core 226M, +78% 容量) + 同 v6/v7 的 FineWeb-edu 2.255B 子集 + bf16 + 激活重计算(T13) + 8 卡 DDP — Design Document
Goal
v6/v7 把数据轴测到了头:同一 2.255B FineWeb-edu 子集,从 1.02 epoch(v6 3.0652)喂到 1.45 epoch (v7 3.0149),FineWeb val 仅 ↓0.05 且走平 = 同子集多 epoch 在 dim768 上近天花板。v7 末尾把下一步的 首选定为「新 FineWeb shards」(真·更多数据),并把「更大模型」列为待测的容量轴——但留了一个明确的 未解问题:dim768(core 127M)在这本语料上到底是数据见够了,还是模型容量不够?(即是否 capacity-limited)
v8 就是去净测这条容量轴:数据完全冻结 = v6/v7 的同一个 2.255B 子集,唯一变量 = 把模型从 dim768 放大到 dim1024(core 127M → 226M,+78% 容量)。
- 数据子集完全冻结 = v6/v7:同一个 2,254,904,418-token FineWeb-edu 子集(
sample/10BT3 个 parquet 分片),同一 1M held-out val(与 v6/v7 同一把尺子,可直接比)。不补任何新数据——这是干净的 dim768-vs-dim1024 A/B。 - 唯一变量 = 模型容量:dim 768 → 1024(32 heads × 32 head_dim),ffn 2048 → 2730(≈ 8/3·dim 的 SwiGLU 惯例),18 层不变 → core 226.5M(+78%)/ 总 329.4M。
- 为装下 dim1024 而启用 T13 激活重计算:dim1024 batch32 激活显存超 32GB → 用 KI-3 的 per-block gradient checkpointing(no-tape 前向 + 反向时重算)压到 16.6GB/卡,恰好装得下。这是 v8 能成立的前置基建。
⚠️ 方法论说明(同 v6/v7)
v8 的 val(FineWeb-edu 2.9801)与 v6(3.0652)、v7(3.0149)同一把尺子、同一个 1M 留出集,三版可以 直接比;但都不能和 v0–v5 的 TinyStories val(~1.1)比大小——真实网页文本熵高,~3.0 是预期值不是回退。
数据(v8 与 v6/v7 的唯一差别 = 模型容量,数据一字不差)
| 项 | v6/v7 | v8 |
|---|---|---|
| 来源 | FineWeb-edu sample/10BT(真实教育类网页) |
同(一字不差的同一子集,非新数据) |
| 语料规模 | 2,254,904,418 tokens(3 parquet 分片) | 2,254,904,418 tokens(同子集) |
| 训练消费 token | v6 ~2.29B(1.02ep) / v7 ~3.28B(1.45ep) | ~2.359B(36000 步 × global 256 × seq 256) |
| epoch 占比 | v6 ~1.02 / v7 ~1.45 | ~1.05(刻意取最接近 v6 的 ~1 epoch,A/B 同 epoch 对照) |
| tokenizer | gpt2 BPE(vocab 50257) | 同 |
| 缓存 | data/fineweb-edu.txt.u16.bin(4.51GB u16) |
同一缓存 |
| held-out val | FineWeb-edu 末尾 1M token | 同(与 v6/v7 可比) |
v8 的 epoch 取 1.05(不是 v7 的 1.45)是刻意的:这样 v8 与 v6(1.02ep)几乎同 epoch,是最干净的 「同数据量、纯放大容量」A/B;同时与 v7(1.45ep,更多 epoch 的小模型)对照,能回答「容量 vs 更多老数据 谁更值」。
架构(v8 唯一变化点 = 容量)
v8 = 把 v4–v7 的 tiny Qwen3(RoPE + RMSNorm + per-head QK-norm + SwiGLU + 独立 lm_head,MHA)按容量轴放大:
| 项 | v4–v7 (dim768) | v8 (dim1024) |
|---|---|---|
| dim | 768 | 1024 |
| heads × head_dim | 24 × 32 | 32 × 32 |
| layers | 18 | 18(不变) |
| SwiGLU ffn | 2048 | 2730 |
| core 参数 | 127,432,704 | 226,495,488(+78%) |
| embed + lm_head | 77.19M | 102.93M |
| 总参数 | 204.63M | 329.42M |
| 导出 tensors | 201 | 201(层数同,张量数同) |
head_dim 保持 32(与全系一致,RoPE/QK-norm 维度不变),靠加头数(24→32)把 dim 撑到 1024;ffn 2730 ≈ (8/3)·1024 取整到偶数,沿用 SwiGLU 的 2/3·4·dim 经验比例。
训练器:8 卡 DDP bf16 + 激活重计算(v8 新启 T13)
复用 v5/v6/v7 的训练栈(fp32 master + AdamW/clip/DDP 全 fp32,linears 走 cublasGemmEx 16BF/fp32 accum、
激活存 bf16,norm/softmax/rope/CE 仍 fp32,8 卡 thread-per-GPU all-reduce 取均值后各 rank 本地 GpuAdamW
step,跨 rank bit-identical),新增一项:
- 激活重计算 ON(T13 / KI-3):per-block gradient checkpointing——前向不建 tape、反向时按块重算激活。 对非重计算版梯度逐位一致(0.00 rel err)。这是 dim1024 batch32 能装进 32GB 卡的关键(16.6GB/卡)。
- 代价:dim1024 下重算税更重,稳态吞吐 ~129,000 tok/s(vs dim768 bf16 的 ~218K)——多一遍块前向 + 更大的矩阵。util 97–100%、16.3GB/卡。wall-clock ~5h 训完 2.359B token(36000 步)。
超参
| 项 | 值 | 备注 |
|---|---|---|
| optimizer | 手写 AdamW(GPU 端 step) | wd=0.1,β/eps 用 xtrain-optim 默认(同全系) |
| LR schedule | 线性 warmup → cosine decay | max_lr 6e-4 → min_lr 6e-5(同 v1–v7) |
| warmup | ~1800 步 | lr 峰值 6e-4,cosine 衰减到末步 6e-5 |
| grad clip | global-norm 1.0 | 平稳 gnorm ~0.21 |
| steps | 36000 | ~5h @ 8 卡(重算税) |
| global batch | 256(per-rank 32 × world 8) | bf16 + 重计算后 dim1024 的甜点区 |
| seq_len | 256 | 同 v2–v7 |
| tokens/step | 256×256 = 65536 | 总训练 token ≈ 2.359B(~1.05 epoch) |
| world size | 8(RTX 5090,sm_120) | |
| 精度 | bf16 混合精度(fp32 master) | T12/KI-2;导出 xserv 同样 BF16 |
| 激活重计算 | ON(per-block,T13/KI-3) | v8 新启,解锁 dim1024 |
结果
- train loss:start 11.1018 → end 3.0586(全程平滑下降)
- best val loss 2.9801(step 35999),final val loss 2.9801(同一步即末步,FineWeb-edu held-out 1M)
- FineWeb val 曲线(抽样):
| step | 499 | 999 | 3999 | 7999 | 11999 | 15999 | 19999 | 25999 | 29999 | 31999 | 33999 | 34999 | 35999 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| val | 5.5736 | 4.6699 | 3.6454 | 3.4184 | 3.2956 | 3.2153 | 3.1506 | 3.0593 | 3.0201 | 2.9999 | 2.9862 | 2.9828 | 2.9801 |
⚠️ 末步即 best、且仍在降:从 33999(2.9862) → 34999(2.9828) → 35499(2.9820) → 35999(2.9801),每 500 步 仍稳定 ↓~0.002,到训练结束没有走平——与 v7 末段(~step44000 后走平、末步还反弹)形成对比。v8 还没吃饱。
⭐ 核心 A/B 结论:容量有用(部分 capacity-limited),但增益 ~3% 边际
三版同一 FineWeb val 尺子,直接可比:
| v6 | v7 | v8 | |
|---|---|---|---|
| 模型 | dim768 (core 127M) | dim768 (core 127M) | dim1024 (core 226M, +78%) |
| epoch | 1.02 | 1.45 | 1.05 |
| 训练 token | 2.29B | 3.28B | 2.36B |
| best val(FineWeb,可比) | 3.0652 | 3.0149 | 2.9801 |
两个干净的对照:
- 同 ~1 epoch、纯放大容量(v6 vs v8):1.02ep → 1.05ep 几乎同数据量,dim768 → dim1024, val 3.0652 → 2.9801(↓0.085)。→ 同样的数据,更大的模型榨出更多:v6/v7 在 dim768 上吃不动的, 不全是「数据见够了」,有一部分是模型容量不够(capacity-limited)。
- 容量 vs「更多老数据的小模型」(v7 vs v8):v8(dim1024,才 1.05ep)2.9801 < v7(dim768,1.45ep 更多数据)3.0149,低 0.035。→ 放大容量比「给小模型多喂 0.4 epoch 老数据」更值——这正面回答了 v7 留下的问题:在这本语料上,下一步的杠杆容量 > 重复老数据。
⚠️ 但要诚实:增益是 ~3% 的边际,不是质变
v8 的 0.085(vs v6 同 epoch)≈ 2.8% 的 val 改善。这和「数据轴单步杠杆」量级一样薄:
- v5(同子集 ×3.5 数据):TinyStories val ↓5%
- v7(同子集 ×1.4 epoch):FineWeb val ↓0.05(~1.6%)
- v8(容量 +78%,dim768→dim1024):FineWeb val ↓~3%
元结论:到 v8,无论拨数据轴还是容量轴,单轴单步的杠杆都收敛到 ~3%/lever = 全面进入边际递减。 这正是 Chinchilla 的教训在小尺度上的复现:容量与数据要匹配地一起 scale,单独猛拨一根轴, 另一根很快成为新瓶颈(v8 容量 +78% 但只配同样的 2.36B token,所以容量没吃满 → val 末步仍在降 = 数据 这边又成了限制)。
采样对比 —— v7(dim768) vs v8(dim1024)(同 xserv、同贪心、同 prompt)
唯一差别是 v8 容量 +78%。喂同样的通用/说明文提示词(greedy,max 60 token):
| prompt | v7(dim768, 1.45ep) | v8(dim1024, 1.05ep) |
|---|---|---|
The history of |
the city of New York is a story of many different people. The first inhabitants… were the Native Americans… the Dutch… | the United States is a history of the United States as a nation… a nation of immigrants. (随后陷入 "a nation of immigrants" 重复) |
In science, |
the term "science" is used to describe the study of the natural world… biology, chemistry, physics, and mathematics… | the term "biological" refers to the organisms that are alive and have been alive for a long time…(随后重复该定义句) |
The most important |
thing to remember is that you can't just buy a new car and expect to pay for it… understand the basics of insurance… | thing to remember is that the best way to prevent a heart attack is to eat a heart-healthy diet.(随后重复该句) |
Water is |
a natural substance that is found in the earth's crust… a very important element in the Earth's ecosystem… | a good thing. It's a good thing to have…(随后漂进对话腔 "I'm not sure if you're going to be able to do that") |
质量观察(诚实读法):两版都写同一语域的连贯说明文英文,没有可感知的质的跃迁——这正是 val 仅 ↓3% 在采样上的体现。更细看:
- v7 在这组 greedy prompt 上反而信息更具体、更耐读(具体地名/学科列表/事实),尽管也有事实瑕疵;
- v8 在 greedy 下更快掉进重复循环("a nation of immigrants" ×6、"heart-healthy diet" ×3),且 "Water is" 漂出说明文语域进了对话——这更像是**采样路径(greedy + 小模型 + 才 1 epoch)**的表现,而非容量带来的退步;
- 两版的小模型重复倾向、轻微事实瑕疵都在。
一句话:val 上 v8 确实更低(容量有用),但在贪心采样的肉眼质量上看不出 v8 明显更好——这与 ~3% 的边际 val 提升完全一致。要把容量优势兑现成可感知的文本质量,多半还需要配套更多数据(v8 才 1.05 epoch、val 未饱和)。
xserv 验证
导出 HF Qwen3 safetensors(命名映射 + 2D 权重转置 [in,out]→[out,in] + BF16,见 T9 docs/08,201 tensors,
config.json hidden_size 1024 / heads 32 / head_dim 32 / intermediate 2730 / layers 18)存入 registry,
用 xserv-cli 加载并贪心生成:
$ xserv-cli ~/projects/tiny-models/v8-fineweb-edu-dim1024 --max-tokens 60
Model: qwen3, layers=18, hidden=1024, heads=32/32 kv, vocab=50257
Loaded 201 tensors
Ready (KV cache, dtype=bf16).
xserv> The history of the United States is a history of the United States as a nation. The United States is a
nation of immigrants…
xserv> In science, the term "biological" refers to the organisms that are alive and have been alive for a long time…
xserv> The most important thing to remember is that the best way to prevent a heart attack is to eat a
heart-healthy diet…
token-match(xtrain f32 贪心 on ckpt vs xserv BF16 贪心 on 导出权重,同 prompt):
Once upon a time/The little→ 逐 token 完全一致(30/30 byte-identical);One day→ 前 ~6 token 一致后在一个 BF16 近似平局处分叉(xtrain f32 "home from school" / xserv BF16 "along the road in the village of Kambala")——这是 v4–v7 一贯报告的 BF16-vs-f32 贪心在近平局处翻拍 的 预期漂移点,非 bug。v8 训练即 bf16(fp32 master),权重本就在 bf16 数值域收敛,导出 BF16 给 xserv 后两侧 数值路径一致——同全系闭环成立。
xserv 加载 qwen3 layers=18 hidden=1024 201 tensors、KV-cache、贪心生成连贯说明文英文,闭环成立。
相比 v6/v7 与 v9 提案
v8 兑现了 v7 提案里的容量轴,并给出一个清晰结论:容量有用(v6/v7 在 dim768 上有一部分是 capacity-limited,不全是数据见够)——但和数据轴一样,单步杠杆只有 ~3%。叠加 v5/v6/v7:
- 数据量轴(同子集多 epoch):饱和,~1.6–5% / 步。
- 数据广度轴(换语料):v6 唯一的质变(小故事 → 真实说明文),但那是一次性换分布的红利。
- 容量轴(v8):有用但 ~3% 边际。
- → 所有单轴都已进入 ~3%/lever 的边际递减区。
Chinchilla 教训(小尺度复现):v8 容量 +78% 但只配 2.36B token(1.05ep),val 末步仍在降 = 数据这边 立刻成了新瓶颈。要继续进步,容量与数据必须匹配地一起 scale,而不是单独猛拨一根轴。v9 选项按此重排:
- 双轴一起 scale(最符合 Chinchilla,真 scale):更大模型(dim1024+ 或更深)+ 新 FineWeb shards (更多样、不重复 token,配上去把 dim1024 喂饱)。⚠️ 投入最大(下载 + 长训 + 磁盘紧需 /dashscope-tmp 暂存)。
- dim1024 多喂数据(最便宜,先验证容量能不能吃满):v8 才 1.05 epoch、val 未饱和——直接给 dim1024 续训 到 2–3 epoch(或加新 shards),看 val 还能降多少。这是验证「v8 的容量是否被数据卡住」的最低成本实验。
- 自然收尾(漂亮里程碑):项目已 8 版 + 从零全栈(autograd/backward/AdamW/DDP/bf16/重计算/export)+ 完整的数据轴 + 容量轴 + Chinchilla 边际分析——作为一条学习线已经讲完了一个完整的故事。
我的判断:作为工程/学习项目,v8 是一个天然的收尾点——8 版把「数据量 / 数据广度 / 容量」三根轴都 测过,并落到「单轴 ~3%、要双轴一起 scale」这个有分量的元结论上,全栈基建闭环。若要再做一版,首选 2 (dim1024 多喂数据):它最便宜、且直接回答 v8 留下的唯一悬念(容量被数据卡住的程度)——v8 val 未饱和说明 这一版很可能立刻见效;真要追求规模再上 1(双轴)。详见对比表与 evolution.md 的「容量轴」与「边际递减」两条。