本文主要是学习[动手写 bert 系列] 02 tokenizer encode_plus, token_type_ids(mlm,nsp)专栏第二个视频的知识归纳总结。

up主源代码链接

1. BERT Tokenizer 基础知识

1.1 Tokenizer 初始化

from transformers import BertTokenizer
model_name = 'bert-base-uncased'
tokenizer = BertTokenizer.from_pretrained(model_name)
  • BertTokenizer 是专门为 BERT 模型设计的tokenizer

  • bert-base-uncased 是一个预训练模型,特点:

    • "base":基础规模(12层,768隐藏层维度)

    • "uncased":不分大小写,所有文本都会转为小写

    • 词汇表大小:30522个token

    • 最大序列长度:512个token

 1.2 Tokenizer 的特殊token

这个上一节视频也有介绍,这节视频介绍得更细致一些。

tokenizer.special_tokens_map
# 输出:
{'unk_token': '[UNK]',
 'sep_token': '[SEP]',
 'pad_token': '[PAD]',
 'cls_token': '[CLS]',
 'mask_token': '[MASK]'}
# 将特殊token转换为对应的ID
tokenizer.convert_tokens_to_ids(special_tokens)
# 输出:[100, 102, 0, 101, 103]
  1. [CLS](分类token):

    • 位置:总是出现在序列开头

    • 作用:用于分类任务的聚合表示

    • 在BERT预训练中,CLS位置用于下一句预测(NSP)任务

  2. [SEP](分隔token):

    • 作用:分隔两个句子

    • 在单个句子中也存在,标记句子结束

    • 对于句子对任务,[SEP]分隔两个句子

  3. [PAD](填充token):

    • 作用:将不同长度的序列填充到相同长度

    • ID为0,便于在注意力机制中屏蔽

  4. [UNK](未知token):

    • 作用:表示词汇表中不存在的词

    • 当遇到未登录词(OOV)时使用

  5. [MASK](掩码token):

    • 作用:用于掩码语言建模(MLM)任务

    • 在预训练中,随机掩盖部分输入token

  • 特殊tokenID对应关系:[UNK] → 100  [SEP] → 102 [PAD] → 0 [CLS] → 101 [MASK] → 103

1.3 编码和解码过程

# 编码特殊token
tokenizer.encode(special_tokens)
# 输出:[101, 100, 102, 0, 101, 103, 102]

# 解码回文本
tokenizer.decode([101, 100, 102, 0, 101, 103, 102])
# 输出:'[CLS] [UNK] [SEP] [PAD] [CLS] [MASK] [SEP]'
  1. tokenizer.encode() 自动添加了 [CLS] 和 [SEP]

  2. 实际编码结果是:[CLS] + 特殊token列表 + [SEP]

  3. 注意有两个 [CLS] 和两个 [SEP],这是因为输入列表中包含这些token,tokenizer又自动添加了它们

2. 20 Newsgroups 数据集

作为了解即可,不是学习的重点内容。

2.1 数据集加载

from sklearn.datasets import fetch_20newsgroups
newsgroups_train = fetch_20newsgroups(subset='train')

数据集特点:

  • 来源:20个不同的新闻组(讨论组)

  • 数据量:训练集11314个文档

  • 类型:文本分类数据集

  • 类别数:20类

2.2 数据结构

# 数据类型
type(newsgroups_train.data)  # list

# 数据量
len(newsgroups_train.data)   # 11314
len(newsgroups_train.target) # 11314(与数据一一对应)

# 查看类别分布
from collections import Counter
Counter(newsgroups_train.target)

类别分布分析:

  • 大部分类别有约600个样本

  • 最少的类别有377个样本(索引19)

  • 最多的类别有600个样本(索引10)

  • 整体相对平衡,但有一定差异

2.3 类别名称

newsgroups_train.target_names

20个类别分为4个大组:

  1. 计算机相关(comp.):comp.graphics:计算机图形学-windows.misc:Windows操作系统comp.sys.ibm.pc.hardware:IBM PC硬件comp.sys.mac.hardware:Mac硬件comp.windows.x:X Window系统

  2. 休闲娱乐(rec.):rec.autos:汽车rec.motorcycles:摩托车rec.sport.baseball:棒球

    rec.sport.hockey:曲棍球
  3. 科学技术(sci.):sci.crypt:密码学sci.electronics:电子学sci.med:医学 sci.space:太空

  4. 讨论话题(talk.,soc.,alt.):talk.politics.guns:政治-枪支talk.politics.mideast:政治-中东talk.politics.misc:政治-杂项talk.religion.misc:宗教-杂项soc.religion.christian:社会-基督教alt.atheism:无神论misc.forsale:二手交易

3. Tokenizer 高级功能

本节的重点学习内容!

3.1 核心概念

input_ids:token在词汇表中的索引,将文本转换为模型可理解的数字序列。

attention_mask:二进制掩码,指示哪些位置是真实token,哪些是padding。1表示真实token,0表示padding token。防止padding token影响注意力计算

token_type_ids(或segment_ids):用于区分句子对中的两个句子。第一个句子标记为0,第二个句子标记为1。在单句任务中,所有token_type_ids都是0。(之前没有提到的重点内容)

tokenizer 的核心方法对比:

方法 输入类型 输出 特点
encode_plus() 单个文本/句子对 完整编码字典 功能最全,可控制所有参数
__call__() 单文本、列表或批处理 完整编码字典 推荐用法,支持批处理
encode() 单个文本 token ID列表 只返回input_ids
batch_encode_plus() 文本列表 批处理的编码字典 已弃用,用__call__替代

encode_plus 是一个功能全面的文本编码方法,但在新版Transformers中,建议直接使用 tokenizer() 方法,它提供了相同的功能且接口更统一。无论是处理单句、句子对还是批量数据,都能灵活应对各种NLP任务的输入需求。

3.2 单句编码

test_news = newsgroups_train.data[:3]
len(test_news[2])  # 1981个字符

# 单句编码
result = tokenizer(test_news[0], truncation=True, max_length=32)

参数详解:

  • truncation=True:如果序列超过最大长度,则截断

  • max_length=32:设置最大序列长度

  • 自动添加的token:[CLS] 和 [SEP]

输出结果包含三个关键字段:

  1. input_ids:token ID序列

  2. token_type_ids:全为0(单句任务)

  3. attention_mask:全为1(没有padding)

3.3 句子对编码

# 句子对编码
result_pair = tokenizer.encode_plus(
    text=test_news[0],      # 第一个句子
    text_pair=test_news[1], # 第二个句子
    max_length=32,
    truncation=True
)

encode_plus 输出详解:

  1. input_ids结构

    • [CLS] + 句子1 + [SEP] + 句子2 + [SEP]

    • 示例:[101, 2013, ..., 102, 2013, ..., 102]

  2. token_type_ids

    • 句子1的token:全部为0

    • 句子2的token:全部为1

    • 清晰区分两个句子

  3. attention_mask

    • 所有真实token为1

3.4 截断策略

  1. 'longest_first'(默认):

    • 从较长的句子开始移除token

    • 直到满足最大长度要求

    • 对于句子对,优先截断较长的那个句子

  2. 'only_first'

    • 只截断第一个句子

    • 第二个句子保持不变

  3. 'only_second'

    • 只截断第二个句子

  4. 'do_not_truncate'

    • 不截断,如果超过最大长度会报错

3.5 解码示例

# 解码示例
decoded_text = tokenizer.decode(result_pair['input_ids'])
# 输出:'[CLS] from : lerxst @ wam. umd. edu [SEP] from : guykuo @ carson. u. washington. edu [SEP]'
  1. 可以看到完整的结构:[CLS] + 句子1 + [SEP] + 句子2 + [SEP]

  2. 电子邮件地址被分割:guykuo @ carson . u . washington . edu

  3. BERT tokenizer使用WordPiece分词,将复杂词分解为子词

4. 实际应用注意事项

4.1 文本预处理

  1. 大小写处理

  • uncased模型会自动转为小写
  • cased模型保留原始大小写
  1. 特殊字符

  • BERT tokenizer能处理各种标点符号
  • 网址、电子邮件等会被适当分割
  1. 中文处理

  • 对中文按字切分
  • 每个中文字符作为独立的token

4.2 BERT预训练任务相关

MLM(掩码语言建模)
  • 随机掩盖输入token(15%概率)

  • 被掩盖的token中:

    • 80%替换为[MASK]

    • 10%替换为随机token

    • 10%保持不变

  • 模型预测被掩盖的原始token

NSP(下一句预测)
  • 输入:两个句子A和B

  • 任务:判断B是否是A的下一句

  • 50%概率是真实下一句,50%是随机句子

  • 使用[CLS]位置的输出进行分类

5. 总结

BERT tokenizer 可以理解为 BERT 模型处理文本时的“翻译官”——它负责把我们的文字翻译成模型能看懂的数字。既要处理好过长文本的截断,又要给短文本补位填充;既能处理单个句子,也能处理两个句子组合的任务;而且还完美契合了 BERT 训练时的掩码预测这些任务需求。在实际用起来的时候,掌握好 tokenizer 的各种小技巧,无论是整理数据、准备输入,还是优化效果、调试问题,都能帮你省不少心。用好这个“翻译官”,你的模型才能更好地理解你想让它理解的东西。

学习不是赛跑,是和自己一起的成长旅行。你走得每一步,都算数。 🌱一起加油吧!

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐