Transformers分词偏移怎样映射回原始文本片段
来源:17golang原创
时间:2026-10-13 20:26:31 330浏览 收藏
如果模型输出的是 token 下标,而页面高亮、实体回链或检索结果需要原文片段,不能直接把 token 拼接后再去猜位置。更稳的做法是使用 Fast tokenizer 的 return_offsets_mapping=True,让每个 token 带上原始字符串中的半开区间 (start, end),再用原文切片得到片段。
官方资料地址:https://huggingface.co/docs/transformers/main_classes/tokenizer
关键结论:先把 token 位置和原始字符串坐标分开,再按特殊 token、序列归属和批次溢出信息过滤;不要用 decode 后的文本反推原始位置。
先确认偏移坐标由 Fast tokenizer 提供
Transformers 官方文档把 return_offsets_mapping 定义为返回每个 token 的字符起止位置,而且这个参数只适用于继承自 PreTrainedTokenizerFast 的 Fast tokenizer。普通 Python tokenizer 不能依赖这项能力。
调用时把偏移和特殊 token 标记一起取出,后面就能把模型输入中的位置映射回送入 tokenizer 的那条原始字符串。坐标的右边界不包含在片段内,因此还原时使用 text[start:end]。

用 offset_mapping 还原非特殊 token 的原文片段
工程代码通常同时检查 special_tokens_mask 和区间长度。CLS、SEP、PAD 等特殊 token 没有可回链的原文字符,常见表现是标记为特殊 token,或得到 (0, 0) 一类空区间。不要把这类位置当成原文开头。
from transformers import AutoTokenizer
def token_spans(text, model_name="bert-base-chinese"):
# use_fast=True 才能请求字符级 offset_mapping。
tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=True)
encoded = tokenizer(
text,
add_special_tokens=True,
return_offsets_mapping=True,
return_special_tokens_mask=True,
)
spans = []
for token, offset, is_special in zip(
tokenizer.convert_ids_to_tokens(encoded["input_ids"]),
encoded["offset_mapping"],
encoded["special_tokens_mask"],
):
start, end = offset
# 特殊 token 或空区间都没有可展示的原文片段。
if is_special or end
这里的 start 和 end 必须与同一条 text 配对保存。若中间对文本做了 trim、大小写归一化、换行替换或 Unicode 标准化,原坐标就不能直接套到新字符串上;应保留 tokenizer 的输入副本,或在预处理阶段建立额外的字符映射。
批量、成对输入和溢出窗口要保留归属
单条字符串只是最简单的情况。批量输入时,offset_mapping[i] 属于第 i 条输入;成对输入时,可以用 sequence_ids() 区分第一段和第二段。窗口截断配合 return_overflowing_tokens=True 后,一条原文可能对应多个编码窗口,overflow_to_sample_mapping 用来把窗口找回原始样本。

def batch_spans(texts, tokenizer, max_length=128, stride=16):
# 溢出窗口保留重叠 token,但每个 offset 仍要回到对应原文。
encoded = tokenizer(
texts,
truncation=True,
max_length=max_length,
stride=stride,
return_overflowing_tokens=True,
return_offsets_mapping=True,
return_special_tokens_mask=True,
)
result = []
owners = encoded["overflow_to_sample_mapping"]
for window_index, offsets in enumerate(encoded["offset_mapping"]):
sample_index = owners[window_index]
source = texts[sample_index]
mask = encoded["special_tokens_mask"][window_index]
window_spans = []
for offset, is_special in zip(offsets, mask):
start, end = offset
# 过滤窗口边界处的特殊 token,避免重复制造空片段。
if not is_special and end > start:
window_spans.append(source[start:end])
result.append({"sample_index": sample_index, "spans": window_spans})
return result
对于序列对,结果还要读取 encoded.sequence_ids(batch_index),只让目标序列的区间进入高亮。否则两个输入的字符坐标都可能看起来合法,但应用层会把问题文本的实体错误标到上下文文本上。
预分词输入不能按普通字符串理解
当输入已经是词列表并设置 is_split_into_words=True 时,tokenizer 处理的是用户提供的词边界。此时更适合通过 word_ids() 把 subword 归并回词索引;如果业务最终要标注原始长字符串,还需要自己保存每个词在长字符串中的起止位置,再做一次词级到字符级的映射。
这也是 NER、词级分类和页面高亮经常混淆的地方:word_ids() 回答“token 属于哪个输入词”,offset_mapping 回答“token 覆盖原始字符串的哪个字符区间”。两者可以组合,但不能互相替代。
把映射规则收进一张工程清单
| 场景 | 主要字段 | 处理重点 |
|---|---|---|
| 普通字符串 | offset_mapping | 用同一份原文做 [start:end] 切片 |
| 特殊 token | special_tokens_mask | 过滤特殊位置和空区间 |
| 序列对 | sequence_ids() | 区分第一段、第二段和特殊位置 |
| 批量溢出 | overflow_to_sample_mapping | 先找回原始样本,再读取区间 |
| 预分词输入 | word_ids() | 先回到词索引,字符坐标由业务维护 |
如果应用要把模型预测实体高亮到页面,建议保存原始文本、token 索引、字符区间和样本归属四项数据。这样即使窗口有重叠,也能按字符区间去重,而不是按 token 文本去重;不同 token 可能解码后相似,但它们在原文中的位置并不相同。
相关问题
为什么 offset_mapping 里会出现 (0, 0)?
常见原因是特殊 token、填充位置或没有对应原文字符的 token。应结合 special_tokens_mask 和区间长度过滤,不能直接把 (0, 0) 切成原文。
普通 tokenizer 能返回字符偏移吗?
官方文档明确说明 return_offsets_mapping 只适用于 Fast tokenizer。需要这项能力时,应加载对应的 Fast tokenizer;不要在慢 tokenizer 上假设该字段存在。
溢出窗口的 offset 要不要加窗口起点?
通常不需要。窗口归属由 overflow_to_sample_mapping 处理,offset_mapping 仍应按对应原始样本解释;只有在你自己把窗口文本再次截成独立字符串时,才需要维护额外的基准偏移。
-
346 收藏
-
235 收藏
-
387 收藏
-
447 收藏
-
360 收藏
-
492 收藏
-
338 收藏
-
326 收藏
-
118 收藏
-
386 收藏
-
科技周边 · 人工智能 | 58分钟前 | 人工智能 · Hugging Face Datasets streaming IterableDataset Dataset.shuffle shuffle buffer130 收藏
-
174 收藏
-
421 收藏
-
301 收藏
-
111 收藏
-
461 收藏
-
153 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习