Tokenizer 深度解析:以情感分析为例解剖Tokenizer的使用逻辑与操作细节。
本文主要是学习五道口纳什[动手写bert系列] 01 huggingface tokenizer (vocab,encode,decode)第一个视频的知识归纳总结。关于如何复现up代码可以参照我的上一篇。五道口纳什[动手写bert系列] 01 huggingface tokenizer (vocab,encode,decode)原理及细节-小白复现总结
刚开始听up主视频确实有点懵,补点基础再看就很清晰了。后面自己总结分析确实感受到up主可能讲解不是很有逻辑顺序,不过内容还是很清晰很细节的。
up主要以情感分析(判断一个句子是积极(positive)还是消极(negative))为例讲解Tokenizer的使用逻辑与操作细节。我的总结也主要围绕up主的讲解进行个人的理解总结描述。up主的一个总体实现架构流程是:1. tokenizer, 构造输入2. model,调用模型3. parse output,输出解析。up主重点讲Tokenize所以只用模型推理不涉及训练微调过程。
文本输入 → Tokenizer处理 → 模型输入 → 模型推理 → 输出解析 → 最终结果
接着我们看到模型选择这里:
1. tokenizer, 构造输入
细节1:model_name = 'distilbert-base-uncased-finetuned-sst-2-english'我们为什么要选用这个模型呢?
因为这个模型适用于情感分类任务!(1)我们可以在huggingface官网上查看了解模型适用什么任务,看见它是Text classification排名比较高的模型。(2)使用Transformer的Pipeline调用快速选用对应任务适用的模型。(注:pipeline是🤗Hugging Face Transformers库提供的一个高级API,它让使用预训练模型进行推理变得极其简单。它的核心作用是将复杂的NLP任务封装成一行代码就能调用的接口。)
下面展示的意思就是用当我们没有指定模型来解决我们给它的情感分析任务的时候,pipeline就会默认选择distilbert-base-uncased-finetuned-sst-2-english这个模型,官方都认定这个模型适用于情感分析了,我们用这个肯定没有问题。

关键点1:Tokenizer 要和model相匹配
Tokenizer必须和Model相匹配,因为它们共同构成一个完整的预训练系统:tokenizer负责将文本按特定规则拆分成词汇表(vocabulary)中的ID序列,而模型嵌入层正是基于该词汇表的ID映射来查找对应的向量表示。如果错配,会导致词汇表ID错乱、分词策略不一致(如BERT的WordPiece与GPT的BPE拆分方式不同)、特殊符号(如[CLS]、[SEP])无法识别等问题,从而让模型处理错误的输入表示,输出毫无意义的结果。
那怎么样让Tokenizer 要和model相匹配呢?
Auto* 类会自动处理匹配问题,让你不需要手动协调。
#自动匹配Auto*Tokenizer, AutoModel*:Generic type
from transformers import AutoTokenizer, AutoModelForSequenceClassification
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name)
这里就是up代码里面写的根据模型名字自动选择匹配模型和分词。
前面都是一些使用细节下面来到重点Tokenizer :
Tokenizer 的核心功能:服务 Model Input
test_sentences = ['today is not that bad', 'today is so bad', 'so good']
# 批量处理
batch_input = tokenizer(
test_sentences,
truncation=True, # 截断过长文本
padding=True, # 填充短文本
return_tensors='pt' # 返回PyTorch张量
)
print(batch_input)
这里就是用分词结果来作为模型输入,上面是最常用的调用方式。

-
input_ids: 转换后的token ID序列 -
attention_mask: 注意力掩码,1表示真实token,0表示padding
Tokenizer的三种调用方式:
# 完整演示tokenizer的三种调用方式
test_sentences = ['today is not that bad', 'today is so bad', 'so good']
# 方式1:直接调用(最常用)
result1 = tokenizer(test_sentences[0])
print("方式1 - 直接调用:")
print(f" 返回类型: {type(result1)}") # <class 'transformers.tokenization_utils_base.BatchEncoding'>
print(f" 内容: {result1}")
# 输出: {'input_ids': [101, 2651, 2003, 2025, 2008, 2919, 102], 'attention_mask': [1, 1, 1, 1, 1, 1, 1]}
# 方式2:encode(只获取input_ids)
result2 = tokenizer.encode(test_sentences[0])
print("\n方式2 - encode:")
print(f" 返回类型: {type(result2)}") # <class 'list'>
print(f" 内容: {result2}")
# 输出: [101, 2651, 2003, 2025, 2008, 2919, 102]
# 方式3:tokenize + convert_tokens_to_ids(最底层)
tokens = tokenizer.tokenize(test_sentences[0])
print("\n方式3 - 分步处理:")
print(f" tokens: {tokens}") # ['today', 'is', 'not', 'that', 'bad']
ids = tokenizer.convert_tokens_to_ids(tokens)
print(f" ids: {ids}") # [2651, 2003, 2025, 2008, 2919]
-
tokenizer()返回完整的字典结构 -
tokenizer.encode()仅返回input_ids -
tokenizer.tokenize()返回token列表,不包含特殊token
Tokenizer 的四个核心功能
功能一:词汇映射(Vocab Mapping)
Tokenizer 的核心是一个词汇表(vocab),存储了 token 到 id 的映射关系
# tokenizer.vocab 的本质
print(type(tokenizer.vocab)) # <class 'collections.OrderedDict'>
print(len(tokenizer.vocab)) # 30522(词汇表大小)
# 实际映射示例
print(f"单词 'today' → ID: {tokenizer.convert_tokens_to_ids('today')}")
print(f"ID 2651 → 单词: {tokenizer.convert_ids_to_tokens(2651)}")
功能二:特殊Token处理
# 完整特殊token体系
special_tokens = {
'cls_token': tokenizer.cls_token, # [CLS] - 分类任务起始标记
'sep_token': tokenizer.sep_token, # [SEP] - 分隔标记
'pad_token': tokenizer.pad_token, # [PAD] - 填充标记
'unk_token': tokenizer.unk_token, # [UNK] - 未知词标记
'mask_token': tokenizer.mask_token # [MASK] - 掩码标记
}
# 对应的ID映射
special_ids = {
name: tokenizer.convert_tokens_to_ids(token)
for name, token in special_tokens.items()
}
# 输出: {'cls_token': 101, 'sep_token': 102, 'pad_token': 0, ...}
功能三:序列规范化处理
# padding逻辑:补齐到最长序列或指定长度
sentences = ['short', 'medium length', 'this is a very long sentence']
# 自动padding
batch1 = tokenizer(sentences, padding=True)
# 所有序列长度变为其中最长序列长度
# 指定最大长度padding
batch2 = tokenizer(sentences, padding='max_length', max_length=10)
# 所有序列长度变为10
# truncation逻辑:截断超长序列
batch3 = tokenizer(sentences, truncation=True, max_length=5)
# 第三个句子被截断
功能四:生成注意力掩码(Attention Mask)
"""
注意力掩码的核心作用:
1. 区分真实token和padding token
2. 告诉模型哪些位置需要计算注意力
3. 确保padding位置不会影响模型计算
"""
# 注意力掩码的生成逻辑
def generate_attention_mask(input_ids, pad_token_id=0):
"""
input_ids: [batch_size, seq_len]
返回: 与input_ids相同形状的mask,1表示真实token,0表示padding
"""
mask = (input_ids != pad_token_id).astype(int)
return mask
# 实际使用中的验证
batch = tokenizer(['hello world', 'hi'], padding=True, return_tensors='pt')
print("input_ids:", batch['input_ids'])
print("attention_mask:", batch['attention_mask'])
# 确保: len(input_ids[i]) == len(attention_mask[i]) 对所有i成立
len(input_ids) == len(attention_mask) 的重要性在于确保输入序列与注意力掩码在长度上严格对齐,这是模型正确理解有效内容与填充位置(padding)的基础。
若长度不一致,会导致掩码无法准确区分真实 token 与填充 token,可能引发注意力权重计算错误、信息泄露(如模型将填充位置误认为有效信息)或运行时维度错误,从而破坏训练和推理的稳定性与有效性。保持二者长度一致是保证注意力机制正常工作的前提,也是数据处理和输入预处理的关键检查步骤。
Tokenizer 方法调用链的深度解析
# 方法层级关系图
"""
tokenizer.__call__()
│
┌─────────────────┼─────────────────┐
│ │ │
encode_plus() batch_encode_plus() encode()
│ │
│ tokenize() + convert_tokens_to_ids()
│
┌───┴─────┐
│ │
build_inputs_with_special_tokens() + create_token_type_ids() + ...
"""
# 底层调用关系
class TokenizerWorkflow:
def __call__(self, text, **kwargs):
# 1. 分词
tokens = self.tokenize(text)
# 2. 添加特殊token
tokens = self.build_inputs_with_special_tokens(tokens)
# 3. 转换为ID
ids = self.convert_tokens_to_ids(tokens)
# 4. 构建注意力掩码
attention_mask = [1] * len(ids)
# 5. 处理padding(如果需要)
if kwargs.get('padding', False):
ids, attention_mask = self.pad_sequence(ids, attention_mask)
return {'input_ids': ids, 'attention_mask': attention_mask}
后面的一些内容就不是本期的重点了,但也有一些干货在里面就大概讲一下。
2.Model:模型推理与配置
模型配置查看
print(model.config)

关键配置项:
-
id2label: ID到标签的映射 -
label2id: 标签到ID的映射 -
vocab_size: 词汇表大小 -
max_position_embeddings: 最大序列长度
import torch
import torch.nn.functional as F
with torch.no_grad(): # 禁用梯度计算,节省内存
outputs = model(**batch_input)
print(outputs)
模型输出:
-
loss: 训练时的损失值(推理时为None) -
logits: 原始预测分数 -
hidden_states: 隐藏层状态(可选) -
attentions: 注意力权重(可选)
3.输出解析:从Logits到标签
Softmax归一化
# 将logits转换为概率分布
scores = F.softmax(outputs.logits, dim=1)
print(scores)
# tensor([[8.4632e-04, 9.9915e-01],
# [9.9980e-01, 1.9531e-04],
# [1.5837e-04, 9.9984e-01]])
获取预测标签
# 获取最高概率的类别
labels = torch.argmax(scores, dim=1)
print(labels) # tensor([1, 0, 1])
# 转换为文本标签
text_labels = [model.config.id2label[id] for id in labels.tolist()]
print(text_labels) # ['POSITIVE', 'NEGATIVE', 'POSITIVE']
4.总结
这次的情感分析任务带我们走了一遍从原始文本到最终分类结果的完整流程:Tokenizer 负责把句子整理成模型能“吃”的格式(比如统一长度、截断太长的句子,再配上 attention_mask 标记有效内容),模型输出 logits 后,经过 Softmax 和 argmax 一步步变成我们看得懂的情感标签。其中,Tokenizer 的作用尤为关键,算是整个流程的“第一道关卡”。希望这段梳理对你有帮助,学习路上继续加油,一起探索更多 NLP 的有趣世界吧!

更多推荐


所有评论(0)