一、简介
25/4/29 发布的Qwen3系列模型,共 8 个模型,其中六个Dense 模型分别为,Qwen3-32B、Qwen3-14B、Qwen3-8B、Qwen3-4B、Qwen3-1.7B 和 Qwen3-0.6B。另外两个MoE 模型分别为,Qwen3-235B-A22B,拥有 2350 多亿总参数和 220 多亿激活参数的大模型,以及 Qwen3-30B-A3B,拥有约 300 亿总参数和 30 亿激活参数的小型 MoE 模型。
1.1 Qwen3-2507
在社区的反馈以及进一步研究的启发下,仅指令(Instruct-only)和仅思考(Thinking-only)模型回归啦!成果就是通义千问 3-2507(Qwen3-2507),three sizes, 235B-A22B, 30B-A3B, and 4B。
Qwen3-Instruct-2507 具备以下特点:
- 泛化能力显著提升,涵盖 指令遵循、逻辑推理、文本理解、数学、科学、编码以及工具使用。
- 长尾知识覆盖在多种语言上大幅增强。
- 在主观和开放式任务中与用户偏好的契合度明显提高,能够生成更有用的回复和更高质量的文本。
- 在 25.6 万长上下文理解方面能力增强,可扩展至 100 万。
Qwen3-Thinking-2507 具备以下特点:
- 推理任务性能显著提升,涵盖逻辑推理、数学、科学、编码以及通常需要人类专业知识的学术基准测试------在开源 thinking 模型中取得了领先的成果。
- 泛化能力显著增强,如指令遵循、工具使用、文本生成以及与人类偏好的一致性。
- 256K 长上下文理解能力得到强化,可扩展至 1M。
1.2 Qwen3-2504( Qwen3)
- 全尺寸稠密与混合专家模型:0.6B, 1.7B, 4B, 8B, 14B, 32B and 30B-A3B, 235B-A22B
- 支持在思考模式 (用于复杂逻辑推理、数学和编码)和 非思考模式 (用于高效通用对话)之间无缝切换,确保在各种场景下的最佳性能。
- 显著增强的推理能力,在数学、代码生成和常识逻辑推理方面超越了之前的 QwQ(在思考模式下)和 Qwen2.5 指令模型(在非思考模式下)。
- 卓越的人类偏好对齐,在创意写作、角色扮演、多轮对话和指令跟随方面表现出色,提供更自然、更吸引人和更具沉浸感的对话体验。
- 擅长智能体能力,可以在思考和非思考模式下精确集成外部工具,在复杂的基于代理的任务中在开源模型中表现领先。
- 支持 100 多种语言和方言,具有强大的多语言理解、推理、指令跟随和生成能力。
二、Qwen 模型结构解析
本节以 qwen3_moe 代码为例,解析一下结构。
2.1 总体网络结构
Qwen3 主要由四个部分组成:
- embed_tokens:嵌入层。这是模型处理输入的第一步。它的核心功能是将输入的离散文本符号(通常是经过 Tokenizer 处理后的 Token ID)转换为连续的、稠密的向量表示(称为嵌入向量或 Embeddings)。
- Decoder layers:多个堆叠的解码器。这是模型的核心计算引擎,负责理解输入序列的上下文、提取特征并进行深度信息处理。模型的能力(如理解、推理、生成)主要源于这些层。
Plain
self.layers = nn.ModuleList(
[Qwen3MoeDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
)
- norm:归一化层。处理完毕后,对最终的隐藏状态 (Hidden States) 进行最后一次归一化。
Plain
self.norm = Qwen3MoeRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
- rotary_emb:旋转位置编码。为模型提供关于序列中 Token 位置的信息。标准 Transformer 的自注意力机制本身是排列不变的(即打乱输入顺序可能得到相同结果),因此需要显式地注入位置信息。
Plain
self.rotary_emb = Qwen3MoeRotaryEmbedding(config=config)


总体网络结构需要结合 Qwen3MoeModel 类来看,Qwen3MoeModel 是基于混合专家(MoE)架构的语言模型,继承自 Qwen3MoePreTrainedModel。具体代码注解如下:
Plain
class Qwen3MoeModel(Qwen3MoePreTrainedModel):
def __init__(self, config: Qwen3MoeConfig):
super().__init__(config)
self.padding_idx = config.pad_token_id
#如 Transformer 解码器层)的特征向量维度,即每个 token 经过隐藏层处理后输出的向量长度
self.vocab_size = config.vocab_size #151936,
#hidden_size:2048
self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
#num_hidden_layers:24
self.layers = nn.ModuleList(
[Qwen3MoeDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
)
self.norm = Qwen3MoeRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
self.rotary_emb = Qwen3MoeRotaryEmbedding(config=config)
self.gradient_checkpointing = False
# Initialize weights and apply final processing
self.post_init()
@check_model_inputs
@auto_docstring
def forward(
self,
#可选的整数张量,通常是输入文本经过分词后的索引序列(如单词 / 子词的 ID),是模型最常见的输入形式。
input_ids: Optional[torch.LongTensor] = None,
#可选的张量,用于标记输入序列中哪些位置是有效内容(1)、哪些是填充(0),避免模型关注无效信息。
attention_mask: Optional[torch.Tensor] = None,
#可选的整数张量,标记每个 token 在序列中的位置,辅助模型理解语序(部分模型会自动生成)。
position_ids: Optional[torch.LongTensor] = None,
#可选的缓存对象,用于存储之前计算的键(key)和值(value),
#在生成式任务(如文本续写)中加速推理,避免重复计算历史序列
past_key_values: Optional[Cache] = None,
#可选的浮点张量,直接输入预计算的嵌入向量(替代input_ids,适用于已处理过的特征输入)。
inputs_embeds: Optional[torch.FloatTensor] = None,
#指定是否采用缓存
use_cache: Optional[bool] = None,
#可选的整数张量,标记缓存中需要更新的位置,配合past_key_values使用。
cache_position: Optional[torch.LongTensor] = None,
**kwargs: Unpack[TransformersKwargs],
) -> MoeModelOutputWithPast:
if (input_ids is None) ^ (inputs_embeds is not None):#异或计算,二者不可同时存在
raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
#使用KV cache,DynamicCache支持灵活的序列长度变化,自动扩展容量
if use_cache and past_key_values is None:
past_key_values = DynamicCache(config=self.config)
#当inputs_embeds为None时(即用户未直接提供输入嵌入),
#通过self.embed_tokens将input_ids(文本的整数编码)转换为对应的嵌入向量(inputs_embeds)。
#self.embed_tokens通常是一个nn.Embedding层,负责将离散的 token 索引映射为连续的向量表示,
#是语言模型中将文本转换为模型可处理的数值形式的核心步骤。
if inputs_embeds is None:
inputs_embeds = self.embed_tokens(input_ids)
#确定当前输入的每个 token 在缓存中的位置,以便后续在生成文本或处理长序列时,
#能正确关联历史缓存和当前输入,实现高效的上下文关联和缓存管理(比如 Transformer 中的 K/V 缓存)。
if cache_position is None:
past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
cache_position = torch.arange(
past_seen_tokens, past_seen_tokens + inputs_embeds.shape[1], device=inputs_embeds.device
)
if position_ids is None:
position_ids = cache_position.unsqueeze(0)
#选择不同的掩码函数
mask_function = create_causal_mask if self.config.sliding_window is None else create_sliding_window_causal_mask
causal_mask = mask_function(
config=self.config,
input_embeds=inputs_embeds,
attention_mask=attention_mask,
cache_position=cache_position,
past_key_values=past_key_values,
position_ids=position_ids,
)
hidden_states = inputs_embeds
# create position embeddings to be shared across the decoder layers
position_embeddings = self.rotary_emb(hidden_states, position_ids)
for decoder_layer in self.layers[: self.config.num_hidden_layers]:
hidden_states = decoder_layer(
hidden_states,
position_embeddings=position_embeddings,
attention_mask=causal_mask,
position_ids=position_ids,
past_key_values=past_key_values,
use_cache=use_cache,
cache_position=cache_position,
**kwargs,
)
hidden_states = self.norm(hidden_states)
return MoeModelOutputWithPast( # only diff with Mistral is the output type, we need MoE
last_hidden_state=hidden_states,
past_key_values=past_key_values,
)
2.2 Qwen3MoeDecoderLayer 解析
Qwen3MoeDecoderLayer 的结构图如下所示:

下面逐个对上图中的 block 进行解释。
2.2.1 Qwen3MoeRMSNorm
- 功能:实现 RMS 归一化,通过对输入特征的均方根进行缩放,稳定数值分布,类似 LayerNorm 但计算更轻量(不含均值中心化)。
- 初始化:定义可学习的缩放权重
weight(维度与hidden_size一致)和数值稳定参数eps(避免除零)。 - 前向传播:

- 装饰器:
@use_kernel_forward_from_hub("RMSNorm")表示从 hub 加载优化的 RMSNorm 内核实现(可能是高效的 C++/CUDA 算子),提升计算速度。
Plain
@use_kernel_forward_from_hub("RMSNorm")
class Qwen3MoeRMSNorm(nn.Module):
def __init__(self, hidden_size, eps=1e-6):
"""
Qwen3MoeRMSNorm is equivalent to T5LayerNorm
"""
super().__init__()
self.weight = nn.Parameter(torch.ones(hidden_size))
self.variance_epsilon = eps
def forward(self, hidden_states):
input_dtype = hidden_states.dtype
hidden_states = hidden_states.to(torch.float32)
variance = hidden_states.pow(2).mean(-1, keepdim=True)
hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
return self.weight * hidden_states.to(input_dtype)
def extra_repr(self):
return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
2.2.2 Qwen3MoeAttention
Qwen3 的注意力机制在 Qwen2 的基础上进行了微调,在 Q、K 的线性投影后面分别加入了一个归一化层,有助于提高稳定性。

2.2.3 Qwen3MoeSparseMoeBlock
我们从 Qwen3 的Transformer的实现来学习下优化方式。
将传统 Transformer 模型中的全连接 MLP 层替换为新型的稀疏专家模块(Qwen3MoeSparseMoeBlock)。该模块由以下两个关键组件构成:
- 包含 num_experts 个轻量级专家网络(Qwen3MoeMLP)的并行计算单元;
- 基于注意力机制的路由网络(gate)。在计算过程中,路由网络通过动态决策机制为每个输入 Token 生成路由决策,筛选出匹配度最高的 top_k 个专家节点。随后,系统将根据路由权重对选定专家的计算结果进行加权融合,最终生成该隐层的表征输出。
那么同样我们对比DeepSeekMOE,Qwen3MOE 有两个点的改变:1)没有 shared expert。2.优化了 MLP 架构,变为 Qwen3MoeSparseMoeBlock。
代码解析:
Plain
class Qwen3MoeSparseMoeBlock(nn.Module):
"""
Qwen3混合专家模型中的稀疏MoE模块,通过路由器选择部分专家处理输入,实现高效计算
"""
def __init__(self, config: Qwen3MoeConfig):
super().__init__()
# 1. 基础配置参数
self.hidden_size = config.hidden_size # 输入特征维度
self.num_experts = config.num_experts # 专家网络总数:128
self.num_experts_per_tok = config.num_experts_per_tok # 每个token激活的专家数:8
# 2. 路由器(Router):决定每个token选择哪些专家
# 输入:[batch_size, seq_len, hidden_size],输出:[batch_size, seq_len, num_experts](专家权重)
self.gate = nn.Linear(self.hidden_size, self.num_experts, bias=False)
# 3. 初始化专家网络(每个专家为独立的MLP)
# 专家网络通常采用与稠密MLP相同的结构(如两次线性变换+激活函数)
self.experts = nn.ModuleList([
Qwen3MoeMLP(config) # 复用Qwen3的MLP结构作为专家
for _ in range(self.num_experts)
])
# 4. 损失函数相关(可选,用于训练时平衡专家负载)
self.router_aux_loss_coef = config.router_aux_loss_coef # 路由器辅助损失系数
def forward(self, hidden_states: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
"""
前向传播:输入特征 → 路由器选专家 → 专家计算 → 融合输出
Args:
hidden_states: 输入特征,形状为 [batch_size, seq_len, hidden_size]
Returns:
output: 融合后的输出特征,形状同输入
router_aux_loss: 路由器辅助损失(用于训练时优化专家负载均衡)
"""
# ===== 步骤1:计算路由器输出(专家权重)=====
# 输入通过线性层得到每个专家的原始分数(logits)
# 形状:[batch_size, seq_len, num_experts]
router_logits = self.gate(hidden_states)
# ===== 步骤2:选择Top-K专家并计算权重 =====
# 对每个token,选择分数最高的num_experts_per_tok个专家
# top_k_weights: 选中专家的权重(经softmax归一化),形状 [batch_size, seq_len, num_experts_per_tok]
# top_k_indices: 选中专家的索引,形状 [batch_size, seq_len, num_experts_per_tok]
top_k_weights, top_k_indices = torch.topk(router_logits, self.num_experts_per_tok, dim=-1)
top_k_weights = nn.functional.softmax(top_k_weights, dim=-1, dtype=torch.float32)
# ===== 步骤3:计算路由器辅助损失(可选,训练用)=====
# 目的是鼓励专家负载均衡,避免少数专家被频繁选中
# 计算方式:对router_logits做softmax后取均值,再求负熵(简化实现)
if self.training:
# 先对专家分数做softmax,得到每个专家被选中的概率
router_probs = nn.functional.softmax(router_logits, dim=-1, dtype=torch.float32)
# 计算每个专家的平均负载(跨batch和seq_len)
expert_load = torch.mean(router_probs, dim=(0, 1)) # 形状 [num_experts]
# 辅助损失:鼓励负载均衡(熵越大,分布越均衡)
router_aux_loss = torch.sum(expert_load * torch.log(expert_load + 1e-10)) # 负熵
router_aux_loss *= self.router_aux_loss_coef # 乘以系数
else:
router_aux_loss = torch.tensor(0.0, device=hidden_states.device) # 推理时无损失
# ===== 步骤4:准备输入,分发到选中的专家 =====
# 调整输入形状为 [batch_size * seq_len, hidden_size],便于批量处理
batch_size, seq_len, hidden_size = hidden_states.shape
hidden_states = hidden_states.view(-1, hidden_size) # 形状 [total_tokens, hidden_size],total_tokens = batch_size * seq_len
# 调整选中专家索引形状:[total_tokens, num_experts_per_tok]
top_k_indices = top_k_indices.view(-1, self.num_experts_per_tok) # [total_tokens, k]
# 调整权重形状:[total_tokens, num_experts_per_tok, 1](便于广播)
top_k_weights = top_k_weights.view(-1, self.num_experts_per_tok, 1) # [total_tokens, k, 1]
# ===== 步骤5:专家计算与结果融合 =====
# 初始化输出张量
final_output = torch.zeros(
(batch_size * seq_len, hidden_size), # 与输入同形状
dtype=hidden_states.dtype,
device=hidden_states.device
)
# 遍历每个专家,处理所有选中该专家的token
for expert_idx in range(self.num_experts):
# 找到所有选中当前专家的token索引
# 掩码:[total_tokens, k] → True表示该位置选中了当前专家
expert_mask = (top_k_indices == expert_idx) # [total_tokens, k]
# 若没有token选中当前专家,跳过
if not expert_mask.any():
continue
# 收集选中当前专家的token及其对应的权重
# 1. 提取这些token的输入特征
expert_input = hidden_states[expert_mask.any(dim=1)] # [num_tokens_for_this_expert, hidden_size]
# 2. 提取这些token对当前专家的权重(取第一个匹配的权重,因每个位置最多选k个专家)
expert_weights = top_k_weights[expert_mask] # [num_tokens_for_this_expert, 1]
# 3. 当前专家处理输入
expert_output = self.experts[expert_idx](expert_input) # [num_tokens_for_this_expert, hidden_size]
# 4. 加权:用该专家的权重乘以输出
expert_output = expert_output * expert_weights # [num_tokens_for_this_expert, hidden_size]
# 5. 将结果累加至最终输出(对应位置)
final_output[expert_mask.any(dim=1)] += expert_output
# ===== 步骤6:恢复输出形状并返回 =====
final_output = final_output.view(batch_size, seq_len, hidden_size) # [batch_size, seq_len, hidden_size]
return final_output, router_aux_loss
三、QWen3 部署实战
你可以在 Hugging Face Hub 的 Qwen3 collection 或 ModelScope 的 Qwen3 collection 中获取 Qwen3 模型。
Plain
使用 huggingface-cli 命令行工具:
安装依赖:首先确保已安装huggingface_hub,可运行命令pip install -U huggingface_hub。
设置镜像地址:为提高下载速度,可设置国内镜像,如export HF_ENDPOINT=https://hf-mirror.com(Linux 系统)。
下载模型:使用huggingface-cli download命令下载,例如huggingface-cli download --resume-download gpt2 --local-dir gpt2,将gpt2模型下载到当前目录下的gpt2文件夹中。若要下载特定文件,可在模型名称后添加文件名,如huggingface-cli download gpt2 config.json。
Plain
huggingface-cli download Qwen/Qwen3-4B-Thinking-2507 --local-dir Qwen3-4B-Thinking-2507
huggingface 中下载的文件:
Plain
├── config.json
├── generation_config.json
├── LICENSE
├── merges.txt
├── model-00001-of-00003.safetensors
├── model-00002-of-00003.safetensors
├── model-00003-of-00003.safetensors
├── model.safetensors.index.json
├── README.md
├── tokenizer_config.json
├── tokenizer.json
└── vocab.json
文件说明
config.json- 模型的结构配置文件,比如隐藏层维度、层数、注意力头数、激活函数等。
AutoModelForCausalLM.from_pretrained()会先读取它,决定用哪种架构初始化模型。
generation_config.json- 文本生成时的默认参数,例如
max_new_tokens,temperature,top_p,do_sample等。 - 如果你用
model.generate(),没手动传参数,就会用这里的默认值。
- 文本生成时的默认参数,例如
merges.txt- BPE (Byte Pair Encoding) 分词的合并规则文件。
- 跟
vocab.json一起定义了 tokenizer 的词表。
vocab.json- tokenizer 的词表文件,存储了 token 到 ID 的映射。
- 例如
"hello" -> 1234。
tokenizer.json- Hugging Face 的标准 tokenizer 文件,包含 vocab 和 merges 的完整定义。
- 用
AutoTokenizer.from_pretrained()会加载它。
tokenizer_config.json- Tokenizer 的额外参数,比如是否大小写敏感、padding/truncation 策略等。
model-00001-of-00003.safetensors, model-00002-of-00003.safetensors, model-00003-of-00003.safetensors- 模型的权重文件,分成了多个分片,每个几 GB。
safetensors是一种比pytorch_model.bin更安全和高效的格式。
model.safetensors.index.json- 权重索引文件,指明每个参数在分片文件中的位置。
- 加载模型时,transformers 会先读这个文件,再去对应分片里加载。
将 huggingface 中的文件全部下载到 "。/Qwen/Qwen3-4B-Thinking-2507"文件夹
在 docker 环境中安装(torch 2.3.0):
Plain
pip3 install transformers==4.55.0
pip3 install accelerate
部署代码:
Plain
from transformers import AutoModelForCausalLM, AutoTokenizer
#下载的权重文件的路径
model_name = "./Qwen/Qwen3-4B-Thinking-2507"
# load the tokenizer and the model
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype="auto",
device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained(model_name)
# prepare the model input
prompt = "你怎么认为普京"
messages = [
{"role": "user", "content": prompt},
]
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
enable_thinking=True, # Switches between thinking and non-thinking modes. Default is True.
)
model_inputs = tokenizer([text], return_tensors="pt").to(model.device)
# conduct text completion
g