Qwen3.8-27B 的 RoPE 维度 64:config.json 里找不到对应字段

2026-08-16

拿 model card 的参数表去核 config.json,是读一个新模型最省事的入口:一行一行往下对,能对上的说明两边口径一致,对不上的地方往往就是需要多看两眼的地方。

Qwen3.8-27B 这张表大部分行都对得很干净。Hidden Dimension: 5120 对应 text_config.hidden_size = 5120Number of Layers: 64 对应 num_hidden_layers = 64,Gated Attention 那行的 Head Dimension: 256 对应 head_dim = 256Intermediate Dimension: 17,408 对应 intermediate_size = 17408 —— 都是同名或近似同名的字段,一眼可查。

唯独有一行会卡住:Rotary Position Embedding Dimension: 64README.md:48)。你在 config.json 里翻遍三层结构,找不到任何一个能被叫做「RoPE 维度」的键。

以下所有数字均以 2026-08-16 的核对为准,对应 Hugging Face 仓库 Qwen/Qwen3.8-27B 的快照 1d4bf0f;模型仓内容随上游更新而变动。

先把找的范围划清楚

config.json 整个文件 4,312 字节、140 行,结构只有三层:顶层、text_configvision_config。顶层 11 个键,text_config 34 个键,vision_config 14 个键。rope_parameterstext_config 下的一个子对象,不构成第四层配置块。

跟旋转位置编码沾边的键,一共就这些:

位置字段名行号
text_config 直属partial_rotary_factor0.25config.json:102
text_config.rope_parametersmrope_interleavedtrueconfig.json:105
text_config.rope_parametersmrope_section[11, 11, 10]config.json:106-110
text_config.rope_parameterspartial_rotary_factor0.25config.json:111
text_config.rope_parametersrope_theta10000000config.json:112
text_config.rope_parametersrope_type"default"config.json:113

rope_parameters 子对象一共就 5 个键,全在上表里。注意 partial_rotary_factor 出现了两次:一次在 text_config 直属层(config.json:102),一次在 rope_parameters 里(config.json:111),两处的值都是 0.25。要动这个值的人,先知道它在文件里有两个位置,比只改一处再去猜为什么没生效省事。

上表这六行里没有任何一个键带 dim 字样,也没有任何一个的值是 64。

另外两个键的取值也一并照录,免得有人以为是我们漏看了:rope_theta 是整数 10000000(一千万),mrope_interleavedtrue。这两个字段具体如何被使用,同样没有可查的依据,这里只记取值。

搜索范围还可以再收一收:vision_config 的 14 个键里没有任何一个与旋转位置编码相关,视觉侧唯一带位置字样的键是 num_position_embeddings = 2304config.json:132)。顶层 11 个键里也没有。所以确实只剩 text_config 这一处可找,上表已经把它翻完了。

那个 64 是乘出来的

config.json 里跟 64 唯一挂得上钩的算式,是 Gated Attention 的头维度乘以那个 partial_rotary_factor

  • text_config.head_dim = 256config.json:16
  • text_config.partial_rotary_factor = 0.25config.json:102

我们用 Python 算了一次,256 × 0.25 的结果是 64.0,与 README.md:48 写的 64 数值相同。

这里必须把话说死:这是我们做的一次算术核对,config.json 本身并没有把 64 作为 RoPE 维度写出来partial_rotary_factor 的确切定义,我们未能从这个仓库的任何一个文件里确认——这是一个模型权重仓,architectures 里写的 Qwen3_5ForConditionalGenerationconfig.json:3)这个类的实现不在仓内,没有建模源码可读。所以这条只能作为「数值吻合」记录,不能反过来当成「config 明写了 RoPE 维度是 64」去引用,更不能拿它推导任何跟效果、开销有关的结论。

顺带记一个同样只是算术的观察:rope_parameters.mrope_section[11, 11, 10],三项之和为 32,恰是 64 的一半。这三项分别对应什么,本仓文件没有说明,我们不解释。

顺着这条路继续算下去很容易上瘾,但也很容易翻车。同一次核对里我们还乘出过 num_attention_heads × head_dim = 24 × 256 = 6144,而 text_config.hidden_size5120config.json:18),两者并不相等。这类乘积在 config.json 里一个都没有直接写着,它们只是我们拿现成字段做的算术。乘出来的数跟参数表能对上是一回事,能不能反推出结构上的解释是另一回事——后者需要建模代码,而这个仓库里没有。

反直觉的地方:在 config 里搜 64,先撞上的是层数

很多人核对参数表的第一动作是在 config.json 里搜字符串 64。这一步在这个仓库里会把人带偏。

config.json 里确实存在值为 64 的字段,但它是 text_config.num_hidden_layers = 64config.json:98);同时 layer_types 这个字符串数组的长度也是 64(config.json:21-86[ 在第 21 行,] 在第 86 行,中间 64 个元素各占一行)。这两个 64 对应的是 model card 里的 Number of Layers: 64README.md:40),是层数,跟位置编码维度不是一回事。

也就是说,这一行对不上的原因不是「值找不到」,而是「没有承载它的字段」。搜到的 64 属于另一行参数表。真正与 README.md:48 那个 64 对应的东西,在 config.json 里是以 2560.25 两个数分开存着的。

顺便说一句:同一张参数表里落空的不止这一行。Number of Parameters: 27BREADME.md:37)在 config.json 里同样没有任何字段承载——整个文件没有一个参数量字段,这个 27B 只能作为 model card 自述来引用,没法从配置核对。所以「参数表某行在 config 里找不到对应字段」本身并不稀奇,值得注意的只是每一行落空的方式不一样:27B 那行是彻底没有来源可查,RoPE 维度这行则是两个字段乘得出来。

怎么自己核一遍

如果你要在自己手上的那份配置里重复这个核对,判定动作是三步:读出 rope_parameters 的全部键名,看有没有名字里带维度的键;把 head_dimpartial_rotary_factor 乘一次;再把值为 64 的字段单独列出来,确认它是不是 num_hidden_layers

我们做算术核对时跑的是这一段(在放置 config.json 的目录下执行):

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('deepstack', v['deepstack_visual_indexes'], type(v['deepstack_visual_indexes']), len(v['deepstack_visual_indexes']))
print('layers/interval', t['num_hidden_layers']/t['full_attention_interval'])
"

本篇只用得上其中两行输出:head_dim*partial 64.0sum mrope_section 32 [11, 11, 10]。其余几行是同一次核对里顺带打出来的别的数值关系。这段命令只读文件、不加载模型,Windows 与 Linux/macOS 下都是同一条 python -c,差别只在你怎么进到放配置文件的那个目录。

什么情况说明你遇到的不是这件事

有两种情况看起来像「RoPE 对不上」,但跟上面这条不是一回事,别混在一起排查。

一是 rope_type。发布配置里 rope_parameters.rope_type 的值是 "default"config.json:113),这个子对象里没有 factor,也没有 original_max_position_embeddings——它就是上面那 5 个键。而 model card 的 Best Practices 一节(README.md:521-536)给出的替换片段里 rope_type"yarn",并多出 factor: 4.0original_max_position_embeddings: 262144。两处并列陈述:开箱下发的不是 YaRN 配置,README 给的是让你自己去改的做法,而且 README 自己在 README.md:557 建议仅在需要处理长上下文时才修改 rope_parameters。如果你的问题出在长上下文相关的配置替换上,那是这一条,不是维度对不上。

二是上下文长度。README.md:53Context Length: 262,144 natively and extensible up to 1,000,000 tokens.,而 config.json:93max_position_embeddings262144tokenizer_config.jsonmodel_max_length 也是 262144;发布的配置里没有任何字段写着 1,000,000。这同样是参数表与 config 之间的一处并列差异,但它落在长度上,不落在维度上。

以上都只是陈述两处写法的差异,我们不推断哪一处「是对的」,也不由此评价任何东西。参数表怎么写、config 怎么存,属于项目自己的表述与工程选择,效果如何我们没有依据评价。

留给读者的一句话

参数表和配置文件是两种写法:前者写给人看,常常把推导过的结果直接写成一个数;后者写给加载器看,只存最原始的那几个量。README.md:48 的 64 与 config.json 里的 2560.25 之间,我们能核到的只有一层算术关系;这两处写的是不是同一件事,本仓文件没有写明,我们不推断,就停在这里。

延伸阅读


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