前几天我那句「一层 12d²」被一份 config.json 打脸,改成「16d² 起步」,今天又拿真 config 核了一遍,发现还是不准。这回把账彻底摊开重写。

一个 transformer 层就两块肉

  • FFN(SwiGLU 三个矩阵):3 × d × m,m = intermediate_size
  • 注意力(GQA):d × head_dim × (2·n_q + 2·n_kv) —— Q 和 O 各吃一份 n_q × head_dim,K 和 V 各吃一份 n_kv × head_dim

归一化那点参数(每层 2 × d)加进去连零头都算不上,手算直接扔。

关键:别记 d² 的系数,先记 m 是 d 的几倍

令 r = m / d,FFN 就是 3r · d²。 注意力折算成 d² 是 2 · head_dim · (n_q + n_kv) / d,我核过的模型基本都落在 2 ~ 2.5。

所以:

每层 ≈ (3r + 2.3) · d²,r 是主角,注意力是零头。

三份真 config 的账

Qwen2.5-1.5B:d=1536,m=8960,12 头 / 2 kv 头 / head_dim 128,28 层,词表 151936,权重绑定

  • FFN:3 × 1536 × 8960 = 41,287,680 / 层
  • attn:Q 2,359,296 + K 393,216 + V 393,216 + O 2,359,296 = 5,505,024 / 层
  • 每层 46.79M ÷ d²(=2,359,296) = 19.8 d²(因为 r = 5.83!)
  • × 28 层 = 1,310,281,728
  • 词表 151,936 × 1536 = 233,373,696(绑定只算一份)
  • 合计 1,543,655,424 → 官方口径 1.54B,对上 ✓

Llama-3.2-1B:d=2048,m=8192(r=4),32 头 / 8 kv / head_dim 64,16 层,词表 128256 绑定

  • 每层 = 12 + 2.5 = 14.5 d²,× 16 层 + 词表 ≈ 12.36 亿 ✓(官方 1.236B)

Gemma-3-27B:d=5376,m=21504(r=4),32 头 / 16 kv / head_dim 128

  • 每层 = 12 + 2.29 = 14.3 d²

我之前为什么一直算歪

因为我一直默认 FFN = 4d。实际 r 从 3.5(Llama3-70B 那种)到 5.83(Qwen 小模型)都有,而且规律是反的:模型越小 FFN 越肥,越大越抠。拿一个固定系数横跨整个尺寸段,误差能到 1.5 倍。所谓 16d²,其实是 MHA 时代(head_dim = d / 头数)的巧合数,GQA + 固定 head_dim 之后那个巧合就不成立了。

新口诀(三步)

  1. 先算 r = m / d,FFN = 3r · d²
  2. 注意力按 2 ~ 2.5 d² 补,kv 头越少越贴近 2
  3. 小模型必须单独加词表:vocab × d,绑定算一份、不绑定算两份。1.5B 这档词表占总量 15%,27B 只占 5%

一层拆到底的真实比例(1.5B 这例):FFN 75% / 注意力 10% / 词表 15%。

以后谁再问我这模型多大,我就这三步,不拍脑袋了。