LongT5 长序列模型完全指南:Local / Transient-Global 注意力原理与 Transformers 实战
LongT5 是面向长序列的 encoder-decoder Transformer 预训练模型,在 🤗 Transformers 中以 longt5 模块 形式提供,其核心创新在于将 T5 的稠密全注意力替换为 Local attention(局部滑窗注意力) 或 Transient-Global(TGlobal,瞬时全局)注意力,从而在保持全局建模能力的同时把序列长度扩展到上万 token。阅读本文后,你将掌握 LongT5 的两大注意力机制及其源码级实现原理、完整的 LongT5Config 参数说明,并能在 16,384 token 的超长输入上完成摘要生成、翻译等 seq2seq 推理与微调实战。
说明:本文以仓库文档 docs/source/en/model_doc/longt5.md 为主体骨架,结合配置与实现源码 configuration_longt5.py、modeling_longt5.py 及自动模型映射展开。
LongT5 是什么:T5 架构的长序列扩展
LongT5 由 Mandy Guo、Joshua Ainslie、David Uthus、Santiago Ontanon、Jianmo Ni、Yun-Hsuan Sung、Yinfei Yang 等人在论文 LongT5: Efficient Text-To-Text Transformer for Long Sequences 中提出(论文于 2021-12-15 发布,模型于 2022-06-13 由 stancld 贡献给 Hugging Face Transformers)。它是一个在 text-to-text 去噪生成式设定下预训练的 encoder-decoder Transformer,本质上是 T5 的扩展——在保持 T5 可扩展架构的同时,把输入长度和模型规模两个维度同时放大。
论文的核心思路是从两条研究线汇合而来:
- 借鉴长输入 Transformer(ETC)中的注意力设计——用稀疏的局部/全局注意力替代全量自注意力;
- 采用摘要式预训练(PEGASUS)的策略,融入 T5 的去噪预训练目标。
最终得到一种新的注意力机制 Transient Global(TGlobal),它模拟了 ETC 的 local/global 注意力,却不需要额外的 side-inputs(如硬性的段落边界或额外输入)。
在 Transformers 中的典型应用场景是长输入序列到序列任务:当输入超过常用的 512 token,甚至到达 16,384 token 量级时,模型依然能高效地完成摘要(summarization)、问答、翻译等任务。下面的文档导读与对应任务指南可供继续深入:
两种高效注意力机制:Local 与 Transient-Global
LongT5ForConditionalGeneration 是 T5ForConditionalGeneration 的扩展:它把传统 encoder 自注意力层替换为两种高效注意力之一,由配置项 encoder_attention_type 决定。注意这里的稀疏注意力只作用于编码器;解码器的自注意力与交叉注意力仍然走标准注意力路径(详见下文“源码架构”小节)。
Local Attention:零新增参数的滑窗稀疏注意力
Local Attention 的思想是:每个 token 只与自己左右各 local_radius 个 token 交互。
- 默认
local_radius = 127,即每个 token 关注左右 127 个 token,滑窗块长block_len = local_radius + 1 = 128; - 不引入任何新参数,纯属计算模式的变化;
- 计算复杂度与输入长度
l呈线性:O(l * r)(r = local_radius)。
Transient-Global(TGlobal)注意力:瞬时构造的全局 token
TGlobal 是 Local Attention 的超集:在局部滑窗之外,它还让每个输入 token 与层内所有其他 token 间接交互。实现上,输入序列被切分成固定长度 k 的块(默认 k = 16),块内所有 token 的 embedding 求和并归一化后得到该块的“全局 token”。
- 每个 token 既能像 Local Attention 一样关注邻近 token,也能关注所有全局 token(即拥有了类似标准全局注意力的感受野);
- “Transient(瞬时)”体现在:全局 token 是在每一次注意力运算内部动态构造的,并非额外输入;
- 作为代价,TGlobal 会引入少量新参数:全局相对位置偏置(global relative position bias) 与全局 token embedding 的 layer normalization;
- 计算复杂度为
O(l * (r + l/k))。
两种机制对比速览
| 特性 | 标准 T5 全注意力 | Local Attention | Transient-Global Attention |
|---|---|---|---|
| 每个 token 关注范围 | 全序列 | 左右各 r(默认 127) |
邻近 token + 全部全局 token |
| 序列分块 | 无 | 块长 block_len = r + 1 |
局部块长 r+1,全局块长 k(默认 16) |
| 新增参数 | — | 无 | 全局相对位置偏置 + 全局输入 LayerNorm |
| 计算复杂度 | O(l²) |
O(l·r) |
O(l·(r + l/k)) |
| 最大可用输入 | 512 量级(受显存限制) | 最高 16,384 token | 最高 16,384 token |
源码级证据:分块与掩码工具函数
Local / TGlobal 的实现大量依赖序列分块辅助函数,它们在 modeling_longt5.py 顶部定义,是整个稀疏注意力的“地基”:
- _pad_to_multiple:把序列长度补齐到
block_len的整数倍,便于均匀切块; - _split_into_blocks:把 tensor 沿序列维切成
(num_blocks, block_len)形状的块; - _concatenate_3_blocks:为每个块拼接前后相邻两块(共 3 块),使滑窗内的 key/value 覆盖左右各一个块的范围;
- _make_3block_relative_position_ids 与 _mask_local_attention_mask:生成“3 块上下文”内的相对位置并强制剔除超出
local_radius的注意力; - _get_local_attention_mask:把标准
attention_mask展开成形状为(batch, 1, num_blocks, block_len, 3*block_len)的局部注意力掩码。
TGlobal 还需要额外的“侧路(side / global)”逻辑:
- _make_global_fixed_block_ids:为每个输入 token 计算所属“固定全局块”的 id(padding token 记为 -1,孤块并入前一块);
- _make_side_relative_position_ids:构造 token 与其全局块之间的相对位置关系;
- _create_global_aggregates:通过 one-hot 块 id 做 einsum 累加,得到每个块的全局聚合表示。
两条注意力主干的类分别是 LongT5LocalAttention 与 LongT5TransientGlobalAttention。TGlobal 的 forward 中可以看到关键的数据流:先由全局块 id 聚合并经 global_input_layer_norm 归一化得到全局输入,再把它投影为 side key/value,torch.cat 拼接到 3 块局部 key/value 之后,使打分维度变成 3*block_len + global_seq_len;随后把局部偏置(position_bias)与全局偏置(compute_side_bias,内部使用独立的 global_relative_attention_bias embedding)拼接后一起加到注意力分数上。
相对位置编码:LongT5 的位置信息载体
与 T5 一样,LongT5 不使用绝对位置 embedding,而是通过相对位置分桶偏置表达位置关系。_relative_position_bucket(同时以静态方法存在于多个注意力类中,源出 T5 实现)的做法是:
- 把相对距离
memory_position - query_position映射到有限数量的桶(bucket); - 双向注意力(encoder)时近一半桶留给正/负方向,之后按绝对值处理;
- 距离小于
num_buckets/2的部分按精确增量分桶,更大的距离按对数刻度分桶,超过relative_attention_max_distance的全部落入同一个桶。
这种“近处细粒度、远处粗粒度”的对数分桶设计保证了模型对超出训练长度的序列有更好的外推性。对 Local/TGlobal 注意力而言,compute_bias 只需对单个窗口(3*block_len)预计算一份分桶偏置即可,这正是稀疏注意力能大幅降低开销的关键;而普通全注意力的 compute_bias 需要针对完整 (query_length, key_length) 计算。相对位置 embedding 只挂在第一层(has_relative_attention_bias=bool(i==0)),并在各层间复用(LongT5Stack 构造)。
LongT5Config 参数全解
LongT5 的配置类定义在 configuration_longt5.py,model_type = "longt5"。下面按默认值汇总了该类声明的全部核心字段,其中加粗项是 LongT5 区别于 T5 的关键配置:
| 参数 | 默认值 | 说明 |
|---|---|---|
vocab_size |
32128 |
词表大小 |
d_model |
512 |
隐藏层维度(与 hidden_size 同义) |
d_kv |
64 |
每头 key/value 投影维度(与 head_dim 同义) |
d_ff |
2048 |
每个 LongT5Block 中前馈层的中间维度 |
num_layers |
6 |
encoder / decoder 层数(num_hidden_layers) |
num_decoder_layers |
None |
为 None 时取 num_layers |
num_heads |
8 |
注意力头数 |
local_radius |
127 |
局部注意力中每个 token 向左/右各自能注意的 token 数 |
global_block_size |
16 |
TGlobal 模式下构造全局 token 的序列分块长度 k |
relative_attention_num_buckets |
32 |
每层相对位置分桶数 |
relative_attention_max_distance |
128 |
相对距离分桶的“远端上限” |
feed_forward_proj |
"relu" |
前馈层类型:"relu" 或 "gated-gelu"。LongT5v1.1 风格使用 "gated-gelu" |
dropout_rate |
0.1 |
dropout 比例 |
layer_norm_epsilon |
1e-6 |
RMS LayerNorm 的 epsilon |
initializer_factor |
1.0 |
权重初始化缩放因子 |
is_encoder_decoder |
True |
是否为 encoder-decoder 架构 |
encoder_attention_type |
"local" |
encoder 注意力类型:"local" 或 "transient-global",二选一 |
use_cache |
True |
是否使用 past key/value 缓存加速解码 |
pad_token_id / eos_token_id |
0 / 1 |
padding 与结束符 id |
tie_word_embeddings |
True |
是否共享输入/输出 embedding |
几点实现细节值得注意:
feed_forward_proj会被__post_init__拆解:末尾段作为激活函数名写入dense_act_fn;当值为"gated-gelu"时激活切换为"gelu_new",并置is_gated_act=True,模型就会选用带门控的双线性前馈层 LongT5DenseGatedActDense(hidden = gelu(xW1) * (xW2)),否则用标准 LongT5DenseActDense。validate_architecture会校验feed_forward_proj格式,只接受{ACT_FN}或gated-{ACT_FN}。- 与 T5 相同的“怪癖”:
tie_word_embeddings=False本表示 1.1 版本风格(不再对 decoder 输出做缩放),但 checkpoint 中只保存shared.weight,因此权重总是共享;源码会把scale_decoder_outputs取为tie_word_embeddings后强制tie_word_embeddings=True。是否缩放 decoder 输出直接影响 LongT5ForConditionalGeneration.forward 中的sequence_output * (model_dim ** -0.5)。
选择哪种注意力,只需在加载模型前指定配置。例如构造一个小规模的 TGlobal 变体:
from transformers import LongT5Config, LongT5ForConditionalGeneration
config = LongT5Config(
encoder_attention_type="transient-global", # "local" 或 "transient-global"
global_block_size=16, # TGlobal 的全局分块长度 k
local_radius=127, # 局部滑窗半径 r
)
model = LongT5ForConditionalGeneration(config)
警告:如果
encoder_attention_type传了"local"、"transient-global"以外的值,编码器块构造会直接抛出ValueError(见 LongT5Block)。
源码架构与模型类解析
模块 src/transformers/models/longt5 由三个文件构成:配置类、模型实现和懒加载入口。类结构的顶层概览如下。
编码器块的三分支路由
LongT5Block 是构成 encoder/decoder 堆栈的基础模块,其自注意力分支选择规则为:
- decoder:标准自注意力 LongT5LayerSelfAttention(因果掩码)+ 交叉注意力
LongT5LayerCrossAttention; - encoder 且
encoder_attention_type == "local":局部自注意力 LongT5LayerLocalSelfAttention; - encoder 且
encoder_attention_type == "transient-global":瞬时全局自注意力 LongT5LayerTransientGlobalSelfAttention; - 其余值抛错。
每种自注意力都遵循“RMS LayerNorm → 注意力 → 残差加和 → FF → 残差加和”的 T5 式结构。而注意力掩码的准备也在 LongT5Stack.forward 中分流:local 模式用 _get_local_attention_mask 生成滑窗掩码,transient-global 模式则把原始扩展掩码传入块内(局部与全局掩码在注意力内部各自计算)。
层归一化的特殊之处
LongT5LayerNorm 是 RMS(Root Mean Square)归一化:只做缩放、不去均值、无 bias,与 T5 风格一致;同时为了数值稳定,方差累加在 fp32 中进行,输出再按参数 dtype 转回半精度。所有层共享同一 layer norm 权重类,LongT5Stack 末尾还有一个 final_layer_norm。
提供的四个公开模型类
模型文件 init.py 与实现文件末尾 __all__ 中暴露了以下类:
- LongT5Model:裸的 encoder-decoder 模型,
forward返回Seq2SeqModelOutput。encoder 与 decoder 之间共享sharedembedding(_tied_weights_keys将两侧embed_tokens.weight指向shared.weight); - LongT5ForConditionalGeneration:在 decoder 之上加
lm_head的语言建模头,继承GenerationMixin,支持generate自回归解码与labels有监督训练;lm_head.weight同样与shared.weight绑定; - LongT5EncoderModel:仅编码器版本,适合为长文本提取稠密向量(embedding/检索场景);
LongT5PreTrainedModel:基类,声明base_model_prefix = "transformer"、支持梯度检查点、_no_split_modules = ["LongT5Block"](便于大模型切分)。
在自动模型体系中,三类模型均可通过 AutoModel 系列加载:映射关系记录在 modeling_auto.py(longt5 -> LongT5Model)与 modeling_auto.py(longt5 -> LongT5ForConditionalGeneration),配置类映射见 auto_mappings.py。
官方 checkpoint 家族
文档源码 docstring 中出现的可用预训练 checkpoint 有:
google/long-t5-local-base:google/long-t5-local-base系列(Local 注意力)提供 local/tglobal × base/large,其中 large 版本按文档描述支持 16,384 token 输入;Stancld/longt5-tglobal-large-16384-pubmed-3k_steps:社区贡献的、在 PubMed 上微调 3k 步的 TGlobal-large 摘要模型(见下文实战示例)。
加载时无需手动指定注意力类型——checkpoint 的 config.json 已写入 encoder_attention_type 等全部配置。
实战一:在超长输入上做摘要评估
原文档给出了一个可直接运行的端到端示例:用微调后的 LongT5 模型在 PubMed(scientific_papers)验证集上生成摘要,再用 ROUGE 指标评估。它覆盖了“超长 tokenizer 截断 + 半精度推理 + beam search + 批量 ROUGE 评估”的完整链路:
import evaluate
from datasets import load_dataset
from transformers import AutoTokenizer, LongT5ForConditionalGeneration
dataset = load_dataset("scientific_papers", "pubmed", split="validation")
model = (
LongT5ForConditionalGeneration.from_pretrained(
"Stancld/longt5-tglobal-large-16384-pubmed-3k_steps", device_map="auto"
)
.to("auto")
.half()
)
tokenizer = AutoTokenizer.from_pretrained("Stancld/longt5-tglobal-large-16384-pubmed-3k_steps")
def generate_answers(batch):
inputs_dict = tokenizer(
batch["article"], max_length=16384, padding="max_length", truncation=True, return_tensors="pt"
)
input_ids = inputs_dict.input_ids.to(model.device)
attention_mask = inputs_dict.attention_mask.to(model.device)
output_ids = model.generate(input_ids, attention_mask=attention_mask, max_length=512, num_beams=2)
batch["predicted_abstract"] = tokenizer.batch_decode(output_ids, skip_special_tokens=True)
return batch
result = dataset.map(generate_answers, batched=True, batch_size=2)
rouge = evaluate.load("rouge")
rouge.compute(predictions=result["predicted_abstract"], references=result["abstract"])
要点提示:
tokenizer(..., max_length=16384, truncation=True)直接把输入截断/补齐到 LongT5 支持的最大长度——这正是 T5 无法低成本做到、而 LongT5 的设计目标所在;.half()把权重转为 fp16。源码中 LongT5Block 对 fp16 下的inf做了 clamp,说明该实现显式兼容 fp16 推理;padding="max_length"是安全的,因为 LongT5 基于相对位置编码,文档明确说明输入可在左右任意一侧做 padding;generate参数(max_length、num_beams)来自 Transformer 的通用GenerationMixin,LongT5 解码器对past_key_values有完整缓存支持。
实战二:零样本快速体验与训练/微调
快速生成体验
在未微调的权重上同样可以体验长输入生成。以下改编自 LongT5ForConditionalGeneration.forward 的官方 docstring 示例:把同一句话重复 100 次拼成一个超长输入,观察模型的续写行为。
from transformers import AutoTokenizer, LongT5ForConditionalGeneration
tokenizer = AutoTokenizer.from_pretrained("Stancld/longt5-tglobal-large-16384-pubmed-3k_steps")
model = LongT5ForConditionalGeneration.from_pretrained(
"Stancld/longt5-tglobal-large-16384-pubmed-3k_steps"
)
# 构造超长输入(约数千 token)
inputs = tokenizer(100 * "studies have shown that owning a dog is good for you ", return_tensors="pt")
input_ids = inputs.input_ids
outputs = model.generate(input_ids)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
从模型内部构造理解数据流
- 训练时传入
labels即可:LongT5ForConditionalGeneration.forward会在未提供decoder_input_ids时自动调用_shift_right(labels)做右移构造 decoder 输入,并以pad_token_id作为decoder_input_ids的起始 token(源码实现);loss 采用CrossEntropyLoss(ignore_index=-100),把labels中-100的位置忽略。 - 编码器输入通过
input_ids/attention_mask传入;若要复用已编码结果(如多次解码),可先取encoder_outputs再传入encoder_outputs参数,避免重复前向编码。 - 与 T5 不同,LongT5 不使用任务前缀(task prefix),且预训练目标受 PEGASUS 启发(摘要式去噪),这两点是选型与迁移时的重要背景(依据原文档 Usage tips)。
通用 API 形态
若只关心输入句子的上下文表示(不做生成),可这样使用:
from transformers import AutoTokenizer, LongT5Model
tokenizer = AutoTokenizer.from_pretrained("google/long-t5-local-base")
model = LongT5Model.from_pretrained("google/long-t5-local-base")
# 用重复语句构造一个很长的 encoder 输入
input_ids = tokenizer(
100 * "Studies have been shown that owning a dog is good for you", return_tensors="pt"
).input_ids
decoder_input_ids = tokenizer("Studies show that", return_tensors="pt").input_ids
outputs = model(input_ids=input_ids, decoder_input_ids=decoder_input_ids)
last_hidden_states = outputs.last_hidden_state
仅编码器版本(适用于对长文本做表征提取、检索或分类特征)则更轻量:
from transformers import AutoTokenizer, LongT5EncoderModel
tokenizer = AutoTokenizer.from_pretrained("google/long-t5-local-base")
model = LongT5EncoderModel.from_pretrained("google/long-t5-local-base")
input_ids = tokenizer(
100 * "Studies have been shown that owning a dog is good for you ", return_tensors="pt"
).input_ids
outputs = model(input_ids=input_ids)
last_hidden_states = outputs.last_hidden_state
行为验证与测试用例
仓库提供了与模型行为直接对应的测试套件 test_modeling_longt5.py,可在对实现进行修改或排障时作为参照。它验证的内容包括模型与 T5 家族通用的前后向数值稳定性、generate 与缓存一致性、encoder_attention_type 两种配置下的等价性等。对源码结构感兴趣的读者可以配合阅读 tests/models/longt5 下的全部测试文件。
适用边界与注意事项总结
- 长序列定位:LongT5 面向的是“输入显著超过 512 token”的 seq2seq 任务;官方文档声明其可处理最长 16,384 token 的输入序列。实际是否可用取决于显存——TGlobal 仍是
O(l²/k)量级的全局开销,输入越长越需要大显存或梯度检查点配合。 - 参数引入差异:Local 变体零新增参数,可直接从结构相近的 T5 checkpoint 做权重迁移研究;TGlobal 变体额外引入
global_relative_attention_bias与global_input_layer_norm两组参数。 - 无任务前缀:与 T5 不同,LongT5 不在输入前拼接
"summarize:"之类的任务前缀;其预训练目标也更接近 PEGASUS 的摘要式去噪。 - 配置合法性:
encoder_attention_type仅支持"local"与"transient-global";feed_forward_proj仅支持"relu"或"gated-gelu",非法取值会由配置校验或模块构造直接报错。 - 推理数值:源码针对 fp16 做了显式
infclamp,说明官方实现已将半精度推理作为一等公民支持,可按需使用.half()与device_map="auto"组合(多卡分片)。
上述所有配置、类与示例均可在当前仓库内对应路径找到第一手实现,遇到与本文描述不一致的行为时,建议直接以 src/transformers/models/longt5/modeling_longt5.py 与 src/transformers/models/longt5/configuration_longt5.py 的当前代码为准。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00