BERT 做分类时,对"可用上下文长度"有明确上限,而且这个上限是硬限制。
不是训练技巧问题,而是 模型结构决定的。
下面从 原理 → 具体数值 → 工程影响 → 解决方案 四个层面,把这个问题一次讲透。
一、BERT 为什么有上下文长度上限?
根本原因:绝对位置编码 + Transformer 自注意力
BERT 的输入结构是:
[CLS] token_1 token_2 ... token_N [SEP]
其中:
- 每个 token 都需要一个 位置 embedding
- 自注意力复杂度是 O(N²)
BERT 的位置 embedding是「固定表」
以 bert-base-chinese 为例:
text
max_position_embeddings = 512
👉 超过 512 的 token,没有位置编码,模型无法处理。
二、具体数值:BERT 能"看"多长?
1️⃣ 官方 BERT 系列
| 模型 | 最大 token 数 |
|---|---|
| bert-base / bert-large | 512 |
| chinese-bert | 512 |
| roberta-wwm | 512 |
| albert | 512 |
⚠️ 注意:
- 这是 token,不是字符、不是词
- 中文 1 个字 ≈ 1 token(大致)
2️⃣ 输入实际可用长度
因为要预留:
[CLS][SEP]
所以:
text
max_text_tokens ≈ 510
三、这个限制对"分类任务"意味着什么?
1️⃣ 超长文本直接截断 → 信息丢失
python
tokenizer(
text,
max_length=512,
truncation=True
)
- 默认:只保留前 512
- 后文内容完全丢失
👉 对 RAG chunk 打标来说,这是最常见坑。
2️⃣ "CLS 能概括全文"是有前提的
成立条件:
- 文本不长(≤512)
- 关键信息集中
❌ 不成立:
- 操作步骤在后半段
- 错误说明在结尾
四、工程上如何处理"长上下文分类"?(重点)
✅ 方案一:控制 chunk 长度(最推荐)
这是 RAG 场景的最佳实践:
- 200~400 中文字符
- 语义完整
- 与 embedding chunk 尺寸保持一致
📌 优点:
- 简单
- 稳定
- 性能最好
✅ 方案二:滑动窗口(Sliding Window)
适合:必须保留全文
做法
text
窗口大小:512
步长:256
对每个窗口分类:
text
text → window_1 → label scores
→ window_2 → label scores
聚合策略
- max pooling(常用)
- mean pooling
- voting
📌 示例:
text
final_score(label) = max(score_window_i)
✅ 方案三:层级分类(Hierarchical)
text
文档
├── chunk_1 → 标签
├── chunk_2 → 标签
└── 聚合为 doc 标签
非常适合:
- RAG
- 文档级打标
- 知识库管理
⚠️ 方案四:强行"改 BERT 上限"(不推荐)
技术上可以:
- 扩展 position embedding 到 1024
- 插值位置编码
- 继续 pretrain
❌ 但问题:
- 成本高
- 收益不稳定
- 容易崩
五、如果你真的需要"超长文本分类",该用什么?
1️⃣ Longformer / BigBird(推荐)
| 模型 | 上下文 |
|---|---|
| Longformer | 4k~16k |
| BigBird | 4k+ |
📌 适合:
- 法律文书
- 合同
- 长报告
2️⃣ 两段式方案(工业常用)
text
长文档
→ BERT / embedding 切 chunk
→ chunk 分类
→ LLM / 规则聚合
六、针对 RAG 打标的"最佳建议"
不要用 BERT 去"硬吃长文档"
而是:
- 文档 → 语义 chunk
- chunk ≤ 512 token
- BERT 分类 + 向量检索统一尺度
七、一句话总结
- ✅ BERT 分类上下文有硬上限:512 tokens
- ❌ 超过就只能截断或切分
- ✅ RAG 场景:短 chunk + 多标签分类是最优解
- 🚫 不要迷信"CLS 能看全文"