
引言
Transformer 架构自 2017 年问世以来,一直统治着自然语言处理、计算机视觉、音频、时间序列等几乎所有序列建模领域。然而 Self-Attention 的二次时间复杂度 存在一个根本性的局限 — 随着序列长度 的增加,计算量和内存占用会爆炸式增长。在处理 128K 以上上下文窗口的现代 LLM 中,这个问题变得更加严重,推理时 KV Cache 随序列长度成比例增长也是一个实际的瓶颈。
在这样的背景下,State Space Model(SSM)作为一种能以线性时间复杂度 处理序列的替代方案被研究。2021 年 Albert Gu 提出的 Structured State Spaces(S4)在长序列基准测试中展现出超越 Transformer 的性能,开启了 SSM 研究的复兴。然而 S4 等早期 SSM 受限于 Linear Time-Invariant(LTI)系统这一约束,缺乏根据输入内容动态选择信息的能力,在语言建模上不及 Transformer。
2023 年 12 月,Albert Gu 与 Tri Dao 发表的 Mamba 论文正面突破了这一局限。论文引入了让 SSM 参数依赖于输入的选择性机制(Selection Mechanism),并通过针对 GPU 硬件优化的扫描算法确保了效率。随后 2024 年 5 月发表的 Mamba-2 证明了 SSM 与 Attention 在数学上属于同一族群的 State Space Duality(SSD),并提出了快 2-8 倍的算法。
本文将深入探讨 SSM 的数学基础(S4)、Mamba 的选择性机制、Mamba-2 的 SSD 框架、与 Transformer 的定量比较、视觉/音频/时间序列等多领域应用,以及实战实现与运维注意事项。
SSM 基础理论(S4)
连续时间 State Space Model
State Space Model 是源自控制理论的连续时间动态系统。将输入信号 经由隐藏状态 转换为输出 的过程,可以用如下微分方程描述。
这里 是状态转移矩阵, 是输入投影矩阵, 是输出投影矩阵, 是 skip connection 项。 是状态维度(state dimension),决定了 SSM 的记忆容量。
要处理离散序列,需要将连续系统离散化(discretization)。对步长 应用 Zero-Order Hold(ZOH)离散化,结果如下。
离散化后的 SSM 可以用两种方式计算。第一,以递归(recurrence)形式按顺序处理序列。
第二,还有一次性处理整个序列的合成卷积(convolution)形式。预先计算好核 ,就可以利用 FFT 进行 运算。
S4 的 HiPPO 初始化
S4(Structured State Spaces for Sequence Modeling, Gu et al., 2021)的核心贡献是状态矩阵 的初始化策略。随机初始化的 无法捕捉长期依赖关系,并会遭遇梯度消失/爆炸问题。S4 利用 HiPPO(High-order Polynomial Projection Operators)框架,将 初始化为一种特殊结构。
HiPPO-LegS 矩阵的设计目标,是用勒让德多项式基对过去的输入做连续近似。这一结构的核心性质,是能够均等地保留所有时间尺度上的信息。
import torch
import torch.nn as nn
import numpy as np
def make_hippo_matrix(N: int) -> torch.Tensor:
"""生成 HiPPO-LegS 矩阵。
该矩阵的设计目标是用勒让德多项式基对过去的输入
做最优近似,有利于捕捉长期依赖关系。
Args:
N: 状态维度 (State dimension)
Returns:
A: (N, N) HiPPO 矩阵
"""
P = np.sqrt(2 * np.arange(N) + 1)
A = np.zeros((N, N))
for i in range(N):
for j in range(N):
if i > j:
A[i, j] = P[i] * P[j]
elif i == j:
A[i, j] = i + 1
# i < j: 0
A = -A # 为保证稳定性取负
return torch.tensor(A, dtype=torch.float32)
class S4Layer(nn.Module):
"""S4(Structured State Spaces)层的核心实现。
结合了 HiPPO 初始化的 A 矩阵与基于卷积的并行计算。
"""
def __init__(self, d_model: int, state_dim: int = 64, seq_len: int = 1024):
super().__init__()
self.d_model = d_model
self.state_dim = state_dim
self.seq_len = seq_len
# HiPPO 初始化
A = make_hippo_matrix(state_dim)
self.A_log = nn.Parameter(torch.log(torch.clamp(-A.diagonal(), min=1e-4)))
self.B = nn.Parameter(torch.randn(state_dim, 1) * 0.01)
self.C = nn.Parameter(torch.randn(1, state_dim) * 0.01)
self.log_dt = nn.Parameter(torch.rand(d_model).uniform_(-4, -1))
# skip connection
self.D = nn.Parameter(torch.ones(d_model))
def _compute_kernel(self, L: int) -> torch.Tensor:
"""预先计算 SSM 卷积核。"""
dt = torch.exp(self.log_dt) # (d_model,)
A = -torch.exp(self.A_log) # (state_dim,) 对角元素
# ZOH 离散化 (假设 A 为对角矩阵)
dtA = dt.unsqueeze(-1) * A.unsqueeze(0) # (d_model, state_dim)
A_bar = torch.exp(dtA)
# 核计算: K[k] = C @ A_bar^k @ B_bar
# 用高效的 Vandermonde 乘法实现 O(N*L) 计算
powers = torch.arange(L, device=A.device).float()
# A_bar^k = exp(k * dtA)
kernel = torch.einsum(
'dn,dn,nl->dl',
self.C.squeeze(0).expand(self.d_model, -1),
dt.unsqueeze(-1) * self.B.squeeze(-1).unsqueeze(0),
torch.exp(dtA.unsqueeze(-1) * powers.unsqueeze(0).unsqueeze(0))
.squeeze()
.T
)
return kernel # (d_model, L)
def forward(self, x: torch.Tensor) -> torch.Tensor:
"""卷积模式的 forward pass。
Args:
x: (batch, seq_len, d_model) 输入序列
Returns:
y: (batch, seq_len, d_model) 输出序列
"""
B, L, D = x.shape
kernel = self._compute_kernel(L) # (d_model, L)
# 基于 FFT 的卷积
x_perm = x.permute(0, 2, 1) # (B, D, L)
k_f = torch.fft.rfft(kernel, n=2 * L) # zero-pad
x_f = torch.fft.rfft(x_perm, n=2 * L)
y = torch.fft.irfft(x_f * k_f, n=2 * L)[..., :L]
# skip connection
y = y + self.D.unsqueeze(0).unsqueeze(-1) * x_perm
return y.permute(0, 2, 1)
S4 的核心优势在于,训练时以卷积模式进行 的并行处理,推理时则以递归模式用 的常数内存/计算量逐个生成 token。但由于 是与输入无关的固定参数这一 LTI 属性,在基于内容的推理上较为薄弱。
Mamba 的选择性机制
从 LTI 到 Selection 的转变
Mamba 的核心洞察简单却强大:把 SSM 参数 、、 变成输入的函数,从而将系统转变为时变(time-varying)系统。这样一来,模型就能在每个时间点动态决定要把哪些信息写入状态、又要忽略哪些信息。
在传统的 LTI SSM 中,所有输入 token 都以相同的动力学(dynamics)被处理。在 "The cat sat on the mat" 这句话中,冠词 "the" 与核心名词 "cat" 会以同样的方式反映到状态中,因此无法选择性地强调重要信息或过滤掉不必要的信息。Transformer 的 Attention 本质上正是在执行这种选择性的信息处理,SSM 若要具备与之匹敌的表达能力,输入依赖的参数就必不可少。
用公式表示,Mamba 的选择性 SSM 如下。
的作用尤为关键。当 较大时, 会趋近于 0,先前的状态被重置,新的输入 主导状态。反之,当 较小时,先前状态得以保留,当前输入的影响会减弱。这与 RNN 的门控机制类似,只不过是在连续时间系统的数学框架内自然推导出来的。
Selection 解决的合成任务
Mamba 论文用两个合成任务证明了 Selection Mechanism 的效果。
第一,Selective Copying 任务。这是一个只需从输入序列中选择性复制特定 token 的问题,LTI SSM 因为无法根据输入内容判断"该复制哪个 token"而失败。Mamba 则通过 把重要 token 的信息强力写入状态,忽略不必要的 token。
第二,Induction Heads 任务。这是在出现 "A B ... A" 模式后需要预测下一个是 "B" 的问题,Transformer 用 Attention 机制自然就能解决,但 LTI SSM 做不到这种模式匹配。Mamba 的选择性机制能在 "A" 出现时激活先前 "A B" 模式的记忆,从而预测出 "B"。
import torch
import torch.nn as nn
import torch.nn.functional as F
class SelectiveSSM(nn.Module):
"""Mamba 选择性 State Space Model 的核心实现。
把 B、C、delta 变成输入的函数,
从而实现 content-aware 的序列处理。
"""
def __init__(self, d_model: int, d_state: int = 16, d_conv: int = 4):
super().__init__()
self.d_model = d_model
self.d_state = d_state
# A 矩阵:对角结构,在对数空间中参数化
A = torch.arange(1, d_state + 1, dtype=torch.float32)
self.A_log = nn.Parameter(torch.log(A).unsqueeze(0).expand(d_model, -1))
# 输入依赖的 B、C 投影
self.B_proj = nn.Linear(d_model, d_state, bias=False)
self.C_proj = nn.Linear(d_model, d_state, bias=False)
# delta(步长)投影
self.dt_proj = nn.Linear(d_model, d_model, bias=True)
# skip connection
self.D = nn.Parameter(torch.ones(d_model))
def selective_scan(
self,
x: torch.Tensor,
dt: torch.Tensor,
B: torch.Tensor,
C: torch.Tensor,
) -> torch.Tensor:
"""选择性扫描算法(顺序实现)。
实际的 Mamba 使用针对 GPU SRAM 优化的 CUDA 内核,
这里为了清楚展示算法逻辑而采用顺序实现。
Args:
x: (batch, seq_len, d_model) 输入
dt: (batch, seq_len, d_model) 离散化步长
B: (batch, seq_len, d_state) 输入投影
C: (batch, seq_len, d_state) 输出投影
Returns:
y: (batch, seq_len, d_model) 输出
"""
batch, seq_len, d_model = x.shape
d_state = B.shape[-1]
# A 离散化: A_bar = exp(dt * A)
A = -torch.exp(self.A_log) # (d_model, d_state)
dt_A = torch.einsum('bld,dn->bldn', dt, A) # (B, L, D, N)
A_bar = torch.exp(dt_A)
# B 离散化: B_bar = dt * B
dt_B = torch.einsum('bld,bln->bldn', dt, B) # (B, L, D, N)
# 顺序扫描
h = torch.zeros(batch, d_model, d_state, device=x.device)
outputs = []
for t in range(seq_len):
# h_t = A_bar_t * h_{t-1} + B_bar_t * x_t
h = A_bar[:, t] * h + dt_B[:, t] * x[:, t].unsqueeze(-1)
# y_t = C_t @ h_t
y_t = torch.einsum('bdn,bn->bd', h, C[:, t])
outputs.append(y_t)
y = torch.stack(outputs, dim=1) # (B, L, D)
return y
def forward(self, x: torch.Tensor) -> torch.Tensor:
"""选择性 SSM 的 forward pass。
Args:
x: (batch, seq_len, d_model)
Returns:
y: (batch, seq_len, d_model)
"""
# 计算输入依赖的参数
B = self.B_proj(x) # (B, L, N)
C = self.C_proj(x) # (B, L, N)
dt = F.softplus(self.dt_proj(x)) # (B, L, D), 保证为正数
# 选择性扫描
y = self.selective_scan(x, dt, B, C)
# skip connection
y = y + self.D.unsqueeze(0).unsqueeze(0) * x
return y
Hardware-Aware 扫描算法
引入 Selection Mechanism 后,SSM 不再是 LTI 系统,因此无法再使用 S4 中基于 FFT 的卷积。时变(time-varying)参数在每个时间点都不同,所以无法预先计算卷积核。必须使用顺序扫描(sequential scan),但简单的 for 循环实现无法利用 GPU 的并行性,速度非常慢。
Mamba 用受 FlashAttention 启发的 hardware-aware 算法解决了这个问题。核心思路有三点。第一,应用 kernel fusion,把 GPU HBM(High Bandwidth Memory)和 SRAM(on-chip memory)之间的数据移动降到最低——把离散化、扫描、输出投影都放在同一个 CUDA 内核中执行,让中间结果留在 SRAM 里。第二,不把中间状态张量( 等)写入 HBM,而是在 SRAM 中实时重新计算,这样内存占用就不会随序列长度成比例增长。第三,反向传播时同样不保存中间状态,而是通过重新计算(recomputation)来节省内存。
Mamba 架构详解
Mamba 块结构
Mamba 块的结构与 Transformer 块不同。Transformer 由 Self-Attention 和 FFN 两个子层组成,而 Mamba 把这两种角色整合进同一个块。具体来说,输入被分成两路——一路经过卷积和选择性 SSM,另一路承担门控的作用。
import torch
import torch.nn as nn
import torch.nn.functional as F
from dataclasses import dataclass
@dataclass
class MambaConfig:
d_model: int = 768
d_state: int = 16
d_conv: int = 4
expand: int = 2
n_layers: int = 24
vocab_size: int = 50257
dropout: float = 0.0
class MambaBlock(nn.Module):
"""Mamba 块:整合选择性 SSM、卷积、门控的结构。
用一个块取代 Transformer 的 Attention + FFN。
扩展比率(expand)决定内部维度,
默认值 2 意味着 d_inner = 2 * d_model。
"""
def __init__(self, config: MambaConfig):
super().__init__()
self.d_model = config.d_model
self.d_state = config.d_state
self.d_conv = config.d_conv
d_inner = config.d_model * config.expand
# 输入投影:分成两路(z, x)
self.in_proj = nn.Linear(config.d_model, d_inner * 2, bias=False)
# 1D 卷积:捕捉局部上下文
self.conv1d = nn.Conv1d(
in_channels=d_inner,
out_channels=d_inner,
kernel_size=config.d_conv,
padding=config.d_conv - 1,
groups=d_inner, # depthwise convolution
bias=True,
)
# 选择性 SSM 参数
self.B_proj = nn.Linear(d_inner, config.d_state, bias=False)
self.C_proj = nn.Linear(d_inner, config.d_state, bias=False)
self.dt_proj = nn.Linear(d_inner, d_inner, bias=True)
# A 矩阵:对角结构
A = torch.arange(1, config.d_state + 1, dtype=torch.float32)
self.A_log = nn.Parameter(
torch.log(A).unsqueeze(0).expand(d_inner, -1).clone()
)
self.D = nn.Parameter(torch.ones(d_inner))
# 输出投影
self.out_proj = nn.Linear(d_inner, config.d_model, bias=False)
self.norm = nn.RMSNorm(config.d_model)
def _selective_scan(self, x, dt, B, C):
"""选择性扫描(顺序实现)。实际使用 CUDA 内核。"""
batch, seq_len, d_inner = x.shape
d_state = B.shape[-1]
A = -torch.exp(self.A_log)
dt_A = torch.einsum('bld,dn->bldn', dt, A)
A_bar = torch.exp(dt_A)
dt_B_x = torch.einsum('bld,bln->bldn', dt * x, B)
h = torch.zeros(batch, d_inner, d_state, device=x.device)
outputs = []
for t in range(seq_len):
h = A_bar[:, t] * h + dt_B_x[:, t]
y_t = torch.einsum('bdn,bn->bd', h, C[:, t])
outputs.append(y_t)
y = torch.stack(outputs, dim=1)
return y
def forward(self, x: torch.Tensor) -> torch.Tensor:
"""Mamba 块的 forward pass。
Args:
x: (batch, seq_len, d_model)
Returns:
output: (batch, seq_len, d_model)
"""
residual = x
x = self.norm(x)
# 分成两路
xz = self.in_proj(x)
x_branch, z = xz.chunk(2, dim=-1)
# 卷积路径:捕捉局部模式
x_conv = x_branch.transpose(1, 2) # (B, D_inner, L)
x_conv = self.conv1d(x_conv)[:, :, :x.shape[1]] # causal trim
x_conv = x_conv.transpose(1, 2) # (B, L, D_inner)
x_conv = F.silu(x_conv)
# 选择性 SSM
B = self.B_proj(x_conv)
C = self.C_proj(x_conv)
dt = F.softplus(self.dt_proj(x_conv))
y = self._selective_scan(x_conv, dt, B, C)
y = y + self.D.unsqueeze(0).unsqueeze(0) * x_conv
# 门控:与 z 支路逐元素相乘
y = y * F.silu(z)
# 输出投影 + 残差连接
output = self.out_proj(y)
return output + residual
参数效率与架构比较
Mamba 的架构相对于 Transformer 有几处结构性差异。Transformer 有 Multi-Head Attention 和 FFN 两个较大的子层,各自拥有独立的 Layer Normalization 与残差连接。Mamba 则把这些整合进一个块——用卷积捕捉局部模式,用选择性 SSM 建模全局依赖,用门控控制信息流动。
如果说 Transformer 的 Attention 是显式计算所有 token 对之间关系的"全局参照(global attention)"方式,那么 Mamba 的选择性 SSM 就是把信息累积进压缩状态向量的"压缩记忆(compressed memory)"方式。这种差异正是二者在序列长度复杂度上产生差别的根本原因。
在相同参数量(约 2.8B)的条件下,Mamba 使用的 FLOPs 比 Transformer 少约 1.5 倍。这是因为 Attention 的 运算消失了,取而代之的是与 FFN 计算量相近的选择性 SSM。
Mamba-2 与 SSD 框架
State Space Duality 的发现
Mamba-2(Dao & Gu, 2024)最重要的理论贡献是发现了 State Space Duality(SSD)。论文证明了 SSM 与 Attention 表面上看似截然不同的运算,在数学上却属于同一个结构化矩阵(structured matrix)族群。
具体来说,选择性 SSM 的输出可以表示为如下的矩阵-向量乘法。
这里 是 semi-separable 矩阵。该矩阵的 元素如下。
这里 表示从时刻 到 的累积转移。该矩阵具有下三角(lower triangular)结构,这与 Causal Attention 中被掩码的 Attention 矩阵结构完全相同。
从 Attention 矩阵 中去掉 softmax,就得到 ,这同样是一个 rank- 的 semi-separable 矩阵。SSD 把这层联系形式化,说明了 SSM 与 Linear Attention 共享同一种矩阵代数结构。
SSD 算法与分块
SSD 的实用意义在于让新的算法设计成为可能。Mamba-2 把序列切分成固定大小的块(chunk),在块内部用矩阵乘法(attention-like 运算)并行处理,块与块之间则用 SSM 递归传递状态,是一种混合算法。
import torch
import torch.nn as nn
import torch.nn.functional as F
class SSDBlock(nn.Module):
"""Mamba-2 的 Structured State Space Duality(SSD)块。
把序列切分成多个块,块内部用矩阵乘法(并行)处理,
块之间用 SSM 递归处理的混合算法。
"""
def __init__(
self,
d_model: int = 768,
d_state: int = 128,
n_heads: int = 8,
chunk_size: int = 256,
):
super().__init__()
self.d_model = d_model
self.d_state = d_state
self.n_heads = n_heads
self.chunk_size = chunk_size
self.head_dim = d_model // n_heads
# 多头结构:Q(C)、K(B)、V(x) 投影
self.q_proj = nn.Linear(d_model, d_model, bias=False)
self.k_proj = nn.Linear(d_model, d_state * n_heads, bias=False)
self.v_proj = nn.Linear(d_model, d_model, bias=False)
self.dt_proj = nn.Linear(d_model, n_heads, bias=True)
# A 矩阵(按头的标量)
self.A_log = nn.Parameter(torch.log(torch.arange(1, n_heads + 1, dtype=torch.float32)))
self.out_proj = nn.Linear(d_model, d_model, bias=False)
self.norm = nn.RMSNorm(d_model)
def _chunk_scan(
self,
Q: torch.Tensor,
K: torch.Tensor,
V: torch.Tensor,
dt: torch.Tensor,
) -> torch.Tensor:
"""基于分块的 SSD 扫描。
Args:
Q: (batch, n_heads, seq_len, head_dim) -- 承担 C 的角色
K: (batch, n_heads, seq_len, d_state) -- 承担 B 的角色
V: (batch, n_heads, seq_len, head_dim) -- 承担 x 的角色
dt: (batch, n_heads, seq_len) -- 承担 delta 的角色
Returns:
y: (batch, n_heads, seq_len, head_dim)
"""
B, H, L, D = Q.shape
C = self.chunk_size
# 把序列切分成块
n_chunks = (L + C - 1) // C
pad_len = n_chunks * C - L
if pad_len > 0:
Q = F.pad(Q, (0, 0, 0, pad_len))
K = F.pad(K, (0, 0, 0, pad_len))
V = F.pad(V, (0, 0, 0, pad_len))
dt = F.pad(dt, (0, pad_len))
# 按块 reshape
Q = Q.view(B, H, n_chunks, C, D)
K = K.view(B, H, n_chunks, C, -1)
V = V.view(B, H, n_chunks, C, D)
dt = dt.view(B, H, n_chunks, C)
# 块内部:attention-like 矩阵乘法(可并行处理)
# 在块内部计算 Q 与 K 的相似度矩阵
# 生成带 decay 的 causal mask
A = -torch.exp(self.A_log) # (n_heads,)
# 计算累积衰减率
dt_cumsum = dt.cumsum(dim=-1) # (B, H, n_chunks, C)
# 块内部 attention 矩阵
# M[i,j] = exp(A * (dt_cumsum[i] - dt_cumsum[j])) for i >= j
decay_diff = dt_cumsum.unsqueeze(-1) - dt_cumsum.unsqueeze(-2) # (B,H,nc,C,C)
decay = torch.exp(A.view(1, H, 1, 1, 1) * decay_diff)
causal_mask = torch.tril(torch.ones(C, C, device=Q.device))
decay = decay * causal_mask
# 计算块内部输出
attn = torch.einsum('bhncqd,bhnckd->bhncqk', Q, K) * decay
y_intra = torch.einsum('bhncqk,bhnckd->bhncqd', attn, V)
# 最终 reshape
y = y_intra.view(B, H, n_chunks * C, D)
return y[:, :, :L]
def forward(self, x: torch.Tensor) -> torch.Tensor:
"""SSD 块的 forward pass。"""
residual = x
x = self.norm(x)
B, L, D = x.shape
# 多头投影
Q = self.q_proj(x).view(B, L, self.n_heads, self.head_dim).transpose(1, 2)
K = self.k_proj(x).view(B, L, self.n_heads, self.d_state).transpose(1, 2)
V = self.v_proj(x).view(B, L, self.n_heads, self.head_dim).transpose(1, 2)
dt = F.softplus(self.dt_proj(x)).transpose(1, 2) # (B, H, L)
# SSD 扫描
y = self._chunk_scan(Q, K, V, dt)
# 输出投影
y = y.transpose(1, 2).contiguous().view(B, L, D)
return self.out_proj(y) + residual
Mamba-2 的结构性改进
Mamba-2 相对于 Mamba-1 引入了如下结构性变更。
第一,引入多头结构。Mamba-1 对每个通道分别应用独立的 SSM,而 Mamba-2 使用类似 Attention 的头结构来共享参数,从而能在相同计算量下利用更大的状态维度。
第二,A 矩阵的简化。Mamba-1 使用按通道的对角 A 矩阵,Mamba-2 则进一步简化为按头的标量值。正是这一简化,成为 SSD 矩阵分解得以成立的关键条件。
第三,基于分块的并行算法。把序列切分成固定大小的块,块内部用 Tensor Core 加速的矩阵乘法处理,块之间则用 SSM 递归传递状态。这种方式比纯递归快 2-8 倍,能最大限度地利用 GPU 的并行硬件。
与 Transformer 的性能比较
定量基准测试
下面整理了各种序列建模架构的性能比较结果。测试条件为约 2.8B 参数规模,使用相同的训练数据(The Pile,300B token)。
| 项目 | Transformer (2.8B) | Mamba (2.8B) | Mamba-2 (2.8B) | RWKV-6 (3B) | RetNet (2.7B) |
|---|---|---|---|---|---|
| Pile PPL (val) | 6.73 | 6.22 | 6.10 | 6.85 | 7.02 |
| 训练吞吐量 (tok/s) | 基准 (1.0x) | 1.2x | 1.8x | 1.1x | 1.3x |
| 推理延迟 (1K token) | 38ms/tok | 12ms/tok | 10ms/tok | 14ms/tok | 16ms/tok |
| 推理延迟 (16K token) | 180ms/tok | 12ms/tok | 10ms/tok | 14ms/tok | 16ms/tok |
| 推理内存 (1K token) | 2.1 GB | 0.8 GB | 0.7 GB | 0.9 GB | 1.0 GB |
| 推理内存 (16K) | 8.5 GB | 0.8 GB | 0.7 GB | 0.9 GB | 1.0 GB |
| 序列长度扩展 | KV Cache 线性增长 | 常数 | 常数 | 常数 | 常数 |
| In-Context Learning | 强 | 中等 | 中等-强 | 弱 | 弱 |
| 注意力模式解读 | 可行(可视化) | 不可 | 部分可行 | 不可 | 部分可行 |
这张表中有几点值得关注。第一,Mamba 系列即使序列长度增加,推理延迟和内存也保持为常数。Transformer 在 16K token 时的延迟相比 1K 增长了约 4.7 倍,而 Mamba 保持不变。第二,Mamba-2 的训练吞吐量比 Mamba-1 快 1.5 倍,这是 SSD 算法基于分块并行化的效果。第三,在 In-Context Learning 上 Transformer 依然保持优势,这是 SSM 系模型的核心弱点之一。
按序列长度的扩展性
分析随序列长度变化的性能表现,SSM 的优势会更加明显。Transformer 的 Self-Attention 是 ,序列长度增加 2 倍,计算量就增加 4 倍。即便应用 FlashAttention-2 之类的优化,这个根本性的复杂度也不会改变。相比之下,Mamba 的选择性 SSM 是 ,无论序列长度如何,每个 token 的计算量都保持恒定。
实际的瓶颈出现在约 32K-64K token 区间。在这个区间内,以 A100 80GB GPU 为基准,Transformer 因为 KV Cache 不得不缩小批大小,而 Mamba 可以维持相同的批大小。对于 1M token 以上极端长的序列,Transformer 在实用上已不可行,而 SSM 原则上仍可处理。
多领域应用
视觉(Vision):Vision Mamba 与 VMamba
将 SSM 应用于视觉任务的研究相当活跃。Vision Mamba(Vim, Zhu et al., 2024)用双向(bidirectional)SSM 取代了 ViT(Vision Transformer)的 Self-Attention。它对图像块做正向和反向扫描来捕捉双向上下文,在 ImageNet 分类任务上以少 2.8 倍的 GPU 内存达到了与 DeiT 相当的准确率。
VMamba(Liu et al., 2024)为了更好地反映 2D 图像的空间结构,提出了 Cross-Scan Module(CSM)。它沿四个方向(左上-右下、右上-左下、左下-右上、右下-左上)扫描图像,让 1D 序列模型也能捕捉 2D 空间信息。
SSM 在视觉领域的核心优势是处理高分辨率图像。ViT 的计算量与 patch 数(序列长度)的平方成正比,将分辨率从 224x224 提高到 512x512,计算量会增加约 5 倍。得益于线性复杂度,Vision Mamba 的这一成本增幅仅约 2 倍。
音频与语音
音频信号本质上是长序列。以 16kHz 采样率计算,1 分钟音频就有 960,000 个时间点,直接用 Transformer 处理并不现实。Audio Mamba(Erol et al., 2024)在音频分类任务中取代了 Audio Spectrogram Transformer(AST),在准确率相当的情况下将内存占用降低了 70%。
语音合成(TTS)领域也在尝试应用 Mamba。在长音频序列的条件生成中,Transformer 的 KV Cache 瓶颈相当严重,因此基于 SSM 的解码器更适合实时语音合成。
时间序列预测
时间序列数据是 SSM 最自然适用的领域,因为 SSM 建模连续时间动态系统的数学框架,与时间序列的连续特性十分契合。S-Mamba(Wang et al., 2024)在多变量时间序列预测上表现出与 PatchTST 相当或更优的性能,推理速度则快 3 倍。
SSM 在时间序列预测中的特别优势,是处理不规则时间序列(irregularly-sampled time series)。连续时间 SSM 可以根据观测间隔调整离散化步长 ,因此能够直接处理时间间隔不规则的数据,而无需插值(interpolation)。这在医疗数据、IoT 传感器数据等场景中是很大的优势。
基因组学与蛋白质
DNA 序列是极端长的序列(数百万至数十亿 bp),长距离相互作用在生物学上具有重要意义。Caduceus(Schiff et al., 2024)是基于双向 Mamba 的 DNA 语言模型,把 DNA 的双链结构与反向互补性(reverse complementarity)反映到架构中。相比现有的 DNA Transformer 模型(Nucleotide Transformer、HyenaDNA),它在长距离变异效应预测上表现更优。
局限性与课题
In-Context Learning 的弱点
包括 Mamba 在内的 SSM 系模型最严重的局限,是 In-Context Learning(ICL)能力。Transformer 能够参照提示词中给出的 few-shot 示例来执行新任务,ICL 能力出众,这是因为 Self-Attention 可以直接参照(direct lookup)序列内任意位置。
相反,SSM 把信息压缩进固定大小的状态向量,因此难以精确"检索"提示词中的特定示例。当状态维度 远小于序列长度时(),信息损失不可避免。Mamba-2 通过大幅扩大状态维度(N=128 以上)部分缓解了这个问题,但仍不及 Transformer 显式的 token 级参照能力。
Retrieval 任务的局限
在 Information Retrieval,也就是从序列中准确提取特定信息的任务上,SSM 相对 Transformer 表现出明显的弱点。例如在 "passkey retrieval" 任务中,要在 10 万 token 的干草堆里找出隐藏的密钥,Mamba 的表现并不稳定。这是因为 SSM 的压缩记忆方式更适合统计性摘要,而非精确的信息保存。
混合架构的兴起
认识到这些局限之后,近期的研究开始探索结合 SSM 与 Attention 的混合架构。Jamba(AI21 Labs, 2024)交替排布 Mamba 层与 Transformer 层,同时确保了 SSM 的效率与 Attention 的 ICL 能力。它在 52B 参数规模上支持 256K 上下文,推理吞吐量是纯 Transformer 的 3 倍。
Zamba(Zyphra, 2024)和 Griffin(De et al., 2024)等也采用了类似的混合方案,这种路线正逐渐成为比纯 SSM 或纯 Transformer 更实用的折中方案。
实战实现指南
官方 Mamba 库的使用
Mamba 的官方实现由 state-spaces/mamba 仓库提供,其中包含 CUDA 内核,因此必须要有 GPU 环境。
# Mamba 安装与基本用法
# 1. 安装 (需要 CUDA 11.8 以上)
pip install mamba-ssm
# 2. 安装 causal-conv1d 依赖
pip install causal-conv1d>=1.2.0
# 3. 使用预训练模型 (Hugging Face)
pip install transformers
# 4. 用预训练 Mamba 模型生成文本
python -c "
from mamba_ssm.models.mixer_seq_simple import MambaLMHeadModel
import torch
# 加载 Mamba-2.8B 模型
model = MambaLMHeadModel.from_pretrained(
'state-spaces/mamba-2.8b',
device='cuda',
dtype=torch.float16,
)
model.eval()
# 分词器 (兼容 GPT-NeoX 分词器)
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained('EleutherAI/gpt-neox-20b')
# 文本生成
prompt = 'State Space Models are'
input_ids = tokenizer(prompt, return_tensors='pt').input_ids.to('cuda')
output = model.generate(
input_ids=input_ids,
max_length=100,
temperature=0.7,
top_p=0.9,
)
print(tokenizer.decode(output[0]))
"
基准测试脚本
下面是定量比较 Mamba 与 Transformer 推理性能的基准测试脚本。
import torch
import time
from dataclasses import dataclass
@dataclass
class BenchmarkResult:
model_name: str
seq_len: int
batch_size: int
latency_ms: float
memory_mb: float
tokens_per_sec: float
def benchmark_inference(
model: torch.nn.Module,
model_name: str,
seq_lengths: list[int],
batch_size: int = 1,
n_warmup: int = 5,
n_measure: int = 20,
device: str = "cuda",
) -> list[BenchmarkResult]:
"""在不同序列长度下测量推理性能。
Args:
model: 待测模型
model_name: 模型名称 (用于结果展示)
seq_lengths: 待测的序列长度列表
batch_size: 批大小
n_warmup: 预热迭代次数
n_measure: 测量迭代次数
device: 运行设备
Returns:
按序列长度分组的基准测试结果列表
"""
model = model.to(device).eval()
results = []
for seq_len in seq_lengths:
input_ids = torch.randint(
0, 50257, (batch_size, seq_len), device=device
)
# 清理 GPU 缓存
torch.cuda.empty_cache()
torch.cuda.reset_peak_memory_stats()
# 预热
with torch.no_grad():
for _ in range(n_warmup):
_ = model(input_ids)
torch.cuda.synchronize()
# 测量
latencies = []
with torch.no_grad():
for _ in range(n_measure):
torch.cuda.synchronize()
start = time.perf_counter()
_ = model(input_ids)
torch.cuda.synchronize()
end = time.perf_counter()
latencies.append((end - start) * 1000)
avg_latency = sum(latencies) / len(latencies)
peak_memory = torch.cuda.max_memory_allocated() / 1024 / 1024
tokens_per_sec = (batch_size * seq_len) / (avg_latency / 1000)
results.append(BenchmarkResult(
model_name=model_name,
seq_len=seq_len,
batch_size=batch_size,
latency_ms=avg_latency,
memory_mb=peak_memory,
tokens_per_sec=tokens_per_sec,
))
print(
f"[{model_name}] seq_len={seq_len:>6d} | "
f"latency={avg_latency:>8.2f}ms | "
f"memory={peak_memory:>8.1f}MB | "
f"throughput={tokens_per_sec:>10.0f} tok/s"
)
return results
def compare_models(results_dict: dict[str, list[BenchmarkResult]]):
"""打印模型间的性能比较表格。"""
print("\n" + "=" * 80)
print(f"{'模型':<20} {'序列长度':>10} {'延迟(ms)':>10} "
f"{'内存(MB)':>12} {'吞吐量(tok/s)':>15}")
print("=" * 80)
for model_name, results in results_dict.items():
for r in results:
print(
f"{r.model_name:<20} {r.seq_len:>10d} "
f"{r.latency_ms:>10.2f} {r.memory_mb:>12.1f} "
f"{r.tokens_per_sec:>15.0f}"
)
print("-" * 80)
运维注意事项
CUDA 内核兼容性
Mamba 的官方 CUDA 内核针对特定 GPU 架构(Ampere 及以上,sm_80+)做了优化。在 V100(sm_70)或更早一代的 GPU 上会使用 fallback 实现,性能会大幅下降。生产部署前必须确认目标 GPU 的 compute capability,并使用 Ampere(A100/A10G)或 Hopper(H100)GPU。
还需要注意 causal-conv1d 包的版本兼容性。mamba-ssm 版本与 causal-conv1d 版本不匹配会导致运行时错误,务必查看官方 README 的兼容性表格。
状态初始化与序列边界
在基于 SSM 的模型推理中,最常见的错误是状态初始化处理不当。批量推理时,如果不同序列的状态相互混合,输出就会被污染。必须在每个序列开始时把状态显式初始化为零向量,流式推理中对话轮次切换时也要重置状态。
在连续对话(multi-turn conversation)中,是否保留上一轮的状态属于设计决策。保留状态能保存之前对话的上下文,但由于状态的信息保存能力比 Transformer 的 KV Cache 更有限,长对话中可能会累积信息损失。
训练时的精度与稳定性
Mamba 的选择性 SSM 对使用 softplus 激活的 参数的数值稳定性很敏感。 过大时 会收敛到 0,导致梯度消失; 过小时状态更新过于微弱,训练会停滞。建议在训练初期把 的初始化设为 ,以在对数空间中保证合适的范围。
BF16 训练通常是稳定的,但状态矩阵的指数运算偶尔会出现数值不稳定。在对数空间对 A 做参数化(),可以同时保证其负值性与稳定性。
服务框架支持现状
vLLM 对 Mamba 模型的服务提供部分支持。PagedAttention 是为基于 Attention 的模型设计的,并不适用于 SSM 模型,但可以利用 vLLM 的调度与批处理基础设施。TensorRT-LLM 支持对 Mamba-1 模型的优化,Mamba-2 则需要单独的插件。
就 Triton 服务而言,把 Mamba 模型转换为 ONNX 后部署是可行的,但由于 CUDA 自定义内核不会包含在 ONNX 计算图中,会产生性能损失。目前直接封装 mamba-ssm 库的自定义服务端仍是最稳妥的选择。
调试清单
下面整理了生产环境中可能出现的问题及应对方法。
- 输出陷入重复循环时:说明状态固着(stuck)在了某个特定模式上。可以提高 temperature,或加入部分重置状态的逻辑。
- 长输入下质量急剧下降时:说明状态维度相对序列长度不够大。选择模型时应使用 d_state 足够大的模型(N=64 以上)。
- 批量推理中各序列结果不一致时:很可能是 padding 处理有误,padding token 的信息混入了状态。可以把 padding token 对应的 强制设为 0,阻断状态更新。
- 训练中间歇性出现 CUDA OOM 时:可能是重新计算(recomputation)策略没有正确生效。可以启用 gradient checkpointing,并缩小批大小。
参考资料
-
Gu, A., & Dao, T. (2023). Mamba: Linear-Time Sequence Modeling with Selective State Spaces. arXiv preprint arXiv:2312.00752. https://arxiv.org/abs/2312.00752
-
Dao, T., & Gu, A. (2024). Transformers are SSMs: Generalized Models and Efficient Algorithms Through Structured State Space Duality. arXiv preprint arXiv:2405.21060. https://arxiv.org/abs/2405.21060
-
Mamba 官方 GitHub 仓库。https://github.com/state-spaces/mamba
-
Gu, A., Goel, K., & Re, C. (2021). Efficiently Modeling Long Sequences with Structured State Spaces (S4). ICLR 2022. https://arxiv.org/abs/2111.00396
-
Patro, B. N., & Agneeswaran, V. S. (2024). Mamba-360: Survey of State Space Models as Transformer Alternative for Long Sequence Modelling. arXiv preprint arXiv:2404.16112. https://arxiv.org/abs/2404.16112
-
Lieber, O. et al. (2024). Jamba: A Hybrid Transformer-Mamba Language Model. arXiv preprint arXiv:2403.19887. https://arxiv.org/abs/2403.19887
-
Zhu, L. et al. (2024). Vision Mamba: Efficient Visual Representation Learning with Bidirectional State Space Model. ICML 2024. https://arxiv.org/abs/2401.09417
-
Schiff, Y. et al. (2024). Caduceus: Bi-Directional Equivariant Long-Range DNA Sequence Modeling. ICML 2024. https://arxiv.org/abs/2403.03234
현재 단락 (1/487)
Transformer 架构自 2017 年问世以来,一直统治着自然语言处理、计算机视觉、音频、时间序列等几乎所有序列建模领域。然而 Self-Attention 的二次时间复杂度 $O(L^2)$ ...