在自然语言处理(NLP)领域,我们面临的一个核心挑战是如何处理那些模型从未见过的词汇。传统的处理方法往往难以有效应对这一问题,这使得它们不再适用于现代的应用场景。现有的方法主要存在以下几个问题:
- 词级分词会导致词汇表极其庞大(包含超过 500,000 个标记)
- 词汇表外(OOV)的词汇(生僻词或新词)会彻底破坏模型的预测能力
- 字符级分词会丢失语义信息
- 模型必须从头学习构词法,这效率非常低
WordPiece 分词提供了一种解决方案,它已成为 BERT 和 GPT 等 Transformer 模型的基石。它在词汇表大小和语义保留之间找到了完美的平衡点。
目录
词汇表膨胀与生僻词问题
让我们考虑这样一个句子:“The bioengineering startup developed unbreakable materials.”(这家生物工程初创公司开发出了坚不可摧的材料)。传统的词级分词器需要分别为“bioengineering”、“startup”、“unbreakable”和“materials”建立独立的索引。如果这些词中有任何一个不在训练词汇表中,模型就会失效。
传统分词方法面临的主要挑战:
- 词汇表会随着文本语料库的大小呈指数级增长
- 技术术语和专有名词会制造无穷无尽的边缘情况
- 对于庞大的词汇表,内存需求变得令人望而却步
- 模型训练的计算成本变得极其高昂
WordPiece 通过将单词分解为有意义的子单元来解决这些问题。它不再将“unbreakable”视为一个单一的未知标记,而是将其分解为可识别的部分:["un", "##break", "##able"]。这里的“##”前缀表示该标记是前一部分的延续,它既保留了单词的边界,也实现了灵活的分解。
这种方法确保了即使是全新的单词也能通过其组成部分被理解,从而提高了模型的鲁棒性和泛化能力。
WordPiece 分词的工作原理
该算法采用数据驱动的方法来构建词汇表。它从单个字符开始,逐步合并最常出现的相邻字符对,直到达到目标词汇表大小。
算法步骤:
- 初始化词汇表,包含所有单个字符
- 统计语料库中所有相邻符号对的频率
- 将出现频率最高的一对合并为一个单独的标记
- 用新的合并标记更新语料库
- 重复上述步骤,直到达到预期的词汇表大小(通常为 30K-50K 个标记)
在实际的分词过程中,WordPiece 使用了一种贪婪的最长匹配策略。对于每个单词,它会找到词汇表中存在的尽可能长的子词,然后将其标记为一个 token,并对剩余的字符重复此过程。
分词过程:
- 从每个单词的开头开始
- 在词汇表中查找匹配的最长子词
- 将其添加到标记列表中,并加上适当的前缀
- 移动到下一个未处理的字符
- 继续直到处理完整个单词
这种统计学基础确保了常见的模式自然地涌现为单独的标记,而罕见的组合则被分解为更熟悉的组件。
Wordpiece 的具体实现
让我们使用 transformers 库 来实现基本的 WordPiece 分词。本示例重点关注核心功能,避免不必要的复杂性。
- 导入 BertTokenizer 并加载预训练的 bert-base-uncased 模型,该模型应用了 WordPiece 分词并将输入文本转换为小写。
- 定义一个函数 simple_tokenize(text) 来展示 BERT 如何将输入分词为子词并将其映射为 token IDs。
- 对整个句子进行分词,打印生成的 WordPiece 标记,并将它们转换为相应的词汇表 ID。
- 将输入拆分为单个单词,并展示 BERT 如何将每个单词分解为子词标记。
- 在三个示例句子上测试该函数,以说明对生僻词、复合词和复杂单词的处理,示例之间有清晰的分隔。
Python
“
from transformers import BertTokenizer
Initialize the tokenizer with pre-trained BERT vocabulary
tokenizer = BertTokenizer.from_pretrained(‘bert-base-uncased‘)
def simple_tokenize(text):
"""
Basic WordPiece tokenization example
"""
print(f"Original text: {text}")
# Convert text to WordPiece tokens
tokens = tokenizer.tokenize(text)
print(f"WordPiece tokens: {tokens}")
# Convert tokens to numerical IDs
tokenids = tokenizer.converttokenstoids(tokens)
print(f"Token IDs: {token_ids}")
# Show how individual words break down
words = text.split()
print("
Word breakdown:")
for word in words:
word_tokens = tokenizer.tokenize(word)
print(f" ‘{word}‘ → {word_tokens}")
Test with different examples
test_sentences = [
"Tokenization helps handle rare words.",
"The unbreakable smartphone survived.",
"Bioengineering revolutionizes manufac