游乐游手机版
首页/AI热点日报/热点详情

Qwen1.5 MoE解析稀疏大模型的高效智能

类型:热点整理2026-06-12
引言2025年3月28日,阿里正式宣布开源其首个MoE技术大模型——Qwen1 5-MoE-A2 7B。这一消息在当时引发了广泛关注,背后逻辑颇具深意:用更少的激活参数,去挑战更大参数规模模型的性能表现,为稀疏大模型的高效智能落地开辟了新路径。该模型基于已有的Qwen-1 8B模型,激活参数仅为2

引言

2025年3月28日,阿里正式宣布开源其首个MoE技术大模型——Qwen1.5-MoE-A2.7B。这一消息在当时引发了广泛关注,背后逻辑颇具深意:用更少的激活参数,去挑战更大参数规模模型的性能表现,为稀疏大模型的高效智能落地开辟了新路径。

解读Qwen1.5 MoE:稀疏大模型的高效智能

该模型基于已有的Qwen-1.8B模型,激活参数仅为2.7亿,但在多项基准评估中表现出色,达到了7B级别模型的水准。更重要的是,相比传统7B模型,它在训练成本和推理速度上均展现出显著优势。接下来,我们将结合官方博客与开源代码,详细拆解模型的设计细节与实现逻辑。

文章将从参数规模、模型结构、训练推理效率等核心维度展开,并深入代码层面,剖析MoE机制的具体运作方式。

参数量

Qwen1.5-MoE-A2.7B的名称直接揭示了关键信息:它仅包含27亿个激活参数,但性能足以与当前最优秀的70亿参数模型——如Mistral 7B和Qwen1.5-7B——正面抗衡。

对比一下更直观:Qwen1.5-7B的Non-Embedding参数为65亿,而Qwen1.5-MoE-A2.7B仅有20亿,仅为前者的三分之一。实际效果如何?训练成本直降75%,推理速度提升至1.74倍。换句话说,投入更少、运行更快、效果相当——这正是MoE架构的独特魅力。

官方评测也佐证了这一点:Qwen1.5-MoE-A2.7B在与最佳7B模型的对标中,取得了非常接近的性能

模型结构

Qwen1.5-MoE在结构上进行了若干富有创意的改进。通常,像Mixtral这类模型会在每个Transformer块安排8个专家,并采用top-2门控路由。但Qwen团队认为其中仍有优化空间,于是从三个方面着手改进:

Finegrained Experts

这一概念在DeepSeek-MoE和DBRX中已验证有效。从FFN层过渡到MoE层时,常见做法是简单复制多次FFN以生成多个专家。但Finegrained Experts的思路不同:它旨在在不增加总参数数的前提下,生成更多专家。具体做法是将单个FFN分割成几个部分,每一部分作为一个独立专家。最终,Qwen1.5-MoE设计了总共64个专家,该配置在效果与效率之间找到了不错的平衡点。

初始化策略

初始化环节至关重要。早期实验表明,从零开始训练MoE模型不仅效率低,而且难以达到预期的理想性能。因此,Qwen团队选择了一条更务实的路径:直接基于已有的Qwen-1.8B进行改造,将其升级为Qwen1.5-MoE-A2.7B。同时,在初始化阶段引入一定程度的随机性,可显著加快收敛速度。不过,该随机性具体施加在哪个环节,目前博客与代码中尚未明确体现,这仍是一个值得深挖的技术细节。

新的路由机制

当前MoE领域有一个明显趋势:引入共享专家与路由专家相结合的机制。从宏观角度看,这本质是一种广义路由方法——如果去掉共享专家,就会退化为传统MoE路由设置。Qwen1.5-MoE的做法是:整合4个始终被激活的共享专家,以及60个路由专家,每次只激活其中4个。这种设计既灵活,又在实验中表现出最佳效率。

总结该模型的MoE配置如下:

  • “类似”8×1.8B的Top2激活MoE:但实际上并非8个专家,而是采用了Finegrained Experts,总共64个专家,每次激活8个“Finegrained Expert”
  • Finegrained Experts:参照DeepSeek-MoE和DBRX的思路,将单个FFN拆分多个部分,最终形成64个专家,激活8个
  • 初始化:基于Qwen-1.8B,并引入随机性以加速收敛
  • 路由机制:4个共享专家(始终激活) + 60个路由专家(每次激活4个)

训练成本与推理效率

MoE模型与Dense模型在训练成本上的差异十分显著。虽然MoE模型的总参数规模通常更大,但得益于稀疏性,训练开销反而能明显降低。我们先看几个关键参数:总参数量、激活参数量和Non-Embedding参数量。

  • 尽管MoE模型的总参数量较大,但Non-Embedding激活参数量远小于7B模型
  • 实际操作中,使用Qwen1.5-MoE-A2.7B相比Qwen1.5-7B,训练成本降低了75%
  • 由于采用了特殊的初始化方法,模型无需训练相同数量的token即可达到良好效果,这进一步压低了训练成本

代码解读

Config配置解析

从配置文件中,我们可以洞察许多设计细节:

{
  "architectures": [
    "Qwen2MoeForCausalLM"
  ],
  "attention_dropout": 0.0,
  "bos_token_id": 151643,
  "eos_token_id": 151643,
  "hidden_act": "silu",
  "hidden_size": 2048,
  "initializer_range": 0.02,
  "intermediate_size": 5632,
  "max_position_embeddings": 8192,
  "max_window_layers": 21,
  "model_type": "qwen2_moe",
  "num_attention_heads": 16,
  "num_hidden_layers": 24,
  "num_key_value_heads": 16,
  "rms_norm_eps": 1e-06,
  "rope_theta": 1000000.0,
  "sliding_window": 32768,
  "tie_word_embeddings": false,
  "torch_dtype": "bfloat16",
  "transformers_version": "4.39.0.dev0",
  "use_cache": true,
  "use_sliding_window": false,
  "vocab_size": 151936,
  "decoder_sparse_step": 1,
  "moe_intermediate_size": 1408,
  "shared_expert_intermediate_size": 5632,
  "num_experts_per_tok": 4,
  "num_experts": 60,
  "norm_topk_prob": false,
  "output_router_logits": false,
  "router_aux_loss_coef": 0.001
}
  • 激活函数:silu
  • 上下文长度:8K
  • rope_theta:100万
  • tie_word_embeddings:未启用参数共享
  • moe_intermediate_size与shared_expert_intermediate_size:对应4个共享专家
  • num_experts_per_tok与num_experts:对应60个专家选4个
  • router_aux_loss_coef:专家负载均衡的辅助损失系数

模型主结构介绍

Qwen2MoeForCausalLM基于混合专家架构,采用稀疏激活策略,每个输入序列只激活少数专家。下面拆解其核心组件:

模型构造(__init__方法)

  • self.model:由多个Qwen2MoeDecoderLayer组成的解码器模型,每个解码层包含注意力层和前馈网络(可能是Qwen2MoeMLPQwen2MoeSparseMoeBlock
  • self.lm_head:一个线性层,将解码器输出映射为词汇表长度的logits
  • self.router_aux_loss_coef:用于辅助损失计算的系数,帮助平衡各专家负载

前向传播(forward方法)

  • 调用self.model执行解码器前向传播
  • 隐藏状态送入self.lm_head生成logits
  • 如果提供了labels,则计算交叉熵损失
  • 如果需要输出路由器logits,则计算并返回路由每个token到不同专家的概率分布

该模型的几个特色部分:

  • Mixture of Experts(MoE):通过Qwen2MoeSparseMoeBlock实现,根据路由决策将计算分配给不同专家
  • 共享专家:通过self.shared_expert定义了一个始终被激活的全连接层
  • 稀疏激活:使用routing_weightsselected_experts实现,每个输入仅激活权重最高的几个专家
  • 路由器辅助损失:通过load_balancing_loss_func促进负载均衡,避免少数专家被过度使用
  • 参数初始化:通过_init_weights方法引入随机性,提高收敛速度

简而言之,Qwen2MoeForCausalLM通过混合专家架构、稀疏激活、共享与路由专家结合、辅助损失以及精巧的初始化策略,实现了参数利用率与计算效率的双重提升。

路由机制

接下来是核心代码部分——Qwen2MoeSparseMoeBlock模块,它是整个MoE机制的关键实现:

class Qwen2MoeSparseMoeBlock(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.num_experts = config.num_experts
        self.top_k = config.num_experts_per_tok
        self.norm_topk_prob = config.norm_topk_prob
        self.gate = nn.Linear(config.hidden_size, config.num_experts, bias=False)
        self.experts = nn.ModuleList(
            [Qwen2MoeMLP(config, intermediate_size=config.moe_intermediate_size) for _ in range(self.num_experts)]
        )
        self.shared_expert = Qwen2MoeMLP(config, intermediate_size=config.shared_expert_intermediate_size)
        self.shared_expert_gate = torch.nn.Linear(config.hidden_size, 1, bias=False)

    def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
        batch_size, sequence_length, hidden_dim = hidden_states.shape
        hidden_states = hidden_states.view(-1, hidden_dim)
        router_logits = self.gate(hidden_states)
        routing_weights = F.softmax(router_logits, dim=1, dtype=torch.float)
        routing_weights, selected_experts = torch.topk(routing_weights, self.top_k, dim=-1)
        if self.norm_topk_prob:
            routing_weights /= routing_weights.sum(dim=-1, keepdim=True)
        routing_weights = routing_weights.to(hidden_states.dtype)
        final_hidden_states = torch.zeros(
            (batch_size * sequence_length, hidden_dim), dtype=hidden_states.dtype, device=hidden_states.device
        )
        expert_mask = torch.nn.functional.one_hot(selected_experts, num_classes=self.num_experts).permute(2, 1, 0)
        for expert_idx in range(self.num_experts):
            expert_layer = self.experts[expert_idx]
            idx, top_x = torch.where(expert_mask[expert_idx])
            if top_x.shape[0] == 0:
                continue
            top_x_list = top_x.tolist()
            idx_list = idx.tolist()
            current_state = hidden_states[None, top_x_list].reshape(-1, hidden_dim)
            current_hidden_states = expert_layer(current_state) * routing_weights[top_x_list, idx_list, None]
            final_hidden_states.index_add_(0, top_x, current_hidden_states.to(hidden_states.dtype))
        shared_expert_output = self.shared_expert(hidden_states)
        shared_expert_output = F.sigmoid(self.shared_expert_gate(hidden_states)) * shared_expert_output
        final_hidden_states = final_hidden_states + shared_expert_output
        final_hidden_states = final_hidden_states.reshape(batch_size, sequence_length, hidden_dim)
        return final_hidden_states, router_logits

__init__方法

  • num_experts:专家总数
  • top_k:每个token选择的专家数
  • norm_topk_prob:是否对top-k权重归一化
  • gate:线性层,生成每个专家的logits
  • experts:专家网络列表,每个都是Qwen2MoeMLP
  • shared_expert:始终激活的共享专家
  • shared_expert_gate:控制共享专家在最终输出中的权重

forward方法

  • 将隐藏状态reshape为[batch_size * sequence_length, hidden_dim]
  • 通过gate计算路由器logits,再经softmax获得路由权重
  • 选取top_k权重最大的专家
  • 若启用归一化,则按行归一化
  • 创建零张量用于累积专家计算结果
  • 对每个专家:检查是否有token被分配,若有则计算该专家输出,并按路由权重缩放后累加
  • 共享专家处理所有隐藏状态,经sigmoid门控缩放后加到最终结果上
  • 将结果恢复为[batch_size, sequence_length, hidden_dim]并返回

整个流程清晰实现了MoE机制:将隐藏状态分配给各个专家和一个共享专家,汇总所有输出后再传递至下一层。这样既大幅提升了模型的表示能力,又保持了计算效率。

几个值得留意的点

  • 路由机制是60选4,通过self.gate线性层和top-k实现
  • 虽然代码中只出现了一个shared_expert,但在配置中shared_expert的隐藏层大小已经设置为4倍于普通专家——因此这4个共享专家被“打包”在了一起

总结

阿里首个MoE模型Qwen1.5-MoE-A2.7B,以不到三分之一的激活参数,达到了当前最优秀7B模型的性能水平。更重要的是,它在训练成本和推理速度上实现了相当显著的优化。对于正在探索平衡模型性能与计算成本的团队来说,这一方向确实值得深入研究。

来源:https://www.53ai.com/news/qianyanjishu/959.html

相关热点

继续查看同栏目近期热点。

延伸阅读

补充最近整理过的热点入口。