tokenizer截断丢失信息,如何处理?
简短回答:
标准的
truncation
会丢失末尾信息,但我们可以通过以下方法缓解或避免:
方法 | 是否推荐 | 说明 |
---|---|---|
1. 滑动窗口 + 池化 | ✅✅✅ 强烈推荐 | 将长文本分段编码,再融合 |
2. 保留尾部 truncation | ✅ 推荐 | 让 tokenizer 保留最后部分 |
3. 使用长文本模型(Longformer, BigBird) | ✅✅ 强烈推荐 | 原生支持 4k~16k 长度 |
4. 提取关键句 + 摘要 | ✅ 推荐 | 预处理阶段压缩内容 |
5. 双向截断(首尾结合) | ✅ 可用 | 同时保留开头和结尾 |
下面我们详细讲解每种方法,并给出代码示例。
方法1:滑动窗口 + 池化(Sliding Window + Pooling)
思路:
将长文本切分为多个重叠的窗口,分别编码,然后对所有 token 或 [CLS]
向量做 池化(如 mean/max pooling),得到最终表示。
优点:
- 保留全文信息
- 兼容 BERT 等标准模型
- 可控制计算量
缺点:
- 计算开销增加
- 实现稍复杂
示例代码:
import torch
from transformers import AutoTokenizer, AutoModeltokenizer = AutoTokenizer.from_pretrained('bert-base-uncased')
model = AutoModel.from_pretrained('bert-base-uncased')def encode_long_text_sliding(text, max_length=128, stride=64):# 分词但不截断inputs = tokenizer(text,max_length=max_length,truncation=False, # 先不分段return_tensors='pt',return_overflowing_tokens=True, # 启用滑动窗口stride=stride # 重叠部分)input_ids = inputs['input_ids'] # (num_segments, max_length)attention_mask = inputs['attention_mask']with torch.no_grad():outputs = model(input_ids=input_ids, attention_mask=attention_mask)# 取最后一层所有 token 的隐藏状态last_hidden_states = outputs.last_hidden_state # (num_segments, max_length, 768)# 对每个 segment 做 mean pooling(去掉 [PAD])pooled_vectors = []for i in range(last_hidden_states.size(0)):mask = attention_mask[i].unsqueeze(-1) # (max_length, 1)seg_vec = last_hidden_states[i] * maskpooled = seg_vec.sum(dim=0) / mask.sum() # mean poolingpooled_vectors.append(pooled)# 对所有 segment 的 pooled vector 再做 mean poolingfinal_vector = torch.stack(pooled_vectors).mean(dim=0) # (768,)return final_vector
适用场景:长篇财经报道、年报、研报摘要。
方法2:保留尾部截断(Truncate from Beginning)
思路:
默认 truncation
是从开头保留,但我们可以通过设置让 tokenizer 保留末尾部分。
优点:
- 简单!一行代码
- 适合“结论在后”的文本
缺点:
- 丢失开头信息(如主语、背景)
示例代码:
text = "The company reported strong revenue growth in Q2, however management lowered full-year guidance due to supply chain issues."enc = tokenizer(text,max_length=32,truncation=True,padding='max_length',return_tensors='pt',# 关键参数:truncation='only_first', # 只对第一个句子截断stride=0,pad_to_max_length=True,# 让 tokenizer 从前面截断(即保留后面)# 实际上:PyTorch Transformers 默认保留前面,但我们可以手动控制
)# 更直接的方法:使用 return_overflowing_tokens 并取最后一个 segment
inputs = tokenizer(text,max_length=32,truncation=True,return_tensors='pt',return_overflowing_tokens=True,stride=16
)# 取最后一个 segment(即末尾部分)
input_ids_tail = inputs['input_ids'][-1] # 保留最后 32 tokens
适用场景:新闻结尾常有“但是…”、“然而…”、“展望…”等关键信息。
方法3:使用长文本模型(Longformer / BigBird / LED)
推荐模型:
模型 | 最大长度 | 特点 |
---|---|---|
Longformer | 4096 | 支持全局 attention(如 [CLS] ) |
BigBird | 4096 | 稀疏 attention,更快 |
LED | 16384 | 专为长文档摘要设计 |
示例代码:
from transformers import AutoTokenizer, AutoModeltokenizer = AutoTokenizer.from_pretrained('allenai/longformer-base-4096')
model = AutoModel.from_pretrained('allenai/longformer-base-4096')text = "你的超长金融新闻文本..." # 可达 4096 tokensinputs = tokenizer(text, return_tensors='pt', truncation=False, padding=True)
with torch.no_grad():outputs = model(**inputs)
优点:
- 原生支持长文本,无需分段
- 信息完整保留
缺点:
- 显存消耗大
- 推理速度慢
适用场景:年报、政策文件、完整研报。
方法4:预处理:提取关键句或摘要
思路:
在输入模型前,先用 NLP 方法提取最重要的句子。
方法:
- TextRank / TF-IDF:无监督关键词/句提取
- 预训练摘要模型:如
BART
,Pegasus
- 规则匹配:找“but”, “however”, “despite”, “guidance” 等关键词所在句
示例(使用 BART 摘要):
from transformers import pipelinesummarizer = pipeline("summarization", model="facebook/bart-large-cnn")text = "长文本..."
summary = summarizer(text, max_length=100, min_length=30, do_sample=False)
print(summary[0]['summary_text'])
# 输出精简版,再输入你的主模型
适用场景:数据预处理阶段,提升效率。
方法5:双向截断(首尾结合)
思路:
保留前半部分 + 后半部分,中间丢弃。
实现思路:
def truncate_keep_head_tail(text, tokenizer, max_length=128):tokens = tokenizer.tokenize(text)if len(tokens) <= max_length - 2:return tokenizer.encode(text, max_length=max_length, padding='max_length')# 保留 [CLS] + 前半 + 后半 + [SEP]half_len = (max_length - 4) // 2 # 减去 [CLS], [SEP], 和两个部分的平衡head = tokens[:half_len]tail = tokens[-half_len:]new_tokens = ['[CLS]'] + head + tail + ['[SEP]']input_ids = tokenizer.convert_tokens_to_ids(new_tokens)# 填充if len(input_ids) < max_length:input_ids += [0] * (max_length - len(input_ids))return input_ids
适用场景:中等长度文本,兼顾开头(主语)和结尾(结论)。
总结:如何选择?
你的需求 | 推荐方法 |
---|---|
想快速改进,且关键信息在结尾 | ✅ 保留尾部 truncation |
文本很长(>512),信息分散 | ✅✅ Longformer / BigBird |
显存有限,但想保留全文 | ✅✅ 滑动窗口 + 池化 |
想减少输入长度,提升速度 | ✅ 摘要提取 + BART |
兼顾开头和结尾 | ✅ 双向截断 |