tokenizer

什么是 Tokenizer?

Tokenizer(分词器)可以将原始文本(raw text)转换为模型能够理解的数字序列,在模型输入和输出的两个主要阶段中发挥重要作用:

模型输入(编码 Encode)阶段

  1. 分词(Tokenize)

    将文本拆分为词元(Token),常见的分词方式包括字级、词级、子词级(如 BPE、WordPiece)、空格分词等。

    1
    2
    输入: "你好"
    分词: ["你", "好"]
  2. 映射(Mapping)

    将每个词元映射为词汇表中的唯一 ID,生成的数字序列即为模型的输入。

    1
    2
    分词: ["你", "好"]
    映射: [1001, 1002]

模型输出(解码 Decode)阶段

  1. 反映射(De-mapping)

    模型输出的数字序列通过词汇表映射回对应的词元,二者是一一对应的关系。

    1
    2
    输出: [1001, 1002]
    反映射: ["你", "好"]
  2. 文本重组

    将解码后的词元以某种规则重新拼接为完整文本。

    1
    2
    反映射: ["你", "好"]
    重组: "你好"

直观感受

访问 Tiktokenizer,通过右上角选取不同的 Tokenizer 进行尝试

词汇表

两种常见的构建词汇表的方法:

  • BPE(Byte-Pair Encoding):用于 GPT、GPT-2、RoBERTa、BART 和 DeBERTa 等模型。
  • WordPiece:用于 DistilBERT、MobileBERT、Funnel Transformers 和 MPNET 等模型。

BPE

BPE(Byte Pair Encoding,字节对编码)在 NLP 里是一种贪心式的子词(subword)分词算法
理解:从“字符”开始,反复把出现次数最多的相邻字符对合并成新的符号,并加入词汇表,直到达到预设的词汇表大小。

为什么可以处理 OOV(Out-Of-Vocabulary)情况

因为所有词汇都是由字符或词根组成的,通过对单个字符的学习,可以组成oov的词汇

为什么需要词汇表

编码时,从文本到模型:需要将文本分词为 Tokens,再通过词汇表将 Tokens 转换为 Token IDs,再传给transformer

解码时,从模型到文本:需要通过词汇表Token IDs 转换为 Tokens,再把Tokens 拼接为文本

步骤
  1. 初始化词汇表 $V$
    • $V$ 包含语料库中的所有唯一字符,即单词字符的集合。
  2. 统计字符对的频次
    • 对于每个单词的字符序列,统计相邻字符对的出现频次。
  3. 找到频次(Score)最高的字符对并合并
    • 选择出现频率最高的字符对 $(x, y)$,将其合并为新符号 $xy$。
  4. 更新词汇表并重复步骤 2 到 4
    • 将新符号添加到词汇表 $V = V \cup {xy}$。
    • 更新语料库中的单词表示,重复统计和合并过程,直到满足停止条件(例如,词汇表达到预定大小)。

示例

我们需要将语料库(corpus)的文本拆分为单词,假设当前语料库包含的单词和对应频次如下:

1
("low", 5), ("lower", 2), ("newest", 6), ("widest", 3)

步骤 1:初始化词汇表

将单词拆分为字符序列

1
2
3
4
("l", "o", "w"), 5  
("l", "o", "w", "e", "r"), 2
("n", "e", "w", "e", "s", "t"), 6
("w", "i", "d", "e", "s", "t"), 3

词汇表 V

1
{'l', 'o', 'w', 'e', 'r', 'n', 's', 't', 'i', 'd'}

步骤 2:统计字符对的频次

1
2
3
4
5
6
7
8
9
10
11
12
字符对频次统计结果:
('l', 'o'): 7 # 5 (low) + 2 (lower)
('o', 'w'): 7 # 5 (low) + 2 (lower)
('w', 'e'): 8 # 2 (lower) + 6 (newest)
('e', 'r'): 2
('n', 'e'): 6
('e', 'w'): 6
('e', 's'): 9 # 6 (newest) + 3 (widest)
('s', 't'): 9 # 6 (newest) + 3 (widest)
('w', 'i'): 3
('i', 'd'): 3
('d', 'e'): 3

步骤 3:找到频次最高的字符对并合并

选择频次最高的字符对

  • ("e", "s")("s", "t"),频次均为 9。可以任选其一进行合并,假设选择排序第一的: ("e", "s")

合并 ("e", "s") 为新符号 es

记录合并操作

1
Merge 1: ("e", "s") -> "es"

步骤 4:更新词汇表并重复

更新单词序列

1
2
3
4
("l", "o", "w"), 5  
("l", "o", "w", "e", "r"), 2
("n", "e", "w", "es", "t"), 6
("w", "i", "d", "es", "t"), 3

更新词汇表 V

1
{'l', 'o', 'w', 'e', 'r', 'n', 's', 't', 'i', 'd', 'es'}

重复步骤 2 到 4,直到达到预定的词汇表大小

WordPiece

WordPiece 是 Google 在 2016 年为语音识别与 BERT 提出的子词(subword)分词算法,可看作 BPE 的“似然改进版”。理解:“用概率贪心而不是频次贪心,从字符开始逐步合并子词。”

与 BPE 不同,WordPiece 的 Score 由字符对频次与其组成部分频次的比值决定,定义 Score:

其中, $\text{freq}(x)$, $\text{freq}(y)$ 和 $\text{freq}(xy)$ 分别表示符号 $x$, $y$ 和它们合并后的符号 $xy$ 的频次。

步骤
  1. 初始化词汇表 $V$
    • 与 BPE 相同, $V$ 包含语料库中的所有唯一字符,但处理方式略有不同:对于每个单词,除了首个字符外,其他字符前都加上 ## 前缀。
  2. 统计字符对的频次及 Score
    • 对于每个可能的字符对 $(x, y)$,计算 $\text{freq}(x)$, $\text{freq}(y)$, $\text{freq}(xy)$,并计算 Score。
  3. 找到 Score 最高的字符对并合并
    • 选择 Score 最高的字符对 $(x, y)$,将其合并为新符号 $xy$,注意:
      • 如果第二个符号以 ## 开头,合并时去掉 ## 前缀再进行连接。
      • 新符号是否以 ## 开头,取决于第一个符号是否以 ## 开头。
  4. 更新词汇表并重复步骤 2 到 4
    • 将新符号添加到词汇表 $V = V \cup {xy}$。
    • 更新语料库中的单词表示,重复统计和合并过程,直到满足停止条件。

映射(Mapping)

以 BPE 为例,最终词汇表 $V$ 中的 Token 和对应的频次分别为:

1
2
3
4
5
6
7
8
9
10
vocab = {
'lo': 7,
'w': 16,
'e': 8,
'r': 2,
'n': 6,
'est': 9,
'i': 3,
'd': 3
}

输出

1
2
Token to ID: {'lo': 0, 'w': 1, 'e': 2, 'r': 3, 'n': 4, 'est': 5, 'i': 6, 'd': 7}
ID to Token: {0: 'lo', 1: 'w', 2: 'e', 3: 'r', 4: 'n', 5: 'est', 6: 'i', 7: 'd'}

当然,也可以根据频次或者其他规则进行特殊处理。

以上是编码部分的概述,实际上在文本预处理的时候还会增加特殊标记,但这些以及后续的解码部分大多是一些文本处理的规则,这里就不过多赘述了,Tokenizer 之间的核心差异在于使用的分割方法和词汇表的构建策略。

transformer中的分词

在 Transformers 中,分词(tokenization) 实际上包含以下几个步骤:

  1. 标准化(Normalization):对文本进行必要的清理操作,例如删除多余空格或重音符号、进行 Unicode 标准化等。
  2. 预分词(Pre-tokenization):将输入拆分为单词。
  3. 通过模型处理输入(Running the input through the model):使用预分词后的单词生成一系列词元(tokens)。
  4. 后处理(Post-processing):添加分词器的特殊标记,生成注意力掩码(attention mask)和词元类型 ID(token type IDs)。

流程图如下

image-20250808100051423

注意力掩码(Attention Mask)和词元类型 ID (Token Type IDs)是什么?

image-20250808100813881

1️⃣ 注意力掩码(Attention Mask)
• 目的:告诉模型“哪些位置可以被看到”,其余位置直接屏蔽。
• 典型场景:
自注意力里做 padding 掩码:把 <pad> 对应的位置设为 −∞,softmax 后权重=0。
解码器自回归掩码:生成任务用下三角掩码,避免第 i 个 token 看到未来 token。

2️⃣ 词元类型 ID(Token Type IDs,也叫 Segment IDs)
• 目的:区分同一次输入里不同句子或段落,让模型知道“这段属于 A,那段属于 B”。
• 典型场景:
– BERT 做句子对分类(NSP):[CLS] 句子A [SEP] 句子B [SEP] → TypeID = 0 0 0 0 1 1 1。
– RoBERTa、GPT 等单句模型则不需要 Token Type IDs。

注意力掩码确保模型只关注实际的词元,忽略填充部分,从而避免无效的计算:

  • 1:表示模型应关注的词元(Tokens)
  • 0:表示模型应忽略的词元(通常是填充 padding 的部分)。

词元类型 ID 用于区分输入中的不同句子或段落:

  • 0:表示第一个句子的词元。
  • 1:表示第二个句子的词元。

CLS,SEP,PAD都是什么意思

[CLS](Classification),作用:对应位置的隐藏状态被当作整句/句对的“整体表示”,用来接分类头做句子级任务(情感分类、NLI 等)。

[SEP](Separator),作用:让模型知道分段 / 句子边界,配合 Token Type IDs 区分句子 A 和句子 B。

[PAD](padding token)的作用是 批量训练时把不同长度的序列补齐到同一长度,让张量可以堆叠成规整的矩阵;模型在计算注意力时通过 Attention Mask 把 [PAD] 对应的位置屏蔽掉,不让它们影响有效 token 的表示。

rag embedding与llm embedding

image-20260519182742953

这个问题非常切中本质。虽然 RAG 的 Embedding 模型和 LLM(大语言模型)底层通常都基于 Transformer 架构,但它们在设计目的、输出形态以及训练范式上有着根本的分歧。

可以将 LLM 的 Embedding 视作“字词级的基础翻译官”,而 RAG 的 Embedding 则是“篇章级的语义指纹提取器”。

以下是它们核心不同点的深度剖析:

1. 核心目的与作用的不同

维度 LLM Embedding (如 Llama/GPT 的输入层) RAG Embedding (如 BGE, OpenAI text-embedding)
核心目的 将离散的 Token(字/词汇)转换为连续向量,为模型后续的自注意力机制计算做准备。 将一整段文本(句子/段落/文档)压缩成一个固定维度的向量,用于计算文本间的相似度。
表示层级 Token 级别(词元级)。 Chunk/Sentence 级别(段落/句子级)。
输出形态 输入 $N$ 个 Token,输出 $N$ 个向量。形状通常为 [Sequence_Length, Hidden_Dim] 输入 $N$ 个 Token 的段落,通过池化(Pooling)输出 1 个综合向量。形状为 [1, Hidden_Dim]
工程角色 是 LLM 内部的一个组件(权重矩阵 $V \times d$),不可独立拆解使用。 是一个独立的模型服务,专为检索系统(如向量数据库)提供特征提取。

2. 训练方式的区别 (The “How”)

它们之所以有上述的差异,根本原因在于训练目标(Loss Function)完全不同。

LLM Embedding 的训练:自监督预测 (Self-Supervised Learning)

LLM 的 Embedding 是在训练整个大模型时“顺带”训练出来的。

  • 训练目标: 典型的生成式模型(如 GPT 系列)使用的是下一个词预测(Next-Token Prediction)
  • 训练过程: 模型读取一段上文,尝试预测下一个 Token 是什么。系统计算预测概率分布与真实下一个 Token 之间的交叉熵损失(Cross-Entropy Loss),然后通过反向传播更新网络中所有的参数,包括最底层的 Embedding 矩阵。
  • 结果特性: 这种训练使得 LLM 的 Embedding 极度擅长捕捉语法结构、词性关联以及上下文搭配。它知道“苹果”和“手机”在某些语境下经常一起出现。

RAG Embedding 的训练:对比学习 (Contrastive Learning)

RAG 使用的 Embedding 模型(通常是类似 BERT 的编码器架构)需要经历专门的对比学习训练,这是它具备强大检索能力的关键。

  • 训练目标: 并非预测下一个词,而是拉近相似文本的距离,推远不相关文本的距离

  • 训练过程: 通常采用 InfoNCE Loss 等对比损失函数。训练数据需要组织成三元组:(Query, Positive_Document, Negative_Document)

    • Query (锚点): 用户的查询语句。

    • Positive (正样本): 真正能回答该查询的文档段落。

    • Negative (负样本): 干扰项,特别是“困难负样本(Hard Negatives)”——那些字面上有重合但语义不相关的文档。

    • 数学直觉: 优化目标是最大化正样本对的余弦相似度,同时最小化负样本对的相似度:

  • 结果特性: 这种训练强制模型放弃局部的语法细节,去提取宏观的中心思想和语义匹配度

总结来说:

LLM Embedding 的使命是“理解字词的组合规律以生成文本”,因此采用自监督的序列预测训练;而 RAG Embedding 的使命是“判断段落之间的语义等价性以实现精准检索”,因此必须依赖高质量正负样本的对比学习训练。

参考资料

AI-Guide-and-Demos-zh_CN/Guide/21. BPE vs WordPiece:理解 Tokenizer 的工作原理与子词分割方法.md at master · Hoper-J/AI-Guide-and-Demos-zh_CN