Qwen3.8-27B 的 64 层怎么排:全注意力恰好 16 个,下标全是 4k+3
看模型仓的配置文件,最容易犯的懒是:model card 上写了一行公式,看着挺清楚,就直接把那行公式当成结论抄走。Qwen3.8-27B 就是一个很典型的例子——它的 model card 里确实有一行把 64 层排布写得很紧凑的式子,但真正落到发布文件上的东西,是 config.json 里一个 64 个元素的字符串数组。这两样东西要不要自己对一遍?我们对了一遍,本文就是这次对照的全过程。
先说清边界:我们没有下载权重、没有加载模型、没有推理过一个 token。下面所有内容都只是 model card 与随仓配置文件的文本口径。
这个数组在文件里的哪个位置
layer_types 是 text_config 下的一个字段,位于 config.json:21-86:左中括号在第 21 行,右中括号在第 86 行,中间 64 个元素各占一行,也就是 config.json:22 到 config.json:85。整个 config.json 我们实读是 4,312 字节、140 行,只有三层结构:顶层、text_config、vision_config。也就是说,光这一个数组就占掉了整个配置文件将近一半的行数。
这一点本身就值得注意:一个只有 140 行的配置文件,你翻开它第一眼看到的是六十多行长得一模一样的字符串。人眼扫过去很难分清哪一行是哪一行,中间少一项、多一项、或者某一处顺序颠倒,肉眼几乎不可能发现。这也是我们坚持用脚本打印再统计、而不是滚动条拉一遍就下结论的原因。
顺带说明一下这个数组在配置文件里的层级关系:config.json 顶层我们数出 11 个键,text_config 下 34 个键,vision_config 下 14 个键。layer_types 属于 text_config 这 34 个键中的一个,和它同层的还有 num_hidden_layers、full_attention_interval 这两个下面要用到的字段。
打出来数:16 个 full_attention,48 个 linear_attention
在模型仓目录下执行(本文所有路径都写成仓库内相对路径):
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)])
"
我们拿到的输出原样是这几行:
full count 16full idx (0-based) [3, 7, 11, 15, 19, 23, 27, 31, 35, 39, 43, 47, 51, 55, 59, 63]full idx (1-based) [4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64]linear count 48all full idx %4==3 (0based)? Trueinterval diffs [4]groups of 4: [['linear_attention','linear_attention','linear_attention','full_attention'], ['linear_attention','linear_attention','linear_attention','full_attention']]
翻译成人话,这个数组的事实是这样几条:
- 数组长度 64,与
text_config.num_hidden_layers的64(config.json:98)相同。 - 取值只有两种:
"linear_attention"与"full_attention",没有第三种。 full_attention共 16 项,linear_attention共 48 项,16 + 48 = 64。- 16 个
full_attention的 0 基下标全部满足对 4 取余等于 3,相邻下标的差值集合是{4}——等间距,一个例外都没有。 - 换成 1 基去说「第几层」,就是第 4、8、12、16、20、24、28、32、36、40、44、48、52、56、60、64 层。
- 首尾两端:下标 0(第 1 层)是
linear_attention(config.json:22),下标 63(第 64 层)是full_attention(config.json:85)。这个数组是以full_attention结尾的。
最省事的记法是把 64 项按 4 个一组切开,得到 16 组,每组都是 ["linear_attention", "linear_attention", "linear_attention", "full_attention"]。我们打印了前两组做核对,两组完全一致,同时下标校验覆盖了全部 16 个 full_attention。
这里多说一句关于「第几层」的口径:数组下标是 0 基的,而人说话习惯从第 1 层数起,两套口径差 1。上面那句「下标全是 4k+3」和「第 4、8、12……层」说的是同一件事,只是起点不同。写文档、写 issue 的时候把用的是哪套口径讲明白,能省掉一半的来回确认。
那个叫 full_attention_interval 的字段
text_config 里另有一个 full_attention_interval,值是 4(config.json:15)。它和上面数出来的排布在数值上是自洽的:64 ÷ 4 = 16,恰好等于 full_attention 的出现次数;我们数出来的相邻下标间距也恒为 4。
我们只陈述这个数值一致性,到此为止。这个字段究竟是怎么被推理代码使用的、当它和 layer_types 冲突时以哪个为准,本仓文件没有说明——这是一个模型权重仓,config.json:3 里写的那个类 Qwen3_5ForConditionalGeneration 的实现不在这个仓库里,没有建模源码可读。所以「配置里出现」这件事,只能停在「配置里出现」。
与 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))
拿它和数组对:
- 公式的外层重复次数是 16,与我们数出的 16 组一致;
- 每个重复块里有 3 个 DeltaNet 单元加 1 个 Attention 单元,算下来是 16×3 = 48 与 16×1 = 16,与
linear_attention48 项、full_attention16 项的数量分别对上; - 块内顺序是「先 3 个,后 1 个」,与下标全为 4k+3、即每组前三项是 linear、第四项是 full 的顺序对上。
在「块数 16」「块内比例 3:1」「块内先 linear 后 full」这三点上,README.md:41 与 config.json:21-86 完全吻合,我们没有发现矛盾。同一节里 Number of Layers: 64(README.md:40)也与 num_hidden_layers 和数组长度对得上。
但有两样东西,这个数组核不出来
对完之后,必须把没对上的部分同样说清楚,否则「公式和配置一致」这句话会被读者放大成它不该有的意思。
第一,公式里的 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.json 里另有 attn_output_gate: true(config.json:11)与 output_gate_type: "swish"(config.json:100)两个字段,它们和公式里的 “Gated” 字样是不是指同一件事,本仓文件没有说明,我们不推断。
在这个仓库里,“DeltaNet” 与 “Gated” 只出现在 model card 的 README.md:41,42,43,45 那几行,也就是 Model Overview 内部;配置文件那边一个字都没有。
顺手记下的一处命名差异
数数组的时候会顺带看见另一件事:model card 标题是 # Qwen3.8-27B(README.md:7),而 config.json:3 的 architectures 是 ["Qwen3_5ForConditionalGeneration"]、config.json:7 的 model_type 是 "qwen3_5"、config.json:94 的 text_config.model_type 是 "qwen3_5_text"。model card 里另有一句原文写着 Built on the architectural foundation of Qwen3.5(README.md:20)。这几处并列在这里,只是提醒你按 model_type 去查资料时别按错关键词,我们不对命名做任何解释。
另外,model card 的 Model Overview 只写了语言模型这一侧的参数,vision_config 里的 depth = 27(config.json:124)等数字在 README.md 中一个都查不到——视觉侧的层数字段名也不叫 num_hidden_layers,而叫 depth。要数视觉侧,得换一个字段名去找。
这件事给读配置文件的人留下什么
有几点是可以带走的做法,与这个模型本身无关:
- 只要配置里出现「一个长得像分布规律的数组」,就用脚本打印一遍再下结论,别靠肉眼看六十多行相同字符串。
- 数完之后,去找配置里那个描述同一件事的标量字段(这里是
full_attention_interval),看两者对不对得上。对得上就并列记下来,不要替它们编排优先级。 - 把文档公式与配置数组对照时,分成「对上的部分」和「配置里根本没有对应物的部分」两栏写。像 FFN 和 “Gated” 这类只存在于文档措辞里的东西,写成「核不出来」比写成「一致」诚实得多。
至于这样一种 3:1 的层排布在效果上意味着什么——这属于架构选择,我们没有依据评价,也不打算评价。
延伸阅读
- 从头读起:Qwen3.8-27B 是什么:一个模型仓里有哪些文件、各自负责什么
- 本专题共 35 篇,完整分组目录见专题页
- Qwen3.8-27B 的 Hidden Layout 公式怎么跟 layer_types 对上
- Qwen3.8-27B 的混合注意力:由 config 里哪几个字段承载
本文依据 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 自述,我们没有复现。
模型仓库内容随上游更新而变动,请以官方最新说明为准。