Qwen3.5-MoE-35B-A3B 架构 QA

101 问,覆盖 CH1 演进脉络 → CH2 整体架构 → CH3 Gated DeltaNet → CH4 混合注意力 → CH5 MoE 路由 → CH6 训练体系 → CH7 支撑项 → CH8 源码映射 → CH9 总结与对比


Q1.1 Qwen系列从2.5到3.5经历了哪些关键架构演进?

简短回答:Qwen从Dense Transformer演进到全层MoE+线性注意力混合架构,经历了Qwen2.5(Dense+GQA)、Qwen3(引入MoE 128E k=8)、Qwen3-Next(引入Gated DeltaNet并确立3:1混合比例)、Qwen3.5(全层MoE 256E+原生多模态+MTP+262K上下文)四次重大架构升级。

详细解释:Qwen2.5使用标准Dense Transformer加GQA和SwiGLU,是经典的密集架构。Qwen3首次引入MoE(128个专家,k=8)和GQA,但MoE仅在部分层使用,属于过渡性探索。Qwen3-Next做出了两个关键决策:引入Gated DeltaNet线性注意力替代部分softmax注意力层,并确立了3:1的GDN:Full Attention混合比例。这为长上下文支持奠定了基础。

Qwen3.5是这一演进路线的集大成者。它将MoE扩展到所有40层(mlp_only_layers为空),专家数从128翻倍到256,新增原生多模态视觉编码器(27层ViT+3D卷积patch embedding),引入MTP(Multi-Token Prediction)训练模块,并将上下文窗口扩展到262K token。参数配置从Qwen3-235B-A22B(总235B/激活22B)演进到Qwen3.5-35B-A3B(总35B/激活3B),激活效率从9.4%提升到8.6%,实现了更极致的稀疏化。

面试要点:记住四次升级的关键词——2.5(Dense)→3(MoE)→3-Next(GDN+3:1)→3.5(全MoE+多模态+MTP+262K)。Qwen3.5相比GPT-4(Dense)和DeepSeek V3(纯MoE)的核心差异化在于"线性注意力+MoE"的组合,而非单纯增加专家数。

延伸阅读:主报告 CH1(Qwen系列全景演进图);论文/tech-report Qwen3.5 技术报告 §2(架构概述)。


Q2.1 35B-A3B中的"35B"和"A3B"分别如何计算?

简短回答:35B是所有参数的总和(40层×256个专家权重+嵌入+LM Head+视觉编码器等);A3B是单个token前向传播时实际参与计算的参数数(1个token mixer+8个路由专家+1个共享专家+嵌入+LM Head)。

详细解释:35B总参数的计算覆盖了模型的所有可训练权重:MoE专家权重约32.36B(占92.5%),这是绝对主体;Token Mixer(GDN+Full Attention)约1.28B(3.7%);嵌入层和LM Head各约508.6M,合计约1.02B(2.9%);视觉编码器约0.5B(1.4%);其他(RMSNorm、Router等)约0.2B。

A3B激活参数的计算则只计算单次前向中实际被使用到的权重:嵌入层508.6M,每层激活约59M(GDN约34M+MoE约25M),40层约2.36B,LM Head508.6M,合计约3.38B(实际标称3B,因精度舍入和部分参数共享)。具体来说,每层MoE部分只有256个专家中的9个(8个路由+1个共享)被激活,专家利用率仅为3.5%。

易混淆:A3B不是"有3B参数",而是"每次推理只用3B参数"。35B的容量通过稀疏激活分布在不同token的不同专家组合上得以体现。DeepSeek V3的激活比为5.5%(671B/37B),Mixtral为27%(47B/13B),Qwen3.5为8.6%——越低意味着稀疏化越激进。

延伸阅读:主报告 CH2.1(参数量分解表);Qwen3_5MoeSparseMoeBlock.forward 源码中专家选择与计算逻辑。


Q2.2 为什么选择hidden_size=2048而非更大的维度?

简短回答:2048是效率和质量的平衡点——配合head_dim=256和16个注意力头提供足够表示空间,同时通过256个专家(而非增加宽度)来提升模型容量,避免所有层的计算量平方级膨胀。

详细解释:hidden_size是Transformer中影响最大的超参数之一,它决定了每层的矩阵乘法规模。如果使用hidden=4096,每层GDN参数将从33.7M增至135M(4倍),每个MoE专家从3.15M增至12.6M(4倍),总参数将远超35B的目标规模。Qwen3.5的设计理念是"横向扩展"(增加专家数量)而非"纵向扩展"(增加隐藏维度),因为MoE机制天然适合通过专家数来提升容量而不增加激活计算量。

2048这个数值还有良好的整除性——可以被16个注意力头整除(128维/头的基本维度),配合head_dim=256(需要额外的KV repeat实现),并为GQA的2个KV头提供了合理的维度分配。与同类模型对比:Llama 3-70B使用hidden=8192(Dense),Mixtral 8x7B使用hidden=4096,Qwen3.5-MoE的2048是最小的,但通过256个专家弥补了宽度不足,在激活参数仅3B的情况下达到了与更大hidden模型相近的质量。

面试要点:“为什么不用更大的hidden_size"是经典面试题。核心答案:MoE通过专家数量提升容量,不需要更大的宽度。深层逻辑:width↑→所有层计算量↑,experts↑→仅激活部分专家计算量↑。

延伸阅读:主报告 CH2.2(超参数全景表);config.json: hidden_size=2048


Q2.3 40层的深度是如何确定的?

简短回答:40层与full_attention_interval=4形成10个完整的"3 GDN + 1 Full Attention"循环单元,既提供了足够的表示层次,又便于张量并行切分。

详细解释:每4层构成一个基本混合单元(3层线性注意力+1层全局注意力),40层正好是10个完整单元。这种设计避免了不完整的混合模式——如果层数是37或43,某些单元会被截断,可能导致注意力模式的不均匀分布。10个完整循环意味着模型在10个不同抽象层次上都执行了"局部压缩→全局整合"的认知循环。

从参数分配角度看,40层时MoE总参数约占32.36B,占总参数35B的92.6%,这是最合理的容量分配——如果层数更多,Token Mixer的参数比例会上升(因为每层都需要独立的GDN/Attention权重),挤占MoE专家的参数预算。对比同类模型:DeepSeek V3使用60层(更深的MoE,配合更大的hidden=7168),Mixtral 8x7B使用32层。Qwen3.5的40层在深度和专家数量间取得了平衡。

面试要点:40 = 4 × 10,数字的内在逻辑是full_attention_interval=4。如果考官问"为什么不是48层”,回答:更多层→每层参数预算减少→MoE专家数或中间维度必须缩减→总容量可能不升反降。

延伸阅读:主报告 CH2.3(层结构分析);configuration_qwen3_5_moe.py:81 num_hidden_layers=40


Q2.4 GQA中16Q/2KV(ratio=8)的设计依据是什么?

简短回答:8:1的极端GQA比例大幅减少KV缓存(从16头降至2头,节省87.5%),同时因为Full Attention层仅占25%,可以承受更小的KV头数而不显著损失注意力质量。

详细解释:16个Query头配合2个Key/Value头意味着每8个Q头共享1对KV头。这种设计使每层KV缓存从(16×256)=4096维降至(2×256)=512维。Qwen3.5能使用如此激进的GQA比例的原因在于:75%的层使用GDN线性注意力(完全不需要KV缓存),只有10个Full Attention层需要KV缓存。因此总体KV缓存压力已经大幅降低,减少KV头数的边际收益(87.5%×10层)仍然显著。

2个KV头在实践中提供了足够的表示多样性——通常一个头偏向局部模式(捕捉相邻token的句法关系),另一个头偏向全局模式(捕捉远距离的语义关联)。这种分工在8:1的比例下自然形成。相比Llama 3 70B的8个KV头,Qwen3.5的选择更激进,但这是在全层MoE+GDN混合架构的上下文下做出的合理取舍。

易混淆:不要孤立地评价GQA比例。8:1在纯注意力模型中可能过于激进,但在仅有25%层使用Full Attention的Qwen3.5中,每个Full Attention层的注意力质量更重要(它们承担了全局信息整合的任务),而GQA的轻微质量损失被KV缓存的大幅节省所抵消。

延伸阅读:主报告 CH2.4(注意力配置分析);config.json: num_attention_heads=16, num_key_value_heads=2


Q2.5 262K上下文的支持依赖哪些机制?

简短回答:四个机制协同工作:(1)Gated DeltaNet的O(n)复杂度和恒定状态大小;(2)Full Attention仅10层需要KV缓存;(3)GQA将KV头数降至2;(4)RoPE theta=10M支持超远位置区分。推理时总缓存仅约5.4GB。

详细解释:GDN是支持超长上下文的核心——它将历史信息压缩为固定大小的矩阵S_t(32×128×128),每次更新仅需O(1)时间,与序列长度无关。30个GDN层总计仅需63MB状态存储。Full Attention层的KV缓存是主要内存消耗者:262K token时,每层KV缓存=2 heads×262144×256 dim×2(K+V)×2 bytes=537MB,10层合计5.37GB。

RoPE theta=10M是位置编码的关键——标准theta=10000仅能可靠区分约4K-8K范围内的位置,提升到10M后理论最大可区分位置增加了约1000倍。此外,Qwen3.5可能使用了NTK-aware或YaRN等RoPE扩展方法进行分阶段训练(短序列预训练→逐步扩展→长序列微调),确保位置编码在超长上下文下的泛化能力。

面试要点:如果被问"为什么能做到262K",先讲GDN(O(1)复杂度,75%的层),再讲KV缓存优化(GQA 2头+仅10层),最后讲RoPE theta。对比:全注意力模型在262K时KV缓存约214GB(完全不可行),Gemini 1.5 Pro声称1M+但架构未公开,GPT-4 Turbo支持128K。

延伸阅读:主报告 CH2.5(上下文支持分析);CH3(GDN长序列加速原理);config.json: max_position_embeddings=262144, rope_theta=10000000


Q2.6 为什么要所有层都用MoE而非部分层?

简短回答:全层MoE最大化参数效率,确保每一层的FFN都可以从稀疏激活中受益,避免Dense FFN层成为表示容量的"瓶颈"。Qwen3的经验表明部分层使用Dense FFN会导致那些层成为信息流动的限制因素。

详细解释:在混合架构中,如果部分层使用Dense FFN(如Qwen3的做法),那些层的FFN容量是固定的,无法通过增加专家数来扩展。当序列经过这些Dense层时,无论输入多么复杂,FFN的表达能力都是有限的。而MoE层则可以根据输入动态选择最合适的专家组合,提供更大的表示灵活性。统一使用MoE确保了所有层的表示容量分布均匀,没有"短板"。

此外,全层MoE简化了实现——所有的Decoder Layer使用相同的MoE配置(Qwen3_5MoeSparseMoeBlock),不需要为不同层类型维护不同的FFN实现。从训练角度看,统一的MoE也意味着路由策略和负载均衡可以在所有层上一致地优化。若仅20层用MoE,总参数将降至19B,激活参数可能降至2B,表达能力显著下降。Mixtral 8x7B也在所有32层使用MoE,验证了这一设计的有效性。

面试要点:全层MoE = 没有瓶颈层。如果考官追问"浅层和深层是否需要不同配置",回答:当前所有层使用相同配置是简化实现的选择,但学术界有研究表明浅层可能需要更少的专家(处理低级特征),深层需要更多(处理抽象概念),这是未来的优化方向。

延伸阅读:主报告 CH2.6(MoE层分布分析);config.json: mlp_only_layers=[] 为空列表。


Q2.7 嵌入矩阵248320×2048的参数占比多少?

简短回答:嵌入参数=248320×2048=508.6M,占总参数35B的约1.45%。LM Head同样大小(tie_word_embeddings=false),两个矩阵合计约1.02B,占2.9%。在MoE专家占92%的模型中是较小的开销。

详细解释:Qwen3.5选择不共享嵌入和输出权重(tie_word_embeddings=false),这与Llama系列一致,与GPT-2不同。不共享的原因可能有两个:(1)嵌入层需要将离散token映射到连续空间,输出层需要将连续表示映射回概率分布,两者的学习目标不同;(2)在多模态场景下,嵌入层还需处理视觉token的占位符嵌入,独立的嵌入层提供了更大的灵活性。

248320的词汇表大小包含了大量的多语言token和特殊token(如视觉标记248053-248057)。虽然嵌入矩阵的参数占比看似很小(1.45%),但在推理时嵌入查找是每个token必须执行的操作。若使用权重共享(tie_word_embeddings=True),可节省508.6M参数,这对小模型有显著影响,但对35B模型影响仅1.5%,权衡后选择了更大的表示灵活性。

面试要点:embedding参数占比在MoE模型中天然很低(因为专家权重占主导),但在Dense模型中可能占5-10%。GPT-2使用权重共享(节省参数),Llama/Qwen不共享(更大灵活性)。

延伸阅读:主报告 CH2.7(参数分解饼图);config.json: tie_word_embeddings=false, vocab_size=248320


Q2.8 为什么RMSNorm epsilon选择1e-6?

简短回答:1e-6是RMSNorm的标准选择(Llama、Mistral等均使用),足够小以不显著改变归一化分布,又足够大以防止BFloat16精度下的除零和下溢。

详细解释:在hidden=2048时,输入向量的方差期望约为1.0,因此RMSNorm的分母约为sqrt(1.0+1e-6)≈0.9999995≈1.0,epsilon的影响极小(<0.0001%)。从数值稳定性角度,BFloat16的最小正规数约为1.17e-38,1e-6远大于此,提供了一个安全的「地板」。

选择1e-6而非1e-5(GPT-3 LayerNorm使用的值)是因为RMSNorm去掉了均值中心化步骤,分母本身就是RMS值(通常接近1),不需要过大的epsilon来防止方差过小的情况。Llama系列也使用1e-6,这已成为RMSNorm的事实标准。

相比之下,LayerNorm的epsilon通常为1e-5——因为它先减均值再除标准差,减均值后的分布更可能接近零,需要更大的epsilon保护。两者的选择不是任意的:RMSNorm 的 RMS 值天然接近 1(归一化后的 hidden vector 期望 RMS = 1),所以 1e-6 足够;LayerNorm 减均值后的标准差可能远小于 1,需要 1e-5 的保护。在hidden=2048时,输入向量的方差期望约为1.0,因此RMSNorm的分母约为sqrt(1.0+1e-6)≈0.9999995≈1.0,epsilon的影响极小(<0.0001%)。从数值稳定性角度,BFloat16的最小正规数约为1.17e-38,1e-6远大于此,提供了一个安全的"地板"。

选择1e-6而非1e-5(GPT-3 LayerNorm使用的值)是因为RMSNorm去掉了均值中心化步骤,分母本身就是RMS值(通常接近1),不需要过大的epsilon来防止方差过小的情况。Llama系列也使用1e-6,这已成为RMSNorm的事实标准。

易混淆:LayerNorm的epsilon通常为1e-5(需要处理减均值后的更小方差),RMSNorm的epsilon通常为1e-6(RMS值在归一化后更稳定)。两者不要混用。

延伸阅读:主报告 CH2.8(归一化策略分析);config.json: rms_norm_eps=1e-06


Q2.9 总参数35B的各模块分解比例?

简短回答:MoE专家权重占总参数的绝对主导地位(约92.5%),是MoE模型的普遍特征。其余模块按参数大小依次为:Token Mixer(3.7%)>嵌入+LM Head(2.9%)>视觉编码器(~1.4%)>其他(RMSNorm、Router等,~0.5%)。

详细解释:具体来说,MoE专家权重32.36B(92.5%)包括40层×256个路由专家×3.15M/专家+40层×1个共享专家×3.15M/专家。Token Mixer 1.28B(3.7%)包括30个GDN层(每层33.7M)和10个Full Attention层(每层27.3M)。嵌入和LM Head各508.6M,合计1.02B(2.9%)。视觉编码器约0.5B(1.4%),包括27层ViT、3D patch embedding和patch merger。

这个参数分布揭示了MoE模型的核心设计哲学:将绝大多数参数分配给FFN(专家),因为不同token需要不同的知识处理能力,而token mixing(注意力)相对统一,不需要太多参数。这也解释了为什么MoE模型适合做知识密集型任务——大量专家可以存储不同类型的知识。

面试要点:记住92%这个数字——“MoE模型中专家权重占90%以上"是普适规律。如果模型规模和专家数变化,这个比例会有小幅波动但基本维持在这个水平。

延伸阅读:主报告 CH2.9(参数分解表与可视化);modeling_qwen3_5_moe.py 中各类定义。


Q2.10 layer_types配置如何影响架构灵活性?

简短回答:layer_types是40个字符串的列表,每层可以是"linear_attention”、“full_attention"或"mlp_only”。默认由full_attention_interval=4自动生成(3 GDN + 1 Full的重复模式),但用户可以自定义任意排列而无需修改模型代码,支持快速的架构搜索实验。

详细解释:配置类Qwen3_5MoeTextConfig.__post_init__在layer_types未指定时自动根据interval生成默认模式。例如interval=4产生[“linear_attention”,“linear_attention”,“linear_attention”,“full_attention”]×10。但如果用户传入自定义列表如[“linear_attention”]×20+[“full_attention”]×20(1:1比例),模型会直接使用而无需任何代码修改。

这种设计体现了"配置驱动架构"的理念——核心建模代码不硬编码层类型模式,而是通过配置参数灵活控制。层类型的条件逻辑在Qwen3_5MoeDecoderLayer.__init__中处理:根据layer_types[layer_idx]选择实例化Qwen3_5MoeGatedDeltaNetQwen3_5MoeAttention或None(mlp_only)。这比DeepSeek V3通过YAML配置的方式更直观和Pythonic。

面试要点:layer_types是Qwen3.5源码中架构灵活性的最佳体现。它使得研究人员可以在不改代码的情况下实验不同混合比例、非均匀分布甚至前密后疏的模式。

延伸阅读:主报告 CH2.10(层类型配置机制);configuration_qwen3_5_moe.py:112-119 自动生成逻辑。


Q2.11 MTP如何与主模型交互?

简短回答:MTP(Multi-Token Prediction)通过mtp_num_hidden_layers=1配置,在训练时添加1个额外的Decoder Layer同时预测下一个token。训练损失包含当前位置和未来位置的交叉熵。推理时MTP层可作为投机解码的draft模型,提升生成速度1.5-2x。

详细解释:MTP的额外Decoder Layer与标准Decoder Layer结构相同(约845M参数),但不计入主模型参数量。在训练时,主模型处理当前token并输出hidden states,MTP层接收这些hidden states并预测下一位置的token。最终损失是标准LM损失加上MTP预测损失的加权和:loss=CE(pred(t), true(t))+CE(pred_mtp(t+1), true(t+1))。这使训练的每token信号密度提升约2倍。

推理时MTP的价值体现在投机解码(speculative decoding):主模型生成token t,MTP层作为轻量draft模型预测token t+1,然后主模型并行验证。如果draft正确,一次forward确认多个token。权重管理通过_keys_to_ignore_on_load_unexpected=[r"^mtp.*"]实现——当不需要MTP时,这些权重在加载时被自动忽略。

面试要点:MTP是当前主流LLM的训练增强技术(Meta LLaMA 3论文提及,GLM-5.1也使用)。其核心价值是"训练时增加信号密度,推理时加速生成"。与Medusa的区别:MTP集成在模型内部,更优雅。

延伸阅读:主报告 CH2.11(MTP机制详解);CH7(推理加速策略);Qwen3_5MoePreTrainedModel._keys_to_ignore_on_load_unexpected


Q2.12 attn_output_gate的作用是什么?

简短回答:attn_output_gate=true意味着Full Attention层的输出经过sigmoid门控(attn_output * sigmoid(gate)),gate从Q投影的双倍输出中派生。这个门控允许模型学习何时"信任"注意力输出,对提升训练稳定性和最终模型质量有帮助。

详细解释:门控的具体实现是:Q投影的输出维度翻倍(16×256×2 vs 16×256),前半部分是正常的query用于注意力计算,后半部分经过sigmoid作为gate值。Sigmoid门控将输出约束在(0,1)范围——gate接近1时保留注意力输出,接近0时几乎完全抑制。这与GDN层的SiLU门控形成对偶设计:Full Attention使用温和的sigmoid衰减控制,GDN使用更强的SiLU(可放大可抑制)。

门控的额外参数成本约为8.4M(Q投影从4096×2048扩展到4096×2048×2),占Full Attention层参数的比例很小但换来了动态输出调节能力。这在训练初期特别有用——当注意力机制尚未学会有效的注意力模式时,门控可以衰减不可靠的注意力输出,防止错误信号在残差流中传播。

面试要点:门控在Qwen3.5中有两种形式——Full Attention的sigmoid门控(0-1衰减)和GDN的SiLU门控(可放大可抑制)。问哪一种更"强":SiLU更强(可放大至2.86倍),sigmoid更温和(最大衰减到0)。选择反映两种注意力的不同特性。

延伸阅读:主报告 CH2.12(门控机制对比);Qwen3_5MoeAttention.__init__ 中q_proj双倍输出和forward中torch.chunk分离逻辑。


Q2.13 为什么head_dim=256而非更常见的128?

简短回答:更大的head_dim提供了更丰富的每头表示空间,对GQA(仅2个KV头)尤其重要——每个KV头需要承载更多信息。配合partial_rotary_factor=0.25(64维RoPE+192维无位置编码),实现了有效的维度分工。

详细解释:head_dim=256意味着每个注意力头的表示维度是256(GPT-3和Llama 3 70B使用128)。这个选择的背后逻辑是:在GQA 16Q/2KV的配置下,每个KV头需要服务于8个Q头,承载的信息量远大于标准MHA中的KV头。更大的head_dim确保KV头有足够的容量存储和检索信息。

从信息论角度:2 个 KV 头 × 256 维 = 512 维的总 KV 表示。如果 head_dim=128,则总 KV 仅 256 维——可能不足以编码 262K 上下文中的丰富信息。256 维的 head_dim 最直接的好处是:每个 KV 头可以编码更丰富的位置-内容组合模式。

代价:QK 点积的方差 = head_dim = 256(而非 128),softmax 的输入分布更宽,可能导致注意力更「尖锐」(趋向 one-hot)。QK Norm 对此起到了关键的缓解作用——归一化后 QK 方差固定为 1/d,不受 head_dim 影响。所以 head_dim=256 + QK Norm 是一个配套设计。head_dim=256意味着每个注意力头的表示维度是256(GPT-3和Llama 3 70B使用128)。这个选择的背后逻辑是:在GQA 16Q/2KV的配置下,每个KV头需要服务于8个Q头,承载的信息量远大于标准MHA中的KV头。更大的head_dim确保KV头有足够的容量存储和检索信息。

partial_rotary_factor=0.25进一步优化了维度利用——仅64维参与RoPE(提供位置感知),192维不参与(提供内容匹配)。这种分工使得注意力可以同时捕捉位置关系(“前后的token”)和语义关系(“相似的token”),而不会让位置信息过度主导注意力分布。GPT-NeoX和Llama使用全维度RoPE,Qwen3.5的25%部分RoPE是更精细的设计。

面试要点:head_dim=256是Qwen3.5的一个"非标准"选择。如果被问为什么,核心回答:GQA极端比例(8:1)+少KV头(2)+需要大容量KV头→head_dim增大。追问"为什么不用512":太大→QK点积方差增大→softmax趋近one-hot→梯度消失。

延伸阅读:主报告 CH2.13(注意力维度配置);config.json: head_dim=256, partial_rotary_factor=0.25


Q2.14 嵌入层的pad_token_id是如何处理的?

简短回答:pad_token_id在config.json中没有显式设置(为null),这意味着模型没有专门的padding token。nn.Embedding接收None作为pad_token_id,嵌入层不为pad token保留特殊处理,所有248320个token的嵌入均可训练。

详细解释:在HuggingFace Transformers中,nn.Embedding的pad_token_id参数仅用于标记哪个索引是padding——如果设置了,该索引对应的嵌入向量在梯度计算中会被置零。Qwen3.5不设置pad_token_id意味着训练框架(通常通过attention_mask和loss计算中的忽略索引)来处理padding,而非在嵌入层层面处理。

大多数现代LLM采用类似做法——不依赖嵌入层的pad_idx机制,而是使用更灵活的attention_mask+ignore_index方案。Qwen3.5的eos_token_id=248044位于词汇表末尾附近(248320个token中的第248044个),这与GPT-2将eos设为50257的模式不同——Qwen3.5的特殊token集中在词汇表末尾的高索引区域。

易混淆:pad_token_id=null不等于"没有padding机制",只是padding不在嵌入层处理。实际的padding处理通过DataCollator(设置attention_mask)和loss函数(ignore_index)实现。

延伸阅读:主报告 CH2.14(嵌入层配置);Qwen3_5MoeTextModel.__init__: self.embed_tokens


Q2.15 initializer_range=0.02是小还是大?

简短回答:0.02是Transformer模型的标准初始化范围(GPT-2开始使用),对于2048的hidden_size来说恰到好处——确保初始权重足够小以防止梯度爆炸,但又足够大以产生非平凡的初始表示。

详细解释:在N(0, 0.02^2)的初始化下,2048维向量的L2范数期望约为sqrt(2048)×0.02≈0.91,接近单位范数。这恰好适合RMSNorm的输入要求——归一化后期望方差约为1,RMSNorm几乎不做调整就能产生合理的输出分布。如果initializer_range太小(如0.001),初始信号太弱,训练前期会浪费在"放大"上;如果太大(如0.1),可能导致初始激活值过大,触发梯度问题。

GPT-2、BERT、Llama都使用0.02,这在实践中被证明是一个稳健的选择。Qwen3.5沿用了这一标准值,但通过_init_weights对特定模块做了定制初始化——GDN的A_log(log(U(0,16)))、dt_bias(初始化为1)、RMSNorm的weight(初始化为0实现1+0=1的恒等映射)。

面试要点:0.02是"标准答案",但更重要的是理解定制初始化的部分——A_log、dt_bias和RMSNorm weight的特殊初始化对GDN的稳定性至关重要,这表明标准初始化不够,需要针对架构特性定制。

延伸阅读:主报告 CH2.15(初始化策略);Qwen3_5MoePreTrainedModel._init_weights(line 912-928)


Q2.16 base_model_tp_plan中的"moe_tp_experts"策略是什么?

简短回答:moe_tp_experts是一种专家并行+张量并行的组合策略——将256个专家分布在多个设备上,每个设备持有部分专家的完整权重(而非每个专家被切分),减少了专家间的通信开销。

详细解释:标准张量并行(TP)按列/行切分每个矩阵,每层都需要频繁的all-reduce通信。而moe_tp_experts策略不同:在8路TP下,每个设备持有32个完整专家(256/8)。当token需要被某些专家处理时,token被发送到持有该专家的设备上进行完整的前向计算,仅最终输出需要all-reduce汇聚。这大幅减少了通信次数和通信量。

这种策略特别适合推理场景——每个设备的专家子集可以被视为独立的"专家池",token路由相当于在设备间做轻量的all-to-all通信。Qwen3.5在配置中定义了精细的TP plan:"layers.*.mlp.experts": "moe_tp_experts"表明仅专家层使用此策略,其他模块使用标准TP。DeepSpeed-MoE和vLLM都支持类似的专家并行策略。

面试要点:区分"张量并行"(切分单个矩阵)和"专家并行"(分配完整专家)。moe_tp_experts是两者的混合——分布式是专家维度,但保留了完整专家以避免频繁通信。

延伸阅读:主报告 CH2.16(分布式策略);configuration_qwen3_5_moe.py:68 TP plan定义。


Q2.17 attention_bias=false的设计考虑?

简短回答:不使用bias可以节省参数,且现代Transformer研究表明bias对注意力质量影响极小。加上RMSNorm的存在(提供了足够的分布调节),bias的省略是合理且符合现代LLM实践的。

详细解释:对Full Attention层,省略QKV投影的bias每层节省约49K参数(16×256×3+2×256×3)×4 bytes。虽然单层节省微小,但在40层模型中累积可达约2M参数。更重要的是,bias在注意力机制中的作用是提供一个全局偏移,但在RMSNorm已经确保输入分布合理的情况下,这个偏移几乎没有实际价值。

PaLM、Llama、Mistral等现代LLM都使用attention_bias=false,这表明bias的作用在归一化充分的深层Transformer中已经被RMSNorm/LayerNorm取代。Qwen3.5在注意力、MoE专家和GDN投影中都不使用bias(bias=False统一设置),仅在视觉编码器的LayerNorm中保留了bias(遵循ViT的传统设计)。

面试要点:attention_bias=false是现代LLM的标准做法。如果被问"何时需要bias":当没有归一化层时(如某些小型网络),bias提供必要的偏移自由度;当有RMSNorm时,bias被归一化层的可学习参数取代。

延伸阅读:主报告 CH2.17(配置选项分析);config.json: attention_bias=false


Q2.18 32K位置上限(config默认)vs 262K(运行配置)的矛盾?

简短回答Qwen3_5MoeTextConfig类中max_position_embeddings=32768是代码默认值(安全值),但实际模型config.json中设置为262144。Qwen团队通过RoPE扩展(theta从10000提升到10M)将模型上下文从32K扩展到262K。

详细解释:配置类的默认值32768是一个"保守"选择——它确保即使config.json中没有显式设置max_position_embeddings,模型也能正常工作(不会尝试分配超大位置嵌入表)。实际部署时,config.json覆盖了默认值为262144。这种"代码默认值<实际使用值"的模式在HuggingFace模型中很常见。

RoPE theta从10000提升到10000000是关键——标准theta=10000的理论可区分位置范围约4K-8K,theta=10M时扩展到约262K。扩展因子约1000倍,这与max_position_embeddings从32K到262K(约8倍)不完全对应,因为Qwen团队可能使用了NTK-aware scaling等非线性扩展方法,使得高频维度(区分近处位置)和低频维度(区分远处位置)有不同的扩展比例。

面试要点:这是一个经典的"代码vs配置"差异问题。理解:代码默认值是安全的"下限",config.json是实际的"运行值"。RoPE theta不是线性扩展的——NTK-aware scaling对不同频率维度使用不同的扩展因子。

延伸阅读:主报告 CH2.18(上下文扩展技术);configuration_qwen3_5_moe.py:85 默认值;config.json: max_position_embeddings=262144, rope_theta=10000000


Q2.19 Qwen3_5MoeRMSNorm使用(1+weight)而非weight的数学原因?

简短回答:(1+weight)且weight初始化为0,使归一化层在训练开始时为恒等映射(乘以1),提供平滑的训练起点。这与weight初始化为1的weight*x形式在数学上等价,但(1+w)形式更明确地表达了"以1为中心的微调"语义。

详细解释:两种写法的数学对比——标准Llama RMSNorm:output = x * weight,weight初始化为1→恒等。Qwen3.5 RMSNorm:output = x * (1 + weight),weight初始化为0→恒等。两者初始行为完全相同,但参数化方式不同。Qwen3.5的选择有两个优点:(1)weight=0作为起点更自然(“零表示无修改”);(2)训练后weight的范围通常在[-0.1, 0.1],更直观地表示"与1的微小偏差"。

这种设计与residual learning的理念一致——学习"残差"或"偏差"而非"绝对值"。在float32计算中,output = output * (1.0 + self.weight.float())将weight转为float32再与1相加,确保精度(BF16下1.0+0.001≈1.001可能有舍入误差,FP32下精确)。

易混淆:不要认为(1+weight)和weight在训练中有不同行为——它们完全等价(只是weight的参数空间平移了1)。区别仅在于代码可读性和初始化语义。

延伸阅读:主报告 CH2.19(归一化实现细节);Qwen3_5MoeRMSNorm.forward


Q2.20 Qwen3.5-MoE与Qwen3-MoE在架构上的主要区别?

简短回答:五大区别:(1)Qwen3.5引入Gated DeltaNet替代部分注意力层,Qwen3全部使用标准GQA;(2)Qwen3.5所有层使用MoE,Qwen3仅在部分层使用;(3)Qwen3.5支持262K上下文vs Qwen3的32K/128K;(4)Qwen3.5内置视觉编码器实现原生多模态;(5)Qwen3.5增加了MTP训练模块。

详细解释:从参数规模看,Qwen3-30B-A3B(30B/3B)到Qwen3.5-35B-A3B(35B/3B),总参数增加了5B但激活参数几乎不变(3B),这是因为新增参数主要在两方面:一是专家数从128翻倍到256(每个token仍选8个,激活量不变但专家池更大),二是视觉编码器约0.5B。在相近激活参数下,Qwen3.5通过GDN获得了更高效的推理(长序列下加速可达数百倍)。

GDN的引入是最本质的变化——它改变了模型的计算复杂度特征。Qwen3在长序列时需要为所有层计算O(n^2)或维护KV缓存,Qwen3.5仅在25%的层需要。这使得Qwen3.5的推理效率在序列长度增长时保持稳定,而Qwen3会迅速退化。再加上262K上下文和原生多模态,Qwen3.5的适用范围远大于Qwen3。

面试要点:如果只记一个区别:Qwen3.5=Qwen3+GDN+全MoE+多模态+MTP。专家数128→256是容量提升,GDN才是最核心的架构创新。激活参数3B→3B(不变)说明激活效率提升全部来自架构改进而非增加激活计算。

延伸阅读:主报告 CH1(Qwen全景演进对比表);Qwen3 vs Qwen3.5 config对比。


Q2.21 为什么Full Attention和GDN都使用输出门控?

简短回答:两种注意力都使用门控是因为它们都需要动态调节信息流,但门控方式反映了不同的需求——Full Attention使用sigmoid门控(0-1衰减,因为softmax已经提供了足够的权重分配),GDN使用SiLU门控(可放大可抑制,因为线性注意力缺少softmax的归一化,需要更强的输出调节)。

详细解释:Full Attention的sigmoid门控温和——输出始终在(0,1)范围内被衰减,但不会超过原始值。这适合softmax注意力,因为softmax已经对各token的贡献做了精确的权重分配,门控只需在整体层面决定"信任多少"。GDN的SiLU门控更强——它可以将输出放大到接近线性(SiLU(3)≈2.86)或抑制到接近0(SiLU(-3)≈-0.14)。这是因为线性注意力的Delta Rule输出可能被过度压缩或需要强调,SiLU提供了必要的动态范围。

两者的共同点是都依赖于输入——门控值从当前token的表示中计算得出。这实现了上下文相关的信息门控:对于简单的、可预测的token(如常见词),门控可能衰减注意力输出(因为残差流中的FFN输出已经足够);对于复杂的、需要上下文的token,门控可能放大注意力输出。

面试要点:两种门控的差异是一个"设计哲学"问题:Full Attn+sigmoid=“信任但验证”(softmax已做好权重分配,门控做最终衰减),GDN+SiLU=“动态调节”(线性注意力输出需要更积极的门控干预)。

延伸阅读:主报告 CH2.20(门控机制全景对比);Qwen3_5MoeRMSNormGated: F.silu(gate)Qwen3_5MoeAttention: torch.sigmoid(gate)


Q2.22 Qwen3.5-MoE在分布式部署时的核心架构考量是什么?

简短回答:核心考量是混合并行策略的设计——MoE专家层使用专家并行(moe_tp_experts)减少通信,GDN层因状态矩阵较小而更适合数据并行或张量并行,Full Attention层因KV缓存较大而需要张量并行切分KV头。统一的base_model_tp_plan为每类层指定了最优的并行策略。

详细解释:三种层类型的并行需求差异很大:MoE层(40层)的通信瓶颈在all-to-all(token路由到专家),专家并行使每个设备持有完整专家子集,仅需在路由和结果汇聚时通信;GDN层(30层)的通信需求最小(状态矩阵仅524K元素,可用数据并行),但in_proj矩阵(2048×8192)较大,可能需要张量并行切分;Full Attention层(10层)的KV缓存在长序列时很大(262K×512维/层),需要在KV头维度上做张量并行切分。

Qwen3.5通过tp_plan的层级粒度配置解决这个问题——不同层可以有不同的并行策略,而无需修改模型代码。例如可以将MoE层配置为专家并行(8路)、Full Attention层配置为张量并行(4路),在通信和计算效率之间取得平衡。对于4-8 GPU的部署场景,推荐使用专家并行+张量并行组合,单GPU则依赖GGUF量化+offloading。

面试要点:区分三种并行策略的适用场景——数据并行(GDN,状态小)、张量并行(Full Attn,KV缓存大)、专家并行(MoE,专家多通信少)。实际部署通常是三者的组合(3D并行)。

延伸阅读:主报告 CH2.21(分布式部署策略);configuration_qwen3_5_moe.py:68-69 tp_plan定义。

CH3-4: Gated DeltaNet 与混合注意力层

Q3.1 Gated DeltaNet与标准softmax注意力的根本区别是什么?

简短回答:标准注意力计算所有token pair的相似度矩阵(O(n^2)),而Gated DeltaNet通过递归状态S_t将历史信息压缩为固定大小的矩阵(key_dim×value_dim),每次仅需O(1)的更新和查询。本质上是"记忆压缩"与"精确检索"的tradeoff。

详细解释:在标准softmax注意力中,每个query token需要与所有key token计算点积相似度,产生一个n×n的注意力矩阵,计算复杂度为O(n^2·d)。这对短序列(n<2048)可以接受,但在长序列(n=262K)时完全不可行。而Gated DeltaNet维护一个固定大小的状态矩阵S_t∈R^(32×128×128),通过递推公式S_t=S_{t-1}·exp(g_t)+k_t^T·delta_t将历史信息逐步编码到状态中。

这种压缩是有代价的:固定大小的状态矩阵意味着信息容量有限(524K个浮点数),无法像标准注意力那样精确检索任意历史位置的信息。但优点也是明显的——每次更新仅需O(d_k·d_v)=O(16,384)的FLOPs,与序列长度无关。GDN通过两个关键设计弥补信息压缩的损失:数据相关的衰减率beta(动态遗忘vs记忆)和非对称的KV头设计(32个V头捕获更丰富的value信息)。

面试要点:核心对比——Attention是"每次重新读一遍全文"(O(n^2)),GDN是"边读边记笔记"(O(1)更新+O(1)查询)。笔记容量有限(S_t矩阵),但动态门控(beta)决定记什么、忘什么。追问"何时GDN不如Attention":需要精确复制原文或检索遥远历史中特定信息时。

延伸阅读:主报告 CH3.1(GDN原理深度拆解);论文 DeltaNet §3(Delta Rule形式化定义);torch_chunk_gated_delta_rule(line 243) 分块实现。


Q3.2 为什么GDN使用16 Key heads / 32 Value heads的非对称设计?

简短回答:非对称设计允许Value维度是Key维度的2倍(4096 vs 2048),增加了状态矩阵S的信息容量。Key头用于检索(查询过去的key-value关联),更多的Value头意味着每个key维度可以访问更丰富的value表示,而Key投影参数无需翻倍。

详细解释:状态矩阵S_t的维度是(num_v_heads, head_k_dim, head_v_dim)=(32,128,128)。如果使用对称设计(32 Key + 32 Value),状态容量不变但Key投影参数将翻倍(32×128=4096 vs 16×128=2048),增加约8.4M参数。非对称设计通过将Key头和Value头解耦,在保持检索效率(Key维度较小,点积计算更快)的同时最大化状态容量(Value维度较大,存储更多信息)。

这种设计与信息检索中的"查询压缩"思想类似——查询(Key)只需足够区分不同信息即可,不需要承载所有信息内容;而存储的信息(Value)应该尽可能丰富。16个Key头以2048维进行点积查询,32个Value头以4096维存储信息,这是一个经过验证的高效配比。

面试要点:非对称=参数效率和信息容量的平衡。“为什么不是32:32”:Key投影参数翻倍但状态容量不变→浪费。“为什么不是8:64”:Key头太少→检索粒度太粗→难以精确查询历史信息。

延伸阅读:主报告 CH3.2(GDN维度设计分析);Qwen3_5MoeGatedDeltaNet.__init__: num_k_heads=16, num_v_heads=32


Q3.3 causal_conv1d在GDN中的作用是什么?

简短回答:Causal Conv1D(kernel=4)在QKV投影后、Delta Rule计算前应用,提供局部上下文聚合(每个位置可"看到"前3个位置的信息)。由于线性注意力没有显式的位置编码(无RoPE),卷积层是GDN唯一的位置感知机制。

详细解释:GDN缺少传统Transformer中的RoPE(旋转位置编码),因为Delta Rule的递归更新方式与RoPE的绝对位置旋转不兼容。卷积层弥补了这一缺陷——kernel_size=4的因果卷积使每个token的表示融合了前3个token的信息,提供了局部的序列顺序感知。这与Mamba在SSM前使用conv1d的设计思路完全一致。

Qwen3.5使用depthwise卷积(groups=conv_dim=8192)来控制参数量——仅32,768个参数(4×8192),而非标准卷积的268M参数(4×8192^2)。SiLU激活函数增加了非线性,使卷积不仅是线性平滑,还可以进行特征选择。对于需要精确位置关系的任务(如代码生成、数学推理),卷积提供的局部位置感知对GDN的表现至关重要。

面试要点:GDN中conv1d的定位——“线性注意力唯一的局部位置感知”。没有RoPE(因为与递归状态不兼容),所以依赖conv1d来区分"AB"和"BA"。kernel=4意味着仅3个前置token的局部上下文,更长距离的位置关系由Delta Rule的状态矩阵隐式捕捉。

延伸阅读:主报告 CH3.3(GDN组件分解);Mamba论文 §3.1(conv1d在SSM中的作用);Qwen3_5MoeGatedDeltaNet.forward 中causal_conv1d_fn调用。


Q3.4 beta=sigmoid(B)的作用?

简短回答:beta是数据相关的衰减率(data-dependent decay),控制Delta Rule中"遗忘"旧信息与"接受"新信息的平衡。beta_t接近1时模型更信任新信息(delta_t完整应用),接近0时模型保守(几乎不更新状态)。由输入通过线性投影+sigmoid动态计算。

详细解释:在Delta Rule更新公式delta=(v_t-S_{t-1}·k_t)·beta_t中,beta充当了"新颖性过滤器"——当新来的信息与已有记忆高度一致时(v_t≈S_{t-1}·k_t),delta本身很小,beta的影响有限;但当新信息与记忆矛盾或包含全新内容时,beta决定了模型多大程度上"相信"这个新信息并更新状态。

beta的输入相关性是GDN区别于固定衰减机制(如RetNet的指数衰减)的关键。同一个token序列中,不同的token可能有不同的beta值:对于信息量大的实词(如名词、动词),beta可能较大(需要记住);对于功能词(如"的"、“了”),beta可能较小(不需要更新记忆)。这种选择性记忆使GDN的状态矩阵能更高效地利用有限的信息容量。

易混淆:beta不是"遗忘门"(forget gate)。LSTM的遗忘门直接乘以前一时刻的记忆,beta乘的是"新信息"(delta)。beta→0意味着"我不信这个新信息"而非"我忘掉旧记忆"。旧记忆的衰减由exp(g_t)控制。

延伸阅读:主报告 CH3.4(GDN门控机制详解);Qwen3_5MoeGatedDeltaNet.forward: beta = b.sigmoid()torch_recurrent_gated_delta_rule: delta = (v_t - kv_mem) * beta_t


Q3.5 g_t=-exp(A_log)*softplus(A+dt_bias)的数学意义?

简短回答:g_t是时间步调节因子,控制递归状态的指数衰减速率。三个关键组件——exp(A_log)提供per-head的基础衰减率(A~U(0,16)),softplus(A+dt_bias)提供输入依赖的时间步调节(dt_bias初始化为1确保softplus在零点为正),负号确保g_t<0从而exp(g_t)∈(0,1)即衰减因子。

详细解释:逐组件分析:(1)A_log是32个可学习参数(每value head一个),初始化为log(U(0,16)),因此exp(A_log)在[0,16]范围内(实际排除0因为U(0,16)几乎不会正好采样到0)。exp(A_log)越大→|g_t|越大→衰减越快→“短记忆”。(2)softplus(A+dt_bias):A是与A_log对应的可学习偏置,dt_bias初始化为1使softplus(A·0+1)≈1.313为正,确保即使输入为0也有正向的时间步。(3)负号确保整个表达式为负,使得exp(g_t)始终在(0,1)区间内——这是稳定的指数衰减。

Mamba中也有类似的Δ参数(Δ=softplus(Δ_proj+Δ_bias)),但作用方式不同:Mamba的Δ同时影响状态更新和输出,而GDN的g_t仅控制遗忘速率(通过exp(g_t)乘以前一时刻的状态),信息的接受由beta控制。

面试要点:g_t的物理意义是"时间常数"——|g_t|越大→遗忘越快→有效记忆越短。per-head的A_log允许不同head有不同的记忆长度(有的head专注短期局部模式,有的专注长期全局模式),这与Mamba的设计哲学一致。

延伸阅读:主报告 CH3.5(GDN数学推导);Qwen3_5MoeGatedDeltaNet.__init__ 和forward中g_t计算。


Q3.6 GDN的递归状态S_t的维度是什么?如何初始化?

简短回答:S_t维度为(batch_size, num_value_heads, head_k_dim, head_v_dim)=(B, 32, 128, 128),初始化为全零矩阵。每次更新S_t=S_{t-1}·exp(g_t)+k_t^T·delta_t(矩阵乘法),输出y_t=S_t·q_t(向量乘法)。

详细解释:状态矩阵的每个元素都有明确的物理意义——S_t[i,j,k]表示第i个value head在第j个key维度和第k个value维度上的累积关联强度。零初始化意味着训练/推理开始时模型没有任何历史记忆——所有信息都从第一个token开始逐步积累。这是合理的,因为没有"先验记忆"可以假设。

每次更新的计算量:S_{t-1}·exp(g_t)是逐元素乘法(524K次乘法),k_t^T·delta_t涉及128×128=16,384次外积运算(每个value head),总共约524K FLOPs。输出查询y_t=S_t·q_t涉及32次128×128的矩阵-向量乘法,约524K FLOPs。两者合计约1M FLOPs,远小于标准注意力的O(n·d)(262K×2048≈537M FLOPs)。

易混淆:与Mamba的SSM状态做对比——Mamba状态维度(B, H, d_state, d_inner)通常为(B, 1, 16, 4096)=65K元素,GDN状态(B, 32, 128, 128)=524K元素。GDN状态更大(8倍),因为矩阵状态比向量状态有更强的信息存储能力。

延伸阅读:主报告 CH3.6(GDN状态空间分析);torch_recurrent_gated_delta_rule 中状态初始化和更新。


Q3.7 为什么GDN使用QK L2归一化?

简短回答:L2归一化防止Q和K的幅度在长序列递归中发散。在Delta Rule中,Q和K的大小直接影响状态更新的幅度——没有归一化时,经过数千步递归后状态矩阵可能爆炸或消失。L2归一化将Q和K约束在单位球上(||Q||_2≈1, ||K||_2≈1),确保更新稳定。

详细解释:全精度分析:如果没有归一化,Q和K的幅度可能随输入变化而波动(尤其在长序列中)。在Delta Rule更新S_t=S_{t-1}·exp(g_t)+k_t^T·delta_t中,k_t直接参与外积运算——如果||k_t||_2»1,更新幅度会被放大,长期累积导致状态矩阵发散;如果||k_t||_2«1,更新幅度过小,信息无法有效存入状态。L2归一化消除了这个风险。

与标准注意力的对比:标准注意力使用1/sqrt(d_k)缩放来防止QK点积过大,softmax进一步将注意力权重归一化到和为1。GDN没有softmax(因为是线性注意力),所以依赖更直接的方式——L2归一化——来控制数值范围。这是一个"没有softmax的线性注意力的必备稳定措施"。

面试要点:L2归一化在GDN中的作用≈softmax中的scaling在标准注意力中的作用。两者都解决"QK交互值过大导致梯度问题"。区别在于GDN需要同时约束Q和K(因为是递归累积),而标准注意力只需约束QK^T。

延伸阅读:主报告 CH3.7(数值稳定性分析);torch_chunk_gated_delta_rule: use_qk_l2norm_in_kernel=True


Q3.8 chunk_gated_delta_rule vs fused_recurrent_gated_delta_rule的区别?

简短回答:chunk版本用于prefill阶段(多个token一次性输入),将序列分成chunk_size=64的块进行并行扫描以利用GPU并行性;fused_recurrent版本用于decode阶段(逐token生成),执行O(1)的单步递归更新。选择逻辑是seq_len==1时用recurrent,否则用chunk。

详细解释:Prefill阶段的挑战是处理大量token(如用户输入了4096个token的prompt)。chunk版本将序列切成64个token的块,每个块内部可以并行计算Delta Rule(因为块内的跨token交互可以通过矩阵乘法高效完成),块之间通过传递状态矩阵S串行连接。这使得prefill的时间复杂度为O(n/chunk_size·d^2),而非朴素的O(n·d^2)或串行的O(n·d^2)。

Decode阶段每次只生成1个token,使用chunk没有意义(chunk_size>1意味着要等待多个token才能形成chunk)。recurrent版本直接使用递归公式S_t=S_{t-1}·exp(g_t)+k_t^T·delta_t,单步约524K FLOPs,极快。Qwen3.5通过if seq_len == 1的条件判断自动切换两种模式。这种prefill/decoding双路径设计在Flash Attention中也有对应(prefill用Flash Fwd,decode用Flash Decoding)。

面试要点:“Prefill用chunk(并行),Decode用recurrent(串行但O(1))“是理解GDN推理效率的关键。问"chunk_size为什么是64”:GPU并行效率和数值精度的平衡——类似于Flash Attention的block size选择。

延伸阅读:主报告 CH3.8(GDN双路径推理);Qwen3_5MoeGatedDeltaNet.forward 中seq_len判断逻辑。


Q3.9 GDN层的out_proj参数为什么是4096→2048?

简短回答:Value维度是32 heads×128=4096,需要投影回hidden_size=2048。这是GDN所有投影中最大的单个投影矩阵(4096×2048=8.4M参数),压缩比为2:1,表示GDN的value维度经过out_proj的信息压缩后进入残差流。

详细解释:GDN 的内部计算维度是 4096(16 K 头 × 128 维 + 32 V 头 × 128 维 = 2048 + 4096 = 6144… 实际上 in_proj 将 2048 映射到 8192 = 2048×4),而输出需要回到 hidden_size=2048 以匹配残差流。out_proj 的 4096→2048 表示将内部表示的维度压缩一半。

具体来说,GDN 的前向计算中:in_proj 将 (2048,) 投影到 (8192,)(Q/K/V 各 2048 + Z/B/A 各约数百维),经过 Delta Rule 计算后得到 (4096,) 的内部状态读取结果,out_proj 将其压缩回 (2048,)。这个 2:1 的压缩比意味着 GDN 在内部用一个更大的表示空间(4096 维)进行计算,让 Delta Rule 的状态更新和查询有更丰富的表达空间,但最终输出被压缩以控制残差流的维度增长。

这与 Transformer 中 FFN 的 expansion ratio(通常是 4× 或 8/3×)类似——内部放大、外部还原。GDN的内部表示维度(4096维的value空间)大于模型的hidden_size(2048维),这种"内部膨胀+输出压缩"的模式类似于Transformer FFN中的4x扩展+收缩。膨胀提供了更大的表示空间用于状态更新和门控计算,压缩确保输出与残差流维度一致。out_proj占GDN层总参数的8.4M/33.7M≈25%,是最大的单个组件。

对比Full Attention的o_proj也是4096→2048(16 heads×256=4096),两者输出维度一致。这种一致性很重要——因为残差连接要求每层的输出维度与输入维度相同(都是2048),所以无论内部表示的维度如何,最终都需要通过输出投影映射回2048。

面试要点:out_proj的维度(4096,2048)反映了一个通用模式——“内部表示维度>模型维度,通过输出投影压缩”。类比FFN的up_proj+down_proj。如果被问"为什么不是2048→2048”:那value维度只能有16个头(16×128=2048),减少一半的信息容量。

延伸阅读:主报告 CH3.9(GDN参数分解);Qwen3_5MoeGatedDeltaNet.__init__: self.out_proj = nn.Linear(self.value_dim, self.hidden_size)


Q3.10 GDN在长序列上的理论加速比是多少?

简短回答:对于序列长度n=262K,标准Attention单层FLOPs约5.62×10^14,GDN单层约1.23×10^12,理论加速约457倍。当n<2048时,由于Flash Attention的高度优化,标准注意力可能更快;但当n>2048时,GDN的线性优势开始显现,且随n增长差距指数级扩大。

详细解释:标准注意力的FLOPs主要由QK^T矩阵(4n^2·d)支配。当n=262K时,16 heads×262K^2×256 dimension=约5.62×10^14次操作。GDN的FLOPs主要由Delta Rule的内部矩阵乘法支配:n×(d_k·d_v·num_heads+d^2)=262K×(128·128·32+2048^2)=262K×4.7M≈1.23×10^12次操作。加速比=5.62×10^14/1.23×10^12≈457倍。

然而,这种理论加速在实践中受限于实现效率。Flash Attention使用高度优化的CUDA内核(tiling、recomputation),在短序列(n<2048)时实际速度可能比GDN的纯PyTorch实现更快。但Qwen3.5使用FLA库的融合内核(FusedRMSNormGatedchunk_gated_delta_rule),大幅缩小了实现效率差距。总体来说,n>4096时GDN开始明显快于标准注意力,n=262K时优势压倒性。

面试要点:457倍是理论值,实际取决于实现优化水平。关键判断:“n<2048→Full Attention可能更快(Flash Attention),n>4096→GDN更快,n>32K→GDN压倒性优势”。这就是为什么Qwen3.5保留了10层Full Attention——短序列时它们提供精确的注意力,长序列时GDN接管。

延伸阅读:主报告 CH3.10(GDN加速比分析);CH4.6(混合注意力的序列长度适应性)。


Q3.11 Full Attention为什么需要QK Norm?

简短回答:QK归一化(在Q和K上分别应用RMSNorm)防止attention logits在训练中发散。特别是当head_dim=256时,QK点积期望方差≈256,导致softmax输入过大、分布接近one-hot、梯度消失。RMSNorm将Q和K约束到单位方差范围,保持softmax分布平滑。

详细解释:设Q和K的每个元素是独立同分布的(均值为0,方差为1),则QK^T每个元素是256个乘积的和,期望方差≈256。softmax的输入为均值0、标准差≈16,导致softmax分布极度尖锐(最相关的token-pair几乎独占注意力权重)。RMSNorm将Q和K的每个head_dim维向量归一化到单位方差,使点积的期望方差≈1,softmax分布更加平滑,梯度流更健康。

QK Norm是Qwen3.5的相对保守的设计选择——Gemma 2也使用,但Llama不用(仅依赖RoPE和scaling)。Qwen3.5使用QK Norm的原因可能有两个:(1)head_dim=256比常见的128更大,点积方差问题更严重;(2)Full Attention层承担全局信息整合的关键任务,需要稳定可靠的注意力分布。

易混淆:QK Norm不是QKV投影的一部分,而是注意力计算中的一个额外归一化步骤。它的位置在Q/K投影之后、QK^T计算之前:q=q_norm(W_q·x), k=k_norm(W_k·x), attn=softmax(qk^T/scale)·v

延伸阅读:主报告 CH3.11(注意力稳定性分析);Qwen3_5MoeAttention.forward: query_states = self.q_norm(query_states); key_states = self.k_norm(key_states)


Q3.12 partial_rotary_factor=0.25的意义?

简短回答:仅25%的head_dim(64/256维)参与RoPE提供位置感知,其余75%(192维)不使用位置编码提供内容匹配。这种"部分RoPE"设计平衡了位置感知和内容语义——位置信息主要在前几个维度有效,过度的位置编码会干扰内容-内容的注意力匹配。

详细解释:RoPE通过在Q和K的某一对维度上施加旋转来编码位置信息。当所有维度都应用RoPE时(如Llama),每个维度的QK内积都包含了位置差异项,这可能导致两个语义相似但位置不同的token获得较低的注意力分数——因为位置旋转"干扰"了语义匹配。Partial RoPE通过将维度分为RoPE维度(64维)和pass维度(192维),实现了位置和内容的解耦:RoPE维度负责"这个token在哪里",pass维度负责"这个token是什么意思"。

0.25的比例是一个经验选择——文献表明位置信息主要在前几个频率维度有效,过多的RoPE维度提供冗余的位置信息而没有额外的区分能力。在Qwen3.5的GQA配置(仅2个KV头)下,pass维度的语义匹配能力尤为重要,因为每个KV头需要服务于8个Q头,语义信息必须足够丰富。

面试要点:Partial RoPE = “位置维度+内容维度的分工”。类比Multi-Head Attention——不同head可以专注于不同方面,Partial RoPE在单个head内部就做了这种分工。追问"为什么是0.25不是0.5":实验表明25%是最优帕累托点,更多RoPE维度→位置信息冗余,更少→位置区分不足。

延伸阅读:主报告 CH3.12(位置编码策略);Qwen3_5MoeTextRotaryEmbedding.compute_default_rope_parameters


Q3.13 Full Attention的输出门控sigmoid(gate)与GDN的SiLU(Z)有何不同?

简短回答:(1)sigmoid门控输出范围(0,1)仅做衰减,SiLU门控可放大可抑制(SiLU(3)≈2.86, SiLU(-3)≈-0.14);(2)sigmoid门控从Q投影中分离(共享参数),SiLU门控有独立投影in_proj_z;(3)两者反映不同的设计哲学——Full Attn的softmax已做好权重分配,只需温和的整体衰减;GDN的线性注意力需要更积极的门控调节。

详细解释:两种门控的数学特性决定了它们的能力边界。Sigmoid的输出始终在(0,1),意味着它只能降低注意力输出的幅度,不能放大。这在Full Attention中是合理的——softmax已经精确计算了每个token的注意力权重,输出门控只需回答"整体上信任这个注意力输出吗"这个二元问题。而SiLU可以放大到接近线性(SiLU(3)≈2.86),这在GDN中是必要的——线性注意力的Delta Rule输出可能在某些情况下偏小(因为状态压缩丢失了信息),SiLU门控可以补偿性地放大。

参数来源也不同:Full Attention的gate值从Q投影中"免费"获得(通过双倍输出维度+torch.chunk分离),不增加独立的投影矩阵。GDN的SiLU门控通过独立的in_proj_z矩阵(2048→4096,约8.4M参数)计算。这种参数分配差异反映了GDN对门控的更高要求。

面试要点:一句话区分——“Attention门控是’存疑则减’(只能衰减),GDN门控是’按需调节’(可放大可抑制)"。这与两种注意力的特性一致:softmax精确但昂贵→温和门控;线性注意力近似但高效→积极门控。

延伸阅读:主报告 CH3.13(门控机制对比);CH4(混合架构设计理念)。


Q3.14 为什么GDN的输入投影有5个而Full Attention只有3个?

简短回答:GDN需要更多信息来实现线性注意力的门控机制——QKV(注意力本身)、Z(输出门控)、B(数据相关衰减)、A(时间步调节)。Full Attention的softmax机制本身提供了位置间的权重分配,只需QKV+输出gate。GDN通过更多投影来弥补缺少的softmax归一化。

详细解释:五个投影的具体作用:QKV(与标准注意力相同,做key-value检索和query匹配)、Z(输出门控,SiLU调节输出幅度)、B(beta的门控输入,控制"信不信新信息”)、A(g_t的门控输入,控制"遗忘速率")。B和A的投影维度很小(2048→32),因为它们是per-head的标量调节,参数仅约65K每个。

Full Attention为什么只需要3个?因为softmax为每个token自动分配了与其他所有token的交互权重——这是一个精确的、归一化的、数据相关的信息路由机制。GDN没有这个机制(状态矩阵是累积的而非精确检索),因此需要额外的投影来引导信息流动:什么时候遗忘(A)、什么时候接受新信息(B)、输出如何调节(Z)。这五个投影共同构成了GDN的"门控系统",替代了softmax的功能。

面试要点:5个投影=没有free lunch。线性注意力的O(1)复杂度是用额外的门控机制换来的。B投影(65K参数)虽小但作用关键——它决定了"是否记住这个token",直接影响长序列的信息保留质量。

延伸阅读:主报告 CH3.14(GDN投影分解);Qwen3_5MoeGatedDeltaNet.__init__ 中5个in_proj矩阵定义。


Q3.15 GDN的recurrent_state和Full Attention的KV cache如何共存于推理缓存中?

简短回答DynamicCache同时管理两种状态——Full Attention层存储标准key_states和value_states,GDN层存储conv_state和recurrent_state。不同层类型访问不同的缓存条目,通过layer_idx区分。这是Qwen3.5独有的"混合缓存"设计。

详细解释:DynamicCache为每层维护一个缓存条目,条目中包含可选字段。对于Full Attention层(如layer 3, 7, 11…),条目包含标准的past_key_values((B, 2, seq_len, 256)的K和V张量)。对于GDN层(所有其他层),条目包含conv_states((B, 8192, 3),存储最近3步的卷积输入)和recurrent_states((B, 32, 128, 128),状态矩阵S_t)。

在推理循环中,每层的forward根据层类型调用不同的缓存更新方法:Full Attention调用cache_params.update(key, value, layer_idx),GDN调用cache_params.update_conv_state(...)cache_params.update_recurrent_state(...)。两者通过has_previous_state(layer_idx)检查是否存在缓存来区分prefill和decode阶段。这种统一缓存接口的设计使Qwen3.5无需在模型层面处理不同缓存类型的分支逻辑。

面试要点:混合缓存是Qwen3.5推理引擎需要支持的关键特性。“为什么vLLM需要专门适配Qwen3.5”——因为它需要同时管理KV缓存和SSM状态,标准的KV缓存管理器无法处理GDN的状态更新。

延伸阅读:主报告 CH3.15(缓存架构分析);Qwen3_5MoeGatedDeltaNet.forward 中缓存更新逻辑。


Q3.16 为什么GDN的Chunked Gated Delta Rule使用chunk_size=64?

简短回答:chunk_size=64是GPU并行效率和数值精度的平衡。更大的chunk意味着更多并行(chunk内O(chunk^2)操作可并行),但跨chunk的信息交互减少;更小的chunk意味着更精确的序列信息传递,但并行度降低。64与Flash Attention的block size(64-128)一致,是经验上的最优值。

详细解释:chunk内的Delta Rule计算需要chunk_size^2=4096次内部操作(每个chunk内的位置之间),这些操作可以通过矩阵乘法高效并行。chunk之间的串行连接是不可避免的——每个chunk需要前一个chunk的最终状态矩阵S来初始化自己的初始状态。chunk数=n/64,对于n=262K,约4096个chunk。

如果chunk_size=128,chunk数减半(2048),每个chunk内操作增至16,384次,整体理论上更快(因为减少了串行步骤),但信息精度可能受损——128个token被压缩为一个状态更新可能丢失细粒度的序列信息。如果chunk_size=32,精度更好但串行步骤翻倍(8192个chunk),可能由于CPU-GPU同步开销而变慢。64是Flash Attention社区验证过的稳健选择。

面试要点:chunk_size是线性注意力中"并行度vs精度"的tradeoff。类比Flash Attention的tiling——目的相同(充分利用GPU并行性),但GDN通过状态矩阵传递跨chunk信息(有损压缩),Flash Attention通过rescale传递(精确)。

延伸阅读:主报告 CH3.16(GDN分块策略);torch_chunk_gated_delta_rule: chunk_size=64


Q3.17 layer_types中"mlp_only"层在Qwen3.5-MoE-35B-A3B中为什么不存在?

简短回答:mlp_only_layers=[]表明Qwen团队认为不需要纯MLP层——GDN已经很快(O(1)复杂度),省略token mixing的收益不大,且mlp_only层会破坏残差流的连续性,可能导致表示质量的下降超过计算节省。

详细解释:mlp_only层的理论价值是节省token mixing的计算——如果某些层不需要跨token交互(例如处理已经充分混合的表示),可以跳过注意力/GDN。但在Qwen3.5中,3:1的GDN:Full Attention比例已经确保了每4层中有3层使用极快的GND,token mixing的总成本已经很低。添加mlp_only层仅能节省约5%的计算量(因为token mixer仅占激活参数的约5%),但代价是信息流动被中断。

从残差流的角度看,mlp_only层意味着该层的输入直接通过FFN处理,没有任何跨token的信息交换。如果这样的层被放置在不恰当的位置(例如在需要长距离依赖的深层),可能导致模型在该层"丢失"上下文信息。Qwen团队显然判断纯MLP层的风险大于收益。

面试要点:mlp_only层在Qwen3.5中为空是"有意识的设计决策"而非遗漏。为什么其他模型(如Qwen2)可能有:那些是Dense模型,token mixing更昂贵,省计算更有价值。Qwen3.5中GND已经足够便宜。

延伸阅读:主报告 CH3.17(层类型配置分析);config.json: mlp_only_layers=[]


Q3.18 Full Attention层在推理时的内存瓶颈是什么?

简短回答:在262K上下文下,10个Full Attention层的总KV缓存约5.37GB。但真正的瓶颈在prefill阶段:如果不用Flash Attention,QK^T矩阵需要(B, heads, n, n)=(1,16,262K,262K)×2 bytes≈2.2TB显存(超出任何单卡)。必须依赖Flash Attention的分块计算才能完成prefill。

详细解释:KV缓存5.37GB对于24GB GPU是可管理的(约占22%),但prefill时的峰值显存才是真正的挑战。Flash Attention通过在SRAM中分块计算注意力——不显式构造完整的n×n注意力矩阵,而是将Q和K分成小块,逐块计算softmax并累积结果——将峰值显存从O(n^2·d)降至O(n·d)。即使如此,262K token的prefill仍需要精巧的显存管理(如KV缓存的offloading、序列分段prefill等)。

这就是为什么GDN是必需的——如果没有75%的层使用线性注意力,即使有Flash Attention,262K上下文的prefill在消费级GPU上也不可行。30个GDN层的prefill不需要任何n×n矩阵,仅需分块传递状态矩阵,显存开销极小。10个Full Attention层的prefill在有Flash Attention的情况下勉强可行,是整个推理过程的内存瓶颈。

面试要点:“Full Attention的瓶颈不是KV缓存大小,而是prefill时的峰值显存”——这是一个常见误区。KV缓存5.4GB可管理,但O(n^2)的注意力矩阵2.2TB不可管理。Flash Attention是关键使能技术。

延伸阅读:主报告 CH3.18(推理内存分析);Flash Attention论文(Dao et al., 2022)。


Q3.19 GDN的前向传播包含几次矩阵乘法?与标准Attention对比如何?

简短回答:GDN单层前向约包含6次大矩阵乘法(5个in_proj+1个out_proj)加若干小矩阵操作(Delta Rule内部、conv1d、门控)。标准Attention包含4次投影+QK^T+AV(后两者在长序列时占主导)。短序列(n<512)时Full Attention可能更快(Flash Attention高度优化),长序列时GDN明显更快。

详细解释:GDN的大矩阵乘包括:in_proj_qkv(2048×8192)、in_proj_z(2048×4096)、in_proj_b(2048×32)、in_proj_a(2048×32)、out_proj(4096×2048),以及Delta Rule内部的chunk内并行计算(等价于几个中等大小的矩阵乘)。这些操作的FLOPs总计约1.23×10^12/262K≈4.7M/token(长序列分摊后)。Full Attention在n=262K时的FLOPs约为5.62×10^14/262K≈2.1G/token(长序列时QK^T主导)。

两者的交叉点取决于序列长度和硬件特性。在n<512时,Flash Attention的CUDA内核针对这种规模做了重度优化(融合Kernel、SRAM利用),实际速度可能超过GDN的纯PyTorch/FLA实现。n=512-4096时两者接近。n>4096时GDN的线性优势开始显现。Qwen3.5保留25%的Full Attention层正是因为这个交叉点的存在——短序列时Full Attention更快且更精确。

面试要点:不要简单地回答"GDN比Attention快"——这依赖于序列长度。“短序列Attention快,长序列GDN快"才是准确的。追问"交叉点在哪里”:约512-2048 token(取决于GPU型号和实现优化水平)。

延伸阅读:主报告 CH3.19(计算量对比);Qwen3_5MoeGatedDeltaNet.forward 完整代码路径。


Q3.20 为什么GDN的conv1d使用depthwise(groups=conv_dim)而非标准卷积?

简短回答:Depthwise卷积对每个通道独立做卷积,参数量从kernel_size×conv_dim^2降至kernel_size×conv_dim。conv_dim=8192时,标准卷积需268M参数(不合理),depthwise仅需32,768参数,使卷积的额外开销可以忽略不计。

详细解释:conv_dim=8192是QKV的三个投影拼接后的维度(2048+4096+2048=8192,但注意实际QKV的维度分配可能不同)。标准Conv1d(in=8192, out=8192, kernel=4)的权重矩阵大小为(8192, 4, 8192),参数量约268M——这几乎是GDN层参数(33.7M)的8倍,完全不合理。Depthwise Conv1d(groups=8192)的权重矩阵大小为(8192, 4, 1),仅32,768个参数。

Depthwise卷积的代价是通道间没有信息混合——每个通道只看到自己的历史值。这在GDN中是可接受的,因为通道混合已经由in_proj(全连接)和后续的Delta Rule矩阵运算完成。卷积层仅负责提供局部的时序平滑和位置感知,不需要跨通道交互。MobileNet和Mamba都使用depthwise卷积,验证了这一设计模式的有效性。

面试要点:groups=conv_dim的depthwise卷积=每个通道独立的时间平滑。两个关键数字:32,768参数(depthwise)vs 268M参数(standard)——差了8000倍。追问"为什么不用group=4":减小groups意味着更多通道混合,但Delta Rule后续自有通道混合能力。

延伸阅读:主报告 CH3.20(GDN卷积设计);Mamba论文 §3.1;Qwen3_5MoeGatedDeltaNet.__init__: self.conv1d = nn.Conv1d(..., groups=self.conv_dim)


Q3.21 如何直观理解Delta Rule中的"delta"?

简短回答:delta_t=(v_t-S_{t-1}·k_t)·beta_t计算的是"新信息的价值"。S_{t-1}·k_t是模型基于过去状态对当前key的预测value(自回归预测),v_t是实际的value,(v_t-预测)是预测误差(innovation),beta_t控制接受新信息的程度。这与Kalman Filter的innovation有完全相同的数学形式。

详细解释:DeltaNet可以理解为"带门控的神经Kalman Filter"。在Kalman Filter中,innovation=observation-prediction,Kalman gain控制多少innovation被纳入状态更新。DeltaNet中的对应:S_{t-1}·k_t是prediction(基于当前key从记忆中检索的预期value),v_t是observation(实际value),delta是innovation(预测误差),beta_t是gain(门控增益)。

这个视角解释了为什么Delta Rule能高效压缩信息:如果历史记忆已经能很好地预测当前信息(delta≈0),则状态几乎不更新——信息被"压缩"了(因为新信息与旧信息一致,不需要额外存储)。只有当新信息与记忆不符时(delta大),状态才显著更新——新信息被"纳入"。这种"只存差异"的策略使有限的状态容量被最有效地利用。

面试要点:Delta=innovation(Kalman Filter术语)。核心洞察:“预测对了就不更新,预测错了才更新”——这是高效信息压缩的本质。追问"压缩极限":状态矩阵的容量(524K个float)决定了最多能存储多少"差异"。

延伸阅读:主报告 CH3.21(Delta Rule数学直觉);DeltaNet论文 §2-3;Kalman Filter经典文献。


Q3.22 为什么full_attention_interval=4而非2或8?

简短回答:interval=4产生3:1的GDN:Full Attention比例,在Qwen3-Next实验中被证明是最佳帕累托点。interval=2(1:1)失去线性注意力大部分效率优势(50%层需KV缓存),interval=8(7:1)导致长距离依赖建模不足(仅12.5%层有全局注意力)。4在效率与精度间取得最优平衡。

详细解释:具体计算:interval=2→20个Full Attention层,KV缓存翻倍至10.7GB,GDN的效率优势大打折扣;interval=8→5个Full Attention层,KV缓存减半至2.7GB,但全局注意力的"刷新"频率太低——信息在GDN层中经过7次压缩后才被Full Attention层全局检验,累积误差可能无法及时纠正。interval=4的3:1比例确保每4层中有1层执行精确的全局注意力,及时"纠正"GDN的有损压缩。

从注意力模式的角度,每4层一个Full Attention层意味着模型有10次机会进行全局信息整合——这10个Full Attention层可能在不同的抽象层次上执行不同的全局操作(浅层关注句法整合,中间层关注语义关联,深层关注推理链)。实验表明这个频率足够高,模型不会因为GND的信息压缩而丢失关键的长期依赖。

面试要点:“3:1是经过实验验证的最优比例”——这是Qwen3-Next的主要实验发现之一。不同规模的模型可能需要不同的比例(小模型可能需要更多的Full Attention)。追问"如何验证":通过语言建模loss、长上下文perplexity和下游任务性能的综合评估。

延伸阅读:主报告 CH3.22(混合比例实验分析);configuration_qwen3_5_moe.py:115-119 interval自动生成逻辑。


Q3.23 GDN中的A_log参数初始化和作用?

简短回答:A_log是32个可学习参数(每value head一个),初始化为log(U(0,16)),在forward中通过-exp(A_log)转换为负值(保证g_t<0)。A_log控制递归衰减的基础速率——exp(A_log)大意味着遗忘快(短记忆),小意味着遗忘慢(长记忆)。不同head可以有不同的记忆长度。

详细解释:初始化策略的设计逻辑:A~U(0,16)随机采样,然后A_log=log(A)作为可学习参数。这意味着初始exp(A_log)∈(0,16](U(0,16)几乎不会采样到正好0),覆盖了从极快速遗忘(exp≈16,有效记忆约1/16个时间步)到极慢速遗忘(exp≈0,有效记忆几乎无限)的广泛范围。通过训练,不同head的A_log会分化为不同值,实现记忆长度的专业化——有的head成为"短期记忆"(捕捉局部语法模式),有的成为"长期记忆"(跟踪文档级主题)。

与Mamba的对比:Mamba的A参数也是控制SSM记忆长度的,但Mamba的A是向量(per维度),GDN的A是标量(per head)。GDN的per-head粒度更粗但参数更少(32个标量),每个head内的所有128维共享相同的衰减率。这种设计假设同一head内的不同维度处理相似时间尺度的信息,在实践中被证明是合理的。

易混淆:A_log不是衰减率本身,而是衰减率的对数。g_t=-exp(A_log)×softplus(A+dt_bias)——exp(A_log)在其中充当基础衰减率的"缩放因子"。A_log=0→exp(0)=1→基线衰减;A_log=log(16)≈2.77→exp(2.77)=16→快速衰减。

延伸阅读:主报告 CH3.23(GDN参数初始化);_init_weights: A = torch.empty(32).uniform_(0, 16); module.A_log = log(A)


Q3.24 GDN层的参数量为什么比Full Attention层大(33.7M vs 27.3M)?

简短回答:主要因为三个因素:(1)GDN的value_dim=4096是key_dim=2048的2倍,而Full Attention的KV维度较小(2 heads×256=512);(2)GDN有5个输入投影vs Full Attention的3个;(3)GDN的out_proj(4096→2048)与Full Attention的o_proj(4096→2048)相同但前者占更大比例。额外参数换来的是长序列O(1)的推理复杂度。

详细解释:参数差距的详细分解——GDN的额外参数来自:Z投影(in_proj_z: 2048→4096, 8.4M)+B投影(in_proj_b: 2048→32, 65K)+A投影(in_proj_a: 2048→32, 65K)+Conv1D(33K)+Gated RMSNorm(4K)≈8.6M。而Full Attention的QKV投影中Q占16.8M(16×256×4096/2),KV仅各1.05M(2×256×4096/2),总计18.9M。GDN的QKV投影(in_proj_qkv: 2048→8192, 16.8M)与Full Attention的Q投影等价但合并为一个矩阵。

尽管GDN层参数更多(23% more),但在长序列时计算量远小于Full Attention(457倍加速比)。这种"参数增加换取计算减少"的设计模式在深度学习中是常见的——参数可以预加载和复用,而计算必须在推理时执行。多出的8.6M参数为长上下文推理提供了关键的性能支撑。

面试要点:参数更多但计算更少——这是GDN的核心特质。追问"什么时候参数更少的设计更好":短序列、高吞吐场景下,Full Attention的少参数+Flash Attention优化可能更优。

延伸阅读:主报告 CH3.24(参数-计算tradeoff分析);GDN vs Full Attention参数分解对比表。


Q3.25 如何理解GDN的门控输出"RMSNorm(x)*SiLU(Z)"?

简短回答:这个公式做了两件事——RMSNorm归一化Delta Rule的输出(防止幅度累积),SiLU(Z)提供输入相关的缩放因子(可以放大或压缩)。两者结合实现了可靠的输出调节:归一化确保基本稳定性,门控提供动态适应。

详细解释:Delta Rule的输出可能因为递归累积而具有不同的幅度——某些token的历史信息丰富导致输出大,某些token信息稀疏导致输出小。RMSNorm将输出归一化到统一尺度(RMS=1),消除了幅度差异。但统一归一化可能过度压缩有用的幅度信息(例如"重要"token本应有更大的输出),SiLU(Z)门控弥补了这一点——它根据输入Z动态地缩放归一化后的输出,恢复了对重要性的表达能力。

SiLU激活函数的非单调性在此处特别有用:当Z»0时SiLU(Z)≈Z(线性放大,保留幅度信息),当Z«0时SiLU(Z)≈0(门控关闭,抑制输出),当Z≈0时SiLU(Z)≈0.5Z(过渡区)。这种特性比ReLU(硬阈值)更平滑,比sigmoid(只能衰减)更灵活。GDN的Gated RMSNorm可以视为GLU(Gated Linear Unit)在归一化层的应用。

面试要点:“先归一化再门控"的顺序很重要——先归一化消除尺度差异(稳定),再门控恢复重要性(灵活)。如果反过来(先门控再归一化),归一化会抹平门控的信息,失去动态调节效果。

延伸阅读:主报告 CH3.25(门控RMSNorm分析);Qwen3_5MoeRMSNormGated.forward


Q3.26 Qwen3.5如何通过layer_types支持架构搜索?

简短回答:layer_types是Python list[str],可在配置中直接指定任意层类型排列。__post_init__仅当未指定时根据interval自动生成。研究人员可以实验不同配置(如2:1、5:1、非均匀分布)而无需修改模型代码,降低了架构搜索的门槛。

详细解释:架构搜索的典型用法:研究者可以创建一个自定义配置Qwen3_5MoeTextConfig(layer_types=["linear_attention"]*30+["full_attention"]*10)得到前30层GDN+后10层Full Attention的"前密后疏"模式,或反过来得到"前疏后密"模式。也可以创建非均匀分布如每8层插入一个Full Attention检查点。所有这些都不需要修改Qwen3_5MoeDecoderLayer的代码,因为层的初始化仅在__init__中根据layer_types[layer_idx]做if-else判断。

Qwen3_5MoeDecoderLayer.__init__中的条件逻辑:if layer_type == "full_attention": self.self_attn = Qwen3_5MoeAttention(...)elif layer_type == "linear_attention": self.self_attn = Qwen3_5MoeGatedDeltaNet(...)else: self.self_attn = None(mlp_only)。forward中同样根据self_attn是否为None决定是否调用。这种"配置驱动"的设计比DeepSeek V3的YAML配置更Pythonic,比硬编码模式更灵活。

面试要点:layer_types是Qwen3.5源码中"配置驱动架构"的最佳范例。架构搜索的用武之地:不同任务可能需要不同的混合比例(代码任务需要更多Full Attention做精确复制,长文档任务需要更多GDN做高效压缩)。

延伸阅读:主报告 CH3.26(架构灵活性分析);configuration_qwen3_5_moe.py:112-119Qwen3_5MoeDecoderLayer.__init__ 条件初始化。


Q3.27 GDN在decode阶段的计算需要哪些缓存?

简短回答:Decode阶段需要两个缓存——conv_state(保存最近3个时间步的混合QKV,用于卷积计算)和recurrent_state(保存状态矩阵S_t,用于Delta Rule递归更新)。两者都在每次decode步骤后原地更新(in-place),总约2MB/layer。

详细解释:conv_state维度为(B, conv_dim, kernel_size-1)=(B, 8192, 3)——存储最近3步的卷积输入(kernel_size=4,需要前面3步的状态来计算当前步的因果卷积输出)。每次decode时,当前token的QKV经过in_proj后与conv_state拼接,形成完整的因果窗口,卷积输出后再更新conv_state(用当前值覆盖最旧的值,实现滑动窗口)。

recurrent_state维度为(B, 32, 128, 128)存储状态矩阵S_t,每次decode用Delta Rule公式更新。相较于prefill阶段的chunk并行计算,decode阶段的递归更新简单得多——仅需1次外积(k_t^T·delta_t)+1次逐元素乘法(S_{t-1}·exp(g_t)),约524K FLOPs。对比Full Attention的decode(需要为所有262K历史计算注意力),GDN的decode速度快约1000倍。

面试要点:GDN的decode极端高效——O(1)复杂度,约524K FLOPs/层。但需要维护两个缓存状态,意味着推理引擎需要管理不同于标准KV缓存的"SSM状态”。

延伸阅读:主报告 CH3.27(推理缓存详解);Qwen3_5MoeGatedDeltaNet.forward 中cache_params.update_conv_state和update_recurrent_state。


Q3.28 如何验证GDN训练时的数值稳定性措施是否充分?

简短回答:三个关键措施——QK L2归一化约束输入幅度、A_log均匀初始化确保初始衰减在合理范围、状态矩阵使用float32精度防止下溢。可以通过监控训练日志中的梯度范数、Loss spike和perplexity趋势来验证。Qwen3.5代码中mamba_ssm_dtype=float32显示了对精度的高度重视。

详细解释:QK L2归一化解决的是前向传播中的数值范围问题——确保Q和K的幅度在长序列递归中不发散。A_log初始化解决的是训练初期的不稳定性——如果初始衰减过大(exp(A_log)»10),状态会在几步内归零;如果过小(exp(A_log)«0.01),状态会累积过多噪声。U(0,16)的均匀分布确保初始衰减范围覆盖了从极快到极慢的广泛谱系,训练会自适应地调整。

float32状态精度是另一个关键保障——BF16的尾数仅7位(vs FP32的23位),在数千步递归更新中,BF16的舍入误差会累积。mamba_ssm_dtype=float32确保状态累积和更新的精度足够高。这带来了额外的显存开销(float32状态 vs BF16状态翻倍),但Qwen团队判断这是必要的。Mamba也使用float32状态,表明这是SSM/线性注意力模型的普遍设计准则。

面试要点:训练不稳定的三大来源——输入幅度发散(QK L2 norm解决)、衰减速率不当(A_log初始化解决)、累积舍入误差(FP32状态解决)。追问"如何检测问题":梯度范数突然跃升(gradient spike)是最直接的信号。

延伸阅读:主报告 CH3.28(数值稳定性分析);config.json: mamba_ssm_dtype=float32

CH5-7: MoE 路由 + 训练体系 + 支撑项

Q5.1 为什么选择256个专家而非128或512?

简短回答:256个专家是参数效率和计算开销的平衡点。128个专家时每专家被选中概率更高(8/128=6.25%),专家专业化可能不足;512个专家时路由计算和专家存储开销翻倍。256也与DeepSeek V3的选择一致,表明这是MoE规模的"甜区"。

详细解释:从参数角度看,每个专家的参数量约为3.15M(SwiGLU: gate(2048×512)+up(2048×512)+down(512×2048))。128个专家时每层专家参数403M,总MoE参数约16.1B,总模型参数约19B;256个专家时每层806M,总MoE参数约32.2B,总模型约35B;512个专家时每层1.61B,总参数约67B。Qwen团队显然以35B为目标规模,256个专家是实现这一目标的最佳选择。

从路由角度看,top-k=8的选择意味着每个token接触8/256=3.1%的专家。如果只有128个专家(接触6.25%),每个专家的"责任范围"更大,专业化程度可能不够精细。如果有512个专家(接触1.56%),路由需要更精准的分配能力,且很多专家可能很少被使用(“专家死亡"问题)。256在多样性和利用率之间取得了良好平衡。

面试要点:256不是随意选的——它是参数预算(35B目标)、专家利用率(top-8/256=3.1%)和路由质量(足够专家实现专业化)三者交叉验证的结果。追问"为什么和DeepSeek V3一样”:收敛进化——两者独立到达了相同的工程最优解。

延伸阅读:主报告 CH5.1(MoE规模分析);config.json: num_experts=256


Q5.2 每个专家的SwiGLU中间维度512是如何确定的?

简短回答:512=2048/4,是SwiGLU FFN的标准压缩比(1:4后又压缩为1:0.25)。如果使用标准FFN的4x扩展(8192),每个专家将有约50M参数,256个专家将导致约512B的总参数——完全不可行。512在保持专家多样性的同时严格控制了模型大小。

详细解释:SwiGLU的三个矩阵:gate(2048→512)、up(2048→512)、down(512→2048)。参数量=2048×512×2+512×2048=2.1M+1.05M=3.15M/专家。512的选择源于两方面的考量:一是总参数预算——256×3.15M×40=32.2B的专家参数符合35B的总规模目标;二是专家表达能力——512维的中间表示对于单个专家处理特定类型的token已经足够。

与其他MoE模型对比:Mixtral 8x7B的专家中间维度为4096(hidden=4096, 1:1扩展),每个专家参数约50M;DeepSeek V3的专家中间维度为2048(hidden=7168, ~1:0.29)。Qwen3.5的512/2048=0.25是最小的比例——这意味着Qwen3.5的每个专家比DeepSeek V3的专家更"轻量",但通过更多的专家数(256 vs 256+1)来弥补。这是一种"小而多"的专家设计哲学。

面试要点:512=2048/4这个除法不是巧合——它是SwiGLU结构的内在约束(gate和up各占一半中间维度,down匹配输出维度)。标准4x FFN的中间维度是8192=4×2048,SwiGLU的等效中间维度是2×512=1024=2048/2。

延伸阅读:主报告 CH5.2(专家参数计算);config.json: moe_intermediate_size=512


Q5.3 共享专家与路由专家如何协同工作?

简短回答:共享专家处理所有token(无条件),捕获通用语言知识(如语法、常见搭配)。8个路由专家处理特定token子集,学习专业化知识(如领域术语、特定语言模式)。最终输出是两者的加权和:routed(weighted_sum)+sigmoid(gate)×shared,sigmoid门控允许模型动态选择是否使用共享知识。

详细解释:共享专家的设计动机是"负载均衡的底薪保障"——即使路由专家分配不均(某些token找不到"擅长"的专家),共享专家确保每个token至少获得通用的FFN处理。sigmoid(gate)为每个token产生一个标量权重——当token需要通用知识时gate大(接近1),当token有明确的专家匹配时gate小(接近0),让路由专家的输出占主导。

共享专家在Qwen3.5中的参数与路由专家完全相同(也是SwiGLU, 512中间维度, 3.15M参数),但由于每层只有1个共享专家(vs 256个路由专家),共享专家的总参数占比极小(约0.5%)。共享专家的门控gate通过shared_expert_gate线性投影(2048→1)动态计算,每个token独立决定对共享专家的依赖程度。

面试要点:共享专家=“通用知识兜底”+“负载均衡辅助”。类比:路由专家是专科医生(每人擅长特定领域),共享专家是全科医生(处理所有基础问题)。DeepSeek V2/V3也使用共享专家,Mixtral不使用——有共享专家的MoE在实践中更稳定。

延伸阅读:主报告 CH5.3(共享专家机制);Qwen3_5MoeSparseMoeBlock.forward: shared_expert_output = F.sigmoid(self.shared_expert_gate(x)) * shared_expert_output


Q5.4 路由权重为什么要除以sum(topk_values)?

简短回答:归一化确保8个专家输出的加权和为1(按概率加权),防止某些token的输出被不成比例地放大。没有归一化时,不同token的专家输出可能在差异很大的尺度上,导致训练不稳定。

详细解释:topk_values是从router_probs(softmax后256维概率向量)中取出的top-8个概率值。由于softmax的性质,不同token的top-8概率总和差异可能很大——对于"明确"的token(如常见词),概率集中在少数专家上,top-8和接近1;对于"模糊"的token(如歧义词),概率均匀分布,top-8和可能仅0.1-0.3。如果不归一化,“明确"token的专家输出会被放大10倍以上,造成训练中的尺度不一致。

归一化后每个token的8个专家权重归一化为概率分布(和为1),确保了所有token的MoE输出在同一尺度上,简化了后续的残差连接和归一化。Switch Transformer使用hard routing(权重为1/k)也有类似效果,但softmax+归一化提供了更平滑的梯度。

易混淆:归一化不等于softmax。路由已经过softmax(产生256维概率分布),topk_values除以其总和是第二次归一化——将8个选出专家的权重重新归一化到和为1。这种"双重归一化"在top-k MoE路由中是标准做法。

延伸阅读:主报告 CH5.4(路由权重机制);Qwen3_5MoeTopKRouter.forward: router_top_value /= router_top_value.sum(dim=-1, keepdim=True)


Q5.5 为什么专家权重用3D张量而非list of 2D?

简短回答:3D张量(gate_up_proj: (256, 1024, 2048))支持使用索引操作高效访问单个专家(weights[expert_idx]),提供更优的内存布局(连续内存),并支持潜在的批处理优化。与list of 2D相比减少了内存碎片和指针开销。

详细解释:两种存储方式的内存对比——3D张量在内存中是一块连续的1.07GB区域(BF16: 256×1024×2048×2 bytes),GPU可以一次性高效访问;list of 256个2D张量虽然逻辑大小相同,但每个2D张量需要独立的Python对象头和指针,且256个分布在堆上的小张量可能因内存碎片降低访问效率。

在forward中,专家索引操作self.gate_up_proj[expert_idx]返回该专家的完整gate+up权重(1024×2048),这个操作在GPU上是O(1)的索引访问。如果使用list,需要先找到对应的Python对象再进行索引,有额外的解释器开销。此外,Transformers库中MoE实现的标准做法就是3D张量存储(Mixtral也使用),确保了与训练框架(如DeepSpeed、FSDP)的良好兼容性。

面试要点:3D vs list of 2D不是功能差异,而是性能和工程差异。3D张量=连续内存=更好的GPU利用率。追问"什么时候用list”:当专家数动态变化或需要不同维度的专家时。

延伸阅读:主报告 CH5.5(专家存储格式);Qwen3_5MoeExperts.__init__: self.gate_up_proj = nn.Parameter(torch.empty(num_experts, 2*intermediate_dim, hidden_dim))


Q5.6 aux_loss系数0.001是大了还是小了?

简短回答:0.001(1e-3)是中等偏小的辅助损失系数。Switch Transformer使用0.01(10倍大),Qwen3.5可能因为共享专家(提供负载均衡的基础)而可以使用较小的aux loss。较小的系数意味着模型更关注语言建模质量而非负载均衡。

详细解释:辅助损失(load balancing loss)的计算公式为:aux_loss=0.001×N×sum(f_i×P_i),其中f_i是路由到专家i的token比例,P_i是路由到专家i的平均概率。即使专家严重不均(如某些专家获得10%的token而其他仅获0.1%),aux_loss通常在0.01-0.1范围,对总loss(通常3-8)的贡献<1%。这个较小的系数意味着训练主要受语言建模loss驱动,负载均衡只是一个"软约束"。

较小的aux_loss系数还有一个好处:它允许专家自然分化而不是"被迫均衡"。如果系数太大(如0.01),路由会倾向于均匀分配token以降低aux_loss,可能阻止专家真正的专业化。Qwen3.5使用0.001,配合共享专家的无条件处理,在负载均衡和专家专业化之间取得了更好的平衡。

面试要点:aux_loss系数是MoE训练的关键超参数——太大→“强制均衡”(所有专家趋同),太小→“专家坍塌”(少数专家处理所有token)。Qwen3.5的0.001是"温和引导"策略。DeepSeek V3使用多种辅助损失(aux+z-loss+device-level)实现更精细的控制。

延伸阅读:主报告 CH5.6(负载均衡分析);config.json: router_aux_loss_coef=0.001


Q5.7 MoE的experts前向传播中使用index_add_而非scatter_add_的原因?

简短回答:index_add_在PyTorch中有更优的GPU内核实现(基于原子加),在功能上等价于scatter_add_。对于Qwen3.5的expert-centric循环(每个专家处理分配给它的token组),index_add_将各专家的输出按原始位置累加回final_hidden_states,是更高效且更安全的选择。

详细解释:Qwen3.5的MoE前向使用"expert-centric"模式(每个专家循环处理分配给它的token),而非"token-centric"模式(每个token向8个专家请求处理)。在expert-centric模式下,每个专家的输出需要写回到final_hidden_states中相应token的位置。由于不同专家可能输出到不同的token索引(在当前实现中不重叠),但index_add_的原子加语义提供了额外的安全性。

index_add_内部使用CUDA的atomicAdd操作(在GPU上),能够正确处理多个专家可能写入同一位置的并发场景。scatter_add_虽然语义相同,但在某些PyTorch版本中GPU内核优化不如index_add_。开销方面,每个token被8个专家处理,产生8次index_add_调用,每次O(2048)的元素加法,开销极小。

面试要点:index_add_ vs scatter_add_是PyTorch实现细节,但反映了"expert-centric"的MoE计算范式——先按专家分组token,每个专家批量处理,再按原位置还原。这与DeepSeek V3的token dispatch+combine模式在概念上一致。

延伸阅读:主报告 CH5.7(MoE前向传播流程);Qwen3_5MoeExperts.forward 中index_add_使用。


Q5.8 为什么MoE路由使用softmax而非sigmoid(如Switch Transformer)?

简短回答:Softmax确保所有专家的概率和为1,产生一个概率分布,便于top-k选择和权重归一化。Sigmoid产生独立的专家选择概率,需要额外的阈值机制。Softmax+topk是当前MoE的主流选择(DeepSeek也使用),Switch Transformer的sigmoid+top-1已成为历史。

详细解释:Softmax将256维的router_logits映射为和为1的概率分布。这使得top-k选择有明确的概率语义——选出的k个专家是"最相关"的前k名,且选择后的归一化(除以sum(topk_values))有自然的概率解释。Sigmoid将每个专家独立映射到(0,1),但各专家概率之和不为1——可能有0个或多于k个专家的概率超过阈值,需要额外的选择机制。

Switch Transformer(2021)使用sigmoid+top-1(每token仅1个专家),这是早期MoE的探索性设计。后续的MoE研究(GShard、ST-MoE、Mixtral、DeepSeek)大多转向softmax+top-k,因为softmax的概率分布语义更清晰,且top-k允许每token使用多个专家(增加模型容量)而不会冲突。Qwen3.5沿用了这一成熟方案。

面试要点:这个问题考察对MoE路由演进的理解。“softmax→概率分布,适合多选;sigmoid→独立概率,适合单选或阈值选择”。2021年Switch Transformer后社区共识转向softmax+top-k。

延伸阅读:主报告 CH5.8(路由机制分析);Switch Transformer论文(Fedus et al., 2021);Qwen3_5MoeTopKRouter.forward: router_probs = F.softmax(router_logits, dim=-1)


Q5.9 路由权重初始化为零有什么影响?

简短回答:零初始化使所有专家的初始logits相同(均为0),softmax后每个专家的概率为1/256≈0.39%。top-k选择在训练初期近乎随机——每个token的8个专家从256个中近乎均匀随机选出。随着训练进行,路由学会将特定类型的token分配给特定专家。

详细解释:路由权重初始化为零意味着训练初期所有专家的路由分数都是 0 + sigmoid(0) = 0.5。这确保了训练初期所有 256 个专家有均等的被选中机会——sigmoid(0) = 0.5,加上 0 偏置,每个专家的分数完全相同。Top-8 选择在分数全相等时退化为「随机选 8 个」,这恰好是理想的初始化状态——让所有专家在早期都接收到大致均匀的梯度信号。

如果路由权重初始化为较大的值(如 N(0, 0.02²)),某些专家的 sigmoid 分数可能显著偏离 0.5,导致训练初期出现「富者愈富」的专家坍缩。零初始化是最安全的起点——先让所有专家公平竞争,再让路由器逐渐学会分化。

对比 DeepSeek V3 的做法:V3 同样使用 bias 初始化为 0,路由权重使用小方差初始化。两者策略一致。router_logits=0→softmax(0)=均匀分布→top-8随机选择。训练初期,这意味着专家分配没有规律,每个专家处理大致相同数量的token(按照均匀随机选择的期望)。随着aux_loss和主loss的联合优化,路由权重逐渐分化——某些专家的logits对特定token模式变得更大,形成有意义的专业化。

零初始化可能导致的"冷启动问题":训练前期专家利用极度不均(因为随机选择+训练中主loss信号可能强化某些专家),aux_loss需要时间来纠正。一种潜在的改进是使用小的随机初始化(如DeepSeek V3的做法),但这可能导致不同的初始偏置。Qwen3.5的零初始化配合aux_loss=0.001,在实践中证明了是可行的。

易混淆:零初始化不等于"一开始专家无差异"——虽然初始概率均匀,但梯度更新的随机性(不同token-batch的组合)会迅速打破对称性。真正的风险是某些专家被"冷落"(长期得不到足够的训练信号),这才是aux_loss要解决的核心问题。

延伸阅读:主报告 CH5.9(路由初始化与训练动态);Qwen3_5MoeTopKRouter.__init__: self.weight = nn.Parameter(torch.zeros(...))


Q5.10 MoE Block在所有40层均相同,这个设计是否合理?

简短回答:所有层使用相同的MoE配置简化了实现和分布式策略,但可能不是最优的。浅层可能处理更通用的特征(需要较少但较大的专家),深层处理更抽象的概念(需要较多但较专业的专家)。然而通过训练,各层的路由自然分化(不同层学到不同的专家分布),弥补了配置的统一性。

详细解释:统一配置的优势明显——代码简洁(所有DecoderLayer使用相同MoE配置)、分布式策略统一(所有层的专家并行配置相同)、超参数搜索简化。Qwen团队可能判断配置统一的工程收益超过逐层定制化的性能收益。在35B这个规模上,训练的自我调节能力(层间自然分化)已经足够强。

分层定制化的潜在方向:浅层(0-10)使用较少但较大的专家(如64E,中间维度1024),因为浅层处理的是相对通用的语法特征;深层(30-39)使用较多但较专业的专家(如512E,中间维度512),因为深层处理的是高度抽象和领域特定的语义。若实现这一设计,总参数可能减少约6B(浅层的专家数减少),但实现和分布式复杂度显著增加。DeepSeek V3也使用统一的MoE配置,遵循了相同的工程哲学。

面试要点:“统一配置是工程简洁和性能的折中”。这是一个活跃的研究方向——“MoE架构中的层异质性”。追问"如何验证层间已自然分化":分析各层的专家使用分布和专家激活模式。

延伸阅读:主报告 CH5.10(MoE层分析);Qwen3_5MoeDecoderLayer.__init__ 中统一MoE配置。


Q5.11 负载均衡损失的具体实现与Switch Transformer有何不同?

简短回答:Qwen3.5的实现与Switch Transformer论文公式(4)-(6)基本一致:loss=N×sum(f_i×P_i),其中f_i是路由到专家i的token比例,P_i是路由到专家i的平均概率。主要增强是支持attention_mask来正确处理填充token,避免填充位置干扰负载统计。

详细解释:mask处理的细节——对于变长序列batch(不同长度的句子padding到相同长度),填充位置的hidden_states为0,其router_probs接近均匀分布(1/256≈0.39%)。如果不考虑mask,这些填充token会影响f_i和P_i的计算,使负载统计偏向均匀分布——即使有效token的路由已经严重不均。attention_mask过滤掉填充token,确保仅有效token参与负载计算。

与DeepSeek V3的对比——Qwen3.5仅使用Switch Transformer的基本aux loss,而DeepSeek V3额外使用z-loss(惩罚过大的路由logits,防止数值溢出)和device-level balance loss(确保多设备负载均衡)。Qwen3.5的实现更简洁但控制粒度更粗。这反映了不同的设计优先级——Qwen3.5可能在训练框架层面处理设备均衡(如DeepSpeed的自动负载均衡),不需要在模型中实现。

面试要点:核心公式loss=N×sum(f_i×P_i)需要记住。attention_mask支持是"生产级实现"的标志——说明Qwen3.5考虑了变长序列训练的实际情况。

延伸阅读:主报告 CH5.11(负载均衡实现);Switch Transformer论文公式(4)-(6);load_balancing_loss_func(line 1718-1797)


Q5.12 MTP的1层额外Decoder Layer如何提升训练效率?

简短回答:MTP允许模型同时预测多个未来的token,增加训练信号的密度。标准LM每位置仅1个损失信号,MTP每位置产生2个(当前位置和下一位置)。在仅增加约2%参数(~845M)的代价下,训练信号密度翻倍,等效于在不增加训练数据的情况下提升了训练效率。

详细解释:训练时的具体流程:主模型处理输入序列并产生hidden_states,MTP层接收这些hidden_states并预测t+1位置的token。损失函数为loss=CE(lm_head(hidden_t), token_t)+CE(mtp_head(hidden_t), token_{t+1})。两个损失通常用不同的权重合并(如1:0.3),防止MTP损失干扰主任务。每个训练步的梯度同时优化主模型和MTP层,但MTP层不参与主模型的前向路径。

推理时,MTP层转换角色——从训练辅助变为投机解码的draft模型。主模型自回归生成token t后,MTP层预测token t+1,然后主模型并行验证(计算实际概率并比较)。如果draft正确,1次forward完成2个token;如果不正确,回退到标准解码。理想加速比≈1/(1-p_accept),其中p_accept是draft接受率。

面试要点:“训练时多预测一个token(信号密度翻倍),推理时做投机解码(加速1.5-2x)"——MTP的两个价值。Meta LLaMA 3论文和GLM-5.1都讨论了MTP,这是当前主流训练增强技术。

延伸阅读:主报告 CH5.12(MTP机制);CH7(推理加速);_keys_to_ignore_on_load_unexpected 中mtp.*引用。


Q5.13 vision_config中out_hidden_size=2048(config默认3584)的矛盾?

简短回答:Qwen3_5MoeVisionConfig类默认out_hidden_size=3584,但实际模型config.json中为2048。2048与语言模型的hidden_size匹配(直接对齐),3584是代码默认值(可能用于其他Qwen3.5变体)。这不是矛盾,而是"代码默认值≠特定模型配置值"的标准模式。

详细解释:out_hidden_size决定了视觉特征投影到语言空间的维度。vision encoder的输出经过PatchMerger处理后,需要投影到与LLM hidden_size一致的维度。Qwen3.5-MoE-35B使用hidden_size=2048的语言模型,所以out_hidden_size=2048。如果存在其他变体(如Qwen3.5-397B-A17B可能使用更大的hidden_size),配置类默认3584可能是为那些变体预设的。

这种"代码默认值与特定配置不一致"的模式在Qwen3.5中多处出现——max_position_embeddings默认32768但实际262144,vision_config.out_hidden_size默认3584但实际2048。这不是bug,而是"安全默认值+显式覆盖"的设计模式——代码默认值是合理但不一定最优的值,特定模型的config.json提供精确的配置覆盖。

面试要点:这是一个"代码阅读能力"的测试。看到默认值和实际值不一致时,正确的反应是"理解了代码默认值和配置覆盖的机制”,而非"代码有bug"。实际值以config.json为准。

延伸阅读:主报告 CH5.13(视觉编码器配置);Qwen3_5MoeVisionConfig.out_hidden_size=3584 vs config.json: vision_config.out_hidden_size=2048


Q5.14 视觉编码器的spatial_merge_size=2有什么作用?

简短回答:空间合并将2×2的图像patch融合为一个,将视觉token数量减少4倍。例如224×224图像→(224/16)^2=196 patches→合并后49个tokens。这显著减少了LLM需要处理的视觉token数量,降低了跨模态注意力的计算开销。

详细解释:ViT将图像分割为16×16的patch,224×224图像产生14×14=196个patch tokens。spatial_merge_size=2将相邻的2×2组patch合并为一组——每组4个patch的1152维特征拼接为4608维,然后通过线性层投影到out_hidden_size(2048维,对LLM对齐)。最终token数为(14/2)^2=7×7=49。对于448×448图像:ViT产生28×28=784个tokens,合并后14×14=196个tokens。

这种空间合并是视觉-语言模型中常见的"token压缩"策略(LLaVA也使用)。合并减少了计算量(LLM的输入token更少)但保留了足够的空间分辨率——对于大多数视觉问答和图像理解任务,合并后的分辨率(7×7到14×14)提供了足够的空间信息。spatial_merge_size越大→越少token→越快但越粗糙。

面试要点:spatial_merge_size=2→token数÷4。权衡:更大的merge→更快但丢失空间细节;不merge→更精确但token数多4倍。2是图像理解和效率之间经验最优值。

延伸阅读:主报告 CH5.14(视觉token压缩);vision_config.spatial_merge_size=2Qwen3_5MoeVisionPatchMerger 实现。


Q5.15 mrope_interleaved=true与分块M-RoPE有何区别?

简短回答:Interleaved M-RoPE将T(时间)、H(高度)、W(宽度)三个维度的频率交错排列(THWTHW…),而非分块排列(TTT…HHH…WWW)。交错排列确保每个局部维度组都包含所有三种位置信息,有利于多模态特征的融合。

详细解释:M-RoPE的mrope_section=[11,11,10]定义了三个维度的频率分配——T占11个频率对,H占11个,W占10个,共32个频率对(对应head_dim=256中参与RoPE的64维/2=32对)。Interleaved模式下,这32个对按照T0,H0,W0,T1,H1,W1,…的顺序交错排列。这意味着维度0-2包含完整的(T,H,W)信息,维度3-5也是(T,H,W),以此类推。

分块模式(TTT…HHH…WWW)将同维度的所有频率对放在一起——维度0-10是T,11-21是H,22-31是W。这导致前11个维度对只有时间信息,缺乏空间上下文。Interleaved模式通过交错排列,使每3个连续维度构成一个"三维位置快照",在任何局部窗口内都能感知完整的时空位置。这对视频理解(需要同时处理空间和时间维度)特别有益。

面试要点:Interleaved=“T,H,W交替排列”,使局部维度组包含完整的3D位置信息。适合视频(时空一体);分块=“T,H,W分别堆叠”,适合静态图像或多模态输入(图文混合)。

延伸阅读:主报告 CH5.15(M-RoPE机制);Qwen3_5MoeTextRotaryEmbedding.apply_interleaved_mrope


Q5.16 3D位置编码如何处理不同模态的组合输入?

简短回答:文本token使用递增的1D位置,视觉token使用3D位置(T,H,W)。两者通过position_ids的4通道实现——通道0是纯文本位置(或视觉的时间维度),通道1-3是3D视觉位置。RoPE对文本和视觉token分别应用不同的位置编码维度。

详细解释:position_ids是一个(4, batch, seq_len)的张量。对于纯文本token:通道0有递增的位置ID(0,1,2,…),通道1-3填充为0或无意义值。对于视觉token:通道0是时间位置(视频帧索引),通道1是高度位置(patch在图像中的行),通道2是宽度位置(patch的列),通道3可能为辅助信息。RoPE根据token类型选择使用哪些通道的频率——文本仅用通道0(1D RoPE),视觉用全部4个通道(3D M-RoPE)。

在实际的多模态输入中,文本和视觉token交错排列。Qwen3_5MoeModel.get_rope_index负责为每个token分配正确的位置ID。例如输入"[image_tokens]请描述这张图片",image_tokens获得3D位置(对应它们在图像中的空间位置),文本部分获得递增的1D位置。

面试要点:4D position_ids是Qwen多模态系列(Qwen2-VL, Qwen3.5)的特色。1个文本维度+3个视觉维度=统一的4通道位置表示。LLaVA-NeXT也有类似设计。

延伸阅读:主报告 CH5.16(多模态位置编码);Qwen3_5MoeModel.get_rope_index


Q5.17 vision_config.depth=27层ViT的选择依据?

简短回答:27层可能来源于架构搜索,或与语言模型层数(40层)成比例(约2/3)。对于patch_size=16的图像编码器,27层提供了足够的深度来提取多尺度特征。介于ViT-Large(24层)和ViT-Huge(32层)之间,适合中大规模的多模态模型。

详细解释:27层ViT的每层结构为LayerNorm+MHA(1152 dim, 16 heads)+MLP(1152→4304),总视觉参数约400-500M。这个深度配合spatial_merge_size=2,使得视觉编码器能够从原始像素提取多层语义特征——浅层提取边缘和纹理(低级),中间层提取物体部件(中级),深层提取语义概念(高级),这些多层次特征通过PatchMerger融合后送入LLM。

为什么是27而不是更常见的24或32?27可能是一个"刚好够用"的值——通过实验确定的能充分提取视觉特征而不冗余的最小深度。对于ImageNet级别(224×224)的图像理解,24-32层ViT的性能差异已经很小,27可能是考虑了参数预算(~500M视觉参数在35B模型中占比合理)后的选择。

面试要点:27层=ViT-Large(24)和ViT-Huge(32)之间。不是标准值,但"刚好够用"。追问"如何确定层数":通常通过下游任务(如视觉问答、图像描述)的消融实验确定。

延伸阅读:主报告 CH5.17(视觉编码器架构);vision_config: depth=27


Q5.18 视觉编码器使用LayerNorm而非RMSNorm的原因?

简短回答:视觉编码器使用标准LayerNorm(有bias和mean中心化),而语言模型使用RMSNorm。这可能是历史原因(ViT传统上使用LayerNorm),也可能是因为视觉任务中均值的中心化有助于处理像素值的分布偏移。

详细解释:LayerNorm和RMSNorm的数学区别——LayerNorm: (x-mean)/std×weight+bias(减均值+除标准差+缩放+偏置),RMSNorm: x/rms×weight(仅除RMS+缩放)。LayerNorm多做两步:减均值(中心化)和加bias(偏移)。在视觉任务中,像素值分布可能有显著的均值偏移(如明亮图像vs暗图像的像素均值差异很大),减均值步骤有助于归一化这种分布偏移。而文本token的embedding通常均值为0(由于初始化),不需要均值中心化。

另一种解释是工程惯性——ViT自Dosovitskiy et al. (2021)以来一直使用LayerNorm,CLIP ViT也使用LayerNorm,后续的视觉编码器倾向于延续这一传统。Qwen3.5在视觉编码器中保持LayerNorm,在语言模型中切换到RMSNorm,是"遵循各领域最佳实践"的务实选择。

面试要点:LayerNorm(视觉)vs RMSNorm(语言)=各用各领域的最佳实践。深层原因:视觉需要均值中心化(像素分布偏移),语言不需要(embedding已零均值)。追问"能不能统一用RMSNorm":可以但需要验证,Qwen团队选择了保守方案。

延伸阅读:主报告 CH5.18(归一化策略对比);Qwen3_5MoeVisionBlock: self.norm1 = nn.LayerNorm(...);ViT论文(Dosovitskiy et al., 2021)。


Q5.19 Qwen3.5-MoE对量化的支持情况?

简短回答:config中mamba_ssm_dtype=float32表明GDN状态矩阵需要使用float32精度。这限制了低比特量化在GDN层上的有效性——状态矩阵的float32精度是硬性要求,模型权重可量化到INT4/INT8,但GDN状态必须保持FP32。30层GDN状态约需60MB FP32显存。

详细解释:量化的边界——模型的主体权重(嵌入、Attention、MoE专家、投影矩阵等)可以安全地量化到INT4(~18GB)或INT8(~35GB),因为神经网络的权重对精度损失有一定容忍度。但GDN的recurrent_state(每层524K元素)在数千步递归更新中累积的信息对精度敏感——BF16只有7位尾数,累积误差可能导致状态矩阵逐渐"退化"。float32的23位尾数提供了足够的数值精度。

这种"权重量化+状态保持高精度"的模式在SSM/线性注意力模型中普遍存在——Mamba也要求SSM状态使用float32。对部署的影响:30个GDN层的状态共60MB FP32,加上0.5B的视觉编码器权重(通常也保持BF16或FP32),约500MB+60MB=560MB不能量化的部分。其余~34B权重可量化到INT4(~18GB),总计约18.6GB显存。

面试要点:量化≠所有都可以压缩。GDN状态是"精度敏感"组件,必须保持FP32。追问"为什么BF16不行":BF16尾数7位,经过262K步递归后累积误差可能使状态信息退化。

延伸阅读:主报告 CH5.19(量化支持);config.json: mamba_ssm_dtype=float32


Q5.20 262K上下文的支持是否需要特殊的训练策略?

简短回答:是的,需要分阶段训练:(1)短序列预训练(4K-8K)建立基础语言能力;(2)逐步扩展序列长度(32K→128K→262K);(3)长序列微调以保持位置编码的泛化能力。Qwen团队可能使用了NTK-aware或YaRN等RoPE扩展方法来实现平滑过渡。

详细解释:分阶段训练的必要性——如果直接在262K序列上从头训练,计算成本极高(训练时间与序列长度成正比)且模型难以收敛。标准做法是先在短序列(4K-8K)上进行大规模预训练(此时成本可控),然后在长序列上进行少量额外训练步(“continual pretraining"或"long-context finetuning”)。每个阶段逐步提升RoPE theta以支持更大的上下文窗口。

RoPE扩展方法是关键——直接从theta=10000到10M会导致训练初期位置区分度严重下降(高频维度过快振荡)。NTK-aware scaling通过非线性插值——低频维度扩展更多(区分更远位置),高频维度扩展较少(保持近处位置的区分度)——实现平滑过渡。YaRN(Yet another RoPE extensioN)进一步改进了NTK方法,在温度和频率缩放上做了优化。Qwen3.5的代码虽未公开训练细节,但rope_theta=10M暗示了这些方法的使用。

面试要点:长上下文=分阶段训练+RoPE扩展。追问"为什么不直接训练":计算成本O(n^2) vs O(n),短序列预训练阶段成本可控。NTK-aware:低频多扩(远距离)、高频少扩(近距离)。

延伸阅读:主报告 CH5.20(训练策略);NTK-aware RoPE论文;YaRN论文;config.json: rope_theta=10000000


Q5.21 为什么Full Attention层的KV缓存是模型推理的瓶颈?

简短回答:262K时10层Full Attention的KV缓存约5.37GB(2 KV heads×262K×256 dim×2(K+V)×2 bytes×10 layers)。相比全注意力模型(~214GB)已大幅减少,但仍是消费级GPU(24GB)推理的主要显存消耗——占可用显存的22%。

详细解释:显存预算分解——以RTX 4090(24GB)为目标:模型权重INT4量化后18GB;10层Full Attention KV缓存5.37GB;30层GDN状态(FP32)0.06GB;嵌入层1GB(BF16);其他(activations、临时buffer)~1-2GB。总计约25.4-26.4GB,刚好超出24GB。这意味着单卡24GB部署需要进一步优化(如INT4量化+KV缓存INT8量化,或使用offloading)。

如果所有40层都使用Full Attention,KV缓存将达214GB——完全不可行。GDN将KV缓存从214GB降至5.37GB,是262K上下文实现的关键。但5.37GB仍然是最大的单一显存消耗者——比模型权重(18GB)小但比GDN状态(0.06GB)大近100倍。

面试要点:KV缓存是长上下文推理的最大瓶颈(5.37GB),不是GDN状态(0.06GB)也不是模型权重(18GB)。优化方向:KV缓存INT8量化(减半至2.7GB)、multi-query attention、甚至完全去除Full Attention层。

延伸阅读:主报告 CH5.21(推理显存分析);CH3(GDN效率分析)。


Q5.22 deepstack_visual_indexes=[]空列表表示什么?

简短回答:deepstack_visual_indexes为空表示没有使用DeepStack视觉特征注入——即不在LLM的多层注入视觉特征。Qwen3.5-MoE-35B-A3B使用传统"视觉编码器→LLM输入"的简单方式,视觉特征仅在LLM的起始处输入,而非在多个中间层注入。

详细解释:DeepStack是Qwen2.5-VL引入的一种技术——将视觉特征注入到LLM的多个中间层(如L0, L8, L16, L24),使LLM的不同层次都能直接访问视觉信息,而非仅在第一层输入。这增强了视觉-语言的对齐和细粒度理解能力。Qwen3.5-MoE-35B-A3B未启用此功能(deepstack_visual_indexes=[]),视觉特征仅通过embed_tokens与文本token一起在LLM的起始处输入。

为什么Qwen3.5-MoE不使用DeepStack?可能原因:(1)简化实现——35B的中等规模不需要这么复杂的技术;(2)GDN的混合架构使DeepStack的集成更复杂——GDN层处理前层注入的视觉特征时可能与纯注意力层的行为不同;(3)可能有其他Qwen3.5变体(如更大的397B版本)使用DeepStack。

面试要点:DeepStack=“多层视觉注入”,增强视觉-语言交互。Qwen3.5-MoE未使用可能是为了简化实现。Qwen2.5-VL使用了DeepStack。

延伸阅读:主报告 CH5.22(多模态交互机制);vision_config: deepstack_visual_indexes=[]


Q5.23 rope_parameters中的partial_rotary_factor如何影响位置编码?

简短回答:partial_rotary_factor=0.25意味着仅25%的head_dim参与RoPE(64维),其余75%不使用位置编码。这允许模型同时学习位置相关特征(捕捉token顺序)和位置无关特征(捕捉语义内容),两者在同一head内共存而非分到不同head。

详细解释:具体实现——head_dim=256,参与RoPE的dim=256×0.25=64(对应32对频率),pass_dim=192。在注意力计算中,Q和K的RoPE部分经过旋转后与pass部分拼接:[rotated(64)|pass(192)]形成完整的256维表示。QK点积变为rotated_q·rotated_k+pass_q·pass_k——第一部分包含位置差异信息,第二部分是纯内容匹配。

这种设计的一个有趣特性是"位置方差"——两个token的QK点积中,位置相关的贡献来自64维,内容相关的贡献来自192维。内容匹配占主导(75%),位置信息占次要但必要的比例(25%)。如果位置比例太高(如50%),位置差异会过度干扰内容匹配;如果太低(如10%),位置区分度不足。25%是经验最优值。

面试要点:25%=“位置信息是重要但次要的线索”。GPT-NeoX和Llama使用100%(全维度RoPE),Qwen3.5使用25%。追问"为什么更小更好":更多的pass维度=更强的语义匹配能力,在GQA配置(仅2个KV头)下尤为重要。

延伸阅读:主报告 CH5.23(RoPE配置);Qwen3_5MoeTextRotaryEmbedding.compute_default_rope_parameters


Q5.24 视觉编码器的3D卷积patch embedding与2D的区别?

简短回答:3D卷积(kernel=[temporal=2, spatial=16, spatial=16])同时处理时间和空间维度,使模型能原生处理视频帧。对于静态图像,temporal维度为1(视为单帧视频)。3D patch embedding提供了统一的图像和视频处理管道。

详细解释:3D卷积的参数量——Conv3d: in_channels=3(RGB), out_channels=1152(embed_dim), kernel=(2,16,16), stride=(2,16,16)。参数量=3×1152×2×16×16≈3.54M。对于图像输入(shape: B,3,1,H,W),temporal维度为1,kernel在时间维度上退化为无操作(因为kernel_size=2>1,实际只覆盖1帧),等价于2D卷积但保持接口统一。

对于视频输入(shape: B,3,T,H,W),3D卷积在时间和空间维度上同时做patch化——每2帧、16×16像素为一个patch,同时提取时空特征。这使得视觉编码器无需任何架构修改就能处理视频,且时间维度的信息在patch embedding阶段就被编码。VideoMAE也使用类似的3D patch embedding,Qwen3.5继承了这一成熟设计。

面试要点:3D=“图像和视频统一管道”。对图像:temporal=1→退化为2D;对视频:temporal>=2→真正的3D时空特征。追问"为什么不分别做图像和视频编码器":统一管道减少了代码和维护成本,且3D卷积在单帧上几乎无额外开销。

延伸阅读:主报告 CH5.24(视觉patch embedding);Qwen3_5MoeVisionPatchEmbed: self.proj = nn.Conv3d(...)


Q5.25 MTP如何与KV缓存交互(推理时)?

简短回答:推理时MTP层作为投机解码的draft模型——主模型生成token t,MTP层预测token t+1(作为draft),主模型并行验证。如果draft正确,1次forward确认2个token。MTP层有自己的KV缓存(与主模型独立),但通常较小(仅1层)。加速比理想情况约1.5-2x。

详细解释:投机解码的具体流程:(1)主模型自回归生成token t,将其hidden states传入MTP层;(2)MTP层基于hidden states预测token t+1(1次轻量forward);(3)主模型在下一步同时输入token t和draft token t+1(作为序列),计算两者的实际概率;(4)验证draft t+1与实际生成是否一致——如果一致(接受),确认2个token继续;如果不一致(拒绝),仅确认token t,重新生成t+1。接受率取决于MTP层的预测准确度,通常在70-85%。

MTP层的KV缓存管理——MTP层是额外的Decoder Layer,有自己的attention机制和KV缓存。但由于只有1层,其KV缓存开销极小(约537MB/262K tokens)。与主模型的KV缓存(5.37GB)相比可以忽略。HuggingFace的GenerationMixin可能包含MTP投机解码的集成支持。

面试要点:MTP推理=投机解码(非自回归的辅助预测+自回归的验证)。加速比上限=2x(预测1步),实际约1.5-1.8x。Medusa/Eagle也是投机解码但MTP集成在模型内更优雅。

延伸阅读:主报告 CH5.25(MTP推理加速);CH7(推理优化策略)。


Q5.26 hidden_act=silu是否在模型中统一使用?

简短回答:SiLU在Qwen3.5的语言模型中统一使用——MoE专家的SwiGLU门控激活、GDN的卷积层和输出门控、共享专家的SwiGLU门控全部使用SiLU。唯一的例外是视觉编码器使用gelu_pytorch_tanh(ViT的传统激活函数)。

详细解释:SiLU(Swish)=x×sigmoid(x)的自门控特性使其成为现代LLM的主流激活函数——平滑(有助梯度流)、非单调(提供更丰富的表示)、自正则化(sigmoid自动调制)。在SwiGLU中,SiLU充当门控:SwiGLU(x)=SiLU(gate(x))⊙up(x),SiLU的非单调特性使门控既能放大也能抑制,比ReLU的硬阈值更灵活。

视觉编码器选择gelu_pytorch_tanh则是ViT社区传统的延续——GELU(Gaussian Error Linear Unit)是BERT时代的标准激活函数,ViT继承了它。gelu_pytorch_tanh是GELU的快速近似版本(使用tanh而非erf)。语言模型和视觉编码器使用不同激活函数的不一致性不影响功能(两者不直接交互),但反映了两个领域的独立进化。

面试要点:SiLU=现代LLM标准(Llama、Qwen、Mistral都用),GELU=ViT传统。问"能不能统一":技术上可以,但Qwen团队选择遵循各领域最佳实践。

延伸阅读:主报告 CH5.26(激活函数分析);config.json: hidden_act=siluvision_config: hidden_act=gelu_pytorch_tanh


Q5.27 视觉RoPE的频率计算与文本RoPE有何不同?

简短回答:视觉RoPE使用固定theta=10000(不可配置)和全维度RoPE(head_dim=72,36对频率),应用于每个ViT层的attention中。文本RoPE使用theta=10M和partial_rotary_factor=0.25(仅64维参与)。视觉不需要极端的位置区分能力(图像patch通常最多几百个),所以使用较小的theta。

详细解释:视觉head_dim=72(ViT hidden_size=1152/16 heads=72),远小于文本的256。视觉RoPE的36对频率(theta=10000)足以区分最多几百个图像patch的位置。文本RoPE需要区分262K个位置,所以theta需要提升到10M且使用partial RoPE(仅64维参与)。

频率计算公式相同——inv_freq=1.0/(theta^(2i/d))——但视觉的d=72(全维度),文本的d=64(partial维度)。视觉RoPE的波长范围:最慢频率(2π×10000)≈62832个位置周期,最快频率≈2π×1≈6.28个位置周期。对于最多~800个视觉token(28×28,merge后14×14=196),高频足以区分相邻位置,低频足以编码整体空间布局。

面试要点:视觉theta=10000(小,够用),文本theta=10M(大,需要)。原因:视觉token最多几百个,文本token最多262K个。视觉全维度RoPE(d=72),文本部分RoPE(d=64/256)。

延伸阅读:主报告 CH5.27(RoPE双轨设计);Qwen3_5MoeVisionRotaryEmbedding vs Qwen3_5MoeTextRotaryEmbedding


Q5.28 为什么vision_start_token_id和vision_end_token_id是连续的特殊token?

简短回答:vision_start(248053), vision_end(248054), image_token(248056), video_token(248057)都在词汇表末尾附近(eos=248044)。这些特殊token用于标记多模态输入中视觉内容的边界,使模型能正确区分文本和视觉token。连续编号有助于快速范围检查。

详细解释:输入序列的典型格式:[vision_start][image_token]×49[vision_end]请描述这张图片。模型通过vision_start和vision_end识别视觉segment的起止位置,内部image_token是视觉特征的占位符(在forward中被视觉编码器的输出替换)。image和video使用不同的占位符token(248056 vs 248057),使模型能区分静态图像和视频帧的不同处理方式。

所有特殊token集中在词汇表末尾(248044-248057范围,共14个token),这种设计有两个好处:(1)特殊token的ID不干扰正常文本token(中英文等),避免混淆;(2)可以通过简单的范围检查(token_id>248044?)判断是否为特殊token。

面试要点:特殊token的两种角色——边界标记(start/end)+占位符(image/video token)。LLaVA使用类似方案。追问"为什么连续编号":便于范围检查,减少嵌入层的碎片化。

延伸阅读:主报告 CH5.28(多模态token处理);config.json: vision_start_token_id=248053, image_token_id=248056


Q5.29 如何在推理时减少MoE的专家切换开销?

简短回答:四种策略:(1)专家并行(EP):每个GPU持有部分专家,token仅发送到持有其专家的设备;(2)专家缓存:将最近使用的专家权重保持在GPU内存中,利用token间的专家选择局部性;(3)批量专家计算:将同一专家的多个token批量处理(Qwen3.5已使用expert-centric循环);(4)专家预测:基于前一token的专家选择预测下一token的专家,预加载权重到cache。

详细解释:专家切换开销的来源——每个token需要从256个专家中选择8个,专家权重分散在GPU显存中(共约32GB BF16)。如果不加优化,每个token的随机访问模式的显存读取是极低效的。Expert-centric循环(Qwen3.5的实现)已经做了批量优化——按专家分组所有token,每个专家一次性处理分配给它的全部token,减少专家权重的重复加载。

专家缓存在连续生成场景中特别有效——由于语言有局部性,相邻token的专家选择高度相关(序列中相邻词往往属于同一领域)。缓存最近使用的8-16个专家(约25-50M参数),可显著减少显存访问。专家并行将专家分布到多GPU上,每个GPU的专家子集变小,缓存命中率提高。vLLM的MoE支持实现了这些优化。

面试要点:MoE推理的核心挑战=专家权重的随机访问。核心解决方案=batching(同专家token一起处理)+caching(利用token间局部性)。追问"最坏情况":随机token序列(无局部性)→缓存命中率接近0→expert-centric循环是唯一优化。

延伸阅读:主报告 CH5.29(推理优化);Qwen3_5MoeExperts.forward expert-centric循环实现。


Q5.30 Qwen3.5的隐藏维度2048对MoE设计有何特殊意义?

简短回答:2048配合512的moe_intermediate(比例4:1),使每个专家参数约3.15M——这个大小在GPU L2缓存中可高效处理。若hidden=4096,每个专家中间维度可能需要1024,专家参数约12.6M(4倍),总参数可能达140B。2048还具有良好的整除性——可被16个heads整除。

详细解释:每个专家的矩阵运算(gate/up/down投影)涉及2048维的向量-矩阵乘法。2048×512的矩阵在GPU上可以高效地分块计算(tiling),适合L2缓存。如果hidden翻倍到4096,每个专家的中间维度也需要翻倍到1024(保持表达能力),单专家参数从3.15M增至12.6M。256个专家×40层×12.6M≈129B的MoE参数+其他≈140B总参数——远超35B的目标规模。

2048与注意力配置的整除关系——2048/16 heads=128维/head(基本维度),配合head_dim=256(通过KV repeat实现),以及GQA 16Q/2KV(ratio=8)。这些整除关系确保了维度分配没有浪费(没有padding维度),每个head的表示空间均匀。若hidden=2050(非2的幂),维度无法被16整除,会导致实现复杂化。

面试要点:2048是"精心选择的魔法数字"——整除性(16头、256 head_dim)、参数预算(512中间维度→3.15M/专家→35B总参数)、GPU友好(L2缓存分块)。展示了Qwen团队的参数工程能力。

延伸阅读:主报告 CH5.30(超参数协同设计);config.json: hidden_size=2048, moe_intermediate_size=512

CH8-9: 源码工程 + 对比总结

Q8.1 Qwen3_5MoeForCausalLM和Qwen3_5MoeForConditionalGeneration的区别?

简短回答:Qwen3_5MoeForCausalLM是纯文本因果语言模型(使用Qwen3_5MoeTextModel作为backbone),Qwen3_5MoeForConditionalGeneration是多模态条件生成模型(使用Qwen3_5MoeModel包含视觉编码器)。后者可接受图像/视频输入,支持pixel_values和image_grid_thw参数。

详细解释:两者的类继承关系——都继承自Qwen3_5MoePreTrainedModel,共享权重初始化、加载逻辑等基础能力。CausalLM的__init__中self.model=Qwen3_5MoeTextModel(config)仅初始化文本backbone(嵌入+40层Decoder+LM Head),forward仅接受input_ids和attention_mask。ConditionalGeneration的__init__中self.model=Qwen3_5MoeModel(config)包含self.visual=Qwen3_5MoeVisionModel(config.vision_config),forward接受pixel_values(图像像素)和image_grid_thw(图像grid的时间/高度/宽度信息)。

在实际使用中,纯文本推理应使用CausalLM(不加载视觉编码器节省约500M参数和显存),多模态推理使用ConditionalGeneration。两者共享相同的文本权重(可以互相转换——ConditionalGeneration去掉visual部分即为CausalLM)。这种双接口设计提供了灵活性的同时保持了向后兼容。

面试官视角:这个问题测试候选人对源码类层次的理解。面试官想知道你是否读过modeling_qwen3_5_moe.py,能否区分text-only和multimodal的代码路径。加分回答:提到_keys_to_ignore_on_load_unexpectedr"^model.visual.*"使纯文本模型可以加载multimodal checkpoint。

答题模板:“Qwen3.5提供两个对外接口——CausalLM用Qwen3_5MoeTextModel(纯文本),ConditionalGeneration用Qwen3_5MoeModel(含Qwen3_5MoeVisionModel视觉编码器)。区别在于backbone不同——前者仅有40层Decoder+LM Head,后者多了27层ViT+3D PatchEmbed+PatchMerger。实际使用时,纯文本场景用CausalLM节省显存,多模态场景用ConditionalGeneration。”

面试要点:两个类的shared base是Qwen3_5MoePreTrainedModel。ConditionalGeneration=CausalLM+Vision(~500M)。纯文本权重可以从multimodal checkpoint加载(ignore visual.*)。

延伸阅读:主报告 CH8.1(类层次图);Qwen3_5MoeForCausalLM(line 1801)Qwen3_5MoeForConditionalGeneration(line 1905)


Q8.2 为什么load_balancing_loss_func支持attention_mask?

简短回答:在处理变长序列(padding)时,attention_mask标记有效token。填充位置的零向量会被路由到专家(干扰负载统计),导致aux loss被偏差。attention_mask确保仅有效token参与负载计算,这是生产级MoE实现的标准要求。

详细解释:变长序列训练是LLM预训练的常态——不同长度的句子被padding到batch中最长长度。如果不考虑mask,填充位置(值为0的hidden states向量)会产生接近均匀分布的router_probs(因为W_router×0=0→softmax(0)=uniform),这些填充token会被算入f_i(路由到专家i的token比例)和P_i(路由到专家i的平均概率),使负载统计偏向"均匀"——即使有效token的专家分布已经严重不均,aux loss也被虚假地拉低。

实现细节:load_balancing_loss_func在有效token上计算tokens_per_expert(通过one_hot(topk_idx)并mask掉填充位置)和router_prob_per_expert(mask后平均)。这确保了aux loss反映的是实际内容的负载分布,而非被padding稀释的虚假分布。DeepSeek V3的aux loss也支持序列掩码,验证了这一实践的必要性。

面试官视角:这个问题的隐藏考点是"你是否理解训练和推理的差异"——推理时没有padding(逐token生成),但训练时有。面试官想听到候选人自然提到"变长序列"和"padding的hidden states处理"。

答题模板:“训练时batch内序列长度不同,短序列会被padding。padding位置的hidden states是0,router_probs近似均匀分布(1/256),如果不mask,这些填充token会稀释aux loss——使负载看起来比实际更均匀。attention_mask过滤填充token,确保aux loss只计算有效内容的负载均衡。”

面试要点:记住公式:无mask→填充token的router_probs≈uniform→f_i被稀释→aux loss失真。mask处理是生产级vs实验级MoE实现的标志性差异。

延伸阅读:主报告 CH8.2(负载均衡实现);load_balancing_loss_func(line 1718-1797)


Q8.3 DynamicCache如何支持GDN的conv_state和recurrent_state?

简短回答:DynamicCache扩展了标准KV缓存以支持GDN特有的状态。每层缓存条目包含可选字段conv_states和recurrent_states(仅GDN层使用),Full Attention层仍使用标准past_key_values。has_previous_state(layer_idx)检查是否有缓存状态,统一了prefill/decode的判断逻辑。

详细解释:DynamicCache的数据结构——每层一个缓存条目,条目类型取决于该层的layer_type。对于Full Attention层(如layer 3, 7, 11…),条目是标准的(key_states, value_states)元组。对于GDN层(其他30层),条目包含conv_state((B, 8192, 3)的滑动窗口卷积状态)和recurrent_state((B, 32, 128, 128)的状态矩阵)。这种异构缓存设计是Qwen3.5独有的——其他模型要么全用KV缓存(Dense/标准MoE),要么全用SSM状态(纯Mamba)。

更新逻辑——GDN的forward中通过cache_params.layers[self.layer_idx].conv_states读取并原地更新conv_state,通过.recurrent_states处理状态矩阵。has_previous_state检查这两个字段是否非空,非空则进入decode分支(seq_len==1的递归模式),否则进入prefill分支(chunk模式)。

面试官视角:这个问题测试对"推理引擎需要做什么适配"的理解。面试官想听你说:标准的KV cache管理器无法处理Qwen3.5,因为30层需要SSM状态而非KV缓存。vLLM/SGLang需要专门的适配层(paged state management?)。

答题模板:“DynamicCache是异构缓存——Full Attention层存KV对,GDN层存conv_state+recurrent_state。每层的缓存类型由layer_type决定。推理框架需要同时管理这两种状态,这是Qwen3.5推理引擎适配的重点。`

面试要点:混合缓存=Qwen3.5推理的核心工程挑战。30层GDN状态(60MB)+10层KV缓存(5.37GB)=5.43GB总计。Mamba模型使用类似扩展缓存(conv_state+ssm_state)。

延伸阅读:主报告 CH8.3(缓存架构);Qwen3_5MoeGatedDeltaNet.forward 中cache_params使用。


Q8.4 Qwen3_5MoeRMSNormGated相比普通RMSNorm增加了什么?

简短回答:Gated RMSNorm在标准RMSNorm后增加了SiLU门控:SiLU(gate)×RMSNorm(x)。gate是外部提供的值(来自GDN的in_proj_z),实现了输入相关的输出缩放。FLA库提供融合内核FusedRMSNormGated以减少内存往返提高性能。

详细解释:普通RMSNorm的forward:x=x×rsqrt(mean(x^2)+eps)×weight。Gated RMSNorm在此基础上:x=RMSNorm(x); gate=SiLU(z); output=x×gate。gate来自GDN的in_proj_z投影——一个独立的2048→4096线性层,专门学习何时放大或抑制GDN的Delta Rule输出。SiLU的非单调性使gate能放大(正Z)或抑制(负Z)输出。

FusedRMSNormGated的价值——在普通实现中,RMSNorm和SiLU门控是两个独立的CUDA kernel,中间结果需要写回显存再读取(内存往返)。融合内核将归一化和门控合并为一个kernel,在寄存器中完成所有运算,减少显存带宽消耗约50%。FLA库(flash-linear-attention)提供了这个优化。

面试官视角:这个问题考察对"门控机制"和"算子融合"两个概念的理解。面试官可能追问"为什么不去掉RMSNorm只用SiLU”——去掉归一化后Delta Rule的输出幅度可能发散,RMSNorm提供基本稳定性保障。

答题模板:“RMSNormGated=标准RMSNorm+SiLU门控=稳定归一化+动态调节。RMSNorm确保输出尺度一致(消除幅度累积),SiLU根据输入动态缩放(恢复重要性区分)。FusedRMSNormGated通过算子融合减少内存带宽消耗。”

面试要点:顺序重要——先RMSNorm(消除尺度差异),后SiLU(恢复重要性)。反过来(先门控后归一化)会抹平门控的动态调节效果。GLU在FFN中,Gated RMSNorm在Norm中——概念类似但位置不同。

延伸阅读:主报告 CH8.4(门控归一化);Qwen3_5MoeRMSNormGated.forward


Q8.5 Qwen3_5MoePreTrainedModel._init_weights的特殊处理?

简短回答:_init_weights重写了四个特殊初始化:(1)GDN的dt_bias初始化为1(确保softplus输入为正→时间步非零);(2)GDN的A_log初始化为log(U(0,16))(覆盖广泛的基础衰减范围);(3)RMSNorm的weight初始化为0(实现1+0=1的恒等映射);(4)MoE专家权重使用标准正态分布N(0,0.02)。这些定制初始化对GDN的训练稳定性至关重要。

详细解释:dt_bias=1的数学意义——GDN的g_t=-exp(A_log)×softplus(A+dt_bias)。初始时A=0(因为其输入投影权重初始化为0),softplus(0+1)=softplus(1)≈1.313。这确保初始g_t为负的非零值(具体值为-A×1.313),使递归衰减有意义地启动,而非从零开始(如果dt_bias=0, softplus(0)≈0.693, g_t≈-0.693A,衰减太弱)。A_log=log(U(0,16))覆盖了从近乎无衰减(A→0)到极快衰减(A=16)的广泛谱系,让不同head自然分化为不同的记忆长度。

RMSNorm weight=0是另一种恒等初始化——forward中x×(1+weight)在weight=0时等价于x×1(恒等映射)。标准RMSNorm使用weight=1(也是恒等)。Qwen3.5的选择(weight=0, 乘(1+w))在语义上更清晰——“weight是偏离1的微调量”。

面试官视角:面试官从"为什么需要特殊初始化"切入,考察你是否理解GDN的训练动力学。标准答案是"GDN的递归状态对初始条件敏感——不恰当的初始衰减率会导致状态过早归零或发散,训练无法收敛"。

答题模板:"_init_weights做了四处特殊处理:dt_bias=1→保证初始时间步为正;A_log=log(U(0,16))→覆盖多种记忆长度;RMSNorm weight=0→恒等启动;MoE专家=N(0,0.02)→标准。最关键的是dt_bias和A_log——它们共同决定了GDN的初始衰减行为,不当初始化会导致训练失败。"

面试要点:四个特殊初始化中,dt_bias=1和A_log的U(0,16)最关键。标准Transformer初始化(全部N(0,0.02))不足以让GDN正常训练。这体现了"新架构需要新初始化"的原则。

延伸阅读:主报告 CH8.5(初始化策略);Qwen3_5MoePreTrainedModel._init_weights(line 912-928)


Q8.6 如何从代码中推断模型是text-only还是multimodal?

简短回答:检查类的__init__中使用的backbone——Qwen3_5MoeForCausalLM使用Qwen3_5MoeTextModel(纯文本),Qwen3_5MoeForConditionalGeneration使用Qwen3_5MoeModel(包含self.visual=Qwen3_5MoeVisionModel)。此外,config.json中是否有vision_config字段是另一个判断依据。

详细解释:代码层面的完整判断流程:(1)查看config.json——如果有vision_config字段则是multimodal(因为vision encoder需要配置参数);(2)查看模型类名——ForCausalLM是text-only, ForConditionalGeneration是multimodal;(3)查看__init__——是否初始化了self.visual;(4)查看forward签名——是否接受pixel_valuesimage_grid_thw参数;(5)查看_keys_to_ignore_on_load_unexpected——如果包含r"^model.visual.*"说明text-only模型故意忽略视觉权重。

这种"配置驱动推断"是阅读HuggingFace模型代码的基本技能。Qwen3.5的两种模型接口的设计清晰——函数名准确反映功能:CausalLM=因果语言建模(文本生成),ConditionalGeneration=条件生成(基于视觉条件生成文本)。

面试官视角:这个问题看似简单但考察"代码阅读方法论"——你是否知道从哪些关键特征快速判断模型能力。面试官可能追问"如果只有权重文件没有config.json怎么判断":看state_dict的key名称——有无model.visual.*前缀。

答题模板:“最快方式:看类名——ForCausalLM是text-only, ForConditionalGeneration是multimodal。其次看config.json——有vision_config字段就是multimodal。源码层面看__init__中backbone类型和forward签名中的参数。”

面试要点:三个快速判断特征——类名后缀、vision_config存在性、forward参数(pixel_values)。text-only模型的_keys_to_ignore包含r"^model.visual.*"使其能加载multimodal checkpoint。

延伸阅读:主报告 CH8.6(代码结构分析);Qwen3_5MoeForCausalLM vs Qwen3_5MoeForConditionalGeneration


Q8.7 Qwen3_5MoeVisionPatchMerger的use_postshuffle_norm参数?

简短回答:use_postshuffle_norm=False表示在空间打乱(将2×2空间邻域patch交错排列)之前应用LayerNorm。设为False意味着Norm应用于合并前的单个patch(hidden=1152),而非合并后的4个patch(hidden=4608),更节省计算。

详细解释:PatchMerger的工作流程:(1)空间合并——将2×2的邻域patch的1152维特征拼接为4608维;(2)如果use_postshuffle_norm=False:先在每个1152维patch上做LayerNorm→拼接→Linear(4608→4608)→GELU→Linear(4608→2048);(3)如果use_postshuffle_norm=True:先拼接→在4608维上做LayerNorm→后续相同。两者的区别在于Norm的维度和时机。

use_postshuffle_norm=False的优势是计算效率——LayerNorm的复杂度是O(d),在1152维上做Norm比在4608维上少约4倍计算(虽然绝对值微小)。劣势是可能错过合并后特征之间的交互归一化。Qwen3.5选择False说明Qwen团队判断计算节省的价值大于交互归一化的收益。

面试官视角:此题考察对"归一化顺序"的理解和"是否注意到这个细节参数"的代码阅读深度。面试官期望听到你分析"pre-shuffle norm vs post-shuffle norm"的tradeoff。

答题模板:“use_postshuffle_norm=False→先Norm再合并→Norm在1152维上做(更快但可能损失跨patch交互)。True→先合并再Norm→在4608维上做(更慢但能归一化跨patch的交互)。Qwen3.5选择False是基于计算效率的考量。”

面试要点:pre vs post shuffle norm=计算效率vs特征交互的tradeoff。对于35B模型,选False(pre-norm)省计算是合理的选择。

延伸阅读:主报告 CH8.7(PatchMerger分析);Qwen3_5MoeVisionPatchMerger.__init__


Q8.8 Qwen3.5-MoE在分布式训练中的通信瓶颈是什么?

简短回答:(1)All-to-all通信是MoE的主要开销——每个token需要发送到持有其选定专家的设备;(2)256个专家分布在多设备上时,all-to-all的通信量与专家数和序列长度成正比;(3)Qwen3.5通过moe_tp_experts策略优化了专家布局——每个设备持有完整专家子集,仅需在路由和结果汇聚时通信。

详细解释:All-to-all通信量的计算——每token发送2048维BF16向量(4KB)到最多8个设备=32KB/token。262K tokens×32KB=8.4GB(单层单batch的单向通信)。40层全部做all-to-all→336GB单向。实际训练中由于梯度累积和流水线并行,通信量更分散。张量并行(TP)和数据并行(DP)的通信(all-reduce)是额外的开销。

Qwen3.5的优化策略:(1)moe_tp_experts将专家分布到TP组内——每个TP rank持有32个完整专家(256/8),同一TP组内的token路由仅涉及组内通信;(2)专家并行使设备上常驻部分专家,减少频繁的权重加载;(3)hierarchical all-to-all在跨节点通信时先做节点内汇聚再做节点间通信。

面试官视角:这是一个"分布式训练面试标配题"。面试官关注你是否理解MoE特有的通信模式(all-to-all),以及它与标准Dense模型的通信(all-reduce)有何不同。

答题模板:“MoE的主要通信瓶颈是all-to-all——每个token的hidden states需要发送到持有其专家的设备。通信量=token数×hidden_size×2 bytes×top_k/设备数。Qwen3.5通过moe_tp_experts在TP组内分配专家减少跨组通信,通过expert-centric批处理减少通信次数。相比标准Dense模型,MoE的all-to-all开销是额外的代价,但换来的是参数容量的指数级增长。”

面试要点:MoE通信=all-to-all(token→专家设备),Dense通信=all-reduce(梯度同步)。前者通信量取决于token数和专家分布,后者取决于参数数。All-to-all对长序列和大量专家敏感。

延伸阅读:主报告 CH8.8(分布式训练);configuration_qwen3_5_moe.py:68 tp_plan定义。


Q8.9 模型加载时mtp.*权重被忽略的实现方式?

简短回答:_keys_to_ignore_on_load_unexpected=[r"^mtp.*"]是一个正则表达式列表,HuggingFace的from_pretrained在加载checkpoint时跳过匹配这些模式的权重key。这允许MTP权重存在于checkpoint中但在不需要时被自动忽略,实现了"可选模块"的优雅管理。

详细解释:HuggingFace的权重加载流程——from_pretrained读取checkpoint的state_dict,遍历模型的期望权重key。如果checkpoint中有模型不期望的key(“unexpected keys”),通常会警告甚至报错。_keys_to_ignore_on_load_unexpected预定义了哪些"多余的key"是故意存在的——通过正则匹配告知加载器"这些key不需要警告"。Qwen3.5定义了两个忽略模式:r"^mtp."(MTP模块)和r"^model.visual."(视觉编码器,当使用纯文本模型时)。

这种设计模式的好处——同一个checkpoint可以在不同场景下灵活使用:(1)训练时使用完整模型(含MTP+Vision);(2)纯文本推理时使用CausalLM(仅忽略visual.);(3)不需要投机解码时忽略mtp.。HuggingFace生态中许多模型使用类似机制处理可选模块(如lm_head, vision tower, auxiliary heads)。

面试官视角:面试官从"mtp权重怎么管理"切入,测试你对HuggingFace模型加载机制的理解。认识到_keys_to_ignore_on_load_unexpected是HF标准机制,而非Qwen3.5的独创。

答题模板:“通过_keys_to_ignore_on_load_unexpected=[r’^mtp.’, r’^model.visual.’]实现。from_pretrained加载时自动跳过匹配这些正则的权重key,不报错。这使checkpoint可以包含可选模块权重,在不同场景下选择性加载——纯文本场景忽略visual,不需要MTP时忽略mtp。”

面试要点:_keys_to_ignore_on_load_unexpected是HuggingFace Transformers的标准机制。Qwen3.5用它管理两个可选模块——MTP(~845M)和Vision(~500M)。不应该在加载日志中看到关于这些key的警告。

延伸阅读:主报告 CH8.9(权重管理);Qwen3_5MoePreTrainedModel._keys_to_ignore_on_load_unexpected


Q8.10 为什么Qwen3.5使用tie_word_embeddings=False?

简短回答:不共享嵌入和输出权重允许两者学习不同的表示——嵌入层将离散token映射到连续空间,输出层将连续表示映射回概率分布,目标不同。在多模态场景下,嵌入层还需处理视觉token的占位符嵌入,独立层提供更大灵活性。额外508.6M参数对35B模型占比仅1.5%,代价可控。

详细解释:embedding和LM head的数学角色——Embedding: R^248320→R^2048(查表+投影到连续空间),LM Head: R^2048→R^248320(投影到概率分布)。虽然维度对应(2048⇌248320),但学习目标完全不同:embedding学习"token的语义表示",LM head学习"hidden state到词汇的映射"。在训练中,embedding主要接收token分类信号(来自LM head的反向传播),LM head直接接收交叉熵损失信号——两者的梯度更新方向可能不同。

在多模态场景下,embedding层需要处理image_token和video_token的特殊占位符——这些token的embedding虽然与文本token共享嵌入空间,但它们的"含义"来自视觉编码器的注入而非固定语义。独立的embedding和LM head使这种设计更自然(embedding可以学习特殊的"视觉占位符表示",LM head不需要处理这些特殊token的输出映射)。GPT-2使用权重共享(小模型时代节省参数更有价值),Llama不共享(大模型时代灵活性更重要)。

面试官视角:这是一个"权重共享的tradeoff"问题。面试官想知道你是否理解embedding和LM head的不同角色,以及为什么现代LLM(Llama, Qwen, Mistral)普遍选择不共享。

答题模板:“不共享的原因有三:(1)embedding和LM head学习目标不同——前者学语义表示,后者学概率映射;(2)多模态场景下embedding需要处理视觉占位符的特殊表示;(3)508.6M参数对35B模型占比仅1.5%,代价可控。现代大模型普遍选择不共享(Llama, Mistral也是False),只有GPT-2时代的小模型才共享。”

面试要点:记住"小模型共享(GPT-2)、大模型不共享(Llama, Qwen)“的趋势。核心原因:参数占比小(<2%)→灵活性>参数节省。

延伸阅读:主报告 CH8.10(嵌入层设计);config.json: tie_word_embeddings=false


Q9.1 Qwen3.5-MoE相比Dense模型的最大优势是什么?

简短回答:参数效率。35B参数中仅3B在每次前向传播中激活,推理成本接近3B Dense模型,但质量接近35B Dense模型。这种"35B的容量、3B的成本"是MoE架构的核心价值主张。

详细解释:MoE的参数效率来源于稀疏激活——每个token仅激活256个专家中的8个(+1个共享专家),激活参数约3B(8.6%)。但不同的token组合会激活不同的专家子集,因此模型整体拥有35B参数的知识容量。这就像一个大型组织——每个问题只需要少数专家参与(低激活成本),但整个组织拥有广泛的专业知识(高总容量)。

与Dense模型的对比:35B Dense模型每次推理激活全部35B参数(100%激活比),推理FLOPs约35B×2×n。35B MoE模型激活仅3B参数(8.6%),推理FLOPs约3B×2×n。在相同的推理成本下,MoE模型可以利用更大的参数池(更多的知识)。这在知识密集型任务(如事实问答、长文档理解)中特别有价值。

面试要点:激活比8.6%是核心数字。对比:Mixtral 27%、DeepSeek V3 5.5%、Qwen3.5 8.6%。越低表示稀疏化越激进。追问"为什么不做到1%":极端稀疏化导致路由困难(256选2-3),专家专业化可能崩溃。

延伸阅读:主报告 CH9.1(MoE vs Dense对比);CH2(激活参数计算)。


Q9.2 Qwen3.5-MoE的架构决策中哪个最重要?

简短回答:Gated DeltaNet的引入是最关键的架构决策。它使得262K超长上下文在推理时实际可行(75%层不需要KV缓存),KV缓存从全注意力模型的214GB降至5.4GB。MoE提供参数效率,GDN提供计算效率——两者结合才是Qwen3.5的核心竞争力。

详细解释:GDN的决策优先级分析:(1)没有GDN→全40层Full Attention→262K KV缓存214GB→完全不可行;(2)有GDN→仅10层Full Attention→KV缓存5.4GB→消费级GPU可用;(3)GDN的长序列推理加速约457倍(理论值)。MoE虽然提供了35B→3B的参数效率(约11.6x),但这在"中等长度"序列上已经有价值——GDN让这优势扩展到超长上下文。

如果没有GDN(纯MoE+Full Attention),Qwen3.5仍然是一个好的模型(MoE提供参数效率),但上下文窗口将被限制在32K-128K范围(与Qwen3类似)。GDN使得262K成为可能,这是Qwen3.5区别于其他开源MoE模型(如Mixtral 32K, DeepSeek V3 128K)的核心差异化特征。

面试要点:GDN>MoE的优先级判断——“MoE是量变(参数效率),GDN是质变(长上下文能力)"。DeepSeek V3使用MLA(Multi-head Latent Attention)而非线性注意力来压缩KV缓存——两种方法目标相同但技术路线不同。

延伸阅读:主报告 CH9.2(架构决策优先级);CH3(GDN原理)。


Q9.3 如果要改进Qwen3.5-MoE,优先做什么?

简短回答:(1)专家动态分配——浅层和深层使用不同数量的专家;(2)GDN状态量化——将FP32状态量化为BF16节省显存;(3)更好的路由策略——探索aux-loss-free路由减少对负载均衡损失的依赖;(4)专家合并——训练后合并不常用的专家减少磁盘占用。

详细解释:动态专家分配的合理性——浅层处理通用语法特征(可能需要较少但较大的专家),深层处理抽象语义(可能需要较多但专业的专家)。当前统一的256E配置在各层可能不是最优的。分层定制化可减少浅层的参数浪费(如0-10层用64E、11-30层用256E、31-39层用512E),总参数可能减少但不损失深层容量。

GDN状态量化的挑战——当前mamba_ssm_dtype=float32使状态矩阵占60MB FP32。若能安全量化到BF16(30MB),对总显存影响虽然不大(节省30MB/5.4GB≈0.6%),但可能影响长序列的数值稳定性。需要深入研究量化方法(如混合精度——部分维度保持FP32)。更好的路由策略方向——DeepSeek V3的aux-loss-free策略通过动态偏置而非额外损失来平衡负载,值得借鉴。

面试要点:优先级排序反映工程判断——动态专家分配(最大收益)、路由策略(训练质量)、GDN量化(推理效率)、专家合并(部署便利)。追问"为什么不是增加层数”:层数增加→GDN+Full Attn参数比例上升→挤占专家参数预算。

延伸阅读:主报告 CH9.3(改进方向分析);DeepSeek V3论文(aux-loss-free routing)。


Q9.4 如何在生产环境中部署Qwen3.5-MoE?

简短回答:(1)使用vLLM或SGLang最新版本(支持混合注意力+MoE);(2)应用INT4/INT8权重量化(GDN状态保持FP32);(3)张量并行+专家并行(推荐4-8 GPU);(4)单GPU部署使用GGUF量化+llama.cpp(支持MoE offloading)。

详细解释:多GPU部署方案——4×24GB(RTX 4090或A5000):模型INT418GB+KV缓存(262K)5.4GB+GDN状态0.06GB+activations2GB=25.4GB,略微超出24GB。需要INT4+KVCache INT8(KV缓存减半至2.7GB)→总计约22.7GB,4卡可运行。8×80GB(A100):模型BF1670GB+KV缓存5.4GB+其他5GB=80.4GB,刚好在8卡A100上BF16运行(每卡10GB),无需量化。

vLLM/SGLang的适配需求——标准推理引擎需要识别Qwen3.5的混合架构:(1)GDN层使用chunk/recurrent双路径;(2)混合缓存管理(KV+SSM状态);(3)MoE专家并行(expert-centric批处理)。vLLM v0.6+和SGLang v0.3+已支持Qwen3.5。单GPU部署推荐llama.cpp的GGUF格式——支持Q4_K_M量化(~20GB),MoE专家可以offload到CPU内存,支持262K上下文(通过Flash Attention+GDN双路径)。

面试要点:部署方案取决于GPU配置——4×24GB用INT4+KVCache INT8;8×80GB用BF16;单卡用GGUF+offloading。关键数字:INT4模型~18GB、KV缓存(262K)5.4GB BF16或2.7GB INT8。

延伸阅读:主报告 CH9.4(部署指南);vLLM/SGLang Qwen3.5支持文档。


Q9.5 Qwen3.5-MoE的代码质量如何?

简短回答:代码质量很高。完整的类型注解、支持PyTorch纯实现+FLA加速双路径、支持Dynamo编译(@torch_compilable_check)、清晰的模块化设计(14个主要类各司其职)、良好的docstring和deprecation警告。~2061行代码覆盖了完整的MoE+GDN+多模态管线。

详细解释:架构组织——modeling_qwen3_5_moe.py按层次组织:(1)基础组件(RMSNorm, Embedding, RotaryEmbedding);(2)注意力模块(Qwen3_5MoeAttention, Qwen3_5MoeGatedDeltaNet);(3)MoE模块(Router, Experts, SparseMoeBlock);(4)Decoder Layer(条件初始化不同注意力);(5)Model(TextModel, Model含Vision);(6)对外接口(ForCausalLM, ForConditionalGeneration)+工具函数。这种自底向上的组织清晰易读。

存在的改进空间:(1)部分TODO注释未解决(如apply_mask_to_padding_states的注释);(2)FLA加速和PyTorch回退的双路径增加了代码复杂度(但对用户透明);(3)config.py中多个默认值与实际使用值不一致(需要注释说明或统一);(4)vision encoder和text model的代码风格略有差异。

面试要点:14个类+5个辅助函数+2061行代码。亮点:条件Token Mixer(layer_types驱动)、双路径加速(FLA/PyTorch)、3D专家权重存储。相比Llama modeling(~1200行),Qwen3.5-MoE更复杂但组织良好。

延伸阅读:主报告 CH8(源码映射汇总);modeling_qwen3_5_moe.py(2061行)。


Q9.6 Qwen3.5-MoE的未来演进方向可能是什么?

简短回答:(1)更深——增加层数以提升容量(如60-80层);(2)更宽——增加hidden_size配合更多专家;(3)全线性化——探索100% GDN(无Full Attention),类似Mamba纯SSM架构;(4)MoE层级化——在不同层级使用不同粒度的专家;(5)更好的训练方法——改进MTP、探索蒸馏和RLHF。

详细解释:全线性化的可行性——当前3:1的GDN:Full Attention比例已被证明有效。如果能将Full Attention也替换为更高效的全局注意力机制(如通过更大的GDN状态、或混合Mamba-2等),可以进一步减少KV缓存。但需要验证纯线性注意力在需要精确复制的任务(如代码生成、翻译)上的表现。Qwen团队可能已经在实验更大比例的GDN配置。

MoE层级化——借鉴DeepSeek-V2的细粒度专家思路,可以设计"层级专家”:浅层使用粗粒度专家(如64E,中间维度1024),中间层使用中等粒度(256E,512维),深层使用细粒度专家(512E,256维)。这使模型在不同抽象层次有最适配的专家粒度。更深+更宽的组合——若hidden=3072、60层、384 experts、top-8:总参数约100B,激活约5B(激活比5%),可能是Qwen3.5-100B的方向。

面试要点:五个方向按"激进程度"排序——全线性化(最激进,架构层面)>MoE层级化(架构创新)>更深更宽(规模扩展)>训练方法(优化)。已有Qwen3.5-397B-A17B变体验证了规模可扩展性。

延伸阅读:主报告 CH9.5(演进方向);Qwen3.5-397B-A17B 技术报告。


Q9.7 Qwen3.5-MoE的GDN是否有理论上限?

简短回答:是的。GDN将历史信息压缩为固定大小矩阵S_t(32×128×128,524K个float),信息容量受限于key_dim×value_dim=16,384个自由度/head。对于需要精确检索遥远历史中特定信息的任务,这个容量可能不够。Full Attention通过显式token-token注意力可以完美检索任意位置的精确信息(O(n^2)成本)。

详细解释:信息理论分析——GDN状态的总信息容量:32 heads×(128×128)=524,288个浮点数(float32约2MB)。与之对比,Full Attention在n=8192时的注意力矩阵:16 heads×(8192×8192)=1.07B个注意力权重。信息容量差距约2000倍。GDN的"优势"在于它不需要存储O(n^2)的信息——而是通过压缩策略(Delta Rule的"只存差异")有选择性地保留重要信息。

这个理论上限在实践中的体现——GDN在需要"精确复制"的任务上可能不如Full Attention(如代码生成中需要精确复制变量名、长文档问答中需要定位特定事实)。但在需要"概括理解"的任务上(如文档摘要、情感分析),GDN的压缩反而是一种优势(过滤噪声,保留要点)。Qwen3.5保留10层Full Attention正是为了弥补GDN在精确检索上的不足。

面试要点:GDN的容量上限=524K floats vs Attention的1.07B weights(n=8192时)。这就是为什么保留25%的Full Attention层是必要的——它们提供精确的全局信息检索能力。类比:“GDN是会议纪要(要点),Full Attention是会议录像(完整)"。

延伸阅读:主报告 CH9.6(GDN理论分析);DeltaNet论文 §5(信息容量讨论)。


Q9.8 如何看待Qwen3.5-MoE在开源模型生态中的定位?

简短回答:定位在中大规模的开源前沿模型——35B总参使质量可与更大模型竞争,3B激活使推理成本可与小模型竞争,GDN使长上下文场景有独特优势,原生多模态增加应用场景。与DeepSeek V3(671B/37B)形成互补——Qwen3.5更轻量但功能全面。

详细解释:竞品对比矩阵:(1)Mixtral 8x7B(47B/13B, 32K ctx)——Qwen3.5激活更小(3B vs 13B)+更长上下文(262K vs 32K)+多模态;(2)DeepSeek V3(671B/37B, 128K ctx)——Qwen3.5总参小得多(35B vs 671B)但上下文更长(262K vs 128K)+多模态;(3)Qwen2.5-72B(72B Dense, 128K)——Qwen3.5激活小得多(3B vs 72B)+更省显存。Qwen3.5的独特定位是"激活最小的开源多模态长上下文模型”。

生态支持——HuggingFace Transformers官方支持(一行from_pretrained)、vLLM/SGLang推理加速、llama.cpp GGUF量化、各大云平台(Together AI, Fireworks, Groq等)的托管服务。这个生态覆盖从研究(HF)到生产(vLLM)到边缘(llama.cpp)的全场景。

面试要点:Qwen3.5的生态定位=轻量+长上下文+多模态的MoE模型。关键对比:比DeepSeek V3小但上下文更长+多模态,比Mixtral更高效(激活小+上下文长),比Qwen2.5-72B推理成本低得多。

延伸阅读:主报告 CH9.7(生态定位分析);HuggingFace Open LLM Leaderboard。


Q9.9 从源码角度,Qwen3.5-MoE最有学习价值的设计模式是什么?

简短回答:四个有学习价值的设计模式:(1)条件Token Mixer——通过layer_types配置动态选择不同注意力类,而非继承或工厂模式;(2)双路径加速——chunk_gated_delta_rule(FLA优化)和torch_chunk_gated_delta_rule(纯PyTorch)的优雅降级;(3)3D权重存储——专家权重的(num_experts, dim1, dim2)布局使索引访问高效;(4)统一缓存接口——DynamicCache同时支持KV缓存和SSM状态。

详细解释:条件Token Mixer的实现——Qwen3_5MoeDecoderLayer.__init__中根据layer_types[layer_idx]选择注意力类,forward中根据self.self_attn的类型(或是否为None)做条件调用。这比继承(每种层类型一个子类)或工厂模式(额外抽象层)更简洁直观。对于需要灵活层结构的模型设计,这是一个优秀的参考模式。

双路径加速的优雅降级——代码先尝试导入FLA库(_has_fla=importlib.util.find_spec("fla") is not None),如果FLA可用则使用融合CUDA内核(更快),否则回退到纯PyTorch实现(更兼容)。这种"检测-使用最优-优雅降级"的模式保证了模型在任何环境(有无FLA)下都能运行。3D专家权重存储和统一缓存接口也是值得借鉴的实现模式。

面试要点:条件Token Mixer = “配置驱动而非继承驱动"的设计哲学。双路径加速 = “检测并自动使用最优实现"的工程实践。两个模式都可以直接应用到其他模型设计中。

延伸阅读:主报告 CH8(源码映射);CH9.8(设计模式总结);Qwen3_5MoeDecoderLayer.__init__ 条件初始化。


Q9.10 总结:Qwen3.5-MoE架构的"一个核心思想"是什么?

简短回答:“用最少的激活计算实现最大的模型容量”。通过三个机制实现:(1)Gated DeltaNet将token-mixing成本从O(n)降至O(1);(2)MoE(256E, k=8+1)将FFN成本控制在~29M参数/token;(3)3:1混合比例在最需要处保留Full Attention(25%层)。结果是35B的容量、3B的成本、262K的上下文。

详细解释:这个核心思想的三个支柱——(1)计算效率(GDN):30层线性注意力,每层仅需O(1)更新,长序列加速457倍;(2)参数效率(MoE):40层全MoE,激活比8.6%,11.6x参数效率提升;(3)精度保障(Full Attention):10层全局注意力,每4层一次精确信息整合。三者缺一不可——纯GDN会丢失精确检索能力,纯MoE无法支持超长上下文,纯Full Attention成本不可接受。

这个思想的"一个数字”——激活参数/总参数=3B/35B=8.6%。如果它是Dense模型,这个数字是100%。Qwen3.5用8.6%的成本实现了接近100%的容量。这在工程上是优雅的——没有牺牲质量,只是聪明地分配了计算资源。与DeepSeek V3的5.5%相比,Qwen3.5在效率与简洁性之间取得了良好的平衡。

面试要点:如果只被允许说一句话介绍Qwen3.5-MoE:“35B的容量、3B的成本、262K的上下文,靠GDN+MoE+3:1混合比例实现。“三个数字和三个技术一一对应。这是面试总结题的"标准答案”。

延伸阅读:主报告 CH9.9(核心思想总结);完整主报告各章节。