Qwen3.8-27B 的 64 层怎么排:全注意力恰好 16 个,下标全是 4k+3

2026-08-16

看模型仓的配置文件,最容易犯的懒是:model card 上写了一行公式,看着挺清楚,就直接把那行公式当成结论抄走。Qwen3.8-27B 就是一个很典型的例子——它的 model card 里确实有一行把 64 层排布写得很紧凑的式子,但真正落到发布文件上的东西,是 config.json 里一个 64 个元素的字符串数组。这两样东西要不要自己对一遍?我们对了一遍,本文就是这次对照的全过程。

先说清边界:我们没有下载权重、没有加载模型、没有推理过一个 token。下面所有内容都只是 model card 与随仓配置文件的文本口径。

这个数组在文件里的哪个位置

layer_typestext_config 下的一个字段,位于 config.json:21-86:左中括号在第 21 行,右中括号在第 86 行,中间 64 个元素各占一行,也就是 config.json:22config.json:85。整个 config.json 我们实读是 4,312 字节、140 行,只有三层结构:顶层、text_configvision_config。也就是说,光这一个数组就占掉了整个配置文件将近一半的行数。

这一点本身就值得注意:一个只有 140 行的配置文件,你翻开它第一眼看到的是六十多行长得一模一样的字符串。人眼扫过去很难分清哪一行是哪一行,中间少一项、多一项、或者某一处顺序颠倒,肉眼几乎不可能发现。这也是我们坚持用脚本打印再统计、而不是滚动条拉一遍就下结论的原因。

顺带说明一下这个数组在配置文件里的层级关系:config.json 顶层我们数出 11 个键,text_config 下 34 个键,vision_config 下 14 个键。layer_types 属于 text_config 这 34 个键中的一个,和它同层的还有 num_hidden_layersfull_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 16
  • full 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 48
  • all full idx %4==3 (0based)? True
  • interval diffs [4]
  • groups of 4: [['linear_attention','linear_attention','linear_attention','full_attention'], ['linear_attention','linear_attention','linear_attention','full_attention']]

翻译成人话,这个数组的事实是这样几条:

  1. 数组长度 64,与 text_config.num_hidden_layers64config.json:98)相同。
  2. 取值只有两种:"linear_attention""full_attention",没有第三种。
  3. full_attention16 项,linear_attention48 项,16 + 48 = 64。
  4. 16 个 full_attention 的 0 基下标全部满足对 4 取余等于 3,相邻下标的差值集合是 {4}——等间距,一个例外都没有。
  5. 换成 1 基去说「第几层」,就是第 4、8、12、16、20、24、28、32、36、40、44、48、52、56、60、64 层。
  6. 首尾两端:下标 0(第 1 层)是 linear_attentionconfig.json:22),下标 63(第 64 层)是 full_attentionconfig.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,值是 4config.json:15)。它和上面数出来的排布在数值上是自洽的:64 ÷ 4 = 16,恰好等于 full_attention 的出现次数;我们数出来的相邻下标间距也恒为 4。

我们只陈述这个数值一致性,到此为止。这个字段究竟是怎么被推理代码使用的、当它和 layer_types 冲突时以哪个为准,本仓文件没有说明——这是一个模型权重仓,config.json:3 里写的那个类 Qwen3_5ForConditionalGeneration 的实现不在这个仓库里,没有建模源码可读。所以「配置里出现」这件事,只能停在「配置里出现」。

与 model card 那行公式的对照

model card 的 “Model Overview” 节在 README.md:32README.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_attention 48 项、full_attention 16 项的数量分别对上;
  • 块内顺序是「先 3 个,后 1 个」,与下标全为 4k+3、即每组前三项是 linear、第四项是 full 的顺序对上。

在「块数 16」「块内比例 3:1」「块内先 linear 后 full」这三点上,README.md:41config.json:21-86 完全吻合,我们没有发现矛盾。同一节里 Number of Layers: 64README.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: trueconfig.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-27BREADME.md:7),而 config.json:3architectures["Qwen3_5ForConditionalGeneration"]config.json:7model_type"qwen3_5"config.json:94text_config.model_type"qwen3_5_text"。model card 里另有一句原文写着 Built on the architectural foundation of Qwen3.5README.md:20)。这几处并列在这里,只是提醒你按 model_type 去查资料时别按错关键词,我们不对命名做任何解释。

另外,model card 的 Model Overview 只写了语言模型这一侧的参数,vision_config 里的 depth = 27config.json:124)等数字在 README.md 中一个都查不到——视觉侧的层数字段名也不叫 num_hidden_layers,而叫 depth。要数视觉侧,得换一个字段名去找。

这件事给读配置文件的人留下什么

有几点是可以带走的做法,与这个模型本身无关:

  • 只要配置里出现「一个长得像分布规律的数组」,就用脚本打印一遍再下结论,别靠肉眼看六十多行相同字符串。
  • 数完之后,去找配置里那个描述同一件事的标量字段(这里是 full_attention_interval),看两者对不对得上。对得上就并列记下来,不要替它们编排优先级。
  • 把文档公式与配置数组对照时,分成「对上的部分」和「配置里根本没有对应物的部分」两栏写。像 FFN 和 “Gated” 这类只存在于文档措辞里的东西,写成「核不出来」比写成「一致」诚实得多。

至于这样一种 3:1 的层排布在效果上意味着什么——这属于架构选择,我们没有依据评价,也不打算评价。

延伸阅读


本文依据 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?报名体系课或加入会员,照着学、照着用。