跳转至

推理优化

⚠️ 时效性说明:本章涉及前沿模型/价格/榜单等信息,可能随版本快速变化;请以论文原文、官方发布页和 API 文档为准。

LLM 推理服务架构图

图源:Google Cloud GKE 教程 - Build and deploy an agentic AI application with ADK and vLLM on GKE,许可:CC BY 4.0。这里用真实推理服务部署图替换原占位导图,帮助理解 CPU 编排层、GPU 推理层与模型服务边界。

📌 定位说明:本章侧重推理优化的工程部署实践。 - 📖 推理优化的算法原理与技术研究请参考 LLM 学习/02-大模型核心技术/02-推理优化技术

📖 章节导读

推理优化是通过各种技术和方法提高大模型推理速度、降低资源消耗的关键技术。本章将深入探讨推理优化的原理、方法和实践。

🎯 学习目标

  • 理解推理优化的核心原理
  • 掌握 KV Cache 等关键技术
  • 学会使用批处理和流水线优化
  • 了解编译优化技术
  • 掌握 vLLM 与 SGLang 推理框架的使用与选型
  • 理解 Speculative Decoding 的原理、变种与生产部署
  • 掌握大厂面试中的相关问题

📑 本章目录

12.1 推理优化概述

12.1.1 为什么需要推理优化

挑战:

  1. 计算成本高:
  2. 大模型参数量大
  3. 计算复杂度高
  4. 需要大量 GPU 资源

  5. 延迟要求高:

  6. 实时应用需要低延迟
  7. 用户体验要求快速响应
  8. 竞争需要高性能

  9. 并发需求大:

  10. 多用户同时访问
  11. 需要处理高并发
  12. 需要弹性扩展

  13. 成本压力大:

  14. GPU 成本高
  15. 能耗成本高
  16. 需要成本优化

优化目标:

  1. 降低延迟:减少推理时间
  2. 提高吞吐量:增加并发处理能力
  3. 降低成本:减少资源消耗
  4. 保持精度:优化不损失精度

12.1.2 优化层次

优化层次:

Text Only
算法层 → 模型层 → 框架层 → 硬件层

各层优化:

  1. 算法层:
  2. KV Cache
  3. Flash Attention
  4. 算法优化

  5. 模型层:

  6. 模型量化
  7. 模型剪枝
  8. 知识蒸馏

  9. 框架层:

  10. 框架优化
  11. 算子融合
  12. 内存优化

  13. 硬件层:

  14. GPU 优化
  15. 专用芯片
  16. 分布式计算

12.1.3 优化指标

关键指标:

  1. 延迟(Latency):
  2. 单次推理时间
  3. 首字延迟(TTFT)
  4. 总生成时间

  5. 吞吐量(Throughput):

  6. 每秒请求数(RPS)
  7. 每秒 Token 数
  8. 并发处理能力

  9. 资源利用率:

  10. GPU 利用率
  11. 显存占用
  12. 能耗

  13. 成本:

  14. 每次推理成本
  15. 每小时成本
  16. 总成本

12.2 KV Cache

12.2.1 KV Cache 原理

定义:KV Cache 是一种缓存机制,用于缓存 Transformer 模型中自注意力层的 Key 和 Value 矩阵,避免重复计算。

原理:

在自回归生成过程中,每个 token 的生成都需要计算之前所有 token 的注意力。 KV Cache 将之前计算的 K 和 V 缓存起来,后续生成时直接使用,避免重复计算。

数学表示:

对于第 t 个 token,注意力计算为:

Text Only
Attention(Q_t, K_{1:t}, V_{1:t})

使用 KV Cache 后:

Text Only
Attention(Q_t, Cache_K, Cache_V)

其中: - Cache_K: 缓存的 K 矩阵 - Cache_V: 缓存的 V 矩阵

优化效果:

  • 计算量:减少约 50%
  • 显存占用:增加约 30%
  • 速度提升:2-3 倍

12.2.2 KV Cache 实现

基础实现:

Python
import torch
import torch.nn as nn

class KVCache:
    """KV Cache实现"""

    def __init__(self, max_batch_size: int, max_seq_len: int, num_heads: int, head_dim: int):
        """
        初始化KV Cache

        Args:
            max_batch_size: 最大批次大小
            max_seq_len: 最大序列长度
            num_heads: 注意力头数
            head_dim: 每个头的维度
        """
        self.max_batch_size = max_batch_size
        self.max_seq_len = max_seq_len
        self.num_heads = num_heads
        self.head_dim = head_dim

        # 初始化缓存
        self.cache_k = torch.zeros(
            max_batch_size,
            num_heads,
            max_seq_len,
            head_dim
        )
        self.cache_v = torch.zeros(
            max_batch_size,
            num_heads,
            max_seq_len,
            head_dim
        )

        # 当前位置
        self.current_pos = 0

    def update(self, k: torch.Tensor, v: torch.Tensor):
        """
        更新缓存

        Args:
            k: Key张量 [batch_size, num_heads, seq_len, head_dim]
            v: Value张量 [batch_size, num_heads, seq_len, head_dim]
        """
        batch_size = k.size(0)
        seq_len = k.size(2)

        # 更新缓存
        self.cache_k[:batch_size, :, self.current_pos:self.current_pos+seq_len, :] = k
        self.cache_v[:batch_size, :, self.current_pos:self.current_pos+seq_len, :] = v

        # 更新位置
        self.current_pos += seq_len

    def get(self) -> tuple:
        """
        获取缓存

        Returns:
            (k, v) 缓存的Key和Value
        """
        k = self.cache_k[:, :, :self.current_pos, :]
        v = self.cache_v[:, :, :self.current_pos, :]
        return k, v

    def clear(self):
        """清空缓存"""
        self.current_pos = 0
        self.cache_k.zero_()
        self.cache_v.zero_()

# 使用示例
kv_cache = KVCache(
    max_batch_size=1,
    max_seq_len=1024,
    num_heads=12,
    head_dim=64
)

# 生成过程
for i in range(10):
    # 模拟计算K和V
    k = torch.randn(1, 12, 1, 64)
    v = torch.randn(1, 12, 1, 64)

    # 更新缓存
    kv_cache.update(k, v)

    # 获取缓存用于注意力计算
    cached_k, cached_v = kv_cache.get()

    # 使用缓存的K和V进行注意力计算
    # ...

12.2.3 KV Cache 优化

优化策略:

  1. PagedAttention:
  2. 将 KV Cache 分页管理
  3. 动态分配显存
  4. 减少显存浪费

  5. KV Cache 压缩:

  6. 压缩 KV Cache
  7. 减少显存占用
  8. 平衡精度和效率

  9. 多级缓存:

  10. 使用多级缓存
  11. 提高缓存命中率
  12. 减少计算量

代码实现:

Python
class PagedKVCache:
    """分页KV Cache"""

    def __init__(self, num_heads: int, head_dim: int, page_size: int = 128):
        """
        初始化分页KV Cache

        Args:
            num_heads: 注意力头数
            head_dim: 每个头的维度
            page_size: 页大小
        """
        self.num_heads = num_heads
        self.head_dim = head_dim
        self.page_size = page_size

        # 页表
        self.page_table = {}

        # 页池
        self.page_pool_k = []
        self.page_pool_v = []

    def allocate_page(self) -> int:
        """分配页"""
        if not self.page_pool_k:
            # 创建新页
            page_k = torch.zeros(1, self.num_heads, self.page_size, self.head_dim)
            page_v = torch.zeros(1, self.num_heads, self.page_size, self.head_dim)
            self.page_pool_k.append(page_k)
            self.page_pool_v.append(page_v)
            return len(self.page_pool_k) - 1
        else:
            # 复用空闲页
            return self._find_free_page()

    def update(self, k: torch.Tensor, v: torch.Tensor, seq_idx: int):
        """
        更新缓存

        Args:
            k: Key张量
            v: Value张量
            seq_idx: 序列索引
        """
        # 计算页索引
        page_idx = seq_idx // self.page_size
        offset = seq_idx % self.page_size

        # 分配页
        if page_idx not in self.page_table:
            self.page_table[page_idx] = self.allocate_page()

        # 更新页
        actual_page_idx = self.page_table[page_idx]
        self.page_pool_k[actual_page_idx][:, :, offset:offset+1, :] = k
        self.page_pool_v[actual_page_idx][:, :, offset:offset+1, :] = v

    def get(self, seq_len: int) -> tuple:
        """
        获取缓存

        Args:
            seq_len: 序列长度

        Returns:
            (k, v) 缓存的Key和Value
        """
        # 计算需要的页数
        num_pages = (seq_len + self.page_size - 1) // self.page_size

        # 收集页
        k_pages = []
        v_pages = []

        for page_idx in range(num_pages):
            if page_idx in self.page_table:
                actual_page_idx = self.page_table[page_idx]
                k_pages.append(self.page_pool_k[actual_page_idx])
                v_pages.append(self.page_pool_v[actual_page_idx])

        # 拼接页
        k = torch.cat(k_pages, dim=2)[:, :, :seq_len, :]
        v = torch.cat(v_pages, dim=2)[:, :, :seq_len, :]

        return k, v

12.3 批处理优化

12.3.1 批处理原理

定义:批处理是将多个请求组合成一个批次一起处理,以提高 GPU 利用率和吞吐量。

优势:

  1. 提高 GPU 利用率:
  2. GPU 擅长并行计算
  3. 批处理充分利用 GPU
  4. 提高计算效率

  5. 降低延迟:

  6. 分摊固定开销
  7. 减少启动时间
  8. 提高吞吐量

  9. 降低成本:

  10. 提高资源利用率
  11. 降低每次推理成本
  12. 优化整体成本

挑战:

  1. 序列长度不同:
  2. 不同请求长度不同
  3. 需要 Padding
  4. 浪费计算资源

  5. 动态批次:

  6. 请求到达时间不同
  7. 需要动态组批
  8. 增加复杂度

12.3.2 连续批处理

原理:连续批处理(Continuous Batching)允许不同长度的序列在同一个批次中处理,避免 Padding 浪费。

实现:

Python
import torch
from collections import deque

class ContinuousBatchProcessor:
    """连续批处理器"""

    def __init__(self, max_batch_size: int, max_seq_len: int):
        """
        初始化连续批处理器

        Args:
            max_batch_size: 最大批次大小
            max_seq_len: 最大序列长度
        """
        self.max_batch_size = max_batch_size
        self.max_seq_len = max_seq_len

        # 请求队列
        self.request_queue = deque()  # deque双端队列,两端操作O(1)

        # 活跃请求
        self.active_requests = {}

    def add_request(self, request_id: str, input_ids: torch.Tensor):
        """
        添加请求

        Args:
            request_id: 请求ID
            input_ids: 输入token IDs
        """
        self.request_queue.append({
            'id': request_id,
            'input_ids': input_ids,
            'seq_len': input_ids.size(0)
        })

    def get_batch(self) -> tuple[torch.Tensor, list[str]]:
        """
        获取批次

        Returns:
            (batch_input_ids, request_ids)
        """
        if not self.request_queue:
            return None, []

        # 选择请求组成批次
        batch_requests = []
        total_tokens = 0

        while self.request_queue and len(batch_requests) < self.max_batch_size:
            request = self.request_queue[0]

            # 检查是否超过最大序列长度
            if total_tokens + request['seq_len'] > self.max_seq_len:
                break

            # 添加到批次
            batch_requests.append(self.request_queue.popleft())
            total_tokens += request['seq_len']

        if not batch_requests:
            return None, []

        # 准备批次数据
        max_len = max(req['seq_len'] for req in batch_requests)
        batch_input_ids = torch.zeros(
            len(batch_requests),
            max_len,
            dtype=torch.long
        )

        request_ids = []
        for i, req in enumerate(batch_requests):  # enumerate同时获取索引和元素
            batch_input_ids[i, :req['seq_len']] = req['input_ids']
            request_ids.append(req['id'])

        return batch_input_ids, request_ids

    def complete_request(self, request_id: str):
        """
        完成请求

        Args:
            request_id: 请求ID
        """
        if request_id in self.active_requests:
            del self.active_requests[request_id]

# 使用示例
processor = ContinuousBatchProcessor(
    max_batch_size=4,
    max_seq_len=1024
)

# 添加请求
processor.add_request("req1", torch.randint(0, 1000, (50,)))
processor.add_request("req2", torch.randint(0, 1000, (100,)))
processor.add_request("req3", torch.randint(0, 1000, (75,)))

# 获取批次
batch_input_ids, request_ids = processor.get_batch()
print(f"批次大小: {len(request_ids)}")
print(f"批次形状: {batch_input_ids.shape}")

12.3.3 动态批处理

原理:动态批处理根据请求到达时间动态组批,平衡延迟和吞吐量。

实现:

Python
import time
import asyncio  # Python标准异步库

class DynamicBatchProcessor:
    """动态批处理器"""

    def __init__(self, max_batch_size: int, max_wait_time: float = 0.01):
        """
        初始化动态批处理器

        Args:
            max_batch_size: 最大批次大小
            max_wait_time: 最大等待时间(秒)
        """
        self.max_batch_size = max_batch_size
        self.max_wait_time = max_wait_time

        # 请求队列
        self.request_queue = []
        self.last_batch_time = time.time()

    async def add_request(self, request_id: str, input_ids: torch.Tensor):  # async def定义协程函数
        """
        添加请求

        Args:
            request_id: 请求ID
            input_ids: 输入token IDs
        """
        self.request_queue.append({
            'id': request_id,
            'input_ids': input_ids,
            'seq_len': input_ids.size(0),
            'arrival_time': time.time()
        })

    async def get_batch(self) -> tuple[torch.Tensor, list[str]]:
        """
        获取批次

        Returns:
            (batch_input_ids, request_ids)
        """
        if not self.request_queue:
            return None, []

        current_time = time.time()
        time_since_last_batch = current_time - self.last_batch_time

        # 检查是否应该组批
        should_batch = (
            len(self.request_queue) >= self.max_batch_size or
            time_since_last_batch >= self.max_wait_time
        )

        if not should_batch:
            return None, []

        # 组批
        batch_requests = self.request_queue[:self.max_batch_size]
        self.request_queue = self.request_queue[len(batch_requests):]
        self.last_batch_time = current_time

        # 准备批次数据
        max_len = max(req['seq_len'] for req in batch_requests)
        batch_input_ids = torch.zeros(
            len(batch_requests),
            max_len,
            dtype=torch.long
        )

        request_ids = []
        for i, req in enumerate(batch_requests):
            batch_input_ids[i, :req['seq_len']] = req['input_ids']
            request_ids.append(req['id'])

        return batch_input_ids, request_ids

# 使用示例
async def main():
    processor = DynamicBatchProcessor(
        max_batch_size=4,
        max_wait_time=0.01
    )

    # 添加请求
    await processor.add_request("req1", torch.randint(0, 1000, (50,)))  # await等待异步操作完成
    await asyncio.sleep(0.005)
    await processor.add_request("req2", torch.randint(0, 1000, (100,)))

    # 获取批次
    batch_input_ids, request_ids = await processor.get_batch()
    print(f"批次大小: {len(request_ids)}")

# 运行
asyncio.run(main())  # 创建事件循环运行顶层协程

12.4 编译优化

12.4.1 TorchScript

原理:TorchScript 将 PyTorch 模型转换为静态图,优化推理性能。

实现:

Python
import torch
import torch.nn as nn

# 定义模型
class SimpleModel(nn.Module):
    def __init__(self):
        super().__init__()  # super()调用父类方法
        self.linear = nn.Linear(10, 5)

    def forward(self, x):
        return self.linear(x)

# 创建模型
model = SimpleModel()

# 转换为TorchScript
scripted_model = torch.jit.script(model)

# 保存模型
scripted_model.save("model_scripted.pt")

# 加载并推理
loaded_model = torch.jit.load("model_scripted.pt")
input_data = torch.randn(1, 10)
output = loaded_model(input_data)

12.4.2 TensorRT

原理:TensorRT 是 NVIDIA 的高性能推理优化器,支持多种优化技术。

实现:

Python
import torch
from torch2trt import torch2trt
import tensorrt as trt

# 创建模型
model = SimpleModel()
model.eval()

# 示例输入
example_input = torch.randn(1, 10).cuda()

# 转换为TensorRT
trt_model = torch2trt(
    model,
    [example_input],
    fp16_mode=True,
    max_workspace_size=1 << 25
)

# 保存模型
torch.save(trt_model.state_dict(), "model_trt.pth")

# 推理
output = trt_model(example_input)

12.4.3 ONNX Runtime

原理:ONNX Runtime 是一个跨平台的高性能推理引擎。

实现:

Python
import torch
import onnx
import onnxruntime as ort

# 导出模型为ONNX
model = SimpleModel()
model.eval()

dummy_input = torch.randn(1, 10)
torch.onnx.export(
    model,
    dummy_input,
    "model.onnx",
    input_names=["input"],
    output_names=["output"],
    dynamic_axes={"input": {0: "batch_size"}}
)

# 使用ONNX Runtime推理
ort_session = ort.InferenceSession("model.onnx")

input_name = ort_session.get_inputs()[0].name
output_name = ort_session.get_outputs()[0].name

# 推理
input_data = torch.randn(1, 10).numpy()
outputs = ort_session.run([output_name], {input_name: input_data})

12.5 推理框架: vLLM 与 SGLang

推理框架是连接模型与线上服务的关键基础设施。 vLLM 和 SGLang 是当前最主流的两个开源 LLM 推理框架。

12.5.1 vLLM 简介

vLLM 由 UC Berkeley Sky Lab 团队开发,以 PagedAttention 为核心,是目前社区使用最广泛的推理引擎。

核心特性: - PagedAttention:将 KV Cache 按页管理(类似操作系统虚拟内存),消除显存碎片 - Continuous Batching:动态插入/移除请求,最大化 GPU 利用率 - Tensor Parallelism / Pipeline Parallelism:多卡推理支持 - OpenAI 兼容 API/v1/chat/completions 直接对接现有代码

Python
# vLLM 快速启动服务
from vllm import LLM, SamplingParams

llm = LLM(model="Qwen/Qwen2.5-7B-Instruct", tensor_parallel_size=1)
params = SamplingParams(temperature=0.7, max_tokens=512)

prompts = ["解释什么是Transformer", "写一首关于AI的诗"]
outputs = llm.generate(prompts, params)

for output in outputs:
    print(output.outputs[0].text)

12.5.2 SGLang 详解

SGLang ( Structured Generation Language )由 UC Berkeley LMSYS 团队开发,专注于结构化生成高效前缀共享,在多轮对话和 Agent 场景中性能突出。截至 2025 年 6 月,SGLang 在 GitHub 上已获得近 15K Stars,被 xAI、浪潮信息等企业用于生产部署。

📌 SGLang 版本说明:当前稳定版本为 v0.4.x,带来零开销批处理调度器、缓存感知负载均衡器等重大性能提升。

核心特性

特性 说明
RadixAttention 基于 Radix Tree 自动管理前缀 KV Cache ,相同前缀的请求共享缓存,无需手动管理
Compressed FSM / xgrammar 使用压缩有限状态机约束结构化输出(JSON Schema),比逐 token 采样快 数倍,v0.4 版本基于 xgrammar 库,JSON 解码速度可达其他方案的 10 倍
零开销批处理调度器 v0.4 新特性,CPU 调度与 GPU 计算重叠,吞吐量提升 1.1 倍
缓存感知负载均衡器 v0.4 新特性,智能路由机制,吞吐量提升 1.9 倍,缓存命中率提高 3.8 倍
Chunked Prefill 将长前缀分块预填充,避免长 prompt 阻塞短请求
Torch.compile 深度集成 自动编译计算图,Llama 70B 推理速度提升 ~20%(v0.3 版本提升 1.5 倍)
DeepSeek MLA 优化 v0.3 版本优化,DeepSeek 模型注意力速度提升 7 倍
FP8 量化推理 原生支持 FP8 W8A8 量化 + FP8 KV 缓存,兼顾精度与速度
多 LoRA 批处理 支持多个 LoRA 适配器同时加载和推理
推测解码 内置投机采样加速,DeepSeek 模型解码吞吐量提升 1.9 倍
张量/流水线/专家/数据并行 支持 TP、PP、EP、DP 多种并行策略
多模态支持 支持 LLaVA-OneVision(多图像/视频)、视觉语言模型

RadixAttention 原理

Text Only
传统方式:每个请求独立计算全部 KV Cache
  请求A: [System Prompt] + [User A] → 独立 KV Cache
  请求B: [System Prompt] + [User B] → 独立 KV Cache(重复计算 System Prompt)

RadixAttention:自动识别共享前缀,复用 KV Cache
  Radix Tree:
    [System Prompt] ── KV Cache(共享)
        ├── [User A] → 增量 KV Cache
        └── [User B] → 增量 KV Cache
  → 节省 ~30-60% 显存,吞吐提升 2-5x(前缀越长效果越明显)

SGLang v0.4 性能突破

Text Only
SGLang v0.4 版本核心优化(2025年发布):

1. 零开销批处理调度器
   ├── CPU 调度与 GPU 计算时间重叠
   └── 吞吐量提升 1.1 倍

2. 缓存感知负载均衡器
   ├── 智能请求路由,优先分配到缓存命中高的节点
   ├── 吞吐量提升 1.9 倍
   └── 缓存命中率提高 3.8 倍

3. DeepSeek MLA 优化
   ├── 针对 DeepSeek 模型专用注意力机制优化
   └── 速度提升 7 倍

4. Torch.compile 加速
   └── 推理速度提升 1.5 倍

实测性能(共享前缀批量请求):
  - 吞吐量:158,596 token/s
  - 缓存命中率:75%

SGLang 安装方法

Bash
# 方法1:pip 安装(推荐)
pip install --upgrade pip
pip install "sglang[all]"
pip install flashinfer -i https://flashinfer.ai/whl/cu121/torch2.4/

# 方法2:从源码安装(获取最新功能)
git clone -b v0.4.0 https://github.com/sgl-project/sglang.git
cd sglang
pip install --upgrade pip
pip install -e "python[all]"
pip install flashinfer -i https://flashinfer.ai/whl/cu121/torch2.4/

# 方法3:Docker 部署
docker run --gpus all -p 30000:30000 -v /models:/models lmsysorg/sglang

SGLang Engine 代码示例

Python
import sglang as sgl

# 方式1:离线批量推理
llm = sgl.Engine(model_path="Qwen/Qwen2.5-7B-Instruct")

prompts = ["什么是大模型推理优化?", "解释KV Cache的作用"]
outputs = llm.generate(prompts, sampling_params={"temperature": 0.7, "max_new_tokens": 256})
for out in outputs:
    print(out["text"])

llm.shutdown()
Python
# 方式2:启动 OpenAI 兼容服务器
# 终端执行:
# python -m sglang.launch_server --model Qwen/Qwen2.5-7B-Instruct --port 8000

# 客户端调用(与 vLLM/OpenAI 完全兼容)
import openai
client = openai.OpenAI(base_url="http://localhost:8000/v1", api_key="none")

response = client.chat.completions.create(
    model="Qwen/Qwen2.5-7B-Instruct",
    messages=[{"role": "user", "content": "用JSON格式输出三个中国城市的信息"}],
    response_format={"type": "json_object"},  # 结构化输出
)
print(response.choices[0].message.content)

SGLang 性能优化技巧

Python
# 1. 启用 Torch.compile 加速
llm = sgl.Engine(
    model_path="meta-llama/Llama-3-8B-Instruct",
    enable_torch_compile=True,  # 推理速度提升 1.5 倍
)

# 2. 张量并行(多卡部署)
llm = sgl.Engine(
    model_path="meta-llama/Llama-3-70B-Instruct",
    tensor_parallel_size=4,  # 使用 4 卡并行
)

# 3. DeepSeek 模型优化(启用 MLA)
llm = sgl.Engine(
    model_path="deepseek-ai/DeepSeek-R1-Distill-Qwen-7B",
    enable_mla=True,  # MLA 注意力加速 7 倍
)

# 4. FP8 量化部署
llm = sgl.Engine(
    model_path="meta-llama/Llama-3-8B-Instruct",
    quant_method="fp8",  # FP8 W8A8 量化
)

# 5. 结构化输出(xgrammar 加速)
json_schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "age": {"type": "integer", "minimum": 0},
    },
    "required": ["name", "age"]
}
output = llm.generate(
    "生成一个AI工程师的简介",
    sampling_params={"max_new_tokens": 200, "json_schema": json_schema}
)

SGLang 部署示例(浪潮信息 NF5688G7 + DeepSeek R1 671B)

Bash
# 单机部署 DeepSeek R1 671B,支持 1000+ 并发
python -m sglang.launch_server \
    --model-path deepseek-ai/DeepSeek-R1 \
    --tensor-parallel-size 8 \
    --port 30000 \
    --enable-mla \
    --quantization fp8

# 性能指标:
# - 显存带宽:4.8 TB/s
# - GPU P2P 带宽:900 GB/s
# - 并发用户:1000+
# - 吞吐量:158,596 token/s
Python
# SGLang 原生支持 JSON Schema 约束(Compressed FSM 加速)
from sglang import Engine

llm = Engine(model_path="Qwen/Qwen2.5-7B-Instruct")

json_schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "age": {"type": "integer", "minimum": 0},
        "skills": {"type": "array", "items": {"type": "string"}}
    },
    "required": ["name", "age", "skills"]
}

output = llm.generate(
    "生成一个AI工程师的简介",
    sampling_params={"max_new_tokens": 200, "json_schema": json_schema}
)
print(output[0]["text"])  # 保证输出符合 JSON Schema

12.5.3 vLLM vs SGLang 对比

对比维度 vLLM SGLang
开发团队 UC Berkeley Sky Lab UC Berkeley LMSYS
核心创新 PagedAttention RadixAttention + xgrammar
KV Cache 管理 分页内存管理(按页) Radix Tree 前缀共享(按语义)
结构化输出 Outlines 集成 原生 xgrammar ,JSON 速度快 10 倍
多轮对话 普通性能 前缀共享显著加速(2-5x)
编译优化 有限 Torch.compile 深度集成(1.5x)
v0.4 新特性 PagedAttention v2 零开销调度器 + 缓存感知负载均衡
社区生态 更成熟,文档丰富 快速增长(15K+ Stars)
模型支持 更广泛 主流模型均支持
吞吐量(共享前缀场景) 基准 1.5-5x 提升
首 Token 延迟 基准 通常更低( Chunked Prefill )
API 兼容性 OpenAI 兼容 OpenAI 兼容
多模态支持 可用 LLaVA-OneVision 原生支持
国产模型优化 一般 DeepSeek MLA 7 倍加速
量化支持 FP8/AWQ/INT4 FP8/FP4/INT4/AWQ/GPTQ

选择建议

Text Only
选择 vLLM 的场景:
  ✅ 需要最广泛的模型兼容性(HuggingFace 默认支持)
  ✅ 团队已有 vLLM 经验和部署
  ✅ 简单的单轮问答服务
  ✅ 需要稳定的生产环境(生态更成熟)

选择 SGLang 的场景:
  ✅ Agent 多轮调用(大量共享 System Prompt)
  ✅ 需要 JSON/结构化输出约束(xgrammar 加速)
  ✅ 共享前缀的批量请求(如同一文档的多个问题)
  ✅ DeepSeek/国产模型部署(MLA 7 倍加速)
  ✅ 追求极致吞吐和低延迟(v0.4 性能领先)
  ✅ 多模态应用(LLaVA-OneVision)

生产环境选型参考(2025 Q2)

场景 推荐框架 原因
电商客服(多轮对话) SGLang 前缀共享 + 结构化输出
代码生成平台 SGLang DeepSeek 优化 + 高吞吐
通用 API 服务 vLLM 生态成熟 + 模型广泛
文档问答(RAG) SGLang 长上下文 + 批量查询
多模态应用 SGLang LLaVA-OneVision 原生支持
边缘部署 vLLM 资源受限场景优化更好

12.6 Speculative Decoding (投机解码)

Speculative Decoding 是一种利用小模型加速大模型推理的技术,在不损失输出质量的前提下显著降低延迟。

12.6.1 核心原理

思路:用一个小而快的 Draft Model 先「猜」多个 token ,再用大模型一次性验证,接受正确的 token 、拒绝错误的 token 。

Text Only
传统自回归解码(每步 1 token):
  大模型 → t1 → 大模型 → t2 → 大模型 → t3 → ... (N 步)

Speculative Decoding(每步可能接受 γ 个 token):
  小模型 → [t1, t2, t3, t4, t5](猜 γ=5 个)
  大模型 → 一次前向传播验证 → 接受 [t1, t2, t3],拒绝 t4
  → 平均每步 >1 token,加速 2-3x

12.6.2 数学保证

Speculative Decoding 的关键优势是无损——输出分布与直接使用大模型完全一致

验证策略基于拒绝采样: - 对 Draft Model 生成的 token \(t_i\),计算接受概率:

\[P_{\text{accept}} = \min\left(1, \frac{p_{\text{target}}(t_i)}{p_{\text{draft}}(t_i)}\right)\]
  • 若拒绝,从修正分布中重新采样:
\[p_{\text{resample}}(t) = \text{norm}\left(\max\left(0, p_{\text{target}}(t) - p_{\text{draft}}(t)\right)\right)\]

12.6.3 实现示例

Python
import torch

def speculative_decode(target_model, draft_model, input_ids, gamma=5, temperature=1.0):
    """
    Speculative Decoding 简化实现
    target_model: 大模型(如 70B)
    draft_model: 小模型(如 7B,同系列)
    gamma: 每轮猜测的 token 数
    """
    generated = input_ids.clone()

    while len(generated[0]) < input_ids.shape[1] + 100:  # 简化:生成100个token
        # Step 1: Draft Model 自回归生成 γ 个候选 token
        draft_tokens = []
        draft_probs = []
        draft_input = generated.clone()

        for _ in range(gamma):
            with torch.no_grad():  # 禁用梯度计算,节省内存(推理时使用)
                logits = draft_model(draft_input).logits[:, -1, :] / temperature
                probs = torch.softmax(logits, dim=-1)
                token = torch.multinomial(probs, 1)
                draft_tokens.append(token)
                draft_probs.append(probs)
                draft_input = torch.cat([draft_input, token], dim=-1)

        # Step 2: Target Model 一次前向传播验证所有候选
        with torch.no_grad():
            all_input = torch.cat([generated] + draft_tokens, dim=-1)
            target_logits = target_model(all_input).logits / temperature

        # Step 3: 逐个验证,基于拒绝采样
        accepted = 0
        for i in range(gamma):
            target_prob = torch.softmax(target_logits[:, -(gamma - i + 1), :], dim=-1)
            draft_prob = draft_probs[i]
            token = draft_tokens[i]

            # 接受概率
            accept_prob = torch.min(
                torch.ones_like(target_prob),
                target_prob / (draft_prob + 1e-10)
            )

            if torch.rand(1).item() < accept_prob[0, token[0, 0]].item():
                generated = torch.cat([generated, token], dim=-1)
                accepted += 1
            else:
                # 从修正分布采样
                residual = torch.clamp(target_prob - draft_prob, min=0)
                residual = residual / residual.sum(dim=-1, keepdim=True)
                new_token = torch.multinomial(residual, 1)
                generated = torch.cat([generated, new_token], dim=-1)
                break

        if accepted == gamma:
            # 所有猜测都被接受,额外从 target 采样一个
            bonus_prob = torch.softmax(target_logits[:, -1, :], dim=-1)
            bonus_token = torch.multinomial(bonus_prob, 1)
            generated = torch.cat([generated, bonus_token], dim=-1)

    return generated

12.6.4 加速效果与适用场景

场景 加速比 说明
Draft = 同系列小模型 2-3x 如 Llama-70B + Llama-7B
Draft = 自身浅层层 1.5-2x Self-Speculative Decoding
高温度采样(创意写作) 1.3-1.5x 接受率低,加速有限
低温度/贪心解码(代码/翻译) 2.5-3.5x 接受率高,加速显著

适用条件: - ✅ Draft Model 与 Target Model 分布相近(同系列效果最好) - ✅ 解码为主的任务(非长 prefill ) - ✅ 模型越大加速越明显(大模型前向传播开销大) - ❌ 不适合 batch 很大的高吞吐场景(额外的 draft 计算抵消收益)

12.6.5 变种与发展演进

Speculative Decoding 自 2022 年提出以来,学术界和工业界发展出多种变种,在接受率实现复杂度适用场景等方面各有优劣。

12.6.5.1 EAGLE(投矛策略)

EAGLE(Enhanced Autoregressive Model via Greedy Leveraging and Efficiency)来自 Meta AI,采用自回归树搜索策略,被 ICML 2024 接收。

核心思想: - Draft Model 不再逐 token 生成,而是构建一棵多叉树同时探索多个候选路径 - 利用已计算的 KV Cache 加速树的扩展 - 验证时并行处理树的多个分支

Text Only
传统 Speculative Decoding(γ=4):
    [A] → [B] → [C] → [D]  (线性串行猜测)

EAGLE(多叉树猜测):
           [ROOT]
         /   |   \\
       [A]  [B]  [C]    (第一层:3个候选)
      / \\  / \\  / \\
    [D][E][F][G][H][I] (第二层:扩展6个候选)

性能表现: - Llama-2-70B 加速 2.5-3x - 接受率比传统方法提升 15-20% - 适合代码生成、数学推理等多步推理任务

EAGLE 代码示例

Python
import torch
import torch.nn.functional as F

class EAGLEDrafting:
    """EAGLE 投机解码实现"""

    def __init__(self, target_model, draft_model, max_depth=3, branching=3):
        self.target_model = target_model
        self.draft_model = draft_model
        self.max_depth = max_depth
        self.branching = branching

    def build_draft_tree(self, input_ids, kv_cache=None):
        """
        构建 draft 树

        Args:
            input_ids: 输入 token IDs
            kv_cache: 现有的 KV Cache

        Returns:
            tree_tokens: 树中所有候选 token
            tree_probs: 对应概率分布
            path_to_accept: 每个分支的接受路径
        """
        tree_tokens = [input_ids]
        tree_probs = []

        current_ids = input_ids
        current_kv = kv_cache

        for depth in range(self.max_depth):
            # 批量预测所有叶子节点
            next_layer_tokens = []
            next_layer_probs = []

            for branch_idx, branch_ids in enumerate(tree_tokens):
                # 复用 KV Cache 加速
                logits = self.draft_model(branch_ids, past_key_values=current_kv)
                probs = F.softmax(logits[:, -1, :], dim=-1)

                # 选择 top-k 候选
                top_k_probs, top_k_ids = torch.topk(probs, self.branching, dim=-1)

                for i in range(self.branching):
                    next_token = top_k_ids[:, i:i+1]
                    next_layer_tokens.append(
                        torch.cat([branch_ids, next_token], dim=-1)
                    )
                    next_layer_probs.append(top_k_probs[:, i])

            tree_tokens = next_layer_tokens
            tree_probs = next_layer_probs

        return tree_tokens, tree_probs

    def verify_and_sample(self, tree_tokens, tree_probs):
        """
        并行验证树中所有候选,选择最优路径

        Returns:
            accepted_ids: 被接受的 token 序列
            accepted_count: 被接受的 token 数量
        """
        # 拼接所有候选序列
        max_len = max(t.shape[1] for t in tree_tokens)
        padded_tokens = [
            torch.cat([t, t.new_zeros(1, max_len - t.shape[1])], dim=-1)
            for t in tree_tokens
        ]
        batch_tokens = torch.cat(padded_tokens, dim=0)

        # Target Model 一次前向传播验证
        with torch.no_grad():
            target_logits = self.target_model(batch_tokens).logits

        # 计算每条路径的接受概率
        best_path_idx = 0
        best_path_score = 0.0

        for idx, (tokens, draft_prob) in enumerate(zip(tree_tokens, tree_probs)):
            # 计算这条路径的累积得分
            path_score = draft_prob.item()
            if path_score > best_path_score:
                best_path_score = path_score
                best_path_idx = idx

        return tree_tokens[best_path_idx], best_path_score

12.6.5.2 Medusa(多头投机)

Medusa 来自 UT Austin 和 Princeton,提出多头投机策略,每个"头"独立预测一个候选 token,然后并行验证。

核心思想: - 不再使用独立的 Draft Model,而是在 Target Model 最后一层添加多个预测头 - 每个预测头预测下一个 token 的不同候选 - 一次前向传播同时生成多个候选序列

Text Only
传统投机解码:
    输入 → [小模型] → [大模型验证] → 输出

Medusa:
    输入 → [共享主干] → [头1: 预测 t+1] → [头2: 预测 t+2] → [头3: 预测 t+3]
            [Target Model 一次前向传播,输出多个预测]
            并行验证所有候选,接受正确的 token

Medusa 训练: - 预测头与原模型联合训练 - 使用原模型输出的分布作为监督信号 - 训练目标是最小化预测头与主模型输出的 KL 散度

Python
class MedusaPredictionHead(nn.Module):
    """Medusa 预测头"""

    def __init__(self, hidden_size, vocab_size, num_heads=3):
        super().__init__()
        self.num_heads = num_heads
        # 每个头有自己的输出层
        self.heads = nn.ModuleList([
            nn.Linear(hidden_size, vocab_size, bias=False)
            for _ in range(num_heads)
        ])

    def forward(self, hidden_states):
        """
        Args:
            hidden_states: [batch, seq_len, hidden]

        Returns:
            predictions: [batch, seq_len, num_heads, vocab_size]
        """
        return torch.stack([head(hidden_states) for head in self.heads], dim=2)


class MedusaModel(nn.Module):
    """带 Medusa 头的模型"""

    def __init__(self, base_model, num_medusa_heads=3):
        super().__init__()
        self.base_model = base_model
        self.hidden_size = base_model.config.hidden_size
        self.vocab_size = base_model.config.vocab_size

        # 添加 Medusa 预测头
        self.medusa_heads = MedusaPredictionHead(
            self.hidden_size,
            self.vocab_size,
            num_heads=num_medusa_heads
        )

    def forward(self, input_ids):
        # 主干网络前向传播
        outputs = self.base_model(input_ids, output_hidden_states=True)
        hidden_states = outputs.hidden_states[-1]  # 最后一层

        # Medusa 预测
        medusa_logits = self.medusa_heads(hidden_states)

        return {
            'main_logits': outputs.logits,
            'medusa_logits': medusa_logits
        }

    def medusa_decode(self, input_ids, temperature=1.0):
        """
        Medusa 解码流程

        1. 一次前向传播获取所有预测头的输出
        2. 构建候选序列
        3. 验证并接受
        """
        outputs = self.forward(input_ids)

        # 解析各预测头的输出
        medusa_logits = outputs['medusa_logits']  # [batch, seq, num_heads, vocab]

        candidates = []
        for head_idx in range(self.medusa_heads.num_heads):
            head_logits = medusa_logits[:, -1, head_idx, :] / temperature
            probs = F.softmax(head_logits, dim=-1)
            token = torch.multinomial(probs, 1)
            candidates.append(token)

        # 构建候选序列:[input, cand1, cand2, cand3]
        candidate_sequence = torch.cat([input_ids] + candidates, dim=-1)

        # Target 验证
        with torch.no_grad():
            verification_logits = self.base_model(candidate_sequence).logits

        # 逐个验证并接受
        accepted = []
        for i, cand_token in enumerate(candidates):
            target_prob = F.softmax(verification_logits[:, input_ids.shape[1] + i, :], dim=-1)
            cand_prob = F.softmax(medusa_logits[:, -1, i, :], dim=-1)

            accept_prob = torch.min(
                torch.ones_like(target_prob),
                target_prob / (cand_prob + 1e-10)
            )

            if torch.rand(1).item() < accept_prob[0, cand_token[0, 0]].item():
                accepted.append(cand_token)
            else:
                # 拒绝:从修正分布采样
                residual = torch.clamp(target_prob - cand_prob, min=0)
                residual = residual / residual.sum(dim=-1, keepdim=True)
                new_token = torch.multinomial(residual, 1)
                accepted.append(new_token)
                break

        return torch.cat([input_ids] + accepted, dim=-1)

性能表现: - 无需额外小模型,部署简单 - Llama-7B 加速 2x - 接受率取决于预测头数量(通常 3-5 个头) - 适合资源受限场景(边缘设备、移动端)

12.6.5.3 Self-Speculative Decoding(自投机)

Self-Speculative Decoding 不使用外部 Draft Model,而是利用 Target Model 自身的浅层预测、深层验证。

核心思想: - 将模型分为两部分:浅层(Draft)和深层(Target) - 浅层快速生成候选 token - 深层一次前向传播验证所有候选

Text Only
模型结构:
  [浅层 1-12] → 生成候选 t+1, t+2, t+3...
  [深层 13-48] → 验证候选(一次前向)

工作流程:
  输入 → [浅层] → 候选1, 候选2, 候选3
        [深层一次前向] → 验证 → 接受/拒绝

实现方式: - Early Exit:在浅层处 exit,用浅层输出预测下一个 token - Shadow Model:训练一个浅层版本的网络作为 draft

Python
class SelfSpeculativeDecoder:
    """自投机解码器"""

    def __init__(self, model, num_draft_layers, num_draft_tokens=5):
        """
        Args:
            model: 原始模型
            num_draft_layers: 用作 draft 的层数
            num_draft_tokens: 每轮猜测的 token 数
        """
        self.model = model
        self.num_draft_layers = num_draft_layers
        self.num_draft_tokens = num_draft_tokens
        self.total_layers = model.config.num_hidden_layers

    def draft_generation(self, input_ids, draft_kv_cache):
        """
        浅层 draft 生成
        """
        draft_tokens = []
        current_ids = input_ids
        current_kv = draft_kv_cache

        for _ in range(self.num_draft_tokens):
            # 只通过浅层
            hidden = self.model.model.embed_tokens(current_ids)
            for layer_idx in range(self.num_draft_layers):
                hidden = self.model.model.layers[layer_idx](hidden, current_kv)

            # 预测下一个 token
            logits = self.model.lm_head(hidden[:, -1, :])
            probs = F.softmax(logits, dim=-1)
            next_token = torch.multinomial(probs, 1)

            draft_tokens.append(next_token)
            current_ids = torch.cat([current_ids, next_token], dim=-1)

        return draft_tokens

    def verify(self, input_ids, draft_tokens):
        """
        深层验证
        """
        # 拼接 draft tokens
        all_ids = torch.cat([input_ids] + draft_tokens, dim=-1)

        # 一次完整前向传播
        with torch.no_grad():
            outputs = self.model(all_ids, use_cache=True)

        # 逐个验证
        accepted_tokens = []
        for i, draft_token in enumerate(draft_tokens):
            target_logits = outputs.logits[:, input_ids.shape[1] + i, :]
            target_probs = F.softmax(target_logits, dim=-1)

            # 简化:直接贪心接受 top-1
            target_token = torch.argmax(target_probs, dim=-1, keepdim=True)

            if torch.all(target_token == draft_token):
                accepted_tokens.append(draft_token)
            else:
                # 拒绝:使用 target 的预测
                accepted_tokens.append(target_token)
                break

        return accepted_tokens

性能表现: - 零额外内存开销(无需加载小模型) - 加速比 1.5-2x(比外部 draft 模型低) - 适合无法加载多个模型的场景

12.6.5.4 Falcon(增强半自回归草案)

Falcon 来自中国电信,被 AAAI 2025 接收,提出增强半自回归草案 + 自定义验证树

核心创新: - 半自回归草案:不是逐个生成 token,而是一次生成多个 token(类似 blockwise parallel decoding) - 自定义验证树:根据验证结果动态调整树结构 - 加速比达到 2.91-3.51x,成本降至

Text Only
传统 AR Draft(自回归):
  Token:  [t1] → [t2] → [t3] → [t4] → [t5]  (5次串行生成)

SAR Draft(半自回归):
  Token:  [t1, t2, t3, t4, t5]  (1次并行生成)
  验证树:    [t1]
           /   |   \\
         [t2] [t3] [t4]  (动态选择)

12.6.5.5 MTP(Multi-Token Prediction)

MTP 由 Meta 提出,被 DeepSeek-V3 采用,核心是一次性预测多个 token 而非一个。

与 Speculative Decoding 的区别: - Speculative Decoding:验证已生成的候选(draft → target) - MTP:同时预测多个未来位置(一次前向,多个输出)

Text Only
AR 解码(标准):
  Step 1: 预测 t+1
  Step 2: 预测 t+2
  ...

MTP 解码:
  Step 1: 同时预测 [t+1, t+2, t+3, t+4]  (4个预测头)
  Step 2: 验证并接受

DeepSeek-V3 MTP 实现: - 4 个预测头,每个头预测 1 个 token - 预测头与主模型共享 embeddingoutput projector - 训练时使用独占目标函数(主模型和预测头用不同目标训练)

12.6.5.6 变种对比与选型

变种 Draft 来源 额外内存 加速比 适用场景 局限
传统 SD 独立小模型 高(~7B) 2-3x 追求极致加速 需加载两个模型
EAGLE 独立小模型 2.5-3x 多步推理任务 树搜索复杂度高
Medusa 预测头 1.5-2x 边缘部署 需重新训练
Self-SD 自身浅层 1.5-2x 资源受限 加速效果有限
Falcon SAR Draft 2.9-3.5x 工业部署 实现复杂
MTP 预测头 1.5-2x 训练加速 验证开销大

选型建议

Text Only
选 EAGLE:追求最佳加速效果,且任务有多步推理特征(代码、数学)
选 Medusa:边缘设备部署,内存受限,无法加载多个模型
选 Self-SD:不想改模型,只想用现有模型获得加速
选 Falcon:大厂生产部署,需要 3x+ 加速
选 MTP:训练阶段加速,或配合其他推理优化使用

12.6.6 生产环境实践

vLLM 中的 Speculative Decoding

Python
from vllm import LLM, SamplingParams

# 启用投机解码
llm = LLM(
    model="meta-llama/Llama-3-70B-Instruct",
    tensor_parallel_size=4,
    speculative_model="meta-llama/Llama-3-8B-Instruct",  # 小模型作为 draft
    num_speculative_tokens=5,  # 每轮猜测 5 个 token
)

# 推理(与普通调用完全相同)
params = SamplingParams(temperature=0.7, max_tokens=512)
output = llm.generate(["解释量子计算"], params)
print(output[0].outputs[0].text)

SGLang 中的 Speculative Decoding

Python
import sglang as sgl

# 启动带投机解码的 SGLang
# python -m sglang.launch_server \
#     --model-path meta-llama/Llama-3-70B-Instruct \
#     --tensor-parallel-size 4 \
#     --speculative-model meta-llama/Llama-3-8B-Instruct \
#     --speculative-num-tokens 5

# 或在 Engine 中配置
llm = sgl.Engine(
    model_path="meta-llama/Llama-3-70B-Instruct",
    tensor_parallel_size=4,
    speculative_model="meta-llama/Llama-3-8B-Instruct",
    speculative_num_tokens=5,
)

output = llm.generate("解释量子计算", sampling_params={"max_new_tokens": 512})

HuggingFace Transformers 中的 Medusa

Python
from transformers import AutoModelForCausalLM, AutoTokenizer

# 加载带 Medusa 的模型
model = AutoModelForCausalLM.from_pretrained(
    "princeton-nlp/Llama-7B-Medusa",
    torch_dtype="auto",
    device_map="auto"
)

# Medusa 解码
tokenizer = AutoTokenizer.from_pretrained("princeton-nlp/Llama-7B-Medusa")
input_ids = tokenizer("解释量子计算", return_tensors="pt").input_ids.to(model.device)

# 使用 medusa_decode 而非标准 generate
outputs = model.medusa_decode(input_ids, medusa_temperature=0.7)
print(tokenizer.decode(outputs[0]))

12.7 MoE模型推理优化

📌 章节说明:本节专门讲解混合专家(Mixture of Experts, MoE)模型的推理优化技术,包括DeepSeek-V3、Mixtral、DeepSeek-MoE等主流MoE架构的推理策略与优化实践。

12.7.1 MoE推理优化的核心挑战

Text Only
┌─────────────────────────────────────────────────────────────────┐
│                    MoE推理核心挑战                                │├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. 稀疏激活带来的负载均衡问题                                    │
│     ├── 不同专家被激活概率不均                                    │
│     ├── 部分专家过载,部分专家空闲                                │
│     └── 影响整体计算效率和延迟                                    │
│                                                                 │
│  2. 专家并行通信开销                                             │
│     ├── 跨GPU/跨节点路由激活的专家                               │
│     ├── All-to-All通信开销大                                     │
│     └── 通信与计算难以有效重叠                                   │
│                                                                 │
│  3. 显存占用问题                                                 │
│     ├── 总参数量大(数百B)                                      │
│     ├── 需要存储所有专家权重                                      │
│     └── KV Cache + 专家权重 → 显存压力巨大                       │
│                                                                 │
│  4. 批处理效率问题                                               │
│     ├── 不同请求激活的专家不同                                    │
│     ├── 难以有效 batching                                        │
│     └── GPU利用率不稳定                                          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

12.7.2 MoE推理架构设计

Python
class MoEInferenceArchitecture:
    """MoE推理架构设计"""

    def __init__(self, model_name: str):
        self.model_name = model_name
        self.expert_config = self.get_expert_config(model_name)

    def get_expert_config(self, model_name: str) -> dict:
        """获取各MoE模型的专家配置"""
        configs = {
            # DeepSeek-V3: 671B总参数,37B激活
            "DeepSeek-V3": {
                "total_params": "671B",
                "activated_params": "37B",
                "num_shared_experts": 1,
                "num_routed_experts": 256,
                "top_k": 8,
                "expert_parallel": "EP144",  # Decode阶段
                "部署单元": "18节点",
                "每卡专家数": "2路由 + 1共享",
            },

            # Mixtral 8x7B: 总46B,激活约12B
            "Mixtral-8x7B": {
                "total_params": "46.7B",
                "activated_params": "12B",
                "num_experts": 8,
                "top_k": 2,
                "expert_type": "SwiGLU",
                "部署": "单机8卡",
            },

            # DeepSeek-MoE-16B: 总16B,激活约2B
            "DeepSeek-MoE-16B": {
                "total_params": "16.4B",
                "activated_params": "2B",
                "num_shared_experts": 1,
                "num_routed_experts": 64,
                "top_k": 4,
                "特点": "细粒度专家,组合自由度更高",
            },
        }
        return configs.get(model_name, {})

    def design_ep_layout(self, num_gpus: int, model_name: str) -> dict:
        """
        设计专家并行布局

        Args:
            num_gpus: 可用GPU数量
            model_name: 模型名称

        Returns:
            专家并行配置
        """
        # DeepSeek-V3 推理部署配置
        if "DeepSeek-V3" in model_name:
            return {
                "prefill": {
                    "expert_parallel": 32,  # EP32
                    "dp": 32,
                    "部署单元": "4节点",
                    "冗余专家": 32,
                    "每卡路由专家": 9,
                    "每卡共享专家": 1,
                },
                "decode": {
                    "expert_parallel": 144,  # EP144
                    "dp": 144,
                    "部署单元": "18节点",
                    "冗余专家": 32,
                    "每卡路由专家": 2,
                    "每卡共享专家": 1,
                }
            }
        return {}

12.7.3 专家选择与负载均衡策略

Python
class ExpertLoadBalancer:
    """专家负载均衡器"""

    def __init__(self, num_experts: int, top_k: int):
        self.num_experts = num_experts
        self.top_k = top_k
        self.expert_counts = [0] * num_experts  # 统计各专家使用次数
        self.expert_bias = [0.0] * num_experts   # 专家偏置(用于负载均衡)

    def router(self, input_features: torch.Tensor, temperature: float = 1.0) -> tuple:
        """
        路由器:为每个token选择top_k个专家

        Args:
            input_features: 输入特征 [batch_size, seq_len, hidden_dim]
            temperature: 温度参数(控制分布平滑度)

        Returns:
            (expert_indices, weights): 选中的专家索引和权重
        """
        # 门控网络计算专家得分
        # 实际实现中这是一个线性层 W_g
        gates = self.compute_gates(input_features)  # [batch_size, seq_len, num_experts]

        # 添加偏置用于负载均衡(DeepSeek的无辅助损失策略)
        gates = gates + torch.tensor(self.expert_bias, device=gates.device)

        # 选择top_k专家
        top_k_gates, top_k_indices = torch.topk(gates, self.top_k, dim=-1)

        # Softmax归一化权重
        top_k_weights = F.softmax(top_k_gates / temperature, dim=-1)

        # 更新统计(用于监控和调整)
        self.update_statistics(top_k_indices)

        return top_k_indices, top_k_weights

    def update_statistics(self, expert_indices: torch.Tensor):
        """更新专家使用统计"""
        # 统计每个专家被选中的次数
        for idx in expert_indices.flatten().unique():
            self.expert_counts[idx.item()] += 1

    def adjust_bias(self, target_load: float = 1.0):
        """
        调整专家偏置以实现负载均衡

        DeepSeek的无辅助损失策略:
        - 根据实际负载动态调整专家偏置
        - 避免辅助损失对模型性能的干扰
        - 保持训练稳定性

        Args:
            target_load: 目标负载率
        """
        total_tokens = sum(self.expert_counts)
        if total_tokens == 0:
            return

        for i in range(self.num_experts):
            actual_load = self.expert_counts[i] / total_tokens
            # 如果实际负载低于目标,增加偏置使其更可能被选中
            # 如果实际负载高于目标,减少偏置使其更少被选中
            self.expert_bias[i] += (target_load - actual_load) * 0.1

        # 重置计数
        self.expert_counts = [0] * self.num_experts

    def get_load_balance_metrics(self) -> dict:
        """获取负载均衡指标"""
        total = sum(self.expert_counts)
        if total == 0:
            return {"status": "no_data"}

        loads = [c / total for c in self.expert_counts]
        return {
            "expert_loads": loads,
            "std": np.std(loads),  # 标准差越小越均衡
            "max_load": max(loads),
            "min_load": min(loads),
            "balance_ratio": min(loads) / max(loads) if max(loads) > 0 else 0,
        }

12.7.4 MoE推理优化策略

Python
class MoEInferenceOptimizer:
    """MoE推理优化器"""

    def __init__(self, model: nn.Module, config: dict):
        self.model = model
        self.config = config

    # ========== 策略1: 专家缓存与预加载 ==========

    def cache_experts(self, device: str = "cuda"):
        """
        专家权重缓存:将所有专家权重预加载到GPU

        优化效果:
        - 避免推理时动态加载专家权重
        - 减少显存访问延迟
        """
        for expert in self.model.experts:
            expert.to(device)

    # ========== 策略2: 动态专家并行 ==========

    def dynamic_expert_parallel(
        self,
        batch_size: int,
        seq_len: int,
        phase: str = "decode"
    ) -> int:
        """
        动态选择专家并行度

        Args:
            batch_size: 批次大小
            seq_len: 序列长度
            phase: 阶段(prefill/decode)

        Returns:
            最优专家并行度
        """
        if phase == "prefill":
            # Prefill阶段计算密集,可用较高EP
            return 32
        else:
            # Decode阶段延迟敏感,降低EP以减少通信
            return min(144, self.config.get("num_gpus", 8))

    # ========== 策略3: 计算通信重叠 ==========

    def overlap_compute_comm(
        self,
        expert_ids: list,
        expert_outputs: dict,
        next_batch: dict
    ) -> torch.Tensor:
        """
        计算与通信重叠

        双batch策略:
        - Batch A在进行计算时,Batch B进行通信
        - 两者交错执行,掩盖通信开销
        """
        # 实现细节:需要异步All-to-All通信
        # 使用CUDA流实现计算与通信并行
        pass

    # ========== 策略4: 冗余专家部署 ==========

    def deploy_redundant_experts(
        self,
        num_original: int,
        redundancy: int = 32
    ) -> int:
        """
        部署冗余专家

        DeepSeek-V3策略:
        - 部署单元包含32个冗余路由专家
        - 应对负载不均衡时专家溢出
        - 减少跨节点通信

        Args:
            num_original: 原始专家数
            redundancy: 冗余专家数

        Returns:
            总专家数(含冗余)
        """
        return num_original + redundancy

    # ========== 策略5: 稀疏激活优化 ==========

    def sparse_activation(
        self,
        hidden_states: torch.Tensor,
        top_k: int
    ) -> tuple:
        """
        稀疏激活优化

        仅对top_k专家进行计算,大幅降低计算量
        """
        # 路由计算
        gates = self.model.gate(hidden_states)

        # 选择top_k
        top_k_gates, top_k_indices = torch.topk(gates, top_k, dim=-1)

        # 稀疏执行:仅计算选中的专家
        outputs = self.sparse_forward(hidden_states, top_k_indices)

        return outputs, top_k_indices

    def sparse_forward(
        self,
        hidden_states: torch.Tensor,
        expert_indices: torch.Tensor
    ) -> torch.Tensor:
        """
        稀疏前向传播

        仅对激活的专家进行计算
        """
        # 实际实现中需要处理混合专家的稀疏计算
        # 通常使用专家ID索引进行分组计算
        outputs = []

        for batch_idx in range(hidden_states.size(0)):
            token_hidden = hidden_states[batch_idx:batch_idx+1]

            # 对每个token,选择对应的专家
            for token_idx, experts in enumerate(expert_indices[batch_idx]):
                expert_output = self.compute_single_expert(
                    token_hidden[token_idx],
                    experts
                )
                outputs.append(expert_output)

        return torch.stack(outputs)

12.7.5 DeepSeek-V3 推理优化实践

Python
class DeepSeekV3Optimizer:
    """DeepSeek-V3专用推理优化器"""

    def __init__(self, model_path: str = "deepseek-ai/DeepSeek-V3"):
        self.model_path = model_path
        self.quantization_config = {
            "quant_method": "fp8",
            "fmt": "e4m3",
            "weight_block_size": [128, 128],
            "activation_scheme": "dynamic",
        }

    def optimize_with_fp8(self, model: nn.Module) -> nn.Module:
        """
        FP8量化推理优化

        DeepSeek-V3原生FP8权重,支持:
        - SGLang:原生FP8推理
        - LMDeploy:原生FP8推理
        - TensorRT-LLM:BF16推理
        """
        # 权重量化:Block-wise 128x128
        # 激活量化:Per-token动态量化
        return quantized_model

    def optimize_mla(self, model: nn.Module) -> nn.Module:
        """
        MLA(Multi-Head Latent Attention)优化

        核心优化:
        - KV Cache压缩93.3%
        - 低秩潜在向量替代完整KV矩阵
        """
        # MLA通过将KV压缩到低维空间
        # 推理时仅需缓存压缩向量
        # 需要时通过上投影矩阵恢复
        return optimized_model

    def get_deployment_config(self, num_gpus: int, phase: str = "decode") -> dict:
        """
        获取DeepSeek-V3推理部署配置

        Args:
            num_gpus: 可用GPU数量
            phase: prefill 或 decode

        Returns:
            优化后的部署配置
        """
        if phase == "prefill":
            return {
                "expert_parallel": 32,
                "dp": 32,
                "部署单元": "4节点",
                "冗余路由专家": 32,
                "每卡路由专家": 9,
                "每卡共享专家": 1,
                "通信优化": "双batch计算通信重叠",
            }
        else:  # decode
            return {
                "expert_parallel": min(144, num_gpus),
                "dp": min(144, num_gpus),
                "部署单元": f"{num_gpus // 8}节点",
                "冗余路由专家": 32,
                "每卡路由专家": 2,
                "每卡共享专家": 1,
                "通信优化": "EP内All-to-All",
            }

    def benchmark_throughput(
        self,
        num_gpus: int = 8,
        input_len: int = 1024,
        output_len: int = 128
    ) -> dict:
        """
        基准测试DeepSeek-V3推理吞吐量

        Returns:
            性能指标
        """
        # H800 8卡配置下的预期性能
        return {
            "prefill_throughput": "约10万tokens/s",
            "decode_throughput": "约60 tokens/s",
            "memory_per_gpu": "约40GB",
            "total_memory": f"{num_gpus * 40}GB",
        }

12.7.6 MoE推理框架对比

Text Only
┌─────────────────────────────────────────────────────────────────────────┐
│                    MoE推理框架支持对比                                     │├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  框架        │ MoE支持          │ DeepSeek-V3支持   │ 特点                    │
│  ────────────┼──────────────────┼──────────────────┼─────────────────────────│
│  SGLang      │ 原生支持         │ FP8原生支持       │ RadixAttention前缀复用    │
│  vLLM        │ MoE专家并行      │ BF16/FP8         │ PagedAttention           │
│  LMDeploy     │ MoE优化         │ FP8原生支持       │ TurboMind引擎            │
│  TensorRT-LLM │ 专家并行        │ BF16推理         │ 高性能TensorRT优化        │
│  MindIE      │ MoE支持         │ BF16推理         │ 昇腾AI加速               │
│                                                                         │
│  选择建议:                                                              │
│  • 追求最高推理速度:TensorRT-LLM(需NVIDIA)                           │
│  • 追求FP8原生支持:SGLang 或 LMDeploy                                │
│  • 跨平台兼容:vLLM                                                    │
│  • 昇腾硬件:MindIE                                                   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

12.7.7 MoE推理优化面试要点

Text Only
┌─────────────────────────────────────────────────────────────────────────┐
│                    MoE推理优化面试要点                                   │├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  Q1: MoE模型推理的主要挑战是什么?                                        │
│  ──────────────────────────────────────────────────────────────────────│
│  答:主要挑战有四方面:                                                  │
│  1. 负载均衡:专家激活不均,需要动态调整                                  │
│  2. 通信开销:跨GPU专家并行带来All-to-All通信                           │
│  3. 显存压力:总参数大(数百B),需高效管理                              │
│  4. 批处理效率:不同请求激活专家不同,难以有效batching                    │
│                                                                         │
│  Q2: DeepSeek-V3的MLA如何实现KV Cache压缩?                             │
│  ──────────────────────────────────────────────────────────────────────│
│  答:MLA通过低秩联合压缩技术:                                            │
│  • 将高维KV矩阵压缩到低维潜在空间(d_c << d_h × n_h)                   │
│  • 推理时仅需缓存压缩向量,而非完整KV矩阵                                │
│  • KV Cache压缩率达93.3%                                               │
│  • 通过上投影矩阵在计算时动态恢复                                        │
│                                                                         │
│  Q3: MoE的负载均衡策略有哪些?                                           │
│  ──────────────────────────────────────────────────────────────────────│
│  答:主流策略有:                                                        │
│  • 辅助损失函数:添加负载均衡损失项(简单但影响主任务)                   │
│  • DeepSeek无辅助损失:动态调整专家偏置项                               │
│  • 专家容量限制:设置专家最大处理token数                                 │
│  • 冗余专家部署:部署额外专家应对溢出                                   │
│                                                                         │
│  Q4: MoE推理如何实现计算通信重叠?                                       │
│  ──────────────────────────────────────────────────────────────────────│
│  答:双batch策略:                                                      │
│  • Batch A计算时,Batch B进行通信                                       │
│  • 两者交错执行,掩盖通信延迟                                           │
│  • Prefill阶段尤其有效                                                 │
│                                                                         │
│  Q5: DeepSeek-V3的专家并行配置是什么?                                   │
│  ──────────────────────────────────────────────────────────────────────│
│  答:分阶段配置:                                                        │
│  • Prefill:EP32 + DP32,4节点部署                                     │
│  • Decode:EP144 + DP144,18节点部署                                    │
│  • 每卡:2路由专家 + 1共享专家(Decode)                                │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

12.7.8 MoE推理优化小结

Text Only
┌─────────────────────────────────────────────────────────────────────────┐
│                    MoE推理优化核心要点                                   │├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  1. 架构层面:                                                           │
│     • MLA:KV Cache压缩93.3%                                           │
│     • DeepSeekMoE:共享专家 + 细粒度路由专家                            │
│     • 无辅助损失负载均衡:动态偏置调整                                   │
│                                                                         │
│  2. 部署层面:                                                           │
│     • Prefill:EP32,适合计算密集                                       │
│     • Decode:EP144,适合延迟敏感                                       │
│     • 冗余专家:32个冗余路由专家                                        │
│                                                                         │
│  3. 优化策略:                                                           │
│     • 计算通信重叠:双batch策略                                         │
│     • 稀疏激活:仅计算top_k专家                                         │
│     • FP8量化:E4M3格式,Block-wise 128x128                            │
│                                                                         │
│  4. 框架选择:                                                           │
│     • SGLang/LMDeploy:原生FP8支持                                     │
│     • TensorRT-LLM:高性能NVIDIA优化                                    │
│     • vLLM:通用性好,跨平台支持                                        │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

12.8 练习题

练习题 1:KV Cache

题目:实现一个简单的 KV Cache 机制。

参考答案:

Python
class SimpleKVCache:
    def __init__(self, num_heads, head_dim, max_seq_len):
        self.cache_k = torch.zeros(num_heads, max_seq_len, head_dim)
        self.cache_v = torch.zeros(num_heads, max_seq_len, head_dim)
        self.current_pos = 0

    def update(self, k, v):
        self.cache_k[:, self.current_pos, :] = k
        self.cache_v[:, self.current_pos, :] = v
        self.current_pos += 1

    def get(self):
        return self.cache_k[:, :self.current_pos, :], self.cache_v[:, :self.current_pos, :]

练习题 2:批处理

题目:实现一个简单的批处理器。

参考答案:

Python
class SimpleBatchProcessor:
    def __init__(self, max_batch_size):
        self.max_batch_size = max_batch_size
        self.request_queue = []

    def add_request(self, request_id, input_ids):
        self.request_queue.append((request_id, input_ids))

    def get_batch(self):
        if not self.request_queue:
            return None, []

        batch_requests = self.request_queue[:self.max_batch_size]
        self.request_queue = self.request_queue[len(batch_requests):]

        request_ids = [req[0] for req in batch_requests]
        batch_data = torch.stack([req[1] for req in batch_requests])

        return batch_data, request_ids

12.9 面试准备

12.9.1 大厂面试题

字节跳动面试题:

  1. 问题:什么是 KV Cache?它有什么优势?

参考答案: - KV Cache 缓存自注意力的 Key 和 Value - 优势: - 避免重复计算 - 减少计算量约 50% - 提升推理速度 2-3 倍 - 增加显存占用约 30%

  1. 问题:连续批处理相比传统批处理有什么优势?

参考答案: - 避免 Padding 浪费 - 提高 GPU 利用率 - 降低计算成本 - 提高吞吐量

腾讯面试题:

  1. 问题:如何优化大模型的推理性能?

参考答案: - KV Cache:缓存注意力计算 - 批处理:提高 GPU 利用率 - 模型量化:减少计算量 - 编译优化:优化执行图 - 硬件优化:使用专用硬件

  1. 问题:PagedAttention 有什么优势?

参考答案: - 动态分配显存 - 减少显存浪费 - 支持更长的序列 - 提高显存利用率

阿里巴巴面试题:

  1. 问题:推理优化有哪些层次?

参考答案: - 算法层:KV Cache 、 Flash Attention - 模型层:量化、剪枝、蒸馏 - 框架层:算子融合、内存优化 - 硬件层:GPU 优化、专用芯片

  1. 问题:在实际项目中如何优化推理性能?

参考答案: - 性能分析:识别瓶颈 - 选择优化:选择合适的优化技术 - 实现优化:实现优化方案 - 测试验证:测试优化效果 - 持续监控:监控性能指标 - 持续优化:根据反馈持续优化

12.9.2 面试技巧

技巧 1:理论联系实际

结合实际项目经验,说明如何应用推理优化。

技巧 2:性能分析

展示性能分析的方法和工具。

技巧 3:优化选择

说明如何选择合适的优化技术。

技巧 4:效果评估

说明如何评估优化效果。

📝 本章小结

本章系统介绍了推理优化的核心内容:

  1. ✅ 推理优化概述:为什么需要、优化层次、优化指标
  2. ✅ KV Cache:原理、实现、优化
  3. ✅ 批处理优化:批处理原理、连续批处理、动态批处理
  4. ✅ 编译优化:TorchScript 、 TensorRT 、 ONNX Runtime
  5. ✅ 推理框架:vLLM 与 SGLang 对比及选型
  6. ✅ Speculative Decoding:投机解码原理、变种(EAGLE/Medusa/Self-SD/Falcon/MTP)与生产实践
  7. ✅ 练习题:KV Cache 、批处理
  8. ✅ 面试准备:大厂面试题和解答技巧

通过本章学习,你应该能够: - 理解推理优化的核心原理 - 掌握 KV Cache 等关键技术 - 学会使用批处理和流水线优化 - 了解编译优化技术 - 能够根据场景选择 vLLM 或 SGLang - 理解 Speculative Decoding 的数学保证、变种(EAGLE/Medusa/Self-SD/Falcon/MTP)与工程实现 - 准备好应对大厂面试

🔗 下一步

下一章我们将深入学习多模态应用,掌握如何处理和生成多模态数据。

继续学习: 13-多模态应用.md

💡 思考题

  1. 什么是 KV Cache?它有什么优势?

    缓存已计算的 Key/Value 向量,避免每次生成新 Token 时重复计算前文的 Attention 。优势:生成速度提升数十倍(从 O(n²)降为 O(n))。挑战: KV Cache 显存随序列长度线性增长(128K context 可占数十 GB), PagedAttention/GQA 可缓解。

  2. 连续批处理相比传统批处理有什么优势?

    传统(Static Batching):等所有请求生成完才返回,最慢请求拖慢全扑 0 。连续(Continuous Batching):请求完成即时释放资源并插入新请求, GPU 利用率提升 10-20x 。 vLLM/TGI 均默认启用。核心思想:让 GPU 永远不闲置。

  3. 如何优化大模型的推理性能?

    分层优化:①模型层(量化、蒸馏、剪枝) ②注意力层(FlashAttention 、 GQA 、 MQA 、 Sliding Window) ③解码层(KV Cache 、 Speculative Decoding 、 Early Exit) ④服务层(连续批处理、 PagedAttention 、请求调度) ⑤硬件层(TensorRT 编译、 Tensor 并行)。开箱即用: vLLM 已集成多数优化。

  4. PagedAttention 有什么优势?

    借 Linux 虚拟内存分页思想管理 KV Cache :将连续的 KV Cache 分为固定大小的 Block(页),按需分配。优势:①显存浪费从 60-80%降低刼<4% ②尾部填充减少 ③支持共享 Prefix(多请求复用系统提示词的 KV Cache) ④同等显存下并发请求数提升 5x+。这是 vLLM 的核心创新。

  5. 在实际项目中如何优化推理性能?

    路线:①先用 vLLM 部署(开箱即用大部分优化) ②测量 throughput 和 P99 latency 基线 ③尝试量化(AWQ/GPTQ)观察性能/质量权衡 ④开启 Speculative Decoding(若时延敏感) ⑤调整 batch_size/max_num_seqs 找最优点 ⑥TensorRT 编译(NVIDIA 平台)。监控: token/s 、首 Token 延迟(TTFT)、 GPU 利用率。

  6. SGLang 的 RadixAttention 相比 vLLM 的 PagedAttention 有什么优势?适用于什么场景?

    RadixAttention 用 Radix Tree(基数树)管理 KV Cache ,实现前缀级别的自动复用。优势:多轮对话/多路并行场景下 KV Cache 命中率更高, LRU 自动淘汰无需手动管理。适用场景:大量 Shared Prefix(同一系统提示词)、 Tree of Thoughts 推理、多轮 Agent 对话。 PagedAttention 更通用, RadixAttention 在特定模式下更高效。

  7. Speculative Decoding 为什么能保证输出分布无损?它的加速比受哪些因素影响?

    采用拒绝采样(rejection sampling)策略:草稿模型生成候选 token ,目标模型并行验证,按 min(1, p_target/p_draft)概率接受。数学证明接受的 token 服从目标模型分布。加速比受:①草稿模型接受率(与目标模型越接近越好) ②草稿模型速度(越小越快) ③推测 token 数(通常 4-8) ④任务确定性(确定性任务加速比更高)。典型加速 2-3x 。

📚 参考资料

  1. "Efficient Attention: Attention with Linear Complexities" - Katharopoulos et al.
  2. "FlashAttention: Fast and Memory-Efficient Exact Attention" - Dao et al.
  3. "PagedAttention: Efficient Attention for Long Sequences" - Kwon et al.
  4. vLLM Documentation
  5. SGLang Documentation - https://sgl-project.GitHub.io/
  6. "Efficient Memory Management for Large Language Model Serving with PagedAttention" - Kwon et al.
  7. "Fast Inference of Mixture-of-Experts Language Models with Offloading" - Eliseev & Mazur
  8. "Accelerating Large Language Model Decoding with Speculative Sampling" - Leviathan et al.
  9. TensorRT Documentation

最后更新日期: 2026-03-26 适用版本: LLM 应用指南 v2026