CS336 · 第 4 课

注意力替代方案与混合专家

长上下文会放大标准 attention 的计算与数据移动成本。这一课分开研究两类稀疏性:减少 token 之间的 attention edges,以及让每个 token 只激活一部分专家参数;两者解决的不是同一个瓶颈。

授课:Tatsu Hashimoto 预计用时:80 分钟 难度:中等 → 困难

Spring 2026 官方来源:Lecture 4 PDF · 课程日程

本页是中文学习导读;讲次主题、教师与材料版本以 Stanford 官方页面和讲义为准。

本讲问题:标准 attention 在上下文、KV cache 和计算量上形成瓶颈,因此需要先明确想减少的是 KV heads、序列交互还是每 token 激活的参数量,再选择替代机制。

30 秒知识地图

标准 attention 成本 → GQA / local 等减少 attention 开销 → MoE 让每 token 只激活部分参数 → routing / load balance / communication 成为新约束。

本课问题与 prerequisite refresher

标准 Transformer 的两种扩展压力不同:attention 随 context 增长,dense MLP 则让每个 token 使用全部 FFN 参数。先复习 \(QK^\top\) 的序列轴与 MLP 的逐 token 独立性,再比较“共享”“稀疏激活”究竟改的是哪个对象。

不要把三种“变小”混在一起

GQA: query heads 保留,KV heads 共享 → KV cache / KV projection 变小
local attention: 每个 query 可见的 key positions 变少 → attention edges 变少
MoE: total expert parameters 很大,每 token 只路由到 top-k → active FLOPs 变小

total parameters ≠ active parameters per token
router probability ≠ attention probability
attention sparsity ≠ MoE parameter sparsity
capacity ≠ guaranteed throughput

Toy routing

4 个 experts、top-1 routing、8 个 tokens。若 7 个都选 expert 0,理论上每 token 仍只算一个 expert,但 expert 0 排队而其余空闲;若 capacity factor 只容纳 3 个 token,还会 drop 或 reroute。这个反例说明 MoE 的 FLOPs 优势不自动变成 wall-clock 优势,load balancing 与 all-to-all 是机制的一部分。

关键误区

“GQA 把整个模型缩小 \(h_q/h_{kv}\) 倍”错误,它主要减少 K/V 相关对象;“local attention 让所有长程依赖消失”也不必然成立,多层传播或间歇 global layers 仍可传递信息;“MoE 参数更多所以每 token FLOPs 必更多”混淆了 total 与 active parameters。


标准 Attention 的瓶颈

O(n²) 问题

标准 self-attention 对长度为 n 的序列:

scores = Q @ K^T    # (n, d) @ (d, n) → (n, n)  ← O(n²d) FLOPs
attn = softmax(scores)
output = attn @ V   # (n, n) @ (n, d) → (n, d)  ← O(n²d) FLOPs

朴素训练实现的中间量: 可能物化 (n, n) 的 attention matrix

当 n = 128K(长上下文),n² 超过 16G 元素,单个 bf16 矩阵约 32 GiB。FlashAttention 可以在不改变 full attention 数学结果的前提下避免物化该矩阵;若要降低 O(n²d) 计算量,则需要稀疏、局部、低秩或其他序列模型。

两条突围路径

路径思路代表
更高效的 attention 实现减少 HBM IO,不改变 full attentionFlashAttention
改变 attention/序列模型近似、稀疏化或递归状态Linear Attention, Sliding Window, SSM
稀疏激活每次只激活部分参数Mixture of Experts

注意力替代方案

1. Linear Attention

核心思想:把 softmax 去掉,用核函数近似:

# 标准 attention: O(n²)
attn = softmax(Q @ K^T / √d) @ V

# Linear attention: O(n)
# 利用 (Q @ K^T) @ V = Q @ (K^T @ V) 的结合律
# 先算 K^T @ V(d × d),再算 Q @ (K^T @ V)
output = φ(Q) @ (φ(K)^T @ V)  # φ 是核函数(如 ReLU, elu+1)

# 复杂度: O(nd²) —— 当 n >> d 时远优于 O(n²d)

关键洞察:矩阵乘法满足结合律。标准 attention 先算 (Q @ K^T) 得到 n×n 矩阵,再乘 V。Linear attention 先算 (K^T @ V) 得到 d×d 矩阵,再被 Q 乘。当 n >> d 时,后者快得多。

代价:核化 linear attention 改变了 softmax attention 的相似度与归一化形式,表达能力和数值行为可能不同;token 仍由 Q/K 内容动态加权,并不是“所有 token 被平等对待”。

2. Sliding Window Attention

Mistral 7B 和 LongFormer 使用:

# 每个 token 只看前后 w 个 token
for i in range(n):
    scores[i] = Q[i] @ K[max(0,i-w):i+w]^T  # 只看窗口内的 key
    attn[i] = softmax(scores[i])
    output[i] = attn[i] @ V[max(0,i-w):i+w]

# 复杂度: O(nwd) —— 线性于 n

直觉:大多数语言建模任务中,附近的 token 比远处的更重要。

3. Sparse Attention(全局 + 局部混合)

大多数层用 sliding window
少数层用 full attention("全局层")
例如: 每 6 层有 1 层 global attention

若每层只有 causal local window,理论 receptive field 会随层数扩大,数量级可到 (Lw);但这只是“存在一条传播路径”,不等于远端信息能被可靠检索。加入 global layer 则会改变这个传播边界。

Spring 2026 · learned sparse retrieval

固定窗口预先规定每个 query 能看哪些位置;DeepSeek Sparse Attention 一类方法则增加轻量 indexer,为当前 query 从历史位置中挑选少量相关 key/value。计算图仍是“query 连接一小部分 token”,不是把 FFN experts 做 top-k routing。索引器的额外成本、召回错误与稀疏 kernel 效率共同决定是否真正比 full attention 快。

4. State Space Models(Mamba)

完全抛弃 attention,用经典控制论的状态空间模型:

# SSM: 递推形式
h(t) = A × h(t-1) + B × x(t)
y(t) = C × h(t)

# 训练时: 用并行 scan / 专用 kernel 高效计算
# 推理时: 递推;状态内存相对序列长度为 O(1)

# Mamba 的改进: A, B, C 是输入相关的(选择性 SSM)
# 让模型学会"忘记"不相关的信息

SSM 的边界:固定大小状态会形成信息瓶颈,部分 associative-recall 与精确检索任务可能弱于 attention;混合模型常用少量 attention 层弥补这一点。具体能力取决于 state size、选择机制和训练数据,不能概括为 SSM 一定需要“极大 state”才可工作。

方案对比

方法训练复杂度Attention intermediate memory(不含 KV cache)长程依赖
Full AttentionO(n²d)朴素实现物化 score/probability 为 O(n²);FlashAttention 避免保存完整矩阵任意位置直接交互
Linear Attention常见形式 O(nd²)依实现而定受核函数与状态容量限制
Sliding WindowO(nwd)O(nw)局部好
Hybrid (Sparse)O(nwd + n²/L)O(nw + n²/L)好
SSM/Mamba对序列长度近似线性推理状态不随历史长度增长受固定状态瓶颈限制

不要与 decode KV cache 混淆:上表这一列讨论单次 attention 计算的中间张量。标准 autoregressive decode 的历史 K/V storage 则随已缓存 context length 线性增长;二者是不同对象、不同时间尺度。


Grouped-Query Attention (GQA)

从 MHA 到 MQA 到 GQA

Multi-Head Attention (MHA)
N 个 Q 头, N 个 K 头, N 个 V 头
KV cache: N × d_head × 2 (K+V) per token per layer

Multi-Query Attention (MQA)
N 个 Q 头, 1 个 K 头, 1 个 V 头
KV cache: 1 × d_head × 2 per token per layer
权衡: KV cache 最小,但共享 K/V 可能带来质量损失

Grouped-Query Attention (GQA)
N 个 Q 头, K 个 KV 头 (1 < K < N)
每 N/K 个 Q 头共享 1 个 KV 头
KV cache: K × d_head × 2 per token per layer

为什么 GQA 有效?

KV cache 是推理瓶颈:生成一个 token 时,需要读取所有层的 KV cache。对于长序列和大 batch,这占了推理时间的大部分。

GQA 把 KV cache 缩小 N/K 倍(例如 8 倍),通常能降低 decode 阶段的内存带宽压力;实际延迟还取决于 batch、上下文长度、kernel 和并行策略。

实际效果

Llama 2 70B 配置:
n_heads = 64, n_kv_heads = 8
GQA ratio = 8:1
KV cache 缩小 8 倍!

Ainslie et al. (2023) 的 uptraining 实验中:
GQA 可以接近 MHA 质量并获得接近 MQA 的 KV-cache 优势;
这不是对所有模型、数据与训练方式的“无损”保证。

Multi-head Latent Attention (MLA)

DeepSeek-V2 的进一步创新:

不存储完整的 K, V 向量
存储一个低秩压缩的 "latent" 向量 c (C 维)
需要时通过上投影恢复 K, V

KV cache 存储: C 维 (而不是 N × d_head = 16384 维)
DeepSeek-V2: C = 512 (压缩 32 倍!)

直接把全部 K/V 投影吸收到 latent 计算时会与位置相关的 RoPE 冲突
DeepSeek 用 decoupled RoPE:保留额外的 RoPE key/query 维度
总: 512 + 64 = 576 维 per layer per token

方法KV cache / token / layer相对大小
MHA (Llama 70B)64 × 128 × 2 = 16,3841×
GQA (Llama 70B)8 × 128 × 2 = 2,0480.125×
MQA1 × 128 × 2 = 2560.016×
MLA (DeepSeek V2)5760.035×

Mixture of Experts (MoE)

核心思想

MoE 的直觉:把一个大 FFN 替换为多个小 FFN("专家"),每次只激活其中几个。

模型总参数 = 所有专家参数之和

每次计算量 = 被激活的专家参数之和

效果:每个 token 只路由到少数专家时,总参数量可以远大于每 token 的激活参数量;具体倍数取决于专家数、top-k、共享专家和路由开销,不能固定写成 8×。

MoE 层结构

class MoELayer(nn.Module):
    def __init__(self, d_model, d_ff, num_experts, top_k):
        self.gate = nn.Linear(d_model, num_experts)  # 路由器
        self.experts = nn.ModuleList([
            FFN(d_model, d_ff) for _ in range(num_experts)
        ])
        self.top_k = top_k  # 通常 2-4

    def forward(self, x):
        # 1. 路由: 每个 token 选择 top_k 个专家
        router_probs = self.gate(x).softmax(dim=-1)  # (batch*seq, num_experts)
        top_k_scores, top_k_indices = router_probs.topk(self.top_k, dim=-1)
        top_k_scores = top_k_scores / top_k_scores.sum(dim=-1, keepdim=True)

        # 2. 分发: 把 token 送到对应的专家
        output = torch.zeros_like(x)
        for k in range(self.top_k):
            expert_idx = top_k_indices[:, k]
            expert_mask = (
                expert_idx.unsqueeze(1)
                == torch.arange(len(self.experts), device=x.device)
            )
            # 把属于该专家的 token 送入计算
            for i, expert in enumerate(self.experts):
                token_mask = expert_mask[:, i]
                if token_mask.any():
                    weight = top_k_scores[token_mask, k].unsqueeze(-1)
                    output[token_mask] += expert(x[token_mask]) * weight

        return output

路由策略

策略描述使用
Top-K选分数最高的 K 个专家标准做法
Top-K with noise加噪声后选 top-K训练时负载均衡
Expert choice专家选择固定容量的 tokenExpert Choice Routing 等研究
Soft routing所有专家加权混合Soft MoE

DeepSeek-V3 使用的是 token-choice top-k 路由,并通过可学习的 expert bias 做近似 auxiliary-loss-free 的负载均衡;它不是 Expert Choice Routing。

负载均衡问题

MoE 最大的挑战:负载不均衡。热门专家过载,冷门专家饥饿。

负载不均衡的后果:

辅助损失(Auxiliary Loss)

# Switch Transformer / DeepSeek 类辅助损失的简化示意
# 同时考虑离散 token 分配和连续路由概率

def load_balancing_loss(router_logits, top_k, num_experts):
    # router_logits: (num_tokens, num_experts)
    router_probs = router_logits.softmax(dim=-1)
    selected = router_probs.topk(top_k, dim=-1).indices

    # 实际离散路由后,每个专家接收的 token-assignment 比例
    assignments = F.one_hot(selected, num_classes=num_experts).float()
    token_fraction = assignments.mean(dim=(0, 1))

    # 路由器分配给每个专家的平均概率
    prob_fraction = router_probs.mean(dim=0)

    # Switch/DeepSeek 类辅助项的简化写法
    load_loss = num_experts * (token_fraction * prob_fraction).sum()
    return load_loss

# 总损失 = 主损失 + α × 辅助损失
# α 是需要按模型、路由方案与训练规模调节的超参数

MoE 的通信挑战

在多 GPU 上,不同专家分布在不同 GPU 上:

Token 需要"旅行"到专家所在的 GPU
这需要 all-to-all 通信

一次 MoE 前向通常包含两阶段通信:
1. dispatch all-to-all:把 token hidden states 发到专家所在设备
2. combine all-to-all:把专家输出发回原设备

忽略本地命中、padding 与元数据时,理想化数据量与
2 × top_k × num_tokens × d_model × bytes_per_element 同阶;
实际网络流量取决于 expert-parallel group、专家放置、容量限制和负载均衡。

并行策略

策略描述通信
Expert Parallelism专家分布在不同 GPUall-to-all
Expert + Data ParallelDP 组内 EPall-to-all within group
Token Dropping超载 token 直接跳过无(但损失信息)

现代 MoE 模型

模型总参数激活参数ExpertsTop-K
Mixtral 8×7B47B13B82
DeepSeek-V2236B21B1606
DeepSeek-V3671B37B2568
Qwen 3 235B235B22B1288

MoE 的 scaling 优势:在专家大小和 top-k 不变时,增加专家数可以提高总参数容量,而每个 token 的专家计算量近似不变。不过这并非“免费扩展”:参数内存、路由与负载均衡难度、all-to-all 通信以及推理部署成本都会随系统规模变化。


MoE 的挑战与解决方案

挑战 1:训练不稳定

MoE 训练容易发散,特别是当某些专家被过度使用时。

解决方案:

挑战 2:通信开销

all-to-all 通信在跨节点时成为瓶颈。

解决方案:

挑战 3:专家设计与初始化

专家粒度、共享专家是否有效以及如何从已有 dense 模型初始化,都需要通过模型与数据上的实验验证。

常见设计:

挑战 4:推理内存

虽然激活参数少,但推理时所有专家都要在内存中。

解决方案:


混合架构趋势

最新的模型开始混合不同架构:

Jamba(AI21 Labs)

Transformer + Mamba 混合
比例: 1 层 Transformer : 7 层 Mamba
52B MoE (16 experts, 2 active)
兼顾 Transformer 的质量和 Mamba 的效率

MiniMax-01

Linear Attention + Full Attention 混合
456B MoE
大多数层用 linear attention(高效)
少数层用 full attention(保证质量)

DeepSeek-V3

Multi-head Latent Attention (MLA) + MoE
671B 总参数, 37B 激活参数
256 个路由专家 + 1 个共享专家
每个 token 激活 8 个路由专家 + 共享专家


知识检查

问题 1:GQA 的主要优势是什么?

问题 2:一个 MoE 子层有 64 个等大小 routed experts、top_k=2;忽略共享专家和路由器时,每个 token 激活该专家子层参数的多少?

问题 3:MoE 训练中最大的挑战是什么?

问题 4:Linear attention 为什么比标准 attention 快?

问题 5:MoE 在多 GPU 上的主要通信瓶颈是什么?


Concept Check · active parameters 与负载

4 experts、top-1、8 tokens 全选 expert 0。每 token 是否仍只激活一个 expert?系统是否仍理想并行?

答案:前者是,后者否;active FLOPs 稀疏,但负载集中且可能超过 capacity。

若认为“只激活一个所以一定快”,你把算法 FLOPs 上限当成了均衡后的硬件吞吐。

你现在应该能够解释

通往第 5 课

现在已经知道算法想减少哪些 FLOPs 与 bytes;下一课把这些逻辑对象放进真实 GPU memory hierarchy,解释为什么同样的数学量会有完全不同的 wall time。

如果只能记住 5 件事

GQA

共享 KV 头,KV cache 缩小 N/K 倍

Linear Attention

结合律 + 核近似,O(nd²) 替代 O(n²d)

MoE

稀疏激活,大容量低计算

负载均衡

MoE 的核心挑战,需要辅助损失


延伸阅读

Reference · 延伸阅读与一手资料