一、大模型上下文变长的逻辑

位置编码层
├── 绝对位置编码(原始 Transformer)— 不可外推
├── 相对位置编码(T5, ALiBi)— 有限外推
└── 旋转位置编码 RoPE — 可外推,需调基数或插值

注意力计算层
├── 标准 Dense Attention — O(N²),内存墙
├── Flash Attention — IO-aware,内存降至 O(N)
├── 稀疏注意力 — 计算降至近似 O(N)
│ ├── 静态稀疏(Longformer, BigBird)— 预定义模式
│ └── 动态稀疏(DSA/CSA/NSA)— 模型学习筛选
└── 线性注意力(RWKV, Mamba)— O(N),但表达能力不同

KV Cache 层
├── 标准 MHA — 高维存储
├── MLA — 低秩压缩
├── 块级合并(HCA)— 减少 entry 数量
└── 量化(FP8/FP4)— 降低单 entry 精度

训练策略层
├── 直接长文本训练 — 成本极高
├── 渐进式扩展 — 4K → 32K → 128K → 1M
├── 位置插值(PI)— 压缩位置索引
└── NTK-aware 扩展 — 非线性插值旋转角度

1.1 RoPE

1.1.1 为什么需要位置编码

Transformer 的自注意力机制本身对位置不敏感——句子中的词被打乱顺序后,Attention 计算出的权重分布不会发生变化。但语言显然具有顺序性,"我打你"与"你打我"含义完全不同。位置编码的作用即向模型注入"当前词处于序列中哪个位置"的信息,使模型能够区分不同位置的 Token。

1.1.2 RoPE 的核心思想

RoPE(Rotary Position Embedding,旋转位置编码)不采用为每个位置分配固定向量的方式(如正弦位置编码),而是通过旋转矩阵将位置信息直接编码到 Q/K 向量的内积计算中。具体而言,对 Query 和 Key 向量按维度两两分组,每组应用一个旋转矩阵,旋转角度与位置索引成正比;位置越远的两个 Token,旋转角度差越大,其内积自然衰减。

RoPE 的优势体现在三个方面:一是相对位置敏感,Attention 分数天然反映 Token 间的距离感;二是外推性好,训练时见过的最大长度为 L,推理时可直接外推至 2L、4L,无需重新训练;三是与 FlashAttention 兼容,旋转操作可融合到 Attention 的矩阵计算中,不增加额外开销。

1.1.3 RoPE 与上下文长度的关系

RoPE 的旋转角度公式为 \theta_i = 10000^{-2i/d},其中 i 为维度索引,d 为总维度。基数 10000 决定了模型能感知的最大相对距离。当上下文从 4K 扩展到 128K 时,远距离 Token 的旋转角度差过大,导致内积过度衰减("远距离遗忘")。解决办法包括基数调整(将 10000 改为 500000 等更大值)和 NTK-aware 插值(非线性插值旋转角度,使小角度更密、大角度更疏)。

1.1.4 与 FlashAttention 的兼容性

RoPE 的旋转操作可与 FlashAttention 的核函数融合:加载 Q/K 块时同步应用旋转矩阵,不额外存储旋转后的 Q/K,减少内存占用。这是现代 LLM(Llama、DeepSeek 等)的标准实现方式。


1.2 KV Cache 缓存降低

1.2.1 量化

KV Cache 是推理时的显存大户。以 70B 模型、32K 上下文、batch=1 为例,FP16 KV Cache 约占用 16 GB,量化为 INT8 后减半至 8 GB,量化为 FP4(如 DeepSeek V4)后再减半至 4 GB。

精度演进如下表所示:

精度 每 Token KV Cache 1M 上下文显存占用 特点
FP16 ~2 MB ~2 TB 原始精度,无损失
INT8 ~1 MB ~1 TB 几乎无损,需校准缩放因子
FP8 (E4M3) ~1 MB ~1 TB 动态范围比 INT8 更好
FP4 ~0.5 MB ~0.5 TB 有损失,需配合 HCA/CSA 使用

INT8 量化的工程细节包括:对称量化 x_{int8} = round(x_{fp16} / scale),其中 scale = max(|x|) / 127;反量化 x_{fp16} = x_{int8} \cdot scale;按通道量化(每个 head、每个维度单独计算 scale);以及 SmoothQuant(先对激活做平滑处理,使分布更适合量化)。

1.2.2 MLA(多头潜在注意力)

DeepSeek V3 提出的 MLA 核心思想是对 K/V 做低秩压缩。具体步骤为:将高维 K/V(如 8192 维)投影到低维潜在空间(如 512 维);推理时只缓存低维的 c_{KV}(512 维)和位置相关的 k_R(64 维);每个 Token 的 KV Cache 从 16384 字节(FP16)降至 576 字节,压缩比约 28:1。

关于为何不对压缩后的 c_{KV} 直接加 RoPE:RoPE 的旋转操作会使"把解压矩阵 W_{UK} 吸收进 Query 投影"的加速技巧无法执行。MLA 的解决方案是解耦——把无需旋转的语义内容(走吸收路线)和需要旋转的位置信息(走独立低维路线)分离开,既保住吸收加速,又极低成本地注入位置感知。

1.2.3 PagedAttention

除量化外,KV Cache 可通过分页管理优化:将 KV Cache 分成固定大小的块(如 16 tokens/块),用内存池动态分配以避免碎片,支持共享(多个序列共享前缀)和抢占(GPU 内存不足时换出到 CPU)。


1.3 注意力机制的改变

1.3.1 Flash Attention 算法详解

Flash Attention 是一种 IO-aware 的精确注意力算法,通过分块计算(tiling)和重计算(recomputation)策略,在不近似注意力结果的前提下,将 Attention 计算的内存复杂度从 O(N²) 降低到 O(N)。

标准 Attention 的瓶颈不在计算,而在内存访问:Attention 矩阵 S = QK^T 大小为 [N, N],当 N=100K 时单矩阵占 40GB(FP32),GPU HBM 容量有限,频繁读写导致 IO 瓶颈。

Flash Attention 的核心思想包括:分块计算(将 Q/K/V 分成小块,每次只加载一小块到 SRAM,计算完立即写回,不保留中间矩阵);在线 Softmax(不等待完整 QK^T,逐块计算并维护 running statistics,最后统一缩放);重计算(反向传播时不保存巨大 Attention 矩阵,而是重新计算 forward 的 Attention 分数)。

算法伪代码如下:

输入:Q, K, V ∈ R^(N×d)
输出:O = Attention(Q, K, V)

将 Q 分成块 Q_1, ..., Q_Tc
将 K, V 分成块 K_1, ..., K_Tr, V_1, ..., V_Tr

for each Q_i block:
    初始化 running max m = -inf, running sum l = 0, output O_i = 0
    for each K_j, V_j block:
        S_ij = Q_i @ K_j^T              // 局部 Attention 分数
        m_new = max(m, max(S_ij))       // 更新 running max
        P_ij = exp(S_ij - m_new)        // 局部概率
        l = l * exp(m - m_new) + sum(P_ij)  // 更新 running sum
        O_i = O_i * exp(m - m_new) + P_ij @ V_j  // 更新输出
        m = m_new
    O_i = O_i / l  // 最终归一化
1.3.2 三个版本演进表
版本 核心改进 特点
FlashAttention-1 分块 + 在线 Softmax 首次实现 IO-aware 精确 Attention
FlashAttention-2 减少非矩阵乘法 FLOPs 更好的 Warp 级并行,速度提升约 2x
FlashAttention-3 异步流水线 + FP8 支持 在 Hopper 架构上利用 Tensor Memory Accelerator

Flash Attention 是长上下文训练的基础设施:没有它,训练 100K 上下文需要 TB 级显存;有了它,标准 A100/H100 即可训练 100K+ 上下文。但 Flash Attention 不改变 Attention 的 O(N²) 计算复杂度,只解决内存问题。对于极长序列(1M+),仍需配合稀疏注意力降低计算量。

1.3.3 DSA(动态稀疏注意力)详细流程

DeepSeek 的 DSA(DeepSeek Sparse Attention)思路是:不是每个 Token 都要和所有 Token 算 Attention,只算最相关的那些。

工作流程如下:

  1. 向量投影:输入投影为 Q/K/V,融入 RoPE,并为每个 head 生成动态权重
  2. 索引器快速评分:基于小维度 Key 向量,计算 Query 与所有 Key 的初步注意力得分,乘以逐头权重
  3. Top-k 选择:从整个上下文中精准定位得分最高的 k 个 Token(如 k=2048)
  4. 稀疏 MLA:仅对选中的 Token 执行完整 MLA 计算

DSA 将原始平方级复杂度的注意力计算 O(L²) 降低至近似线性复杂度 O(L·k)。

1.3.4 CSA(分块稀疏注意力)详细流程

CSA(Compressed Sparse Attention)是 DSA 在 V4 中的升级,引入块级压缩:

  1. 线性映射生成初始 KV + 压缩权重:[B, 1000, 8192] → [B, 1000, 512]
  2. 每 4 个 Token 加权合并:[B, 1000, 512] → [B, 250, 512]
  3. 索引器对 250 个块打分:[B, 250, 512] → [B, 250, 1]
  4. Top-K 选择,保留 128 个:[B, 250, 512] → [B, 128, 512]
  5. 对 128 个块做 MLA(低秩投影 → 拆分 → 部分 RoPE)
  6. Query(保持原始 1000 长度)与 128 个 K' 做 Attention

相比前代 V3.2,在 100 万 Token 上下文设置下,单 Token 推理计算量(FLOPs)仅为前者的 27%,KV Cache 占用骤降至 10%。

1.3.5 HCA(混合粗粒度注意力)详细流程

HCA(Heavily Compressed Attention)是 DeepSeek V4 提出的极度压缩注意力机制:

  1. 生成低秩 K_r, V_r:[B, 1000, 8192] → 各 [B, 1000, 低维度]
  2. 每 128 个 Token 合并为一个 entry:[B, 1000, 低维度] → [B, 8, 低维度]
  3. 压缩后的 entry 以 FP4 精度存入 KV Cache
  4. Query 保持原始长度(1000 个),不做合并
  5. Query 与 8 个 entry 做全对全稠密注意力

HCA 和 CSA 在 V4 中交替堆叠:低层多用 CSA(特征还比较底层,需要精细捕捉长距离依赖),高层多用 HCA(特征已经比较抽象高级,用于全局语义整合和存储优化)。关键设计约束是每一层的输出维度与输入完全一致,无论上一层是 CSA 还是 HCA,下一层都可以无缝切换。


1.4 DeepSeek 的做法

1.4.1 完整技术栈组合表

DeepSeek 在长上下文技术上的核心思路是系统化压缩——不是单一优化,而是从位置编码、注意力计算、KV Cache 存储到训练策略的全栈优化。

层级 技术 作用
位置编码 RoPE(基数 500K)+ NTK 插值 支持 1M+ 外推
注意力计算 CSA(低层)+ HCA(高层)交替 计算降至 27%
KV 存储 MLA 低秩 + FP4 量化 存储降至 2%
内存管理 PagedAttention 动态分配,无碎片
训练 渐进式扩展 + NSA 原生稀疏 端到端可训练
1.4.2 效果对比表
方案 上下文长度 KV Cache/Token 相对计算量 代表模型
原始 Transformer 4K ~2 MB 100% GPT-3
+ Flash Attention 128K ~2 MB 100% Llama 2
+ RoPE 外推 128K ~2 MB 100% Llama 2 Long
+ MLA 128K ~0.07 MB 100% DeepSeek V3
+ DSA/CSA 1M ~0.07 MB ~30% DeepSeek V3.2
+ HCA + FP4 1M ~0.01 MB ~10% DeepSeek V4

二、带来的问题-上下文漂移

2.1现象描述

上下文漂移是指 Agent 在长程执行过程中,执行方向逐渐偏离原始目标的现象。你让 Agent "分析这份销售数据,找出下滑原因",理想流程是读数据 → 分析趋势 → 定位原因 → 输出报告;实际却可能是读数据 → 修格式 → 查文档 → 做竞品分析,跑了 10 步原始任务一个字没碰。

这不是模型"变傻了",而是每一步都在做"当前上下文下最合理的下一步",但"最合理"不等于"最符合原始目标"。

2.2 根因分析:注意力机制的内在局限

上下文漂移的根因在于 Self-Attention 机制的两个固有特性:

第一,近因效应(Recency Bias)。Self-Attention 的权重不是均匀分配的,模型倾向于给最近的 Token 更高的权重。原始指令在最前面,中间隔着大量中间结果,到后面几步时,原始指令的注意力权重已经被"稀释"了。Agent 不是"忘了"目标,是目标在它的注意力里占比越来越低。

第二,中间结果抢占焦点。Agent 每一步的输出都追加到上下文里,这些中间结果本身就是新的刺激信号。比如修格式时产生的日志、查文档时看到的内容,都会吸引模型的注意力。上下文越长,干扰信号越多,原始目标越容易被淹没。

这与 "Lost in Middle" 是同一类问题:上下文中间的信息最容易被忽略,而原始指令恰好被推到了"中间"甚至"开头"的位置。从数学上看,Softmax 的分母随序列长度线性增长,每个 Token 分到的注意力权重被稀释;同时 RoPE 的远距离衰减使早期 Token 的 Key 向量与当前 Query 的内积趋近于零,进一步降低了原始指令的可见度。【总结:位置编码的远距离衰减 + Softmax 稀释

2.3 长上下文场景下的加剧因素

当上下文从 4K 扩展到 128K 甚至 1M 时,漂移问题被显著放大:

因素 短上下文(4K) 长上下文(128K+)
原始指令位置 前 10% 前 0.3%
干扰信号数量 指数级增长
注意力权重稀释 轻微 严重
RoPE 远距离衰减 可忽略 显著
检测难度 高(人工难追溯)

2.4 漂移的三种模式

模式 描述 示例
目标漂移 Agent 从任务 A 滑到任务 B 本来在分析销售数据,跑着跑去做竞品分析了
优先级漂移 任务没变,但主次倒置 "找出下滑原因"是主线、"修格式"是支线,结果在支线上花了大半步骤
风格漂移 目标和优先级都没偏,但输出风格变了 开头按要求输出结构化 JSON,跑了几步开始写大段自然语言解释

2.5 检测信号

漂移不是突然发生的,是有信号的:

  • 当前动作与原始目标的关联度:连续两步的输出和原始目标没有直接关系,大概率在漂
  • 步骤重复率:反复执行同一类操作(比如反复修格式),说明卡在子任务里出不来了
  • 目标完成进度:跑了 N 步,原始目标的完成度还是 0%,明显偏了

工程上可以做一个简单的漂移检测:每执行 K 步,把当前状态和原始目标丢给模型,让它判断"当前是否还在朝目标前进"。


三、解决措施

3.1 第一层防线:任务分解

把复杂任务拆成有序子任务,每完成一个检查"原始目标推进了吗"。核心思路是不依赖模型在 100K Token 中保持专注,而是缩短每次需要专注的距离。代价是需提前设计分解策略,简单任务有额外开销。

3.2 第二层防线:上下文压缩

对历史步骤做摘要压缩,只保留关键信息,控制干扰信号。核心思路是减少上下文中的冗余信息,让原始目标在注意力分配中保持较高权重。代价是压缩可能丢失细节。

压缩策略包括:摘要压缩(每 N 轮生成历史摘要,丢弃原始对话)、结构化笔记(维护 Markdown 格式笔记,记录目标、决策、待办)、关键帧提取(只保留关键状态如目标变更、重大决策、错误恢复点)。

3.3 第三层防线:定期 Re-Planning

每隔 N 步暂停执行,让 Agent 重新审视原始目标和当前进度,重新规划。核心思路是强制模型定期"抬头看路",而不是一直"低头拉车"。代价是每次 Re-Planning 是额外 LLM 调用,增加延迟和成本。

Re-Planning 流程包括:收集当前状态(原始目标摘要 + 已完成步骤摘要 + 当前上下文最后 K 轮)、评估偏离度(当前是否在朝目标前进)、生成新计划(保留有效部分、修正偏离部分、补充遗漏部分)、继续执行(用新计划替换旧计划)。


哲学视角

"以压缩提炼理解,以筛选赋予意义,以遗忘换取空间,让记忆成为人类意识的延伸。"

——从 RoPE 编码位置到 MLA 压缩语义,从 Flash Attention 分块破局到稀疏注意力筛选取舍,人类始终在有限算力与无限信息之间寻找平衡;上下文漂移恰如记忆的必然遗忘,提醒我们技术并非对完美的追逐,而是对认知规律的顺应——低维承载高维是组块化的智慧,筛选取代遍历是专注力的习得,定期回顾是对抗遗忘的自觉,上下文即记忆,注意力即认知,每一次扩展都是意识的延伸,每一次压缩都是理解的提炼。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐