DeepSeek MLA + MoE
用更少的计算资源达到大模型的效果 — 低秩压缩注意力 + 稀疏专家混合
- MLA(多头潜在注意力)用低秩压缩 KV 缓存,大幅降低推理成本
- MoE(专家混合)让模型只激活部分参数,提升计算效率
- DeepSeek 证明了"高效架构 + 精细工程"可以匹敌更大规模模型
- 开源策略推动了整个 AI 社区的技术进步
DeepSeek 的创新
DeepSeek 的核心思想:用更少的计算资源达到大模型的效果。传统大模型(如 LLaMA)在推理时需要激活所有参数,计算成本极高。DeepSeek 通过两大创新大幅降低了计算开销。
生活类比:想象一家公司遇到一个复杂问题。传统做法是请一个全能专家(工资很高),而 DeepSeek 的做法是请一组各有所长的专家团队,每次只激活最相关的 2 位专家来解决问题。这样既保证了专业性,又大幅降低了成本。
两大核心创新
Multi-head Latent Attention (MLA)
低秩压缩 KV Cache
将 K、V 压缩到低维空间,推理时只需缓存压缩后的向量,大幅减少显存占用
Mixture of Experts (MoE)
稀疏激活专家网络
路由器根据输入动态选择 Top-K 专家,每次只激活少量专家,大幅减少计算量
DeepSeek 效率对比
Multi-head Latent Attention (MLA)
标准 Multi-head Attention 的问题:推理时需要缓存每个 token 的 K 和 V 向量(KV Cache),序列越长占用显存越大。MLA 通过低秩压缩解决这个问题。
标准 MHA vs MLA 的 KV Cache 对比
假设:模型维度 d=128,注意力头数 h=8,每头维度 dk=16,序列长度 n=1024,压缩维度 dc=32
| KV Cache 显存占用对比 | |||
| 方案 | 每 token 缓存 | 计算公式 | 总显存 (n=1024) |
|---|---|---|---|
| 标准 MHA | K + V | 2 × h × dk = 2 × 8 × 16 | 256 × 1024 = 262,144 |
| MLA (压缩) | cKV | dc = 32 | 32 × 1024 = 32,768 |
| 节省比例 | MLA 只需缓存压缩向量 cKV | 8 倍压缩! | |
MLA 工作流程
cKV = WDKV · h (下投影:压缩)
K = WUK · cKV (上投影:恢复 K)
V = WUV · cKV (上投影:恢复 V)
MLA 低秩分解 — Excel 手推
用一个简化的例子手推 MLA 的压缩和恢复过程。点击蓝色单元格可以编辑输入值!
输入隐藏状态 h
假设输入向量 h 维度 d=4(简化演示),压缩维度 dc=2:
| 输入向量 h(点击编辑) | |||
| h1 | h2 | h3 | h4 |
|---|---|---|---|
| 1.0 | 0.5 | -0.3 | 0.8 |
Step 3a:下投影 — 压缩 cKV = WDKV · h
WDKV 将 d=4 维压缩到 dc=2 维:
| 下投影矩阵 WDKV (2×4) | ||||
| h1 | h2 | h3 | h4 | |
|---|---|---|---|---|
| W1 | 0.5 | 0.3 | -0.2 | 0.1 |
| W2 | -0.1 | 0.4 | 0.6 | 0.2 |
| 压缩结果 cKV = WDKV · h | |||
| 分量 | 计算过程 | 公式 | 结果 |
|---|---|---|---|
| cKV,1 | 0.5×h1 + 0.3×h2 + (-0.2)×h3 + 0.1×h4 | 0.5×1.0 + 0.3×0.5 + (-0.2)×(-0.3) + 0.1×0.8 | 0.76 |
| cKV,2 | (-0.1)×h1 + 0.4×h2 + 0.6×h3 + 0.2×h4 | (-0.1)×1.0 + 0.4×0.5 + 0.6×(-0.3) + 0.2×0.8 | 0.08 |
Step 3b:上投影 — 恢复 K 和 V
从压缩向量 cKV 恢复出完整的 K 和 V:
| 恢复的 K = WUK · cKV | ||
| 分量 | 计算 | 结果 |
|---|---|---|
| K1 | 0.8×0.76 + (-0.2)×0.08 | 0.59 |
| K2 | 0.1×0.76 + 0.5×0.08 | 0.12 |
| K3 | (-0.3)×0.76 + 0.7×0.08 | -0.17 |
| K4 | 0.4×0.76 + 0.1×0.08 | 0.31 |
| 恢复的 V = WUV · cKV | ||
| 分量 | 计算 | 结果 |
|---|---|---|
| V1 | 0.6×0.76 + 0.3×0.08 | 0.48 |
| V2 | (-0.2)×0.76 + 0.9×0.08 | -0.08 |
| V3 | 0.5×0.76 + (-0.1)×0.08 | 0.37 |
| V4 | 0.1×0.76 + 0.4×0.08 | 0.11 |
参数量对比
| 组件 | 标准 MHA | MLA |
|---|---|---|
| KV 投影矩阵 | WK + WV:2 × d × d = 2 × 128 × 128 = 32,768 | WDKV + WUK + WUV:128×32 + 32×128 + 32×128 = 12,288 |
| 压缩比 | 参数量减少 62.5%,KV Cache 减少 8 倍 | |
Mixture of Experts (MoE) — Excel 手推
MoE 的核心:路由器 (Router) 根据输入决定激活哪些专家,然后加权合并专家输出。点击蓝色单元格可以编辑输入值!
Step 4a:路由器计算
路由器是一个简单的线性层 + Softmax,输出每个专家的"被选中概率"。假设 4 个专家,输入 x 维度=2:
| 输入向量 x(点击编辑) | |||
| x1 | x2 | ||
|---|---|---|---|
| 0.8 | 0.3 | ||
| 路由器权重矩阵 Wr (4×2) | |||
| 专家 | Wr1 | Wr2 | |
|---|---|---|---|
| 专家 1 | 1.0 | 0.5 | |
| 专家 2 | 0.3 | 1.2 | |
| 专家 3 | -0.5 | 0.8 | |
| 专家 4 | 0.7 | -0.2 | |
| 路由器输出(logits → Softmax → 权重) | ||||
| 专家 | logit 计算 | logit 值 | Softmax 权重 | Top-2? |
|---|---|---|---|---|
| 专家 1 | 1.0×0.8 + 0.5×0.3 | 0.95 | 0.32 | ✓ |
| 专家 2 | 0.3×0.8 + 1.2×0.3 | 0.60 | 0.23 | ✗ |
| 专家 3 | (-0.5)×0.8 + 0.8×0.3 | -0.16 | 0.11 | ✗ |
| 专家 4 | 0.7×0.8 + (-0.2)×0.3 | 0.50 | 0.20 | ✓ |
Step 4b:专家计算与加权输出
只有被选中的 Top-2 专家参与计算,其他专家不激活(稀疏激活):
| 专家输出与加权合并 | ||||
| 专家 | 专家输出(简化) | 路由器权重 | 加权输出 | 是否激活 |
|---|---|---|---|---|
| 专家 1 | [0.9, 0.2] | 0.32 | [0.29, 0.06] | ✓ 激活 |
| 专家 2 | 未激活 | 0.00 | [0, 0] | ✗ 跳过 |
| 专家 3 | 未激活 | 0.00 | [0, 0] | ✗ 跳过 |
| 专家 4 | [0.5, 0.7] | 0.20 | [0.10, 0.14] | ✓ 激活 |
| 最终输出 | = 专家1加权 + 专家4加权 | [0.39, 0.20] | 2/4 专家 | |
MoE 只用 2/4 = 50% 的专家就完成了计算,但效果接近使用全部专家。
实际 DeepSeek 使用 64 个专家,每次只激活 2 个,计算量仅为 2/64 = 3.125%!
互动实验 — MoE 专家路由
模拟 8 个专家的 MoE 路由。拖动滑块调整输入特征,观察路由器如何动态选择专家。
输入特征
专家激活权重
柱状图显示每个专家的路由器权重,绿色为被选中的 Top-2 专家:
稀疏激活效果
代码实现 (PyTorch)
import torch
import torch.nn as nn
import torch.nn.functional as F
class MultiHeadLatentAttention(nn.Module):
"""Multi-head Latent Attention (MLA) — DeepSeek-V2 核心创新"""
def __init__(self, d_model=512, n_heads=8, d_compress=64):
super().__init__()
self.d_model = d_model
self.n_heads = n_heads
self.d_k = d_model // n_heads
self.d_compress = d_compress
# Q 投影(与标准 MHA 相同)
self.W_q = nn.Linear(d_model, d_model)
# MLA 核心:下投影 + 上投影(替代标准的 K/V 投影)
self.W_DKV = nn.Linear(d_model, d_compress) # 下投影:压缩
self.W_UK = nn.Linear(d_compress, d_model) # 上投影:恢复 K
self.W_UV = nn.Linear(d_compress, d_model) # 上投影:恢复 V
self.W_o = nn.Linear(d_model, d_model)
def forward(self, h, kv_cache=None):
batch, seq_len, _ = h.shape
# Q:标准投影
Q = self.W_q(h)
# MLA:压缩 → 缓存 → 恢复
c_KV = self.W_DKV(h) # [batch, seq, d_compress]
if kv_cache is not None:
c_KV = torch.cat([kv_cache, c_KV], dim=1)
K = self.W_UK(c_KV) # 恢复完整 K
V = self.W_UV(c_KV) # 恢复完整 V
# 多头拆分 + 注意力计算
Q = Q.view(batch, seq_len, self.n_heads, self.d_k).transpose(1, 2)
K = K.view(batch, -1, self.n_heads, self.d_k).transpose(1, 2)
V = V.view(batch, -1, self.n_heads, self.d_k).transpose(1, 2)
scores = torch.matmul(Q, K.transpose(-2, -1)) / (self.d_k ** 0.5)
attn = F.softmax(scores, dim=-1)
out = torch.matmul(attn, V)
out = out.transpose(1, 2).contiguous().view(batch, seq_len, self.d_model)
return self.W_o(out), c_KV # 返回压缩缓存供下次使用
class MixtureOfExperts(nn.Module):
"""Mixture of Experts (MoE) — 稀疏专家混合"""
def __init__(self, d_model=512, n_experts=8, top_k=2, d_ff=1024):
super().__init__()
self.n_experts = n_experts
self.top_k = top_k
# 路由器:决定激活哪些专家
self.router = nn.Linear(d_model, n_experts)
# 专家列表:每个专家是一个简单的 FFN
self.experts = nn.ModuleList([
nn.Sequential(
nn.Linear(d_model, d_ff),
nn.GELU(),
nn.Linear(d_ff, d_model),
)
for _ in range(n_experts)
])
def forward(self, x):
batch, seq_len, d = x.shape
x_flat = x.view(-1, d) # [batch*seq, d]
# 路由器计算
router_logits = self.router(x_flat) # [batch*seq, n_experts]
router_weights = F.softmax(router_logits, dim=-1)
# Top-K 选择
topk_weights, topk_indices = torch.topk(router_weights, self.top_k, dim=-1)
topk_weights = topk_weights / topk_weights.sum(dim=-1, keepdim=True) # 归一化
# 只计算被选中的专家
output = torch.zeros_like(x_flat)
for k in range(self.top_k):
expert_idx = topk_indices[:, k] # 第 k 个被选中的专家
weight = topk_weights[:, k:k+1] # 对应权重
for e in range(self.n_experts):
mask = (expert_idx == e)
if mask.any():
expert_out = self.experts[e](x_flat[mask])
output[mask] += weight[mask] * expert_out
return output.view(batch, seq_len, d)
class DeepSeekBlock(nn.Module):
"""DeepSeek Transformer Block = MLA + MoE"""
def __init__(self, d_model=512, n_heads=8, d_compress=64,
n_experts=8, top_k=2, d_ff=1024):
super().__init__()
self.norm1 = nn.RMSNorm(d_model)
self.attn = MultiHeadLatentAttention(d_model, n_heads, d_compress)
self.norm2 = nn.RMSNorm(d_model)
self.moe = MixtureOfExperts(d_model, n_experts, top_k, d_ff)
def forward(self, x, kv_cache=None):
# MLA 注意力 + 残差连接
attn_out, new_cache = self.attn(self.norm1(x), kv_cache)
x = x + attn_out
# MoE 前馈 + 残差连接
x = x + self.moe(self.norm2(x))
return x, new_cache
# 使用示例
block = DeepSeekBlock(d_model=512, n_heads=8, n_experts=64, top_k=2)
x = torch.randn(2, 128, 512) # [batch=2, seq=128, d=512]
out, cache = block(x)
print(out.shape) # torch.Size([2, 128, 512])
小测验
- Transformer — MLA 是 Transformer 注意力的优化
- RNN / Mamba — Mamba 的 SSM 与 RNN 的关系
- MLP — MoE 的专家网络基于 MLP
1. MLA(Multi-head Latent Attention)的主要目的是什么?
2. MoE(Mixture of Experts)中路由器的作用是什么?
3. DeepSeek 相比同规模密集模型(如 LLaMA)的效率优势来自哪里?