以下是基于 transformers 5.19.0 源码编写
python
from transformers import AutoTokenizer, TokenizersBackend
# MODEL_PATH = r"E:\LLM-model\Qwen3.5-4B"
# MODEL_PATH = r'E:\LLM-model\Qwen2.5-1.5B-Instruct'
# MODEL_PATH = r'E:\LLM-model\Qwen2.5-3B-Instruct'
MODEL_PATH = r'E:\LLM-model\Qwen3.5-2B'
print('加载分词器中...')
tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH, trust_remote_code=True)
tokenizer: TokenizersBackend # 写上类型,便于写代码
一、__call__() 是什么?有什么用?
在 Python 中,__call__ 是一个魔法方法,它允许一个类的实例像函数一样被直接调用。也就是说,当你写 tokenizer("你好") 时,底层实际执行的就是 tokenizer.__call__("你好")。
它有什么用? 它是整个 Tokenizer 最核心、最全能、抽象层级最高 的入口方法。 前面提到的 tokenize、encode 都只做了流水线上的"一部分"工作,而 __call__() 是一条完整的全自动流水线。它把以下所有步骤打包在一起,一步到位:
- 分词(Tokenize)
- 查表转 ID(Convert to IDs)
- 添加特殊 Token(Add special tokens,如 BOS/EOS/CLS/SEP)
- 截断(Truncation,超长切断)
- 填充(Padding,不够长补齐)
- 生成辅助张量(如 Attention Mask、Token Type IDs)
- 格式转换(转为 PyTorch/TensorFlow/NumPy 的 Tensor)
一句话总结 :在日常 99% 的推理和训练场景中,你只需要调用 __call__(),把原始文本扔进去,它直接吐出模型能"吃"的 Tensor 字典。
二、完整的函数签名
以下是 PreTrainedTokenizerBase 中 __call__ 的完整签名(:
python
def __call__(
self,
text: Union[str, List[str], List[List[str]]] = None, # [必填] 主文本或文本批次 (注: 虽默认None, 但实际调用时必须提供)
text_pair: Optional[Union[str, List[str], List[List[str]]]] = None, # [可选] 句对任务的第二句话
text_target: Union[str, List[str], List[List[str]]] = None, # [可选] 序列到序列任务的目标文本(标签)
text_pair_target: Optional[Union[str, List[str], List[List[str]]]] = None, # [可选] 目标文本的句对
add_special_tokens: bool = True, # [可选] 是否添加特殊token
padding: Union[bool, str, PaddingStrategy] = False, # [可选] 填充策略
truncation: Union[bool, str, TruncationStrategy] = False, # [可选] 截断策略 (注: 源码默认为 False, 而非 None)
max_length: Optional[int] = None, # [可选] 最大长度
stride: int = 0, # [可选] 滑动窗口步长
is_split_into_words: bool = False, # [可选] 输入是否已经是预分词列表
pad_to_multiple_of: Optional[int] = None, # [可选] 填充到指定倍数
return_tensors: Optional[Union[str, TensorType]] = None, # [可选] 返回的张量类型
return_token_type_ids: Optional[bool] = None, # [可选] 是否返回 token_type_ids
return_attention_mask: Optional[bool] = None, # [可选] 是否返回 attention_mask
return_overflowing_tokens: bool = False, # [可选] 是否返回被截断溢出的token
return_special_tokens_mask: bool = False, # [可选] 是否返回特殊token掩码
return_offsets_mapping: bool = False, # [可选] 是否返回字符级偏移映射
return_length: bool = False, # [可选] 是否返回序列长度
verbose: bool = True, # [可选] 是否打印警告信息
**kwargs, # [可选] 其他向后兼容的参数
) -> BatchEncoding:
三、逐参数详解
- 核心输入参数
text[必填]- 解释 :你要处理的主文本。它的类型非常灵活,可以是单条字符串(
"你好")、字符串列表(["你好", "世界"],代表一个 Batch),甚至是列表的列表([["你", "好"], ["世", "界"]],配合is_split_into_words=True使用)。 - 注意 :虽然签名中默认值为
None,但如果不传且未传text_target,方法会抛出ValueError。
- 解释 :你要处理的主文本。它的类型非常灵活,可以是单条字符串(
text_pair[可选]- 解释 :用于"句对"任务(如自然语言推断 NLI、问答 QA、语义相似度)。如果你传了
text="前提句子", text_pair="假设句子",Tokenizer 会在中间自动插入分隔符(如[SEP]),并生成对应的token_type_ids来区分这两句话。
- 解释 :用于"句对"任务(如自然语言推断 NLI、问答 QA、语义相似度)。如果你传了
text_target/text_pair_target[可选]- 解释 :专门用于 Seq2Seq 任务(如翻译、摘要,比如 T5/BART 模型)。当你需要同时编码输入(encoder)和标签(decoder)时,把标签传给
text_target,它会返回一个包含input_ids和labels的字典。
- 解释 :专门用于 Seq2Seq 任务(如翻译、摘要,比如 T5/BART 模型)。当你需要同时编码输入(encoder)和标签(decoder)时,把标签传给
- 预处理控制参数
add_special_tokens[可选, 默认 True]- 解释 :是否在序列首尾添加模型需要的特殊 Token(如 Qwen 的
<|im_start|>、BERT 的[CLS]和[SEP])。强烈建议保持 True,除非你在做非常底层的自定义拼接。
- 解释 :是否在序列首尾添加模型需要的特殊 Token(如 Qwen 的
padding[可选, 默认 False]- 解释 :控制如何把长短不一的句子补齐到相同长度。
False或"do_not_pad":不填充。True或"longest":填充到当前 Batch 中最长句子的长度。"max_length":填充到max_length参数指定的长度。
- 解释 :控制如何把长短不一的句子补齐到相同长度。
truncation[可选, 默认 False]- 解释 :当文本超过
max_length时如何截断。False或'do_not_truncate':不截断(如果超长,底层 Rust 可能会报错或警告)。True或'longest_first':截断到max_length。如果是句对,优先截断最长的那一句。'only_first':只截断第一句话。'only_second':只截断第二句话。
- 补充 :如果设为
True但未指定max_length,则会自动回退使用模型的model_max_length。
- 解释 :当文本超过
max_length[可选, 默认 None]- 解释 :截断和填充的最大长度阈值。如果不传,默认使用模型配置中的
model_max_length(比如 Qwen 通常是 32768 或更大)。建议显式指定(如 512、2048),以节省显存。
- 解释 :截断和填充的最大长度阈值。如果不传,默认使用模型配置中的
stride[可选, 默认 0]- 解释 :滑动窗口的重叠步长。配合
truncation和return_overflowing_tokens=True使用。当文本被截断时,stride决定了截断窗口之间的重叠词元数。注意:只有当return_overflowing_tokens=True时此参数才生效。常用于长文本的滑动窗口分类任务。 - 例如
max_length=512, stride=128,则每次滑动 384 个 token,保留 128 个 token 的重叠上下文(常用于长文档问答)。
- 解释 :滑动窗口的重叠步长。配合
is_split_into_words[可选, 默认 False]- 解释 :如果你传入的
text已经是分好词的列表(比如["Hello", "world"]而不是"Hello world"),必须设为True,否则 Tokenizer 会把它当成一个包含多个句子的 Batch,即 Tokenizer 会把每个字符串当成一个完整句子,而不是一个词。
- 解释 :如果你传入的
pad_to_multiple_of[可选, 默认 None]- 解释 :把序列长度填充到指定数字的倍数(如 8 或 64)。这在某些硬件(如 GPU 的 Tensor Core)上能加速矩阵乘法。注意:仅在
padding不为False时生效。
- 解释 :把序列长度填充到指定数字的倍数(如 8 或 64)。这在某些硬件(如 GPU 的 Tensor Core)上能加速矩阵乘法。注意:仅在
- 输出控制参数(Return 系列)
return_tensors[可选, 默认 None]- 解释 :极其重要 。决定输出的数据类型。
"pt":返回 PyTorch 的torch.Tensor。"tf":返回 TensorFlow 的tf.Tensor。"np":返回 NumPy 的ndarray。None:返回普通的 Pythonlist。
- 解释 :极其重要 。决定输出的数据类型。
return_token_type_ids[可选, 默认 None]- 解释 :是否返回
token_type_ids(用于区分句对中的第一句和第二句)。默认None表示"如果模型配置需要就返回"。对于不需要句对区分的模型(如 GPT/Qwen),通常不返回。 - 用于区分句 A 和句 B(0 表示句 A,1 表示句 B)。现代 Decoder-only 模型(如 Llama, Qwen)不需要这个,BERT 等 Encoder 模型需要。
- 解释 :是否返回
return_attention_mask[可选, 默认 None]- 解释 :是否返回
attention_mask(1 表示真实 token,0 表示 padding)。默认None表示"如果模型配置需要就返回"。绝大多数现代模型都需要它。
- 解释 :是否返回
return_overflowing_tokens[可选, 默认 False]- 解释:如果文本被截断了,是否把被截掉的那些"溢出"的 token 也返回。常用于长文档处理。
return_special_tokens_mask[可选, 默认 False]- 解释:返回一个掩码,1 表示该位置是特殊 token(如 BOS/EOS/PAD),0 表示普通文本 token。常用于 MLM(掩码语言模型)训练时避免 mask 掉特殊 token。
return_offsets_mapping[可选, 默认 False]- 解释 :只有 Fast Tokenizer 支持 。返回每个 token 在原始字符串中的字符级起止索引(如
(0, 5))。在命名实体识别(NER)和问答(QA)任务中,用于将 token 级别的预测映射回原始文本。Slow Tokenizer 调用此参数会抛出异常。
- 解释 :只有 Fast Tokenizer 支持 。返回每个 token 在原始字符串中的字符级起止索引(如
return_length[可选, 默认 False]- 解释 :是否在返回字典中额外包含一个
length字段,表示每个序列的实际长度(不含 padding)。
- 解释 :是否在返回字典中额外包含一个
verbose[可选, 默认 True]- 解释 :如果文本超长且你没有设置
truncation,是否打印警告日志。生产环境中建议设为False以避免日志污染。
- 解释 :如果文本超长且你没有设置
四、输入与输出到底是什么?
📥 输入 (Input)
- 类型 :
str、List[str]、List[List[str]],或者它们的组合(配合text_pair)。 - 本质 :人类可读的原始自然语言文本(或者已经预分词的列表)。
📤 输出 (Output)
- 类型 :
BatchEncoding(继承自 Python 的dict)。 - 本质 :它是一个字典,但被封装成了一个强大的对象。
- 包含的键(Keys) :
input_ids:Token ID 列表(模型最核心的输入)。attention_mask:注意力掩码(告诉模型哪些是 padding,不要关注)。token_type_ids:(可选)句子类型 ID。- 其他你通过
return_xxx=True请求的字段。
BatchEncoding的核心能力 :- 它像字典一样支持
inputs["input_ids"]。 - 它支持属性访问
inputs.input_ids。 - 它可以直接解包传给模型 :
model(**inputs),因为它实现了字典解包协议。 - 它自带
.to("cuda")方法,可以一键把所有内部的 Tensor 转移到 GPU 上。 - 它自带
.char_to_token()和.word_to_tokens()方法 :当开启了return_offsets_mapping或使用is_split_into_words时,可以通过这两个方法直接将原始字符索引或单词索引映射到 token 索引,这在 NER/QA 后处理中比手动解析 offset_mapping 更安全便捷。 - 它自带
.tokens()方法:可以快速查看某条样本编码后的 token 字符串列表,方便调试。
- 它像字典一样支持
五、常用操作与代码示例
- 最基础的单句编码(日常推理)
python
# 返回 PyTorch Tensor,自动填充,自动截断到 512
inputs = tokenizer(
"你好,世界!",
return_tensors="pt",
padding=True,
truncation=True,
max_length=512
)
# inputs 包含: {'input_ids': tensor(), 'attention_mask': tensor()}
# 直接喂给模型
outputs = model(**inputs)
- 批量编码(Batching)
python
sentences = ["第一句话很短", "第二句话稍微长一点点,需要被填充"]
inputs = tokenizer(
sentences,
return_tensors="pt",
padding=True, # 短句子会被填充到和长句子一样长
truncation=True,
max_length=512
)
- 句对编码(用于相似度、分类任务)
python
# 比如 BERT 的 NLI 任务
inputs = tokenizer(
text="一个男人在踢足球",
text_pair="一个男人在做运动",
return_tensors="pt",
truncation=True
)
# 内部会自动拼接为: [CLS] 一个男人在踢足球 [SEP] 一个男人在做运动 [SEP]
# 并生成 token_type_ids: [0,0,0,0,0,0,0, 1,1,1,1,1,1, 1]
- 获取字符级偏移(用于 NER / 抽取式 QA)
python
# 必须是 Fast Tokenizer 才支持 return_offsets_mapping
text = "Apple 是一家科技公司"
inputs = tokenizer(
text,
return_tensors="pt",
return_offsets_mapping=True
)
# 方式一:手动解析 offset_mapping
offset = inputs["offset_mapping"][0][1].tolist() # 跳过 BOS
print(text[offset[0]:offset[1]]) # 输出: "Apple"
# 方式二【推荐】:使用 BatchEncoding 内置方法
token_idx = inputs.char_to_token(0, char_index=0) # 获取字符' A'对应的token索引
print(inputs.tokens()[token_idx]) # 输出: "Apple"
- 滑动窗口处理超长文本
python
# 当文本有 2000 个 token,但模型 max_length 只有 512
inputs = tokenizer(
very_long_text,
return_tensors="pt",
truncation=True,
max_length=512,
stride=128, # 每次滑动 128 个 token
return_overflowing_tokens=True # 返回被截断的剩余部分
)
# inputs["input_ids"] 会变成一个包含多个 512 长度片段的 Batch
# inputs["overflow_to_sample_mapping"] 会告诉你这些片段属于原始的第几个句子
六、补充:__call__ 与 encode 的核心区别
很多初学者会混淆 tokenizer(text) 和 tokenizer.encode(text),这里做一个明确对比:
| 特性 | __call__() (即 tokenizer(text)) |
encode() (即 tokenizer.encode(text)) |
|---|---|---|
| 返回值 | BatchEncoding (字典,含多个字段) |
list[int] (纯粹的 ID 列表) |
| Padding | ✅ 支持 | ❌ 不支持 |
| Return Tensors | ✅ 支持 (直接转 pt/tf) |
❌ 不支持 (只能返回 Python list) |
| Attention Mask | ✅ 自动生成 | ❌ 不生成 |
| Batch 处理 | ✅ 完美支持 | ⚠️ 仅支持单条(需手写循环) |
| 使用场景 | 直接喂给模型(99% 的场景) | 只需要 ID(如计算 token 数量、自定义拼接) |
终极建议 :只要你的下一步操作是把数据喂给 model(),永远使用 __call__()。