以下是对“理解 Token 索引与字符位置的区别”的内容整理,条理清晰,结构完整,保持技术细节,方便阅读,无多余解释:
🔍 理解 Token 索引 vs 字符位置
文本分块方法中返回的索引是 token 索引,而不是原始文本中的字符位置。理解这一点对正确使用和调试文本处理流程至关重要。
📄 原始文本与 Tokenization 的区别
示例文本
"人工智能正在改变世界。"
字符位置(原始文本)
字符 | 人 | 工 | 智 | 能 | 正 | 在 | 改 | 变 | 世 | 界 | 。 |
---|---|---|---|---|---|---|---|---|---|---|---|
位置 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
Token 位置(tokenizer 处理后)
Tokens = ["人", "工", "智", "能", "正", "在", "改", "变", "世", "界", "。"]
索引 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
🔢 各分块方法的返回值示例
1️⃣ 语义分块(chunk_semantically)
text = "人工智能正在改变世界。它使得许多任务自动化。"
Tokenizer 分词结果:
Tokens = ["人", "工", "智", "能", "正", "在", "改", "变", "世", "界", "。",
"它", "使", "得", "许", "多", "任", "务", "自", "动", "化", "。"]
索引 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]
假设返回值为:
[(0, 11), (11, 22)]
表示:
- 第1块:token索引 0-10,“人工智能正在改变世界。”
- 第2块:token索引 11-21,“它使得许多任务自动化。”
2️⃣ 固定大小分块(chunk_by_tokens)
text = "人工智能正在改变世界。它使得许多任务自动化。"
chunk_size = 5
返回值示例:
[(0, 5), (5, 10), (10, 15), (15, 20), (20, 22)]
表示:
- 第1块:token索引 0-4,“人工智能正在”
- 第2块:token索引 5-9,“改变世界。”
- 第3块:token索引 10-14,“它使得许多”
- 第4块:token索引 15-19,“任务自动化”
- 第5块:token索引 20-21,“。”
3️⃣ 按句子分块(chunk_by_sentences)
text = "人工智能正在改变世界。它使得许多任务自动化。我们需要适应这些变化。"
Tokenizer 分词结果假设为:
句子1:"人工智能正在改变世界。" → token索引 0~10
句子2:"它使得许多任务自动化。" → token索引 11~21
句子3:"我们需要适应这些变化。" → token索引 22~33
设置每个块 1 个句子,返回值:
[(0, 11), (11, 22), (22, 34)]
❓ 为什么返回 token 索引?
原因 | 说明 |
---|---|
✅ NLP 模型处理的是 token | 模型输入必须是 token 序列,不能直接处理原始字符 |
✅ 精确控制长度 | Token 数量直接决定能否通过模型输入限制(如 BERT 的 512 token) |
✅ 对齐后续处理流程 | 向量化、分块拼接、交叉注意力等操作都基于 token 索引进行 |
🔁 实际代码中的字符位置 → Token 索引映射
部分关键转换流程如下:
# 从 splitter 获取字符级位置
nodes = [
(node.start_char_idx, node.end_char_idx)
for node in self.splitter.get_nodes_from_documents(...)
]
# token_offsets 是 tokenizer 返回的每个 token 的字符起止位置
start_chunk_index = bisect.bisect_left(
[offset[0] for offset in token_offsets], char_start
)
end_chunk_index = bisect.bisect_right(
[offset[1] for offset in token_offsets], char_end
)
最终输出的 (start_chunk_index, end_chunk_index)
就是基于 tokenizer 的 token 索引范围。