Qwen3.8-27B 的 GQA:24 个 Q 头配 4 个 KV 头、head_dim 是 256

2026-08-16

翻 Hugging Face 上 Qwen/Qwen3.8-27B 这个仓库的时候,model card 的 “Model Overview” 一节里有两行写得特别短:

Number of Attention Heads: 24 for Q and 4 for KV
Head Dimension: 256

分别在 README.md:46README.md:47,整个 “Model Overview” 一节的范围是 README.md:32README.md:53。短到很容易一扫而过,但这两行其实是整份 model card 里少数几处能和 config.json 严丝合缝对上的地方。这篇就只做一件事:把「24 / 4 / 256」这三个数字放回配置文件里,看清楚它们各自写在哪、管到哪一层、以及哪些看上去顺理成章的推论其实在这个仓库里根本核不出来。

先说清楚本文的边界:Qwen/Qwen3.8-27B 是一个模型权重仓,不是代码仓。仓库里能读的只有 model card 和几个文本配置文件,architectures 里写的那个类 Qwen3_5ForConditionalGenerationconfig.json:3)的实现并不在这个仓库里。所以下面所有说法都停在「配置文件里写着什么」这一层,不越界到「所以运行时会怎样」。

三个数字的确切位置

截至 2026-08-16、对应快照 1d4bf0f,这三项在 config.jsontext_config 下逐个照录如下:

字段名行号
num_attention_heads24config.json:97
num_key_value_heads4config.json:99
head_dim256config.json:16

README.md:46README.md:47 的两行一一对上:24 对 Q、4 对 KV、256 对 Head Dimension,没有出入。标题里我用的「GQA」是这类「Q 头数多于 KV 头数」配置的通行叫法,配置文件里真正存在的字段名就是上表这三个,写脚本、写加载逻辑时要认的是字段名,不是叫法。

顺带把同一组里另外几个开关也记下来,它们同样在 text_config 直属层:attention_biasfalseconfig.json:9)、attention_dropout0.0config.json:10)、use_cachetrueconfig.json:116)、rms_norm_eps1e-06config.json:103)。这些是发布配置里的取值,不是「你跑起来会怎样」的承诺。

最容易读错的一点:这三个数只管 64 层里的 16 层

如果只看 “Model Overview” 那张表,很容易默认 24/4/256 描述的是整个模型的注意力。但 text_config 里还有一个 layer_types 数组(config.json:21-86),长度 64,与 num_hidden_layers64config.json:98)相同,取值只有两种:"linear_attention""full_attention"

数出来的结果是:full_attention 共 16 项,linear_attention 共 48 项。full_attention 的 0 基下标是 3, 7, 11, 15, 19, 23, 27, 31, 35, 39, 43, 47, 51, 55, 59, 63,全部满足对 4 取余等于 3,相邻间距恒为 4;换成 1 基就是第 4、8、12……64 层。数组的第 1 层是 linear_attention,最后一层是 full_attention。另一个字段 full_attention_interval 的值正好是 4config.json:15),而 64 ÷ 4 = 16,与实际出现次数相同。

这意味着:24 个 Q 头、4 个 KV 头、head_dim 256 这一组,对应的是那 16 层。剩下 48 层用的是完全另一套字段,集中在 config.json:87-92——linear_num_key_heads16linear_num_value_heads48linear_key_head_dimlinear_value_head_dim 都是 128。头数和头维度都不是同一组数,读配置时把两侧混起来,后面所有对照都会错位。

model card 用一行公式描述了这个排布(README.md:41):

Hidden Layout: 16 × (3 × (Gated DeltaNet → FFN) → 1 × (Gated Attention → FFN))

在「块数 16」「块内比例 3:1」「块内先 linear 后 full」这三点上,公式与 layer_types 数组是吻合的。但有两处边界必须一起说:公式里的 FFN 在 layer_types 里没有任何对应元素,数组只有两种注意力取值,这一段无从核对;而 “Gated DeltaNet” 与 “Gated Attention” 这两个名词在 config.json 里一次都没出现,配置里对应位置的字符串是 "linear_attention""full_attention"。把它们对上,是按数量 48/16 与前后顺序做的名称层面的对应,不是配置文件里写明的映射

24 × 256 与 hidden_size 对不上

text_config.hidden_size5120config.json:18)。用 Python 乘一下:24 × 256 = 6144,与 5120 不等;KV 侧 4 × 256 = 1024。线性那一侧同样可以乘:48 × 128 = 614416 × 128 = 2048

这几个乘积config.json 里并没有直接写出来,是我们自己算的,所以只作为算术关系记录。6144 与 5120 不等这件事说明的仅仅是「头数乘头维度不等于隐藏维度」这一条算术观察本身;投影矩阵到底怎么摆,本仓没有建模源码可查,我们不推断。同样地,这些数字一律不用来推算参数量、显存占用、KV 缓存大小或任何速度结论——这个仓库里没有任何依据支持那类推算。

RoPE 维度 64:README 写了,config 没有这个字段

README.md:48 那一行写的是 Rotary Position Embedding Dimension: 64。但 config.json没有任何一个名为「RoPE 维度」的字段。和它沾边的只有两个:head_dim = 256config.json:16),以及 partial_rotary_factor = 0.25config.json:102)。

256 × 0.25 = 64.0,数值和 README 的 64 相同。这条只能当作「我们做的算术核对结果与 README 的数字吻合」来引用,不能引用成「config 明写 RoPE 维度是 64」——文件里就是没有这个数。而且 partial_rotary_factor 的确切定义,我们没能从本仓任何文件里确认。

另外两点照实记下:partial_rotary_factortext_config 里出现了两次,一次在直属层(config.json:102),一次在 rope_parameters 子对象里(config.json:111),两处的值都是 0.25rope_parameters 一共 5 个键,还包括 mrope_interleaved: truemrope_section: [11, 11, 10]rope_theta: 10000000rope_type: "default"mrope_section 三项之和是 32,恰为 64 的一半——这同样只是算术关系,它的语义本仓没有说明。

要注意 rope_type 的发布值是 "default",而 rope_parameters 里既没有 factor 也没有 original_max_position_embeddings 这两个键。README 的 “Best Practices” 一节(README.md:521-536)给出的是把 rope_type 改成 "yarn" 并补上 factor: 4.0original_max_position_embeddings: 262144 的替换片段,README 自己也在 README.md:557 建议仅在需要处理长上下文时才修改 rope_parameters。两处并列:开箱配置不是 YaRN 配置。

“Gated” 那个词的落点

model card 把这 16 层叫 “Gated Attention”。config.json 里确实另有两个带 gate 的字段:attn_output_gatetrueconfig.json:11)、output_gate_type"swish"config.json:100)。看上去很像是对应关系,但本仓文件没有写明它们与 README 的 “Gated” 字样是不是同一件事,实现也不在这个仓库里,所以到此为止,我们不往下推。同样处理的还有 attention_bias: false 与那个 true 的门控开关之间有没有关联——没有依据,不写。

想自己核一遍的话

config.json 整个文件 4,312 字节、140 行,只有三层结构:顶层、text_configvision_config。核这几个数不需要下载权重,把配置文件拉到本地目录,用下面这段 Python 把字段值与几个算术关系一次打印出来即可(取自我们采集时用的命令,删去了与本篇无关的一行输出):

python -c "
import json,io,os
raw=io.open('config.json',encoding='utf-8').read()
print('config.json bytes',os.path.getsize('config.json'),'lines',len(raw.split('\n')))
d=json.loads(raw); t=d['text_config']; v=d['vision_config']
print('head_dim*partial', t['head_dim']*t['partial_rotary_factor'])
print('sum mrope_section', sum(t['rope_parameters']['mrope_section']), t['rope_parameters']['mrope_section'])
print('num_attention_heads*head_dim', t['num_attention_heads']*t['head_dim'], 'hidden', t['hidden_size'])
print('linear v heads*dim', t['linear_num_value_heads']*t['linear_value_head_dim'])
print('linear k heads*dim', t['linear_num_key_heads']*t['linear_key_head_dim'])
print('kv heads*head_dim', t['num_key_value_heads']*t['head_dim'])
print('layers/interval', t['num_hidden_layers']/t['full_attention_interval'])
"

在快照 1d4bf0f 上,这条命令的输出是:config.json bytes 4312 lines 140head_dim*partial 64.0sum mrope_section 32 [11, 11, 10]num_attention_heads*head_dim 6144 hidden 5120linear v heads*dim 6144linear k heads*dim 2048kv heads*head_dim 1024layers/interval 16.0。命令与结果都取自我们的采集记录,路径按仓库内相对路径书写,请在存放这些配置文件的目录下执行。

需要提醒的是,上面这些取值全部是快照 1d4bf0f 上、config.json 与 model card 这一层的口径,我们没有下载权重、没有部署、也没有加载过这个模型去验证配置能不能被正确读入。模型仓的内容会随上游更新变动,字段名和默认值都可能改,自己核的时候请以拉到手的那份文件为准,不要照抄本文里的行号。

这篇没打算回答的问题

有几件事在读完配置之后仍然是空白,与其含糊带过,不如列清楚:

  • attn_output_gateoutput_gate_typepartial_rotary_factormrope_interleavedmrope_sectionfull_attention_interval 这些字段具体如何被使用,我们一律未能确认——建模代码不在本仓。
  • layer_typesfull_attention_interval 在数值上冲突时以哪个为准,本仓文件没有说明。二者在当前发布的配置里是自洽的。
  • model card 自述 Number of Parameters: 27BREADME.md:37),但 config.json 里没有任何参数量字段,这个数只能作为 model card 自述引用,无法从配置核对。
  • 这组数字与显存、吞吐、量化后体积、能不能在某张卡上跑起来的关系,本文一个字都不写。这不是谨慎,是确实没有依据——我们没有下载权重,也没有做过任何推理。

顺带一提,config.json 里的类名与 model_type 写的是 Qwen3_5ForConditionalGeneration"qwen3_5"config.json:3config.json:7),text_config.model_type"qwen3_5_text"config.json:94),而仓库名与 model card 标题是 Qwen3.8-27BREADME.md:7)。这几处的版本字样并不相同,写加载脚本、做字段匹配时按文件里的实际字符串来即可,原因我们不做推断。关于这套命名在各个配置文件之间的差异,我们另有一篇专门在讲。

延伸阅读


本文依据 Hugging Face 仓库 Qwen/Qwen3.8-27B 的 model card 与随仓配置文件 (config.jsongeneration_config.jsonpreprocessor_config.jsonchat_template.jinja 等)整理, 核对日 2026-08-16,对应仓库快照 1d4bf0f。 本文内容为 model card 与配置文件口径,我们没有下载权重、没有部署、也没有推理过这个模型, 因此不涉及生成质量、推理速度与显存占用的任何描述;文中所有评测数字均为 model card 自述,我们没有复现。 模型仓库内容随上游更新而变动,请以官方最新说明为准。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。