滑动窗口注意力:限制每个 Token 的感知范围
什么是滑动窗口注意力
滑动窗口注意力(Sliding Window Attention)是一种稀疏注意力机制,它限制每个词(Token)在生成表示时只能关注其前后固定范围内的邻居词,而不是像标准自注意力那样关注整个序列。这种设计将注意力计算从与序列长度平方成正比(O(n²))降至与序列长度线性相关(O(n·w),w 为窗口大小),极大降低了对长文本进行建模的计算和内存开销。
对于初学者来说,可以把滑动窗口注意力理解为:阅读一本书时,如果要理解某个句子,我们通常会回看附近几段,而不是全书通读一遍;这种“只看附近内容”的做法既高效又保留了足够的上下文。
为什么需要滑动窗口注意力
标准自注意力的计算瓶颈
标准的自注意力机制(如 Transformer 中的 Scaled Dot-Product Attention)要求序列中每个位置都与其他所有位置计算相关性。对于长度为 n 的序列,计算量是 n² 级别的,当 n 很大时(例如处理几万字的长文或高分辨率图像),内存和速度都迅速变得不可接受。
Attention(Q, K, V) = softmax(QK^T / √d) V
- Q, K, V 大小均为
(n, d) - QK^T 产生
(n, n)的注意力矩阵,复杂度 O(n² d) - 长序列下,这个 ( n \times n ) 矩阵成为系统的绝对瓶颈。
稀疏化注意力的思路
为了解决这一问题,研究者们提出了多种稀疏注意力变体,通过合理假设“不需要所有 Token 之间都建立连接”来降低复杂度。滑动窗口注意力是其中一种直观且高效的方式,它基于一个核心直觉:局部上下文通常携带了理解一个词所需的大部分信息,长距离依赖虽然重要,但可通过堆叠多个注意力层间接捕获。
滑动窗口注意力机制核心原理
基本定义
在滑动窗口注意力中,设定一个窗口大小 w。对于序列中的第 i 个 Token:
- 它可以关注的范围是:
[i-w, i+w](双边窗口,包含前后各 w 个邻居)或仅左侧窗口(用于自回归语言模型,防止看到未来信息)。 - 其他位置的注意力权重直接置为零(或 -∞ 屏蔽),不参与计算。
这样一来,注意力矩阵从完整的稠密矩阵变成了一个带状稀疏矩阵(宽度为 2w+1 或 w+1)。
计算流程
- 生成查询 Q、键 K、值 V 矩阵,形状均为
(n, d)。 - 对每个查询位置 i,仅提取索引在
[i-w, i+w]范围内的键 K 和值 V。 - 计算缩放点积:
score = Q_i · K_local^T / √d。 - 经过 softmax 归一化得到局部注意力权重,对局部 V 加权求和。
- 结果即为该位置的输出表示。
实际操作中,为了利用现代硬件的并行能力,通常不会真的对每个位置单独切片,而是通过分块(chunk)和带状掩码来实现高效计算。
滑动窗口的实现方式(伪代码示例)
def sliding_window_attention(Q, K, V, window_size):
n, d = Q.shape
# 假设 batch 和 head 维度已处理,此处简化
scale = d ** -0.5
scores = torch.zeros(n, 2*window_size+1) # 每行只存局部窗口内的分数
outputs = torch.zeros(n, d)
# 填充序列左右两端,处理边界(可选)
# 为简便,这里直接做循环示例
for i in range(n):
left = max(0, i - window_size)
right = min(n, i + window_size + 1)
# 提取局部 K, V
K_local = K[left:right] # shape (local_len, d)
V_local = V[left:right]
# 计算当前查询与局部键的点积
score = torch.matmul(Q[i], K_local.T) * scale # (local_len,)
attn = torch.softmax(score, dim=-1)
outputs[i] = torch.matmul(attn, V_local)
return outputs
上述代码仅为示意,实际框架(如 PyTorch、TensorFlow)会通过更高效的掩码矩阵和张量运算加速。例如直接构建一个 (n, n) 的带状掩码,将窗口外的位置置为 -∞,然后调用标准批量矩阵乘法。
滑动窗口注意力的变体与工程优化
单侧窗口与因果建模
在自回归语言模型(如 GPT 系列)中,必须防止模型看到未来信息。此时窗口变为向左看的滑动窗口:位置 i 只能关注 [i-w, i] 范围内的 Token。掩码矩阵成为下三角带状矩阵。这种模式直接兼容因果生成任务。
扩张滑动窗口(Dilated Sliding Window)
受卷积网络中扩张卷积启发,可以在滑动窗口基础上引入空洞(dilation),让窗口在覆盖相同数量元素的同时扩大感受野。例如窗口大小 w=2,dilation=2 时,位置 i 会关注 i-4, i-2, i, i+2, i+4。这样在不增加计算量的前提下能更早地捕获远距离关系。
全局 Token 与混合注意力
单纯使用滑动窗口可能会丢失全局信息(如文章主旨、分类标记)。实践中常加入少量全局注意力位置(例如 [CLS] 标记或特定任务 Token),这些位置可以和所有其他 Token 互相注意。这种混合机制在 Longformer 等模型中广泛应用,兼顾局部细节与全局视野。
分块并行计算
为了充分发挥 GPU 并行能力,实际实现通常将序列分成固定大小的块(chunk),每个块内的所有查询并行计算其窗口范围内的注意力。例如块大小设为 C,则 Q 块大小为 (C, d),对应的 K, V 块在前后各多取 w 个 Token,形状为 (C+2w, d)。利用矩阵乘法一次性完成块内所有查询的注意力计算。
滑动窗口注意力的优点与缺点
优点
- 线性复杂度:计算量和内存占用均降为 O(n·w),w 远小于 n,因此可以轻松处理长文档(如 4K、16K 甚至更长)。
- 局部归纳偏置更自然:语言和图像等领域中,局部相关性往往很强,滑动窗口贴合这一先验,训练收敛更快。
- 易于实现和集成:只需修改注意力掩码,就能将现有 Transformer 模型转换为滑动窗口版本,工程改动小。
- 支持长序列微调:许多预训练模型可以后接滑动窗口注意力,以较小的额外代价适应长文本任务。
缺点
- 远程依赖需多层传递:两个距离很远的 Token 在单层内无法直接交互,需要经过多层逐步传播信息。这要求模型有足够的深度,梯度传播路径更长。
- 窗口大小固定:窗口大小是超参数,对不同任务和输入长度可能并非最优。过小会丢失必要上下文,过大则失去效率优势。
- 边界效应:序列首尾附近的 Token 可用的邻居更少,可能造成表示质量下降。虽然可通过填充缓解,但仍是不完美之处。
- 全局信息摄取受限:例如文章整体的主题、风格等全局特征,仅靠局部窗口难以捕获,通常需额外引入全局 Token 弥补。
典型应用与代表模型
Longformer
Longformer 提出了结合滑动窗口注意力 + 任务相关全局注意力的稀疏模式。窗口大小通常设为 512,并让 [CLS] 等特殊 Token 参与全局注意力。在长文档任务(摘要、QA)上性能可比肩标准 Transformer,但显存和速度大幅优化。
StreamingLLM / Mistral
某些高效推理框架和开源模型也采用滑动窗口注意力。例如 Mistral 7B 使用滑动窗口注意力(窗口大小 4096)并结合分组查询注意力(GQA),实现高速长文本生成和较低的缓存开销。
编码器用途
在文档编码器中,滑动窗口配合多层结构可以逐层扩大有效感受野。每个 Token 在每一层都能融合周围 w 个 Token 的信息,经过 L 层后,理论上最大感受野可达 L * w,因此深层网络可以覆盖很长距离。
何时应该使用滑动窗口注意力
如果你的任务满足以下条件,滑动窗口注意力通常是极佳的选择:
- 输入序列经常超过 1024 Tokens,致使标准注意力内存溢出或速度太慢。
- 任务以局部模式为主(如命名实体识别、局部语法分析)或长距离依赖可以通过多层传递。
- 需要微调或从头训练一个能高效处理长文的模型,且预算有限。
若任务极度依赖全局模式(如全文情感分析、整篇文章的翻译)且序列长度并不是特别长,则可在滑动窗口基础上增加少量全局 Token,或直接使用标准注意力,避免网络深度不足导致信息丢失。
总结
滑动窗口注意力通过稀疏化注意力范围,将标准自注意力的二次复杂度降低为线性,为长序列 Transformer 提供了简单而有效的解决方案。它的核心思想是每个 Token 只与周围固定窗口内的邻居进行交互,依靠多层传播来获得全局视野。在长文本理解、高效推理等场景中已被广泛验证。对于初学者,理解其原理是掌握现代大语言模型和高效注意力机制的重要一步,也是深入学习其他稀疏注意力变体的基础。