知乎这里有一篇不错的文章讲这块:https://zhuanlan.zhihu.com/p/460678461
基本介绍
deepseek给了一个基本的介绍:
好的!以下是对语言模型 Tokenizer 的详细解释,涵盖你的问题:
1. Tokenizer 的作用与使用方式
Tokenizer(分词器) 是语言模型的核心组件之一,负责将原始文本拆分成模型能处理的单元(Token),同时将 Token 转换为对应的数字 ID(或向量)。它的使用分两个方向:
- 预处理(Preprocessing):
将输入文本(如句子、段落)拆分成 Token,并转换为模型输入格式(如input_ids
)。 - 生成(Generation):
将模型输出的数字 ID 转换回可读文本。
具体步骤:
1. 文本清洗:去除特殊符号、统一大小写等。
2. 分词:按规则或算法拆分文本。
3. 编码:将 Token 映射到词汇表(Vocabulary)中的 ID。
4. 添加特殊标记:如 [CLS]
、[SEP]
(用于 BERT),<s>
、</s>
(序列起止符)等。
2. 分词级别:Word/Character/Subword
(1) Word-Level(词级分词)
- 原理:按空格或标点分割成独立单词(如英文直接按空格分,中文需要分词工具)。
- 优点:直观,保留语义。
- 缺点:
- 词汇表庞大(需覆盖所有可能的词),导致内存和计算成本高。
- 无法处理未登录词(OOV, Out-of-Vocabulary)。
- 例子:早期模型如 Word2Vec 使用词级分词。
(2) Character-Level(字符级分词)
- 原理:将文本拆分为单个字符(如字母、标点、汉字)。
- 优点:
- 词汇表极小(如英文仅需 26 字母 + 符号)。
- 天然支持所有词汇,无 OOV 问题。
- 缺点:
- 序列长度过长(如一个句子可能有数百个字符)。
- 难以捕捉语义和词间关系。
- 例子:早期 RNN 模型尝试过字符级分词。
(3) Subword-Level(子词级分词)
- 原理:将词拆分为更小的语义单元(如
"unhappiness"
→"un" + "happiness"
或"un" + "happy" + "ness"
)。 - 优点:
- 平衡词汇表大小与语义表达。
- 可处理未登录词(通过子词组合)。
- 缺点:需要复杂的分词算法。
- 例子:现代主流模型(如 BERT、GPT)均使用子词分词。
3. 常见子词分词算法
(1) BPE(Byte-Pair Encoding,字节对编码)
- 核心思想:通过合并高频的相邻符号对(如字母或字符)逐步构建词汇表。
- 步骤:
- 初始词汇表:所有基础字符(如 ASCII)。
- 统计所有相邻符号对的频率。
- 合并频率最高的符号对,加入词汇表。
- 重复步骤 2-3,直到达到目标词汇表大小。
- 特点:
- 基于频率,优先合并高频组合。
- 常用于 GPT 系列模型。
- 例子:
"low"
→["l", "ow"]
(如果"ow"
是高频对)。
(2) WordPiece
- 核心思想:类似 BPE,但合并策略基于概率(而非频率)。
- 步骤:
- 初始词汇表:所有基础字符。
- 计算合并每对符号后的语言模型概率增益(通过似然函数)。
- 合并增益最大的符号对。
- 重复直到达到目标词汇表大小。
- 特点:
- 优先合并对模型似然提升最大的符号对。
- 常用于 BERT 和 Transformer 系列模型。
- 例子:
"unhappy"
→["un", "##happy"]
(##
表示子词前缀)。
(3) ULM(Unigram Language Model)
- 核心思想:通过语言模型概率评估所有可能的分词方式,选择最优解。
- 步骤:
- 初始化一个大词汇表(如所有可能的子词)。
- 逐步删除对总概率影响最小的子词,缩小词汇表。
- 最终保留高频且重要的子词。
- 特点:
- 支持动态选择最佳分词方式。
- 更灵活,但计算成本较高。
- 例子:常用于 ALBERT 和部分多语言模型。
(4) SentencePiece
- 核心思想:将文本视为 Unicode 字符序列,直接训练子词模型,无需预分词。
- 特点:
- 支持 BPE 或 ULM 作为底层算法。
- 直接处理原始文本(包括空格和特殊符号)。
- 常用于 T5 和 XLNet。
4. 对比与总结
算法 | 核心策略 | 优点 | 典型模型 |
---|---|---|---|
BPE | 合并高频符号对 | 简单高效 | GPT-2, GPT-3 |
WordPiece | 基于概率合并 | 优化语言模型似然 | BERT |
ULM | 语言模型概率评估 | 动态选择最佳分词 | ALBERT, XLM-R |
SentencePiece | 直接处理原始文本 | 支持多语言和空格保留 | T5, XLNet |
5. 如何选择 Tokenizer?
- 任务需求:
- 需要处理多语言?→ 优先 SentencePiece。
- 需要高效性?→ BPE 或 WordPiece。
- 数据特点:
- 专业领域术语多?→ 增大词汇表或自定义子词。
- 模型兼容性:
- 使用预训练模型时,必须匹配其原生的 Tokenizer(如 BERT 用 WordPiece)。
希望这些信息对你有帮助!如果有进一步问题,欢迎随时提问 😊
WordPiece
好的!我们来深入探讨 WordPiece 中基于概率的合并策略,以及与 BPE 的核心区别。
WordPiece 的核心思想
WordPiece 的分词过程与 BPE 类似,但关键区别在于 合并子词的决策依据:
- BPE:合并频率最高的相邻符号对(如 ("e", "r")
→ "er"
)。
- WordPiece:合并能最大化语言模型(LM)似然概率的符号对,即对模型的训练目标(如预测下一个词)最有帮助的合并。
这种差异使得 WordPiece 更关注子词对模型性能的提升,而非单纯频率。
WordPiece 的合并步骤
以下是 WordPiece 构建词汇表的详细流程:
1. 初始化词汇表
- 初始词汇表包含所有基础字符(如英文的字母、中文的汉字)和常见符号。
- 例如:
["a", "b", ..., "z", "##a", "##b", ..., "##z", ...]
(##
表示子词前缀)。
2. 训练语言模型(LM)
- 定义一个简单的 单层语言模型,任务是预测句子中下一个词的概率。
- 假设当前词汇表为
V
,模型通过最大化训练数据的对数似然概率来学习: - $$\text{log-likelihood} = \sum_{s \in \text{数据}} \sum_{t \in s} \log P(t | \text{上下文})$$
这里的t
是当前词(或子词)。
3. 计算合并增益
- 遍历所有可能的相邻符号对(例如
("un", "happy")
),计算 合并后的语言模型概率增益。 - 对每一对符号
(A, B)
,计算合并为AB
后的似然增益: - $$\Delta_{\text{score}} = \frac{\text{count}(A, B)}{\text{count}(A) + \text{count}(B)}$$
其中:count(A, B)
是A
和B
在训练数据中连续出现的次数。count(A)
和count(B)
是A
和B
单独出现的次数。
- 关键点:合并增益高的符号对,意味着它们在上下文中共同出现时对模型预测更有帮助。
4. 合并最佳符号对
- 选择增益最高的符号对(如
("un", "happy")
→"unhappy"
),将其加入词汇表。 - 更新训练数据中的所有实例,将原来的
A B
替换为AB
。
5. 重复直到达到目标词汇表大小
- 重复步骤 3-4,直到词汇表大小达到预设值(如 30k)。
具体例子:合并过程
假设初始词汇表为 ["un", "happy", "##ness"]
,训练数据中有以下句子:
"un happy" → 出现 10 次
"happy ##ness" → 出现 8 次
- 计算合并增益:
- 对符号对
("un", "happy")
: count(un, happy) = 10
count(un) = 10
,count(happy) = 10 + 8 = 18
- 增益:
Δ_score = 10 / (10 + 18) ≈ 0.357
- 对符号对
("happy", "##ness")
: count(happy, ##ness) = 8
count(happy) = 18
,count(##ness) = 8
- 增益:
Δ_score = 8 / (18 + 8) ≈ 0.307
- 对符号对
- 决策:合并
("un", "happy")
,得到新子词"unhappy"
,并更新数据:- 原句子
"un happy"
→ 变为"unhappy"
。
- 原句子
与 BPE 的关键区别
特性 | WordPiece | BPE |
---|---|---|
合并依据 | 最大化语言模型似然(概率增益) | 合并最高频符号对 |
目标导向 | 优化模型预测能力 | 压缩词汇表,减少 OOV |
计算方式 | 依赖语言模型的概率计算 | 直接统计频率 |
典型应用 | BERT、DistilBERT | GPT 系列、RoBERTa |
为什么 WordPiece 有效?
- 语义相关性:
合并能提升语言模型概率的符号对,意味着这些子词组合在语义上更相关(例如"un" + "happy"
→"unhappy"
)。 - 处理未登录词:
通过动态合并子词,可以灵活生成未在词汇表中显式存在的词(如"ChatGPT"
→["Chat", "G", "PT"]
)。 - 控制词汇表大小:
选择对模型最有用的子词,避免冗余。
WordPiece 的局限性
- 计算成本高:需要反复训练语言模型计算增益。
- 依赖初始分词:需预先定义基础子词(如字符或简单子词)。
- 符号标记处理:需特殊符号(如
##
)区分子词位置,增加复杂性。
实际应用中的 WordPiece
以 BERT 为例:
- 词汇表:包含 30k 个子词,如 "the"
、"##ing"
、"##ness"
。
- 分词结果:
```python
from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")
print(tokenizer.tokenize("unhappiness"))
# 输出:['un', '##hap', '##piness']
```
总结来说,WordPiece 通过概率驱动的合并策略,在保留语义的同时高效构建词汇表,成为现代预训练模型(如 BERT)的核心组件。如果需要进一步探讨具体实现细节,可以继续提问! 🚀
这里我又问了一轮:
好的!我们来深入探讨 WordPiece 中基于概率合并子词的核心机制,以及它与 BPE 的区别。这里的关键在于理解 “合并策略如何基于概率”,而不仅仅是频率。
1. WordPiece 的核心思想
WordPiece 的目标是找到一个最优的子词集合,使得这些子词组合后能最大化训练数据的似然概率(即让模型对训练数据的“拟合程度”最高)。
核心问题:如何确定哪些子词组合应该被合并?
答案:合并能最大程度提升语言模型概率的符号对。
2. WordPiece 的训练步骤
步骤 1:初始化词汇表
- 初始词汇表 = 所有基础字符(如英文字母、汉字、标点符号等)。
步骤 2:构建候选符号对
- 将所有相邻的子词对(例如
("a", "b")
、("un", "##h")
)作为候选。
步骤 3:计算合并增益(Merge Gain)
- 核心公式:
对于每个候选符号对(A, B)
,计算将它们合并为AB
后的概率增益:Gain = P(AB) / (P(A) * P(B))
其中:
P(A)
和P(B)
是符号A
和B
在训练数据中的独立概率。P(AB)
是符号对AB
在训练数据中的联合概率。
- 直观理解:
如果Gain > 1
,说明合并后的AB
比单独出现的A
和B
更可能共同出现,合并是有益的。
步骤 4:选择并合并增益最大的符号对
- 选择增益最大的符号对,将其合并为一个新的子词(如
A + B → AB
),并更新词汇表。
步骤 5:重复步骤 2-4
- 不断合并符号对,直到词汇表达到预设大小(例如 30,000)。
3. 概率增益的详细计算
举例说明
假设训练数据中有以下文本:
"the cat is unhappy"
"the unhappy cat"
假设当前词汇表包含 ["un", "##h", "##appy", ...]
,现在要计算是否合并 ("un", "##h")
为 "un##h"
。
- 统计概率:
P(un)
=un
在数据中出现的次数 / 总符号数P(##h)
=##h
在数据中出现的次数 / 总符号数P(un##h)
=un##h
在合并后可能出现的次数 / 总符号数
- 计算增益:
- 如果合并后的
un##h
出现概率显著高于un
和##h
独立出现的概率乘积,则增益较大。
- 如果合并后的
关键点:
- WordPiece 通过最大化训练数据的似然(即让模型更“认可”当前的分词方式),选择合并的子词对。
- 与 BPE 的区别:BPE 直接合并最高频的符号对,而 WordPiece 合并对模型似然提升最大的符号对。
4. 编码阶段的分词逻辑
训练完成后,WordPiece 在分词时采用 贪婪最长匹配优先 策略:
1. 从词汇表中找到最长的可能子词(从左到右扫描)。
2. 如果找不到匹配的子词,则回退到较小的子词或基础字符。
3. 未登录词会被拆分为子词组合(例如 "unhappiness"
→ "un" + "##happiness"
)。
5. 与 BPE 的对比
特性 | BPE | WordPiece |
---|---|---|
合并准则 | 合并最高频的符号对 | 合并对模型似然增益最大的符号对 |
目标函数 | 频率驱动 | 概率驱动(最大化数据似然) |
处理未登录词 | 可能拆分不合理(依赖频率) | 更合理地拆分(基于概率增益) |
典型应用 | GPT 系列 | BERT、DistilBERT |
6. 实际例子
假设词汇表中已有 ["un", "##h", "##appy"]
,现在输入单词 "unhappy"
:
1. 初始拆分:["un", "##h", "##appy"]
2. 检查是否可合并:
- 合并 "un" + "##h"
→ "un##h"
(如果增益足够大)。
3. 最终分词:可能为 ["un##h", "##appy"]
或保持原状。
7. 数学形式化
假设训练数据由句子集合组成,WordPiece 的目标是找到词汇表 V
,使得分词后的句子的对数似然最大化:
maximize Σ_{sentence} Σ_{token} log P(token)
每次合并符号对时,选择使得上述目标增长最大的合并操作。
8. 为什么选择概率而非频率?
- 语义连贯性:高频符号对可能只是巧合(如
"th"
在英文中高频,但未必有意义),而概率增益高的符号对更可能构成有意义的语义单元。 - 灵活性:能适应不同数据分布(如专业术语中的低频但重要的子词组合)。
通过这种基于概率的合并策略,WordPiece 在保留高频子词的同时,能更有效地捕捉有意义的语义单元,这也是 BERT 等模型选择它的重要原因。如果有进一步问题,欢迎继续提问!
WordPiece的Motivation
逐步解释:
WordPiece 的目标是找到一个子词集合,使得用这些子词组合分词后的训练数据概率最大化。这句话的核心在于理解“似然概率最大化”如何指导子词合并:
1. 什么是“似然概率”?
- 定义:在统计学中,似然概率(Likelihood)表示在给定模型参数(这里是子词集合)下,观察到当前训练数据的可能性。
- 语言模型中的意义:假设有一个简单的概率模型,每个子词的出现是独立事件,那么一个句子的概率就是其所有子词概率的乘积。例如:
- 句子
"unhappy"
被分词为["un", "##happy"]
,则句子概率 =P("un") * P("##happy")
。
- 句子
2. 如何通过合并子词最大化似然?
WordPiece 的每一步合并都选择能最大程度提升整体数据概率的子词对。具体过程:
步骤 1:初始状态
- 初始词汇表 = 所有单个字符(如
a, b, c, ...
)。 - 此时每个字符的概率由其出现频率决定。
步骤 2:计算合并候选对的增益
假设当前词汇表中有子词 A
和 B
,合并它们为新子词 AB
。
- 合并前:句子中 A
和 B
独立出现的概率为 P(A) * P(B)
。
- 合并后:AB
作为一个整体出现的概率为 P(AB)
。
- 增益:如果 P(AB) > P(A) * P(B)
,说明合并后的概率更高,合并是有益的。
步骤 3:选择增益最大的合并
合并增益最大的符号对,更新词汇表,并重新计算所有概率。
重复迭代
直到词汇表达到预设大小。
3. 具体例子说明
假设训练数据中频繁出现 "un"
和 "##happy"
的组合,但单独出现的概率较低:
- 合并前:
P("un") = 0.01
, P("##happy") = 0.005
→ 组合概率 0.01 * 0.005 = 0.00005
。
- 合并后:
P("un##happy") = 0.008
(因为合并后作为一个整体更常出现)。
- 增益:0.008 / 0.00005 = 160
(增益远大于 1)。
此时合并 "un"
和 "##happy"
会显著提升整体数据的似然概率。
4. 为什么最大化似然有效?
- 捕捉语义关联:
如果A
和B
经常共同出现(如"un"
和"##happy"
),合并后概率提升,说明它们组成了一个有意义的语义单元(如否定前缀)。 - 减少冗余:
独立出现的低频子词合并后,能降低模型对噪声的敏感度(例如拆分出无意义的片段)。
5. 与 BPE 的对比
- BPE:合并高频符号对(如
("e", "s")
→"es"
),但高频组合可能只是巧合(如"th"
在英文中高频,但未必是完整语义单元)。 - WordPiece:合并概率增益大的符号对,确保合并后的子词具有更强的语义关联性(如
"un"+"##happy"
比单独出现更有意义)。
6. 数学形式化
目标函数是最大化训练数据的对数似然:
\text{Maximize } \sum_{\text{sentence}} \sum_{\text{token}} \log P(\text{token})
每次合并选择使该值增长最大的符号对。
7. 实际意义
通过最大化似然,WordPiece 生成的子词能更准确地反映数据中的语义模式,从而:
1. 提升模型效果:输入表示更贴近真实语义。
2. 减少未登录词:通过组合子词表达新词(如 "ChatGPT"
→ "Chat" + "G" + "PT"
)。
3. 平衡效率与效果:词汇表大小可控,同时保留语义信息。
8. 常见误区澄清
- 不是训练语言模型:WordPiece 训练的是分词器,而非后续的BERT/GPT等模型。它的目标是优化分词方式,使分词后的数据更“适合”语言模型处理。
- 增益公式的简化:实际实现中,WordPiece 可能使用更复杂的概率估计(如考虑上下文),但核心思想仍是选择对似然提升最大的合并。
通过这种机制,WordPiece 能够生成既紧凑又富有语义的子词集合,成为BERT等模型成功的关键因素之一。
ULM
好的!我们来深入探讨 ULM(Unigram Language Model) 分词算法,它与其他子词分词方法(如 BPE、WordPiece)有本质区别,核心思想是基于概率模型反向构建词汇表。以下是详细解析:
1. ULM 的核心思想
ULM 是一种 基于概率的生成模型,其目标是:
找到所有可能的分词方式中,对训练数据概率最大的分词结果。
与 BPE/WordPiece(逐步合并符号对)不同,ULM 先假设一个较大的词汇表,然后逐步删除对整体概率贡献最小的子词,最终保留最优子词集合。
2. ULM 的训练步骤
步骤 1:初始化词汇表
- 初始词汇表需要足够大,通常通过启发式方法生成:
- 包含所有单字符。
- 添加高频词、所有可能的 N-gram(如 2-gram、3-gram)组合。
- 例如:从文本中提取所有出现次数超过阈值的子串。
步骤 2:估计子词概率
假设词汇表为 ( V ),每个子词 ( s \in V ) 有一个概率 ( p(s) ),满足 ( \sum_{s \in V} p(s) = 1 )。
通过 EM 算法(期望最大化) 迭代优化子词概率:
1. E 步(Expectation):
对每个句子,计算所有可能分词方式的概率,并统计每个子词的出现期望值。
2. M 步(Maximization):
根据期望值更新子词概率 ( p(s) )。
步骤 3:剪枝词汇表(Vocabulary Pruning)
逐步删除对总概率贡献最小的子词:
1. 计算移除每个子词 ( s ) 后的 总概率损失 ( L(s) )。
- 损失 ( L(s) ) 越小,说明该子词越不重要。
2. 删除损失最小的子词,更新词汇表。
3. 重复直到词汇表达到目标大小。
步骤 4:最终分词
训练完成后,对输入文本选择 概率最大的分词方式(通过动态规划,如 Viterbi 算法)。
3. 关键细节解析
(1) 如何计算分词概率?
对于一个句子 ( S ),假设其分词方式为 ( \text{Segmentation} = {s_1, s_2, ..., s_n} ),则分词概率为:
P(\text{Segmentation}) = \prod_{i=1}^n p(s_i)
ULM 的目标是找到所有可能的分词中概率最大的方式。
(2) 为什么需要剪枝?
初始词汇表非常大(可能包含冗余子词),剪枝可以:
- 去除低频或冗余子词(如 "ing"
和 "##ing"
可能保留一个)。
- 平衡词汇表大小与语义表达能力。
(3) 如何处理未登录词?
ULM 天然支持通过子词组合表达未登录词。例如:
- 新词 "ChatGPT"
可能被拆分为 ["Chat", "G", "PT"]
(假设这些子词在词汇表中)。
4. ULM 的特点
优点:
- 灵活的分词方式:
支持动态选择最优分词(基于概率),而非固定合并规则。- 例如:
"unhappily"
可以拆分为["un", "happ", "ily"]
或["un", "happy", "ly"]
,选择概率更高的方式。
- 例如:
- 多语言友好:
与 SentencePiece 结合时,可直接处理多语言文本(无需空格分隔)。 - 抗噪声能力:
低频子词会被自动剪枝,减少噪声影响。
缺点:
- 计算成本高:
训练时需要计算所有可能分词的概率,复杂度为 ( O(n^2) )。 - 依赖初始化:
初始词汇表的质量影响最终结果。
5. ULM vs BPE vs WordPiece
特性 | BPE | WordPiece | ULM |
---|---|---|---|
核心策略 | 合并高频符号对 | 合并概率增益大的符号对 | 基于概率剪枝词汇表 |
训练方向 | 自底向上(合并) | 自底向上(合并) | 自顶向下(剪枝) |
分词方式 | 贪婪匹配 | 贪婪匹配 | 动态规划(最优解) |
多语言支持 | 需预分词 | 需预分词 | 直接处理原始文本(结合 SentencePiece) |
典型应用 | GPT | BERT | ALBERT、T5 |
6. 实际案例
案例 1:处理多义性分词
输入句子:"applepie"
- 可能的分词方式:
- ["apple", "pie"]
(概率高,语义合理)
- ["app", "lep", "ie"]
(概率低,无意义)
ULM 会选择概率更高的合理分词。
案例 2:未登录词处理
输入单词:"transformer"
(假设未在训练数据中出现)
- ULM 可能拆分:["trans", "form", "er"]
(如果子词 "trans"
、"form"
、"er"
的概率较高)。
7. 实现工具:SentencePiece
- 功能:
SentencePiece 是一个开源库,支持以 ULM 或 BPE 作为底层算法,直接处理原始文本(无需预分词)。 - 特点:
- 将空格视为普通字符(适合中文、日文等无空格语言)。
- 支持 Unicode 统一处理多语言。
- 使用示例:
import sentencepiece as spm spm.SentencePieceTrainer.train( input='data.txt', model_prefix='ulm_model', vocab_size=8000, model_type='unigram' # 指定 ULM 算法 )
8. 如何选择 ULM?
- 适用场景:
- 数据中包含大量未登录词或专业术语。
- 需要处理多语言混合文本。
- 对分词灵活性要求高(如诗歌、社交媒体文本)。
- 不适用场景:
- 资源受限(训练时间或计算能力不足)。
- 数据分布简单(如纯英文新闻,BPE/WordPiece 已足够)。
9. 数学补充:损失函数
ULM 的优化目标是最大化训练数据的对数似然:
\mathcal{L} = \sum_{x \in \text{data}} \log P(x)
其中 ( P(x) ) 是句子 ( x ) 所有可能分词方式的概率之和。
由于直接计算所有分词方式不可行,实际通过动态规划近似求解。
通过这种概率驱动的剪枝策略,ULM 能够生成既紧凑又语义丰富的词汇表,尤其在处理复杂文本时表现出色。如果想进一步了解具体实现细节或应用案例,欢迎继续提问! 😊
中文tokenizer
以下是中文模型常用的 Tokenizer 类型及其特点的详细说明:
1. 中文分词的挑战
中文没有天然空格分隔词语,且存在大量组合词(如“人工智能” = “人工” + “智能”)。传统方法依赖分词工具(如结巴分词),但现代语言模型更倾向于使用 子词分词(Subword Tokenization),以解决未登录词(OOV)问题,并捕捉词语的组成结构。
2. 中文模型的常见 Tokenizer 类型
(1) 基于字符的分词(Character-Level)
- 原理:将每个汉字视为一个独立的 Token。
- 例如:“我喜欢猫” →
["我", "喜", "欢", "猫"]
。
- 例如:“我喜欢猫” →
- 优点:
- 词汇表极小(约 2 万汉字覆盖 99% 文本)。
- 天然支持所有组合词,无未登录词问题。
- 缺点:
- 序列长度较长(如一篇千字文需处理 1000+ Token)。
- 丢失词语级语义(如“喜欢”拆成“喜”和“欢”可能破坏语义)。
- 应用场景:早期模型如 TextCNN、部分 RNN 模型。
(2) 基于子词的分词(Subword-Level)
现代中文模型的主流选择,结合字符和词语的优点,常用以下算法:
a. WordPiece(如 BERT 中文版)
- 原理:
- 预分词:将文本按字符切分(如“我喜欢猫” →
["我", "喜", "欢", "猫"]
)。 - 合并高频字符对:例如合并“喜”和“欢” →
["我", "喜欢", "猫"]
。
- 预分词:将文本按字符切分(如“我喜欢猫” →
- 实现:
- 中文 BERT 使用
BertWordPieceTokenizer
,词汇表包含汉字和常见子词。 - 例子:词汇表中可能包含
"喜欢"
、"人工"
、"##智能"
(##
表示子词前缀)。
- 中文 BERT 使用
- 优点:
- 平衡序列长度与语义。
- 支持未登录词拆分(如“人工智能” →
["人工", "##智能"]
)。
- 缺点:
- 需要预分词(中文需先按字符切分)。
b. SentencePiece(如 ALBERT、T5 中文版)
- 原理:
- 直接处理原始文本,无需预分词。
- 使用 BPE 或 ULM 算法构建子词词汇表。
- 特点:
- 将空格视为普通字符(适合中文无需空格的特点)。
- 支持多语言混合文本。
- 例子:
# SentencePiece 处理中文的示例 import sentencepiece as spm sp = spm.SentencePieceProcessor() sp.load("chinese_sp.model") text = "人工智能是未来趋势" tokens = sp.encode_as_pieces(text) # 输出:["▁", "人工", "智能", "是", "未来", "趋势"]
- 优点:
- 无需预分词,直接处理原始文本。
- 更适合无空格语言(如中文、日文)。
c. BPE(如 GPT 中文版)
- 原理:合并高频相邻字符对,例如:
- “人工智能” →
["人", "工", "智", "能"]
→ 合并高频对后可能变为["人工", "智能"]
。
- “人工智能” →
- 应用:部分中文 GPT 模型使用 BPE,但需注意中文的字符频率分布与英文不同。
3. 典型中文模型的 Tokenizer 实现
模型 | Tokenizer 类型 | 特点 | 示例词汇表条目 |
---|---|---|---|
BERT-中文 | WordPiece | 预分词为字符,合并高频子词 | "我" , "喜欢" , "##智能" |
ALBERT-中文 | SentencePiece(Unigram) | 直接处理原始文本,概率剪枝词汇表 | "▁人工" , "智能" , "趋势" |
GPT-3 中文 | BPE | 合并高频字符对,适合生成任务 | "人工" , "智能" , "未来" |
HuggingFace 多语言模型 | SentencePiece | 统一处理多语言,支持中文无空格 | "▁Artificial" , "▁人工" |
4. 中文 Tokenizer 的关键设计选择
(1) 是否保留空格?
- 中文无需空格:SentencePiece 直接处理原始文本,将空格视为特殊符号(如
"▁"
)。 - 中英文混合场景:需统一处理,例如
"ChatGPT很棒"
→["Chat", "G", "PT", "▁很棒"]
。
(2) 词汇表大小
- 中文词汇表通常比英文大:
- 英文典型词汇表:30k–50k。
- 中文典型词汇表:20k–60k(包含常用汉字、子词、专有名词)。
(3) 特殊符号处理
- 中文需处理全角/半角符号(如
“”
vs""
)、标点(如,
、。
)等。
5. 实际案例
案例 1:BERT-中文
- Tokenizer:
BertWordPieceTokenizer
- 分词过程:
- 按字符切分:
"人工智能"
→["人", "工", "智", "能"]
。 - 合并高频子词对:
["人工", "智能"]
(如果"人工"
和"智能"
在词汇表中)。
- 按字符切分:
- 词汇表条目:包含单字(如
"人"
)、词语(如"人工"
)和子词(如"##智能"
)。
案例 2:T5-中文(使用 SentencePiece)
- Tokenizer:
SentencePiece (Unigram)
- 分词过程:
- 直接处理原始文本:
"人工智能是未来趋势"
。 - 动态选择概率最大的分词方式:
["▁人工", "智能", "▁是", "▁未来", "▁趋势"]
。
- 直接处理原始文本:
6. 如何选择中文 Tokenizer?
- 任务类型:
- 理解任务(如分类、NER):优先 WordPiece 或 SentencePiece。
- 生成任务(如文本生成):BPE 或 SentencePiece 更灵活。
- 数据特点:
- 专业领域(如医学、法律):需自定义词汇表,添加领域子词。
- 模型兼容性:
- 使用预训练模型时,必须匹配其原始 Tokenizer(如 BERT 中文版用 WordPiece)。
7. 工具与库推荐
- Hugging Face Tokenizers:支持
BertWordPieceTokenizer
,适合中文 BERT。 - SentencePiece:适合多语言、无空格文本,支持 ULM/BPE。
- Jieba 分词:传统中文分词工具,可与子词分词结合使用(预分词后输入)。
总结
中文模型主要使用 子词分词器,主流选择包括:
1. WordPiece(如 BERT-中文):平衡字符与词语,需预分词。
2. SentencePiece(如 ALBERT、T5):直接处理原始文本,适合多语言。
3. BPE(如部分 GPT 模型):合并高频字符对,适合生成任务。
选择时需考虑任务需求、数据特点及模型兼容性。
SentencePiece
SentencePiece 是一种通用的子词分词(Subword Tokenization)工具,专为处理无空格语言(如中文、日文)和多语言混合文本设计。它的核心目标是 直接处理原始文本,无需依赖预分词(如按空格分割),从而简化流程并提升灵活性。以下是 SentencePiece 的详细解析:
1. SentencePiece 的核心功能
- 无需预分词:直接处理原始字符串(包括中文、日文等无空格语言)。
- 统一处理多语言:可同时处理多种语言的混合文本(如中英混杂的句子)。
- 支持多种算法:底层可选择 BPE(Byte-Pair Encoding)或 ULM(Unigram Language Model)。
- 保留空格:将空格视为普通字符(用特殊符号
▁
表示),避免信息丢失。 - 生成紧凑词汇表:通过算法生成子词集合,平衡语义表达与计算效率。
2. 为什么需要 SentencePiece?
传统分词工具的局限性:
- 依赖预分词:
中文等语言需先用工具(如结巴分词)切分词语,再应用子词算法,流程复杂。 - 无法处理无空格语言:
英文 BPE/WordPiece 依赖空格分割单词,但中文、日文等语言没有空格。 - 多语言混合困难:
中英混杂的文本(如"ChatGPT很棒"
)需要统一的分词策略。
SentencePiece 的优势:
- 端到端处理:直接输入原始文本,自动学习子词分割规则。
- 统一编码:无论语言是否有空格,均以相同方式处理。
- 灵活适配:适合训练多语言模型(如 mT5、XLMRoberta)。
3. SentencePiece 的工作原理
步骤 1:文本标准化
- 将输入文本转换为 Unicode 格式。
- 处理空格:在句首和句尾添加特殊符号
▁
(U+2581),并将中间的空格替换为▁
。- 例如:
"Hello 世界"
→"▁Hello▁世界"
。
- 例如:
步骤 2:训练子词模型
选择底层算法(BPE 或 ULM),统计文本中的字符/子词频率,生成词汇表。
a. 使用 BPE 算法
- 合并高频字符对,逐步构建词汇表(与英文 BPE 类似,但直接处理原始字节)。
b. 使用 ULM 算法
- 基于概率剪枝初始词汇表,保留对数据似然贡献最大的子词。
步骤 3:编码与解码
- 编码:将文本转换为子词序列(如
["▁", "Hello", "▁", "世", "界"]
)。 - 解码:将子词序列还原为原始文本(自动去除
▁
并恢复空格)。
4. SentencePiece 的关键特性
(1) 空格处理
- 空格符号
▁
:- 句首和词间的空格用
▁
表示,例如"I love NLP"
→"▁I▁love▁NLP"
。 - 解码时会自动将
▁
转换为空格。
- 句首和词间的空格用
- 意义:
- 保留文本中的空格信息(如专有名词中的空格:
"New York"
→"▁New▁York"
)。 - 统一处理所有语言,避免因空格缺失导致的分词错误。
- 保留文本中的空格信息(如专有名词中的空格:
(2) 支持多语言
- 无语言限制:
SentencePiece 不依赖特定语言的预分词规则,可处理任意 Unicode 字符。 - 混合语言示例:
输入: "ChatGPTは優秀なAIです。" 分词: ["▁", "Chat", "G", "PT", "は", "優秀", "な", "AI", "です", "。"]
(3) 子词算法的选择
- BPE 模式:
- 适合生成任务(如 GPT 系列),合并高频字符对。
- 词汇表大小可控,但可能生成不语义连贯的子词。
- ULM 模式:
- 基于概率剪枝,保留语义更丰富的子词。
- 适合理解任务(如 BERT、T5),但训练成本较高。
5. 实际应用场景
场景 1:中文分词
- 输入:
"人工智能是未来趋势"
- 分词结果(ULM 算法):
["▁人工", "智能", "▁是", "▁未来", "▁趋势"]
- 优势:
无需先按字符或词语切分,直接学习子词组合(如"人工"
+"智能"
)。
场景 2:多语言混合文本
- 输入:
"ChatGPT很棒!"
- 分词结果(BPE 算法):
["▁", "Chat", "G", "PT", "▁很棒", "!"]
- 优势:
统一处理英文单词和中文字符,保留语义信息。
6. 如何使用 SentencePiece?
步骤 1:安装库
pip install sentencepiece
步骤 2:训练模型
import sentencepiece as spm
# 训练配置
spm.SentencePieceTrainer.train(
input="corpus.txt", # 输入文本文件
model_prefix="sp_model", # 输出模型名前缀
vocab_size=20000, # 目标词汇表大小
model_type="bpe", # 选择算法(bpe/unigram)
user_defined_symbols=["[ENT]"], # 自定义保留符号(如实体标记)
pad_id=0, # 特殊符号的ID(需与模型对齐)
)
步骤 3:加载与使用模型
import sentencepiece as sp
# 加载模型
sp_model = sp.SentencePieceProcessor()
sp_model.load("sp_model.model")
# 编码文本
text = "人工智能是未来趋势"
tokens = sp_model.encode_as_pieces(text)
print(tokens) # 输出:['▁人工', '智能', '▁是', '▁未来', '▁趋势']
# 解码
decoded = sp_model.decode_pieces(tokens)
print(decoded) # 输出:人工智能是未来趋势
7. SentencePiece 的优缺点
优点:
- 无需预分词:直接处理原始文本,简化流程。
- 多语言友好:适合中文、日文、代码等无空格文本。
- 灵活控制词汇表:通过调整
vocab_size
和算法平衡效率与效果。
缺点:
- 训练成本高:ULM 模式需要大量计算资源。
- 长文本处理:若未合并长词,可能导致序列过长。
8. 与其他工具对比
工具 | 预分词需求 | 多语言支持 | 典型应用 |
---|---|---|---|
SentencePiece | 不需要 | 优秀 | T5、ALBERT、XLNet |
WordPiece | 需要(如空格) | 一般 | BERT、DistilBERT |
BPE | 需要(如空格) | 一般 | GPT-2、RoBERTa |
9. 常见问题
Q1:如何处理中文专有名词(如“支付宝”)?
- 方案:在训练时增大
vocab_size
,或通过user_defined_symbols
强制保留特定词。
Q2:SentencePiece 是否适合小语种?
- 是:即使数据量较少,也可通过调整
vocab_size
和算法(如 ULM)生成有效子词。
Q3:如何与 Hugging Face 库结合使用?
- 示例:
from transformers import AutoTokenizer # 加载 SentencePiece 模型 tokenizer = AutoTokenizer.from_pretrained("t5-base") # 编码 text = "人工智能是未来趋势" inputs = tokenizer(text, return_tensors="pt")
总结
SentencePiece 是一种 面向无空格语言和多语言场景的通用分词工具,通过直接处理原始文本和灵活选择子词算法(BPE/ULM),解决了传统分词工具的局限性。它在现代多语言模型(如 mT5、ALBERT)中广泛应用,是处理复杂文本任务的理想选择。
文章评论