Qwen3.8-27B 的混合注意力:由 config 里哪几个字段承载
看 model card 的时候,“混合注意力”往往只是一行公式或者一句话。可真要落到配置文件上,问题就变成很具体的一个:这套结构到底是由哪几个字段承载的? 是一个开关,还是一张表,还是两组各自独立的维度参数?
我们把 Hugging Face 仓库 Qwen/Qwen3.8-27B 在 2026-08-16 的快照 1d4bf0f 拉下来,只读其中的文本配置与 model card,逐个字段核了一遍。先说结论:承载这套结构的不是某一个字段,而是一张 64 项的数组 + 一个间隔数 + 两组名字完全不同的头维度字段。下面按 config.json 里的实际位置逐个来。
先看文件本身的形状
config.json 在仓库根目录,我们统计到 4,312 字节、140 行。它只有三层:顶层、text_config、vision_config,没有第四层嵌套的配置块(rope_parameters 是 text_config 下的一个子对象)。用 Python 数键数得到的结果是顶层 11 个键、text_config 34 个键、vision_config 14 个键。
和注意力结构相关的字段全部在 text_config 里。vision_config 那 14 个键里没有任何一个和层类型排布有关,它的层数字段甚至叫 depth(值 27,位于 config.json:124),连 num_hidden_layers 都没有。所以下文说的字段,如无特别说明都在 text_config 下。
第一根承重柱:layer_types
layer_types 是 text_config 下的一个字符串数组,占据 config.json:21-86——[ 在第 21 行,] 在第 86 行,中间 64 个元素各占一行,即 config.json:22 到 config.json:85。
我们把它整个打印出来数过:
- 数组长度是 64,与同文件
num_hidden_layers(config.json:98)的64相同。 - 取值只有两种:
"linear_attention"与"full_attention"。 "full_attention"共 16 项,"linear_attention"共 48 项,16 + 48 = 64。- 按 0 基下标,
full_attention出现在3, 7, 11, 15, 19, 23, 27, 31, 35, 39, 43, 47, 51, 55, 59, 63。这 16 个下标对 4 取余全是 3(Python 断言all(i % 4 == 3)返回True),相邻下标之差的集合是{4},即等间距 4。 - 换成”第几层”来说(1 基),第
4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64层是full_attention,其余 48 层是linear_attention。 - 首层(下标 0,
config.json:22)是linear_attention,末层(下标 63,config.json:85)是full_attention——数组以full_attention结尾。
把 64 项按 4 个一组切开正好 16 组,我们打印了前两组做核对,都是 ['linear_attention', 'linear_attention', 'linear_attention', 'full_attention']。
这就是第一个要点:这套结构在配置里是被”一层一层写死”的,不是靠某个规则字符串描述的,而是 64 行明明白白摆在那儿。你想知道第 37 层是什么类型,直接去数第 37 项就行,不需要推。
第二根承重柱:full_attention_interval
full_attention_interval 在 config.json:15,值是 4。
它和上面那张表在数值上是自洽的:num_hidden_layers ÷ full_attention_interval = 64 ÷ 4 = 16,恰好等于 full_attention 在数组里出现的次数;而我们实际数出来的相邻间距也恒为 4。
这里必须停一下。这两处只是数值一致,仓库文件里并没有写明谁推导谁。这是个模型权重仓,architectures 里那个 Qwen3_5ForConditionalGeneration(config.json:3)的实现不在本仓,我们没有推理代码可查。所以「加载时到底是读 layer_types 还是按 full_attention_interval 现算」「二者冲突时以哪个为准」,本仓文件没有说明,我们不推断。你只能记住一件事:改其中一个而不改另一个,这两处就会对不上,至于对不上会发生什么,我们没有依据说。
第三根承重柱:两组各自独立的头维度字段
很多人默认”注意力头”就那么一组参数。但在这份 config.json 里,头维度字段按前缀分成完全分开的两套命名,一个字都不共用:
| 分组 | 字段名 | 值 | 行号 |
|---|---|---|---|
不带 linear_ 前缀的一组 | num_attention_heads | 24 | config.json:97 |
不带 linear_ 前缀的一组 | num_key_value_heads | 4 | config.json:99 |
不带 linear_ 前缀的一组 | head_dim | 256 | config.json:16 |
带 linear_ 前缀的一组 | linear_num_key_heads | 16 | config.json:89 |
带 linear_ 前缀的一组 | linear_num_value_heads | 48 | config.json:90 |
带 linear_ 前缀的一组 | linear_key_head_dim | 128 | config.json:88 |
带 linear_ 前缀的一组 | linear_value_head_dim | 128 | config.json:91 |
这里只按字段名的前缀分组。两组字段分别作用在 layer_types 的哪一类层上,config.json 里并没有写明,我们不推断;后面对 model card 那一节会说明这层对应关系是怎么被”对上”的、边界又在哪。
两组的写法并不对称:不带前缀的那组只有一个 head_dim(256),config 里没有再分开写 Q 与 KV 的维度字段;带 linear_ 前缀的那组则把 key 与 value 的维度拆成两个字段各写一遍,虽然两个字段的值都是 128。
同一区块(config.json:87-92)还有另外两个字段:linear_conv_kernel_dim = 4(config.json:87)与 mamba_ssm_dtype = "float32"(config.json:92)。后者和 text_config 顶上的 dtype("bfloat16",config.json:13)不是同一个值,两个 dtype 字段并存。这几个字段各自作用于什么、linear_conv_kernel_dim 的确切含义是什么,我们未能从本仓任何文件中确认——本仓只有权重与配置,没有建模源码。照录数值,不解释。
另外两个带 gate 字样的字段也在 text_config 里:attn_output_gate = true(config.json:11)、output_gate_type = "swish"(config.json:100)。它们同样没有本仓文档可对照。
顺带记一处对照:text_config 里还有 attention_bias = false(config.json:9)与 attention_dropout = 0.0(config.json:10),而 vision_config 那 14 个键里这两个字段都不存在(rms_norm_eps、dtype 同样只在 text_config 里有)。也就是说,两边并不是同一套字段表的两份拷贝,看视觉侧时别拿语言侧的字段名去找。
顺手记一组容易看错的乘积
既然两侧的头维度是分开写的,很容易有人拿它们去乘一乘再和 hidden_size 对。我们确实乘过,结果照录如下——这些乘积 config.json 里一个都没有直接写出来,是我们自己算的:
num_attention_heads × head_dim = 24 × 256 = 6144,而hidden_size = 5120(config.json:18),两者不相等;num_key_value_heads × head_dim = 4 × 256 = 1024;linear_num_value_heads × linear_value_head_dim = 48 × 128 = 6144;linear_num_key_heads × linear_key_head_dim = 16 × 128 = 2048。
值得留意的只有一点:上面第一条与第三条算出来都是 6144,数值相同。至于这个相同是不是刻意的、6144 与 5120 不等又意味着什么,本仓文件没有任何说明,我们不推断,也不拿这些数去推算参数量、显存或任何别的东西。列在这里只是想让你在自己算的时候不至于以为算错了。
把 model card 的公式和数组对一遍
model card 的 “Model Overview” 节在 README.md:32–README.md:53。其中 README.md:41 那一行原文是:
Hidden Layout: 16 × (3 × (Gated DeltaNet → FFN) → 1 × (Gated Attention → FFN))
我们的核对步骤是这样走的:
- 读出
layer_types,长度 64,与README.md:40的Number of Layers: 64一致; - 数两种取值的个数:
full_attention16 个、linear_attention48 个。公式里外层重复 16 次,每次 3 个 DeltaNet 单元 + 1 个 Attention 单元,即 16×3 = 48、16×1 = 16——数量对上; - 校验顺序:
full_attention下标全部满足i % 4 == 3,说明每 4 层一组、组内前 3 层是 linear、第 4 层是 full,与公式里”3 个之后接 1 个”的顺序对上; - 切片核对
layer_types[0:4]与layer_types[4:8],两组完全相同。
结论:在”块数 = 16""块内比例 3:1""块内顺序先 linear 后 full”这三点上,README.md:41 的公式与 config.json:21-86 的数组吻合,我们没有发现矛盾。
同一节里其余几行也和 config 对得上:Number of Linear Attention Heads: 48 for V and 16 for QK(README.md:43)对应 linear_num_value_heads = 48 与 linear_num_key_heads = 16;Gated DeltaNet 的 Head Dimension: 128(README.md:44)对应 config 里分开写的那两个 128;Number of Attention Heads: 24 for Q and 4 for KV(README.md:46)对应 24 与 4;Gated Attention 的 Head Dimension: 256(README.md:47)对应 head_dim = 256。
但有两处必须说清的边界
第一,公式里的 FFN 在 layer_types 里没有任何对应元素。数组只有两种注意力类型的取值,不含 FFN 项。model card 说每个单元后面跟一个 FFN,这一点无法从 layer_types 核对,我们也没有权重结构可读来核实。
第二,“Gated DeltaNet” 和 “Gated Attention” 这两个名词在 config.json 里一次都没出现。config 对应位置的字符串是 "linear_attention" 与 "full_attention"。我们是按”数量 48/16 + 顺序 3:1”把两组名词对上的,这是名称层面的对应关系,不是 config 里写明的映射。至于 config 里那两个 gate 字段和 model card 的 “Gated” 字样是否指同一件事,本仓文件没有说明,我们不推断。
你自己怎么核一遍
上面关于排布的那些结论,都出自在仓库根目录读取 config.json 的下面这段(只读文本配置,不加载权重):
python -c "
import json
d=json.load(open('config.json',encoding='utf-8'))
lt=d['text_config']['layer_types']
full=[i for i,v in enumerate(lt) if v=='full_attention']
lin=[i for i,v in enumerate(lt) if v=='linear_attention']
print('full count',len(full)); print('full idx (0-based)',full)
print('full idx (1-based)',[i+1 for i in full]); print('linear count',len(lin))
print('all full idx %4==3 (0based)?', all(i%4==3 for i in full))
print('interval diffs', sorted(set(full[k+1]-full[k] for k in range(len(full)-1))))
print('groups of 4:', [lt[k*4:(k+1)*4] for k in range(2)])
"
我们在快照 1d4bf0f 上得到的输出是:full count 16、full idx (0-based) [3, 7, 11, 15, 19, 23, 27, 31, 35, 39, 43, 47, 51, 55, 59, 63]、linear count 48、all full idx %4==3 (0based)? True、interval diffs [4],以及前两组切片都等于 ['linear_attention','linear_attention','linear_attention','full_attention']。上游仓库内容随时可能更新,请以你手上那份快照跑出来的结果为准。
最后提醒两句和这篇直接相关的事。一是模型名与 config 里的类名字样不同:README.md:7 的标题是 # Qwen3.8-27B,而 config.json:3 的 architectures 是 ["Qwen3_5ForConditionalGeneration"]、config.json:7 的 model_type 是 "qwen3_5"、config.json:94 的 text_config.model_type 是 "qwen3_5_text";README.md:20 另有一句原文 Built on the architectural foundation of Qwen3.5。四处并列,我们不做因果解释,但你按类名去查资料时会用得上。二是这些字段的取值属于架构选择,它带来什么效果、适不适合你的场景,我们没有任何依据评价,也不打算据此推算任何东西。
延伸阅读
- 从头读起:Qwen3.8-27B 是什么:一个模型仓里有哪些文件、各自负责什么
- 本专题共 35 篇,完整分组目录见专题页
- Qwen3.8-27B 的 Hidden Layout 公式怎么跟 layer_types 对上
- Qwen3.8-27B 的线性注意力参数:卷积核宽度与 key/value 头数
本文依据 Hugging Face 仓库 Qwen/Qwen3.8-27B 的 model card 与随仓配置文件
(config.json、generation_config.json、preprocessor_config.json、chat_template.jinja 等)整理,
核对日 2026-08-16,对应仓库快照 1d4bf0f。
本文内容为 model card 与配置文件口径,我们没有下载权重、没有部署、也没有推理过这个模型,
因此不涉及生成质量、推理速度与显存占用的任何描述;文中所有评测数字均为 model card 自述,我们没有复现。
模型仓库内容随上游更新而变动,请以官方最新说明为准。