SepLLM让KV再次被砍价
本文摘要
SepLLM(Separator LLM)基于一个简单但强有力的观察:自然语言中的分隔符(如逗号、句号、换行等)在注意力中常充当信息汇聚点。本文方法在训练/预填充阶段通过 INS 稀疏可见性 强化这一现象:每个 token 仅关注 I(起始 attention sinks)、S(全部历史分隔符)与 N(最近邻若干个)三类关键位置,迫使段落信息被“压缩”到分隔符 token 的 KV 上;推理阶段据此仅保留 I/S/N 的 KV,以此达到原生稀疏注意力与 KV 压缩的统一。
在 Llama‑3‑8B 等模型与多项任务上,SepLLM 在几乎不损失精度的情况下将 KV 缓存减少 >50%;流式长上下文可在有限缓存下稳定处理 ≥4M tokens。训练端在达到相同损失时,计算量降低 28%、训练时间降低 26%、吞吐提升至 1.53×。方法既可作为 training‑free 的 SepCache 直接接入推理侧,也可用于从零训练或后训练,显著提升训练—推理一致性。与 H2O、StreamingLLM、SnapKV 等推理期 KV 选择方法相比,SepLLM 的语义分段与分隔符承载机制在效率、稳定性与可扩展性上展现出优势。
1. 这项工作想解决什么问题?
当 LLM 需要处理很长的上下文时,两件事最吃紧:
- 显存 / 内存占用:KV 缓存在长度 (L) 上是线性累积的;
- 计算开销:注意力要不断与“全部历史”交互,推理时延随上下文拉长。
很多工作(H2O、StreamingLLM、SnapKV 等)都在“推理期选择/舍弃部分 KV”。SepLLM 的关键不同在于:
- 从语言规律出发,提前把信息“压”到少量关键 token(分隔符)上;
- 训练—推理一致:不是到推理时才决定丢谁,而是让模型在训练阶段就学会“把段落语义放到分隔符里”。
这让 SepLLM 既能做成 训练期的原生稀疏注意力,也能做成 推理期的即插即用缓存(SepCache),两条路都能走。
2. 方法核心:INS 稀疏注意力
2.1 观察
在多种模型/层的注意力可视化中,逗号、句号、分号、问号等分隔符 token 的注意力权重普遍偏高。这意味着“片段信息可以被这些分隔符很好地代表”。
2.2 稀疏可见性(训练/预填充)
为让信息真的被“挤”进分隔符,SepLLM 在训练或预填充阶段使用特定的注意力掩码:当前 token 只允许看三类 token:
- I:序列最前面的若干 attention sinks(如 BOS + 若干虚拟槽位);
- S:到当前为止,历史上所有出现过的分隔符 token;
- N:最近邻的 (n) 个 token(保证局部细节不丢)。
其它远处 token 被遮蔽,模型被迫把每段的关键信息“存放”到相应的分隔符 token 上。
2.3 推理与缓存
推理阶段据此 只在 KV 中保留 I/S/N 三类 token:
- 分隔符的 KV 会长期保留(代表每段摘要);
- 最近邻窗口滚动保留;
- 其余普通 token 的 KV 可以安全丢弃,从而显著降低显存与计算。
2.4 流式(Streaming‑SepLLM)
为适配无限长输入,采用四块缓存与周期性压缩,并配合位置编码平移。
2.4.1 四块缓存与变量
- Initial Cache:attention sink,容量 a;
- Local Window Cache:近邻滑窗,容量 w;
- Past Window Cache:近邻溢出后的过渡区,压缩前暂存普通 KV;
- Separator Cache:分隔符专用区,容量 s。
维护量:Size_init、Size_local_w、Size_past_w、Size_sep;运行期总占用 Size_run = 这四者之和,需满足 Size_run ≤ c(c 表示运行期 KV 缓存的总容量上限(token 级配额))。当前最近邻大小 n = Size_past_w + Size_local_w。约束关系:a + s + w < c;触顶时有 Size_past_w + Size_sep ≤ c − a − w。Past 与 Separator 共用同一物理区域,压缩时把 Past 中的分隔符 KV 迁入 Separator,其他丢弃。
2.4.2 迭代流程
- 先填 Initial 到 a,再填 Local 到 w;
- Local 满后新 token 进 Past;
- 未触顶时持续追加,n 线性增长;
- 触顶时压缩:迁入分隔符 KV、丢弃非分隔符、Past 清空;若 Separator 超过 s,淘汰最早分隔符,保留最近 s 个;
- 循环上述过程。Separator 第一次装满后,n 在区间 [w, c − a − s] 内呈现“增长 → 压缩回落 → 再增长”的周期性震荡。
2.4.3 平均占用
- 平均 n(序列无限长):(w + (c − a − s)) / 2。
- 平均 Size_run:平均 n + a + s = (w + c + a + s) / 2,小于 c,保证系统在固定容量下稳定运行。
2.4.4 位置编码
采用基于缓存位置的相对或旋转位置编码平移策略,随缓存滑动重映射相对位置,保证注意力对准缓存中的相对距离。
2.4.5 分隔符集合
默认集合可写作:[点号、逗号、问号、感叹号、分号、冒号、空格、TAB、换行]。中文可加入“,。;?!、”及换行,或设置自定义段落/代码块分隔符。
2.4.6 参数映射(以 SepCache 为例)
cache_size→c,init_cache_size→a,local_size→w,sep_cache_size→s,neighbor_window→n。可开启 USE_MAX_SEP_CACHE,仅保留最新 s 个分隔符;SEP_ACCUMULATION、SEP_PADDING_IN_BATCH 控制历史累计与 batch 对齐。
3. 关键效果(论文报告)
- KV 缓存:在 Llama‑3‑8B 上跑 GSM8K‑CoT,KV 大小减少 > 50%,准确率几乎不变。
- 流式长序列:能稳定处理 ≥ 4,000,000 tokens 的输入,语言建模性能保持稳定。
- 训练端:达到相同 loss 时,计算量 −28%、训练时间 −26%,吞吐 ×1.53;同规模算力下更快收敛。
- 跨模型泛化:在 Pythia、Llama‑3、Falcon‑40B 等多架构上验证,困惑度、吞吐与 r.KV 指标均优。
注:论文观点,本人并未尝试!
4. 与相关工作的对比
方法 | 思路 | 训练参与 | 推理侧改动 | 长序列能力 | 典型优点 | 可能限制 |
---|---|---|---|---|---|---|
SepLLM | 把片段信息压到分隔符;只保留 I/S/N | 可 from-scratch / post‑training;也支持 training‑free 的 SepCache | 需要使用 SepCache 或配套稀疏注意力实现 | 流式可达 4M+ | KV 减半+、一致性强、推理/训练皆加速 | 对分隔符稀少/结构不规则文本,需调 邻近窗口 n 等超参 |
H2O | 高熵优先保留 | 训练后 | 仅推理期 KV 选择 | 中 | 简单通用 | 与模型训练分离,一致性较弱 |
StreamingLLM | 利用 Attention Sink,丢弃远处普通 token | 训练后 | 仅推理期 | 强 | 实现成熟,思路清晰 | 依赖 sink 与距离启发式 |
SnapKV | 生成前快照 query,选“相关” KV | 训练后 | 仅推理期 | 中 | 相关性选择更精细 | 需要额外一次匹配计算 |
上表为作者理解下的简化对照,供入门者快速定位差异。
H2O 并不是笔误,不是水,而是 “Heavy‑Hitter Oracle” 的缩写,是一套在推理阶段压缩/淘汰 KV 缓存的策略:用注意力得分找出贡献最大的少数 “重击手(heavy hitters)” token,并长期保留它们的 KV,同时也保留一部分最近 token;其余就可以丢弃,从而省显存、提吞吐。
5. 快速上手:两种路径
5.1 直接用 SepCache(training‑free)
适合先尝试就绪的推理加速。以 Llama‑3.1‑8B‑Instruct 为例:
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
model_id = "meta-llama/Meta-Llama-3.1-8B-Instruct"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
model_id,
torch_dtype=torch.bfloat16,
device_map="auto",
)
prompt = "请用三句话介绍SepLLM的核心思想。"
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
# 方式 A:Transformers 内置(当版本已集成时)
# from transformers import SepCache
# cache = SepCache(model.config, tokenizer=tokenizer)
# outputs = model.generate(**inputs, max_new_tokens=256, cache_implementation=cache)
# 方式 B:Hugging Face 自定义 generate(社区实现)
outputs = model.generate(
**inputs,
max_new_tokens=256,
custom_generate="Gausson/sep_cache", # 或 "transformers-community/sep_cache"
# 关键超参(可按场景调整)
sep_cache_config={
"neighbor_window": 64, # 最近邻窗口 N
"max_separators": 4096, # 分隔符容量上限
"padding_id": tokenizer.eos_token_id,
},
)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
依赖:
transformers >= 4.53.0
。首次尝试建议从neighbor_window=64
起步,再按延迟/效果折中微调。
5.2 作为原生稀疏注意力训练/后训练
- 若你要 从零训练,可在各层应用 INS 稀疏掩码;
- 若你要 对现有模型后训练(继续预训练 / SFT / DPO),确保训练期也使用相同的 INS 掩码,以减小与推理的不一致;
- 训练内核可配合 PyTorch FlexAttention 等高效实现,易于自定义注意力模式。
6. 实践建议
- 分隔符定义:中英文标点、换行、特殊分隔 token 都可以纳入;对代码/日志类文本,可自定义“分号、花括号、换行”等为分隔符。
- 邻近窗口(N):任务越依赖近场细节,N 越要大;一旦 N 过小,可能导致细粒度信息丢失、困惑度上升。
- 分隔符容量:流式场景中要给分隔符缓存足够空间;若容量打满,要设计迁移/淘汰策略(论文已有实现)。
- 评测维度:除准确率/困惑度外,务必记录 r.KV(KV 保留率)、GPU 端到端时延、吞吐 与 峰值显存,对比基线更直观。
- 与现有优化叠加:PagedAttention、GQA、Flash/FlexAttention、量化、FP8、张量并行等都可以协同;不过要逐项 A/B,避免优化“相互抵消”。
7. 局限与开放问题
- 语体依赖:SepLLM 对“分隔符清晰”的文本受益最大;对于分隔符稀缺或结构极不规则的输入(如部分代码、表格、拼接数据),需要调大 N 或引入自定义分隔符策略。
- 跨语言与多模态:不同语言的标点、语法差异较大;多模态场景(如图文)如何定义“分隔符”与层级摘要,仍有探索空间。
- 与检索/记忆模块的协同:分隔符承载了段落级摘要,或许能与 RAG/外部记忆更自然地结合,作为轻量“局部摘要器”。
- 安全与对齐:当重要细节被过度压缩到少量 token,上下文攻击/绕过是否更容易?需要专门评测。
8. 参考资料
-
Chen et al., SepLLM: Accelerate Large Language Models by Compressing One Segment into One Separator, arXiv:2412.12094.
- PDF/HTML: https://arxiv.org/abs/2412.12094
- OpenReview(ICML 2025 poster):https://openreview.net/forum?id=MhVJCxYEEi
- 项目页:https://sepllm.github.io/
- 代码:https://github.com/HKUDS/SepLLM
-
SepCache(Hugging Face):https://huggingface.co/Gausson/sep_cache
- 要求
transformers >= 4.53.0
,支持custom_generate="Gausson/sep_cache"
。
- 要求
-
PyTorch FlexAttention:
- 官方博客(训练端):https://pytorch.org/blog/flexattention/
- 官方博客(推理端):同站点,搜索 FlexAttention。
-
对照阅读:
- StreamingLLM — arXiv:2309.17453 · GitHub(MIT HAN Lab)
- H2O(Heavy-Hitter Oracle) — arXiv:2306.14048 · GitHub(FMInference/H2O)
- SnapKV: LLM Knows What You are Looking for Before Generation — arXiv:2404.14469 · GitHub(FasterDecoding/SnapKV)
- Compressive Transformers (ICLR 2020) — arXiv:1911.05507