Tokenization
Tokenization (分词)
分词是将原始文本转换为模型可以理解的数字序列(Token IDs)的过程。
1. 粒度 (Granularity)
- Character-level (字符级):
a,b,c. 词表小,但序列极长,缺少语义。 - Word-level (词级):
apple,banana. 词表极大(数十万),会有 OOV (Out of Vocabulary) 问题。 - Subword-level (子词级): 介于两者之间。常用词保持完整,罕见词拆分为子词。平衡了词表大小和序列长度。这是 LLM 的标准做法。
2. 常用算法 (Common Algorithms)
2.1 BPE (Byte Pair Encoding)
- 原理:迭代合并最频繁出现的字符对。
- 流程:
- 初始化词表为单字符。
- 统计所有相邻字符对的频率。
- 合并频率最高的对(如
e,s->es),加入词表。 - 重复直到达到预设词表大小。
- Byte-level BPE: 基于 UTF-8 字节而不是 Unicode 字符进行 BPE。这确保了可以处理任何字符串而不会出现
<UNK>。GPT-2, GPT-3, LLaMA 都使用此方法。
2.2 WordPiece
- 原理:类似 BPE,但合并标准是最大化训练数据的似然概率(Likelihood),而不是频率。
- 应用:BERT, DistilBERT。
2.3 SentencePiece
- 特点:
- 将输入视为原始字节流(Raw Stream),不依赖空格分词(对中文、日文友好)。
- 集成了 BPE 和 Unigram 算法。
- 应用:T5, LLaMA (虽然 LLaMA 用的是 BPE 算法,但实现上常使用 SentencePiece 库或 Tokenizers 库)。
3. Special Tokens (特殊 Token)
<BOS>/<s>: Beginning of Sentence.<EOS>/</s>: End of Sentence.<PAD>: Padding token,用于将 batch 中的序列补齐到相同长度。<UNK>: Unknown token.
4. HuggingFace Transformers 实战
from transformers import AutoTokenizer
# 加载预训练的 Tokenizer
tokenizer = AutoTokenizer.from_pretrained("gpt2")
text = "Hello, world! transforming text."
# 1. 编码 (Encoding): Text -> Token IDs
encoded = tokenizer(text)
print(encoded)
# {'input_ids': [15496, 11, 995, 0, 44265, 2420, 13], 'attention_mask': [1, 1, 1, 1, 1, 1, 1]}
# 查看每个 Token 对应的字符串
tokens = tokenizer.convert_ids_to_tokens(encoded['input_ids'])
print(tokens)
# ['Hello', ',', 'Ġworld', '!', 'Ġtransforming', 'Ġtext', '.']
# 注意:GPT2 使用 Ġ (G with dot) 表示前导空格
# 2. 解码 (Decoding): Token IDs -> Text
decoded = tokenizer.decode(encoded['input_ids'])
print(decoded)
# "Hello, world! transforming text."扩充词表 (Adding Tokens)
如果需要微调特定领域的模型(如代码、医学),可能需要扩充词表。
new_tokens = ["<SPECIAL_TAG>", "python code"]
num_added_toks = tokenizer.add_tokens(new_tokens)
print('We have added', num_added_toks, 'tokens')
# 注意:扩充词表后,必须调整模型 Embedding 层的大小!!
# model.resize_token_embeddings(len(tokenizer))