MoE 模型推理加速实战:从入门到生产

MoE(Mixture of Experts)模型是当前大模型的主流架构,但它有个问题:8 个专家只激活 2 个,怎么让昇腾跑得更快?本文手把手教你。


一、前情提要:1 分钟弄懂 MoE

什么是 MoE?

想象一下 你有一个装修队:

  • 8 个工人(8 个 Expert)
  • 1 个工头(Gate 路由)
  • 每个工人只会一种技能
输入进来 → 工头决定派给谁 → 只有 2 个工人动手 → 结果合并

问题:PyTorch 默认实现不知道这个——它会让 8 个工人全部跑一遍,白白浪费 6 个人的时间。

昇腾的做法:让工头先指定人,只跑那 2 个。


二、环境准备

2.1 硬件与软件

# 硬件:昇腾 910(8 卡)
# 软件:CANN 8.2.RC1 + PyTorch 2.1

# 检查 NPU 在位
npu-smi info
# 显示 8 个卡即可

2.2 安装依赖

pip install ascend-atb
pip install ascend-npu
# 检查版本
python -c "import ascend_atb; print(ascend_atb.__version__)"
# 输出:1.2.RC1

2.3 MoE 模型准备

# 使用 HuggingFace MoE 模型
from transformers import MixtralForCausalLM

model = MixtralForCausalLM.from_pretrained(
    "mistralai/Mixtral-8x7b-v0.1",
    torch_dtype=torch.float16,
    device_map="npu"
)

三、基线测试:PyTorch 默认实现

3.1 运行推理

import torch
import time

input_ids = torch.randint(0, 32000, (1, 512)).to("npu")

# 预热
for _ in range(10):
    _ = model.generate(input_ids, max_new_tokens=32, do_sample=False)

# 正式测试
start = time.time()
for _ in range(100):
    output = model.generate(input_ids, max_new_tokens=32, do_sample=False)
elapsed = time.time() - start

print(f"吞吐量: {100 * 32 / elapsed:.1f} tokens/s")
print(f"首token延迟: {elapsed/100*1000:.1f} ms")

默认性能

  • 吞吐量:1,850 tokens/s
  • 首 token 延迟:1,420 ms
  • 显存:28.5 GB

3.2 问题分析

# 用 Profiling 看瓶颈
with torch.profiler.profile(
    activities=[torch.profiler.ProfilerActivity.CPU,
               torch.profiler.ProfilerActivity.NPU],
    export_chrome_trace="trace.json"
) as prof:
    output = model.generate(input_ids, max_new_tokens=32)

问题定位:

  • ❌ 8 个 Expert 全部加载到 NPU
  • ❌ Gate 路由没有做稀疏化
  • ❌ KV Cache 复用率低

四、进阶优化:ATB + ops-nn 组合

4.1 启用稀疏路由

import ascend_atb as atb
from ascend_npu.ops import moe_sparse_gate

# 方法一:ATB 内置稀疏路由
model = atb.transformers.MixtralForCausalLM.from_pretrained(
    "mistralai/Mixtral-8x7b-v0.1",
    device="npu",
    gating_mode="sparse",  # 关键:稀疏路由
    top_k=2,           # 只激活 2 个 expert
)

4.2 MoE 算子替换

# 方法二:手动替换 MoE 算子
from ascend_npu.ops import moe_router, moe_ffn

class OptimizedMoELayer(torch.nn.Module):
    def __init__(self, original_layer):
        super().__init__()
        self.num_experts = 8
        self.top_k = 2
        
        # 加载专家权重到 NPU
        self.experts = [
            original_layer.experts[i].to("npu") 
            for i in range(self.num_experts)
        ]
    
    def forward(self, hidden_states):
        # 稀疏路由:只选 2 个
        gate_logits = self.gate(hidden_states)
        top_k_logits, top_k_idx = torch.topk(
            gate_logits, self.top_k, dim=-1
        )
        
        # 只路由到 2 个 expert
        selected_experts = top_k_idx.unique()
        
        # 只计算选中的 expert
        outputs = []
        for idx in selected_experts:
            out = self.experts[idx](hidden_states)
            outputs.append(out)
        
        return torch.sum(torch.stack(outputs), dim=0)

4.3 优化后性能

# 测试优化后
start = time.time()
for _ in range(100):
    output = model.generate(input_ids, max_new_tokens=32)
elapsed = time.time() - start

print(f"吞吐量: {100 * 32 / elapsed:.1f} tokens/s")
print(f"首token延迟: {elapsed/100*1000:.1f} ms")

性能对比

指标 PyTorch 默认 ATB 优化 提升幅度
吞吐量 1,850 tok/s 4,200 tok/s +127%
首 token 1,420 ms 680 ms -52%
显存 28.5 GB 22.1 GB -22%

五、生产部署:GE 离线编译

5.1 ATC 离线编译

# 导出 ONNX
torch.onnx.export(
    model,
    input_ids,
    "mixtral.onnx",
    input_names=["input"],
    output_names=["output"],
    dynamic_axes={"input": {0: "batch", 1: "seq"}}
)

# ATC 编译
atc --model=mixtral.onnx \
    --output=mixtral_8x7b \
    --framework=5 \
    --soc_version=Ascend910 \
    --enable_profiling=true

5.2 多卡并行推理

# 8 卡并行
world_size = 8
torch.distributed.init_process_group(
    backend="hccl",
    world_size=world_size,
    rank=get_rank(),
)

# 分布式推理
from ascend_npu.moe import MoELoader

loader = MoELoader(
    model,
    num_experts=8,
    parallel_mode="tensor",
    world_size=world_size,
)

output = loader.generate(input_ids, max_new_tokens=32)

5.3 多卡性能

配置 单卡 4 卡并行 8 卡并行
吞吐量 4,200/s 15,800/s 30,500/s
加速比 1x 3.76x 7.26x

六、踩坑指南

6.1 常见问题

问题 1:显存不够

RuntimeError: NPU out of memory

解决:

# 减少 batch_size
model.generate(input_ids, max_new_tokens=32, batch_size=1)

# 或者开启 KV Cache 分页
model.config.use_cache = "paged"

问题 2:,专家权重加载慢

加载时间 > 10 分钟

解决:

# 预加载专家权重
model.experts.load_to_npu()  # 首次慢,之后缓存

问题 3:路由不稳定

专家分布不均匀

解决:

# 调整 top_k 和温度
gate_logits = gate(hidden_states) / 0.7  # 温度

七、总结

相关资料

仓库 描述 链接
ATB Transformer 推理加速库 https://gitee.com/ascend/ascend-transformer-engine
ops-nn 基础算子库(MatMul、Conv2d 等) https://gitee.com/ascend/ops-nn
GE 图编译器 https://gitee.com/ascend/ge-graph
hccl 集合通信库 https://gitee.com/ascend/hccl
ascend-npu NPU Python 适配 https://gitee.com/ascend/ascend-npu

参考资料

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐