找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

774

积分

0

好友

102

主题
发表于 11 小时前 | 查看: 0| 回复: 0

本文将以 bert-base-chinese 模型为例,深入探讨自然语言处理中的核心组件——Tokenizer。我们将从它的基本作用、内部机制一直讲到主流的分词类型,帮助你由浅入深地理解模型是如何“读懂”文本的。

一、BERT-base-chinese 模型简介

bert-base-chinese 是 Google 在 2018 年发布的原始 BERT 模型之一,专门针对中文文本进行预训练。它使用中文维基百科和通用文本语料,通过掩码语言建模(MLM)任务和下一句预测(NSP)任务训练而成。

选择这个模型为例有三个原因:

  1. 它的 tokenizer 设计相对直观,便于理解核心概念。
  2. 模型本身带有中文优化,能很好地体现中英文处理的差异。
  3. 其他 NLP 模型的 tokenizer 在实现和附加功能上可能有所不同,但主要目的是一致的,正所谓“换汤不换药”。

预训练任务

该模型主要包含两个预训练任务:

  • MLM(掩码语言建模)任务:输入一句带有掩码的句子,模型输出掩码位置可能字符的概率。
    Fill-Mask任务预测界面展示
  • NSP(下一句预测)任务:输入A、B两句话,判断B是否是A的下一句,本质上是一个二分类(是/不是)任务。

    tokenizer = BertTokenizer.from_pretrained(model_path)
    model = BertForNextSentencePrediction.from_pretrained(model_path)
    text_A = "今天天气很好"
    text_B = "我们一起去公园玩"
    encoding = tokenizer(text_A, text_B, return_tensors='pt')
    outputs = model(**encoding)
    probs = torch.softmax(outputs.logits, dim=-1)
    if torch.argmax(probs) == 0:
      print("B是A的下一句")
    else:
      print("B不是A的下一句")
    >> B是A的下一句

支持的下游任务

除了预训练任务,它还支持多种下游任务:

  • SC(序列分类):如文本分类、情感分析、自然语言推理(NLI)。
  • TC(Token分类):如命名实体识别(NER)、词性标注(POS)。
  • QA(问答):如抽取式问答(SQuAD)。
  • MC(多项选择):从多个候选答案中选择一个正确答案。

二、为什么需要 Tokenizer?

NLP模型在训练和推理时,真正“喂进去”的都是数字——即 token ids。当然,模型可能还需要其他输入特征,例如在BERT中:

  • token type ids
  • attention mask
  • position index

Tokenizer 的核心职责,就是决定如何将原始文本转换成这些 token ids 及其他输入特征。 所有模型都需要 token ids,但生成它们的方式却可以多种多样。

三、Tokenizer 做了什么?

可以把 Tokenizer 的工作拆解为以下几个核心作用:

作用 1:分词 (Tokenization)

Tokenizer 的第一步是通过词表 (Vocabulary) 把自然语言文本切分成最小的语义单元,即 token。词表是一个包含了所有已知 token 及其对应 ID 的映射表。

这个过程很像人类学习语言:从单字到词语,再到句子和篇章。因此,把文本合理地分成 token 至关重要。

让我们用代码实验一下:

# 生成一个 bert-base-chinese 的 tokenizer
tokenizer = AutoTokenizer.from_pretrained("./bert-base-chinese/")

# 测试中文
text = " 今天天气不错。 "
tokens = tokenizer.tokenize(text)
print(tokens)
>> ['今', '天', '天', '气', '不', '错', '。']

# 测试英文
text = "today is cloudy."
tokens = tokenizer.tokenize(text)
print(tokens)
>> ['today', 'is', 'cloud', '##y', '.']

可以看到,中文被分成了7个「单字」和「标点」。而英文单词“cloudy”被拆分成了 “cloud”“##y”,这里的 “##” 表示该子词是某个单词的一部分。

提示:分词前通常会有预处理,例如实验中文本首尾的空格就被去掉了。


作用 2:Tokens 映射成 Token IDs

Tokenizer 通过查询词表,将上一步得到的 tokens 映射成对应的 token ids。这是最核心的一步。

tokenizer = AutoTokenizer.from_pretrained("./bert-base-chinese/")
ids = tokenizer.encode("今天天气不错。", add_special_tokens=False)
print(ids)
>> [791, 1921, 1921, 3698, 679, 7231, 511]

提示:这个 token id 就是 token 在词表中的序号。


作用 3:特殊 Token 组装

映射成基础 token ids 后,还需要加入一些具有特殊功能的 token ids。

tokenizer = AutoTokenizer.from_pretrained("./bert-base-chinese/")
ids = tokenizer.encode("今天天气不错。", add_special_tokens=True)
print(ids)
>> [101, 791, 1921, 1921, 3698, 679, 7231, 511, 102]

明明只有7个 tokens,却输出了9个 ids。让我们转回 tokens 看看:

# 再使用 convert_ids_to_tokens 转回 token 看下
tokens = tokenizer.convert_ids_to_tokens(ids)
print(tokens)
>> ['[CLS]', '今', '天', '天', '气', '不', '错', '。', '[SEP]']

原来前后各多了一个 [CLS][SEP],这就是特殊 token。在 BERT 模型中,常见的特殊 token 包括:

  • [MASK]:用于预训练的掩码占位符。推理时也可使用,例如输入“今天的天气[MASK]好。”,模型会预测[MASK]位置最可能的字。
    MASK位置预测概率展示

  • [CLS]:分类标记。通常位于序列开头,其最终的隐藏状态被用作整个序列的聚合表示,供下游分类任务使用。
    CLS特殊标记在序列中的位置

  • [SEP]:分隔符。用于区分句子对中的不同句子,例如在NSP或NLI任务中。
    单句与句子对输入的序列结构对比

  • [UNK]:未知词标记。当遇到词表中不存在的词(OOV, Out-of-Vocabulary)时使用。
    UNK标记处理未登录词示例

  • [PAD]:填充标记。在批量处理时,用于将不同长度的句子填充到同一长度,以便组成张量。
    使用PAD填充前后的序列对比

提示分词Tokens映射到Token IDs特殊Token组装,这三步一起被称为 Tokenizer 的 编码(Encode) 过程,它们都依赖于词表(Vocabulary)。
Tokenizer编码过程示意图


作用 4:生成注意力遮罩 (Attention Mask)

你可能会问,如果句子中本身就有 [PAD] 这个词怎么办?attention mask 就是用来解决这个问题的。它告诉模型在计算注意力时应该忽略哪些位置(通常是填充的位置)。

例如,对于句子“[PAD]是特殊的”:
Attention Mask机制示意图

在 Self-Attention 计算中,通过 attention mask 对被屏蔽位置(mask值为0)加上一个极小的负值,使得经过 Softmax 后这些位置的注意力权重趋近于0,从而阻止模型关注它们。如果你了解 Decoder-Only 模型,会发现因果掩码(Causal Mask)的原理与此类似。


作用 5:生成 Token 位置序号 (Position Index)

Self-Attention 机制本身不具备位置感知能力。因此,需要将位置向量(Position Embedding) 加到每个 token 的向量上。而 token position index 就是用来查找(Look Up)对应位置向量的索引。


作用 6:生成 Token Type ID

对于需要输入句子对的任务(如NSP),token type id 用来区分当前 token 属于第一个句子还是第二个句子。它用于查找分段向量(Segment Embedding),该向量也会被加到 token 的向量上。

第一句的所有 token 的 token type id0,第二句的所有 token 为 1
Token Type IDs区分句子对

提示生成 Token ID 序号(Position Index)生成 Token Type IDs生成 Attention Mask,这三步一起被称为 Tokenizer 的 输入特征化(Input Featureization)
Tokenizer输入特征化完整流程


作用 7:Token IDs 映射回 Tokens(解码)

通过词表将 token ids 映射回 tokens,这一步被称为 Tokenizer 的解码(Decode)。并非所有任务都需要这一步:

  • 需要解码的任务:MLM、TC、QA,它们的输出直接是 token ids。
  • 不需要解码的任务:SC、MC、NSP,它们的输出是类别标签 id。

提示:综上所述,BERT 模型的 Tokenizer 主要完成以下工作:

  • 分词:将句子切分为 tokens。
  • 映射与组装:将 tokens 映射为 token ids,并添加特殊 token ids(用于检索 Token Embedding)。
  • 生成位置信息:生成 position index(用于检索 Position Embedding)。
  • 生成句子信息:生成 token type id(用于检索 Segment Embedding)。
  • 生成注意力控制:生成 attention mask(用于在 Self-Attention 中屏蔽无意义的 token)。
  • 解码:将 token ids 映射回 tokens(并非所有任务必需)。

BERT模型MLM任务全局工作流程

四、Tokenizer 有哪些类型?

Tokenizer 是模型理解文本的“第一道工序”,其分词策略(“切割刀法”)决定了模型看到的是“词”、“字”还是“子词”,直接影响模型的认知方式。当我们讨论不同类型时,核心是在评估它们如何生成词表,因为这直接关系到两个根本问题:

  1. Token 的界定:最小的处理单元是什么?
  2. Vocabulary 的构建:如何从语料中生成一个大小固定且高效的词表?

提示:词表的 生成方式使用方式 共同影响着 Tokenizer。虽然理论上可以任意组合,但实践中存在一些自然适配的配对。我们主要关注生成方式

词级分词 (Word-based)

  • 界定与构建:最简单直观的方法。以空格/标点为界切分(英文按单词,中文按词语)。
    # “today is cloudy.” -> today/is/cloudy/.
    # “今天天气真好。” -> 今天/天气/真好/。
  • 问题
    • 对于英文,不同词形(如 cloud, cloudy)被视为独立词,导致词表膨胀。
    • 对于中文,分词本身是难题(如“南京市长江大桥”存在歧义),容易产生大量未登录词(OOV)。
  • 结论:这种方式在现代模型中已较少使用,因为要么词表巨大,要么OOV严重。

字符级分词 (Character-based)

  • 界定与构建:将文本切分为最小的书写单位(字母、汉字、标点)。
    # “today is cloudy.” -> t/o/d/a/y/ /i/s/ /c/l/o/u/d/y/.
    # “今天天气真好。” -> 今/天/天/气/真/好/。
  • 特点
    • 词表极小(英文几十个,中文几千个),基本无OOV问题。
    • 但丢失了显式的词语等语义单元信息,对模型学习能力要求极高(例如,模型需自行从上下文中学习“银行”和“行走”中“行”的不同含义)。

子词级分词 (Subword-based)

这是目前最主流的分词方式,旨在词级和字符级之间取得平衡。它将“词”拆分为有意义的“子词”单元

1. WordPiece

  • 使用模型:BERT, ALBERT, DistilBERT。
  • 构建方式:从字符开始,迭代合并那些能最大程度提升语言模型似然值的相邻符号对。
  • 特点
    • 有效缩减词表,保留较多语义信息(如token, ##ization能共享-ization后缀的语法信息)。
    • 能部分解决OOV问题(通过子词组合新词)。
    • 对于完全无法拆分的生僻词或特殊符号,仍会标记为[UNK]
      WordPiece分词原理示意图
      BERT实际分词效果示例

      提示:Google官方指出,bert-base-chinese对中文实际采用字符级(Character-based),对其他语言采用WordPiece,是一种混合策略。

2. BPE (Byte-Pair Encoding)

  • 使用模型:GPT-1, 早期机器翻译模型。
  • 构建方式:从字符开始,迭代合并出现频率最高的相邻符号对。
  • 特点
    • 词表颗粒度通常比WordPiece更细。
    • 对未知词的拆分能力更强(例如,unhappiness可能被拆为更细的字符序列)。
    • 但拆出的子串可能不够自然,语义信息较少。
      BPE与WordPiece分词对比

3. Byte-level BPE (BBPE)

  • 使用模型:GPT-2及以后系列、Qwen系列。
  • 构建方式:BPE的升级版,将“最小单位”从字符变为字节(通常采用UTF-8编码)。这意味着所有文本在最初都被视为字节序列。
    UTF-8编码字节表示
    BBPE分词结果示例
  • 特点
    • 彻底解决OOV问题:理论上可以表示任何能用UTF-8编码的字符,包括Emoji、罕见符号等。
    • 天然多语言友好
    • 词表可以更小。
    • 初始分割在字节级别,完全不保留显式的语言单元(词/字),所有语义依赖模型从海量数据中自行学习,因此对模型容量和数据规模要求极高,主要用于大语言模型(LLM)。

结语

Tokenizer 作为连接人类语言与机器计算的桥梁,其设计和选择深刻影响着模型的性能与效率。从基于规则的分词到基于统计的子词切分,再到完全基于字节的表示,演进历程反映了NLP领域追求更高泛化能力和多语言支持的努力。理解这些基础知识,是深入使用和调优各类Transformer架构模型的基石。

希望这篇文章能帮助你建立起对 Tokenizer 的系统认识。如果你想了解更多关于模型实现或开源社区中常见的Tokenization算法实现,欢迎在技术社区继续探索与交流。




上一篇:MySQL索引分类详解:数据结构、物理存储、字段特性与联合索引实战
下一篇:面试复盘:从JVM基础到ZGC实战,GC调优的三层架构方案与高频追问
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-28 18:09 , Processed in 0.273376 second(s), 43 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表