04-Tokenization实战-从BPE到Hugging-Face应用

Tokenization实战:从BPE到Hugging Face应用 | 吴恩达2025最新课程笔记

摘要: 本文深入讲解大语言模型的第一步------分词(Tokenization),从经典的Byte Pair Encoding(BPE)算法到WordPiece、SentencePiece等主流方案,详解GPT、BERT、LLaMA等模型的分词策略。通过Hugging Face Tokenizers库的实战代码,帮助读者掌握分词器训练、词表构建、特殊token处理等工程实践,理解分词对模型性能的深远影响。

关键词: Tokenization、BPE算法、WordPiece、SentencePiece、Hugging Face Tokenizers、词表构建、子词分词、GPT分词器、BERT分词器、吴恩达课程笔记、NLP预处理


一、为什么分词如此重要?

1.1 分词是LLM的"眼睛"

用户输入:

我爱学习AI
分词器

Tokenizer
Token序列:

101, 2769, 4263, 2110, 749, AI, 102

大语言模型

Transformer
模型输出

核心作用:

  1. 将文本转换为模型可处理的数字序列
  2. 决定模型的词汇量和泛化能力
  3. 影响推理速度和显存占用

1.2 分词的三大挑战

解决方案
挑战
📝 OOV问题

Out-of-Vocabulary

未登录词无法处理
🌍 多语言支持

不同语言差异巨大

中文无空格分隔
⚖️ 粒度平衡

词级太粗,字符级太细

需要折中方案
子词分词

Subword Tokenization

BPE/WordPiece/Unigram

1.3 主流分词方案对比

方案 代表模型 优点 缺点 词表大小
词级分词 Word2Vec 语义完整 OOV严重 10万+
字符级 CharRNN 无OOV 序列过长 <100
BPE GPT系列 平衡,通用 无语言先验 5万
WordPiece BERT 语言感知 需要预分词 3万
Unigram T5, mBART 概率模型 计算复杂 3.2万
SentencePiece LLaMA, XLM 端到端 空格特殊处理 3.2万

二、Byte Pair Encoding(BPE)详解

2.1 BPE算法原理

核心思想:从字符开始,迭代合并出现频率最高的相邻token对。
初始: 字符级分词

l o w e r
统计频次:

er出现最多
合并: low er
再统计:

er_出现最多
合并: low er_
继续统计:

low出现最多
最终: low er_

2.2 BPE训练流程

Step 1: 统计词频
python 复制代码
corpus = [
    "lower",
    "lowest",
    "newer",
    "wider"
]

# 字符级分词 + 词频统计
vocab = {
    "l o w e r </w>": 5,    # </w>表示词尾
    "l o w e s t </w>": 2,
    "n e w e r </w>": 6,
    "w i d e r </w>": 3
}
Step 2: 迭代合并

Vocab Counter Corpus Vocab Counter Corpus 迭代1 迭代2 迭代N 统计相邻token对 ("e", "r"): 16次 最高频 合并为"er" l o w er </w> 统计相邻token对 ("er", "</w>"): 16次 合并为"er_" 重复直到达到目标词表大小

Python实现:

python 复制代码
from collections import Counter
import re

def get_stats(vocab):
    """统计相邻token对的频次"""
    pairs = Counter()
    for word, freq in vocab.items():
        symbols = word.split()
        for i in range(len(symbols) - 1):
            pairs[(symbols[i], symbols[i+1])] += freq
    return pairs

def merge_vocab(pair, vocab):
    """合并最高频的token对"""
    new_vocab = {}
    bigram = ' '.join(pair)
    replacement = ''.join(pair)
    
    for word in vocab:
        new_word = word.replace(bigram, replacement)
        new_vocab[new_word] = vocab[word]
    return new_vocab

def train_bpe(corpus, num_merges):
    """
    训练BPE分词器
    
    参数:
        corpus: 文本列表
        num_merges: 合并操作次数
    返回:
        merge_ops: 合并操作序列
    """
    # 1. 初始化词表(字符级)
    vocab = {}
    for text in corpus:
        words = text.split()
        for word in words:
            word_with_boundary = ' '.join(list(word)) + ' </w>'
            vocab[word_with_boundary] = vocab.get(word_with_boundary, 0) + 1
    
    merge_ops = []
    
    # 2. 迭代合并
    for i in range(num_merges):
        pairs = get_stats(vocab)
        if not pairs:
            break
        
        # 选择频次最高的pair
        best_pair = max(pairs, key=pairs.get)
        vocab = merge_vocab(best_pair, vocab)
        merge_ops.append(best_pair)
        
        print(f"Merge {i+1}: {best_pair} (freq: {pairs[best_pair]})")
    
    return merge_ops, vocab

# 示例
corpus = [
    "low low low low low",
    "lower lower lower",
    "newest newest newest newest newest newest",
    "wider wider wider"
]

merge_ops, final_vocab = train_bpe(corpus, num_merges=10)

print("\n最终词表:")
for word, freq in final_vocab.items():
    print(f"{word}: {freq}")

输出示例:

复制代码
Merge 1: ('e', 's') (freq: 6)
Merge 2: ('es', 't') (freq: 6)
Merge 3: ('est', '</w>') (freq: 6)
Merge 4: ('l', 'o') (freq: 8)
Merge 5: ('lo', 'w') (freq: 8)
...

最终词表:
low </w>: 5
lower </w>: 3
newest </w>: 6
wider </w>: 3

2.3 BPE编码(Encoding)

使用训练好的merge_ops对新文本分词:

python 复制代码
def bpe_encode(text, merge_ops):
    """
    使用BPE分词
    
    参数:
        text: 输入文本
        merge_ops: 训练好的合并操作序列
    """
    # 1. 字符级初始化
    word = list(text) + ['</w>']
    
    # 2. 应用merge操作
    for pair in merge_ops:
        i = 0
        while i < len(word) - 1:
            if (word[i], word[i+1]) == pair:
                word[i:i+2] = [''.join(pair)]
            else:
                i += 1
    
    return word

# 示例
text = "lowest"
tokens = bpe_encode(text, merge_ops)
print(f"'{text}' → {tokens}")
# 输出: 'lowest' → ['low', 'est', '</w>']

2.4 BPE的优势

应用
优点
✅ 无OOV问题

任何词都能分解为子词
✅ 数据驱动

无需语言学知识
✅ 压缩效率高

常用词整体保留
✅ 多语言通用

对语言无感知
GPT系列
RoBERTa
BART


三、WordPiece:BERT的分词方案

3.1 WordPiece vs BPE

核心区别:合并策略不同

特性 BPE WordPiece
合并依据 频次最高 似然增益最大
公式 max count(A,B) max P(AB) / (P(A)×P(B))
语言感知 有(似然偏好完整词)
前缀标记 ##表示非词首

似然增益公式 :
Score(A,B)=P(AB)P(A)×P(B) \text{Score}(A, B) = \frac{P(AB)}{P(A) \times P(B)} Score(A,B)=P(A)×P(B)P(AB)

3.2 WordPiece特殊标记

python 复制代码
# BERT的特殊token
special_tokens = {
    "[PAD]": 0,      # 填充
    "[UNK]": 100,    # 未知词
    "[CLS]": 101,    # 句子开始
    "[SEP]": 102,    # 句子分隔
    "[MASK]": 103    # 掩码(用于MLM任务)
}

# 示例分词
text = "playing"
tokens = ["play", "##ing"]  # ##表示非词首

playing
play
##ing
unhappiness
un
##happiness

3.3 Hugging Face实现WordPiece

python 复制代码
from tokenizers import Tokenizer
from tokenizers.models import WordPiece
from tokenizers.trainers import WordPieceTrainer
from tokenizers.pre_tokenizers import Whitespace

# 1. 创建WordPiece tokenizer
tokenizer = Tokenizer(WordPiece(unk_token="[UNK]"))
tokenizer.pre_tokenizer = Whitespace()  # 空格预分词

# 2. 定义trainer
trainer = WordPieceTrainer(
    vocab_size=30000,
    special_tokens=["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"],
    min_frequency=2
)

# 3. 训练
files = ["train.txt"]
tokenizer.train(files, trainer)

# 4. 保存
tokenizer.save("wordpiece-tokenizer.json")

# 5. 使用
output = tokenizer.encode("I love playing football")
print(f"Tokens: {output.tokens}")
print(f"IDs: {output.ids}")

# 输出示例:
# Tokens: ['I', 'love', 'play', '##ing', 'football']
# IDs: [256, 891, 1234, 5678, 9012]

四、SentencePiece:端到端的无损分词

4.1 SentencePiece的特点

核心创新:将空格视为普通字符,直接从原始文本训练。
SentencePiece
WordPiece/BPE
问题
优势
原始文本
预分词

(按空格)
子词分词
原始文本
直接训练

空格=_字符
信息丢失

无法还原
无损可逆

示例:

python 复制代码
# 传统方案
"Hello World" → ["Hello", "World"] → ["Hel", "##lo", "World"]
# 问题:无法区分"HelloWorld"和"Hello World"

# SentencePiece
"Hello World" → ["▁Hello", "▁World"]  # ▁代表空格
# 解码:"▁Hello▁World" → "Hello World" (完美还原)

4.2 SentencePiece训练与使用

python 复制代码
import sentencepiece as spm

# 1. 训练SentencePiece模型
spm.SentencePieceTrainer.train(
    input='train.txt',
    model_prefix='sp_model',
    vocab_size=32000,
    character_coverage=0.9995,  # 字符覆盖率
    model_type='unigram',       # 可选: unigram/bpe/char/word
    pad_id=0,
    unk_id=1,
    bos_id=2,
    eos_id=3,
    user_defined_symbols=['<|endoftext|>']  # 自定义token
)

# 2. 加载模型
sp = spm.SentencePieceProcessor()
sp.load('sp_model.model')

# 3. 编码
text = "I love NLP!"
tokens = sp.encode_as_pieces(text)
ids = sp.encode_as_ids(text)

print(f"Text: {text}")
print(f"Tokens: {tokens}")
print(f"IDs: {ids}")

# 输出示例:
# Text: I love NLP!
# Tokens: ['▁I', '▁love', '▁N', 'LP', '!']
# IDs: [123, 456, 789, 1011, 12]

# 4. 解码(无损还原)
decoded = sp.decode_ids(ids)
print(f"Decoded: {decoded}")
# Decoded: I love NLP!  (完全一致)

4.3 LLaMA的SentencePiece配置

python 复制代码
# LLaMA使用的SentencePiece参数
{
    "vocab_size": 32000,
    "model_type": "BPE",          # LLaMA用BPE而非Unigram
    "character_coverage": 1.0,
    "byte_fallback": True,        # 使用byte级回退处理未知字符
    "split_by_unicode_script": True,
    "split_by_number": True,
    "split_by_whitespace": True,
    "remove_extra_whitespaces": False,
    "normalization_rule_name": "identity"  # 不做规范化
}

五、不同模型的分词策略对比

5.1 GPT系列(BPE)

python 复制代码
from transformers import GPT2Tokenizer

tokenizer = GPT2Tokenizer.from_pretrained('gpt2')

text = "Hello, I'm a language model."
tokens = tokenizer.tokenize(text)
ids = tokenizer.encode(text)

print(f"Tokens: {tokens}")
# ['Hello', ',', 'ĠI', "'m", 'Ġa', 'Ġlanguage', 'Ġmodel', '.']
# 注意:Ġ表示空格

print(f"IDs: {ids}")
# [15496, 11, 314, 1101, 257, 3303, 2746, 13]

# 词表大小
print(f"Vocab size: {tokenizer.vocab_size}")
# 50257

GPT分词特点:
Hello, world!
BPE分词
Hello
,
Ġworld
!

5.2 BERT系列(WordPiece)

python 复制代码
from transformers import BertTokenizer

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')

text = "Hello, I'm learning NLP."
tokens = tokenizer.tokenize(text)
ids = tokenizer.encode(text, add_special_tokens=True)

print(f"Tokens: {tokens}")
# ['hello', ',', 'i', "'", 'm', 'learning', 'nl', '##p', '.']

print(f"IDs: {ids}")
# [101, 7592, 1010, 1045, 1005, 1049, 4083, 17953, 2361, 1012, 102]
# 101=[CLS], 102=[SEP]

# 词表大小
print(f"Vocab size: {tokenizer.vocab_size}")
# 30522

BERT vs GPT分词:

特性 GPT BERT
大小写 保留 小写化(uncased版本)
空格标记 Ġ(前置)
子词标记 ##(后缀)
特殊token <|endoftext|> [CLS], [SEP], [MASK]

5.3 LLaMA系列(SentencePiece BPE)

python 复制代码
from transformers import LlamaTokenizer

tokenizer = LlamaTokenizer.from_pretrained('meta-llama/Llama-2-7b-hf')

text = "Hello! I'm learning AI."
tokens = tokenizer.tokenize(text)
ids = tokenizer.encode(text)

print(f"Tokens: {tokens}")
# ['▁Hello', '!', '▁I', "'", 'm', '▁learning', '▁AI', '.']

print(f"IDs: {ids}")
# [1, 15043, 29991, 306, 29915, 29885, 6509, 319, 29902, 29889]
# 1 = <s>(BOS token)

# 词表大小
print(f"Vocab size: {tokenizer.vocab_size}")
# 32000

LLaMA特点:

  • ✅ 无损可逆(SentencePiece)
  • ✅ 多语言友好
  • ✅ Byte-level fallback处理未知字符

六、分词对模型性能的影响

6.1 词表大小的权衡

词表过大 >100K
❌ 参数膨胀
显存占用高
训练困难
适中 30K-50K
✅ 平衡
效率与性能兼顾
词表过小 <10K
❌ 序列过长
计算量增加
上下文受限

实验数据:

词表大小 平均序列长度 参数量增加 Perplexity
8K 1.8x - 18.5
16K 1.5x +1.2% 17.2
32K 1.0x +2.4% 16.8 (最优)
64K 0.7x +4.8% 17.1
128K 0.5x +9.6% 17.6

6.2 中文分词的挑战

问题:中文没有天然的分隔符

python 复制代码
# 英文
"I love AI" → ["I", "love", "AI"]  # 空格天然分隔

# 中文
"我爱AI" → ???
# 方案1: 字符级 ["我", "爱", "A", "I"]  # 序列过长
# 方案2: 词级 ["我", "爱", "AI"]       # 需要分词工具
# 方案3: 子词级 ["我", "爱", "AI"]      # SentencePiece最佳

对比实验:

python 复制代码
from transformers import BertTokenizer, GPT2Tokenizer

text_cn = "我爱学习人工智能技术"

# BERT(WordPiece,有中文词表)
bert_tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
bert_tokens = bert_tokenizer.tokenize(text_cn)
print(f"BERT: {bert_tokens}")
# ['我', '爱', '学', '习', '人', '工', '智', '能', '技', '术']  # 字符级

# GPT-2(BPE,无中文优化)
gpt2_tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
gpt2_tokens = gpt2_tokenizer.tokenize(text_cn)
print(f"GPT-2: {gpt2_tokens}")
# 乱码或大量未知token!

# LLaMA(SentencePiece,多语言)
llama_tokenizer = LlamaTokenizer.from_pretrained('meta-llama/Llama-2-7b-hf')
llama_tokens = llama_tokenizer.tokenize(text_cn)
print(f"LLaMA: {llama_tokens}")
# ['▁我', '爱', '学习', '人工智能', '技术']  # 子词级,更合理

6.3 分词效率对比

文本压缩效率
英文: 'Hello world'
中文: '你好世界'
GPT: 2 tokens

(Hello, Ġworld)
BERT: 2 tokens

(hello, world)
GPT-2: 8+ tokens

(低效)
BERT中文: 4 tokens

(你,好,世,界)
LLaMA: 2-3 tokens

(你好,世界)


七、Hugging Face Tokenizers完整实战

7.1 从零训练一个BPE Tokenizer

python 复制代码
from tokenizers import Tokenizer
from tokenizers.models import BPE
from tokenizers.trainers import BpeTrainer
from tokenizers.pre_tokenizers import Whitespace
from tokenizers.processors import TemplateProcessing

# 1. 初始化BPE模型
tokenizer = Tokenizer(BPE(unk_token="<unk>"))

# 2. 预分词器(按空格)
tokenizer.pre_tokenizer = Whitespace()

# 3. 定义训练器
trainer = BpeTrainer(
    vocab_size=30000,
    min_frequency=2,
    special_tokens=[
        "<pad>",
        "<unk>",
        "<s>",      # 句子开始
        "</s>",     # 句子结束
        "<mask>"
    ],
    show_progress=True
)

# 4. 训练(可以从文件或迭代器)
files = ["data/train.txt"]
tokenizer.train(files, trainer)

# 或从迭代器训练
# def batch_iterator():
#     for i in range(1000):
#         yield f"This is sentence {i}"
# tokenizer.train_from_iterator(batch_iterator(), trainer)

# 5. 后处理:添加特殊token
tokenizer.post_processor = TemplateProcessing(
    single="<s> $A </s>",
    pair="<s> $A </s> </s> $B </s>",
    special_tokens=[
        ("<s>", tokenizer.token_to_id("<s>")),
        ("</s>", tokenizer.token_to_id("</s>")),
    ],
)

# 6. 保存
tokenizer.save("my_bpe_tokenizer.json")

# 7. 使用
text = "Hello world! This is a test."
output = tokenizer.encode(text)

print(f"Original: {text}")
print(f"Tokens: {output.tokens}")
print(f"IDs: {output.ids}")
print(f"Decoded: {tokenizer.decode(output.ids)}")

7.2 自定义Normalizer和PreTokenizer

python 复制代码
from tokenizers import normalizers, pre_tokenizers
from tokenizers.normalizers import NFD, StripAccents, Lowercase
from tokenizers.pre_tokenizers import ByteLevel

# 1. 组合多个normalizer
tokenizer.normalizer = normalizers.Sequence([
    NFD(),           # Unicode规范化
    StripAccents(),  # 去除重音符号
    Lowercase()      # 小写化
])

# 2. Byte-level预分词(GPT-2风格)
tokenizer.pre_tokenizer = ByteLevel(add_prefix_space=True)

# 测试
text = "Café résumé"
normalized = tokenizer.normalizer.normalize_str(text)
print(f"Normalized: {normalized}")
# Normalized: cafe resume

7.3 批量编码和高效处理

python 复制代码
from tokenizers import Tokenizer

tokenizer = Tokenizer.from_file("my_bpe_tokenizer.json")

# 1. 批量编码
texts = [
    "Hello world!",
    "How are you?",
    "I'm learning NLP."
]

# 单线程
outputs = tokenizer.encode_batch(texts)
for i, output in enumerate(outputs):
    print(f"Text {i}: {output.tokens}")

# 2. 多线程加速
tokenizer.enable_padding(pad_id=0, pad_token="<pad>")
tokenizer.enable_truncation(max_length=512)

outputs = tokenizer.encode_batch(texts, is_pretokenized=False)

# 3. 转换为PyTorch/TensorFlow格式
input_ids = [output.ids for output in outputs]
attention_mask = [output.attention_mask for output in outputs]

import torch
input_ids = torch.tensor(input_ids)
attention_mask = torch.tensor(attention_mask)

print(f"Input IDs shape: {input_ids.shape}")
print(f"Attention mask shape: {attention_mask.shape}")

八、分词器的性能分析与优化

8.1 分词速度对比

python 复制代码
import time
from transformers import AutoTokenizer

models = [
    "bert-base-uncased",
    "gpt2",
    "meta-llama/Llama-2-7b-hf",
    "google/flan-t5-base"
]

text = "This is a test sentence for tokenization speed comparison." * 100

for model_name in models:
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    
    start = time.time()
    for _ in range(100):
        tokens = tokenizer.encode(text)
    end = time.time()
    
    print(f"{model_name}: {(end-start)/100*1000:.2f} ms/iteration")

# 输出示例:
# bert-base-uncased: 0.52 ms/iteration
# gpt2: 0.48 ms/iteration
# meta-llama/Llama-2-7b-hf: 0.61 ms/iteration
# google/flan-t5-base: 0.55 ms/iteration

8.2 内存占用分析

python 复制代码
import sys

tokenizer = AutoTokenizer.from_pretrained("gpt2")

# 1. 词表大小
vocab_size = tokenizer.vocab_size
print(f"Vocab size: {vocab_size}")

# 2. 估算内存占用
# 假设每个token ID是int32(4 bytes)
embedding_memory = vocab_size * 768 * 4 / 1024 / 1024  # 768是embedding维度
print(f"Embedding layer: {embedding_memory:.2f} MB")

# LM Head(输出层)
lm_head_memory = vocab_size * 768 * 4 / 1024 / 1024
print(f"LM Head: {lm_head_memory:.2f} MB")

print(f"Total: {(embedding_memory + lm_head_memory):.2f} MB")

# 输出示例(GPT-2):
# Vocab size: 50257
# Embedding layer: 147.07 MB
# LM Head: 147.07 MB
# Total: 294.14 MB  (占模型总参数的20%+)

8.3 分词质量评估

python 复制代码
def evaluate_tokenizer(tokenizer, test_texts):
    """
    评估分词器质量
    """
    total_tokens = 0
    total_chars = 0
    oov_count = 0
    
    for text in test_texts:
        tokens = tokenizer.tokenize(text)
        total_tokens += len(tokens)
        total_chars += len(text)
        
        # 统计UNK token
        oov_count += sum(1 for t in tokens if t in ['<unk>', '[UNK]', 'Ġ<unk>'])
    
    # 1. 压缩率(越小越好)
    compression_ratio = total_tokens / total_chars
    
    # 2. OOV率(越低越好)
    oov_rate = oov_count / total_tokens
    
    print(f"压缩率: {compression_ratio:.3f}")
    print(f"OOV率: {oov_rate:.3%}")
    print(f"平均token长度: {total_chars/total_tokens:.2f} chars/token")
    
    return {
        'compression_ratio': compression_ratio,
        'oov_rate': oov_rate
    }

# 测试
test_texts = [
    "The quick brown fox jumps over the lazy dog.",
    "I love machine learning and natural language processing!",
    "This is a test of the tokenizer's performance."
]

tokenizer_gpt = AutoTokenizer.from_pretrained("gpt2")
tokenizer_bert = AutoTokenizer.from_pretrained("bert-base-uncased")

print("GPT-2:")
evaluate_tokenizer(tokenizer_gpt, test_texts)

print("\nBERT:")
evaluate_tokenizer(tokenizer_bert, test_texts)

九、常见问题与最佳实践

9.1 特殊token的处理

python 复制代码
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("gpt2")

# 1. 查看特殊token
print(f"BOS token: {tokenizer.bos_token} (ID: {tokenizer.bos_token_id})")
print(f"EOS token: {tokenizer.eos_token} (ID: {tokenizer.eos_token_id})")
print(f"PAD token: {tokenizer.pad_token} (ID: {tokenizer.pad_token_id})")
print(f"UNK token: {tokenizer.unk_token} (ID: {tokenizer.unk_token_id})")

# 2. 添加新的特殊token
new_tokens = ["<|system|>", "<|user|>", "<|assistant|>"]
tokenizer.add_special_tokens({'additional_special_tokens': new_tokens})

print(f"New vocab size: {len(tokenizer)}")

# 3. 使用特殊token
text = "<|system|> You are a helpful assistant. <|user|> Hello!"
tokens = tokenizer.tokenize(text)
print(f"Tokens: {tokens}")

# 4. 重要:模型embedding需要resize
# model.resize_token_embeddings(len(tokenizer))

9.2 处理长文本

python 复制代码
# 方案1: Truncation(截断)
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")

long_text = "This is a very long text..." * 1000

encoded = tokenizer(
    long_text,
    max_length=512,
    truncation=True,     # 超过512截断
    padding='max_length',
    return_tensors='pt'
)

print(f"Input shape: {encoded['input_ids'].shape}")  # torch.Size([1, 512])

# 方案2: Sliding Window(滑动窗口)
def encode_with_sliding_window(text, tokenizer, max_length=512, stride=256):
    """
    滑动窗口编码
    """
    tokens = tokenizer.tokenize(text)
    
    chunks = []
    for i in range(0, len(tokens), stride):
        chunk = tokens[i:i+max_length]
        chunk_ids = tokenizer.convert_tokens_to_ids(chunk)
        chunks.append(chunk_ids)
        
        if i + max_length >= len(tokens):
            break
    
    return chunks

chunks = encode_with_sliding_window(long_text, tokenizer)
print(f"分成 {len(chunks)} 个chunks")

9.3 多语言分词

python 复制代码
# 使用XLM-RoBERTa(100+语言)
from transformers import XLMRobertaTokenizer

tokenizer = XLMRobertaTokenizer.from_pretrained('xlm-roberta-base')

texts = {
    'en': "Hello world!",
    'zh': "你好世界!",
    'ja': "こんにちは世界!",
    'ar': "مرحبا بالعالم!",
    'ru': "Привет мир!"
}

for lang, text in texts.items():
    tokens = tokenizer.tokenize(text)
    print(f"{lang}: {tokens}")

# 输出:
# en: ['▁Hello', '▁world', '!']
# zh: ['▁你好', '世界', '!']
# ja: ['▁', 'こんにちは', '世界', '!']
# ar: ['▁مرحبا', '▁بالعالم', '!']
# ru: ['▁Привет', '▁мир', '!']

十、总结与最佳实践

10.1 分词器选择指南

英文为主
多语言
中文为主
生成
理解
字符级
子词级
你的需求?
语言?
English
Multi
Chinese
模型类型?
GPT系列

BPE
BERT系列

WordPiece
XLM-RoBERTa

SentencePiece
粒度?
BERT中文

字符级
LLaMA

SentencePiece

10.2 关键要点总结

问题 解决方案 最佳实践
OOV 使用子词分词(BPE/WordPiece) 词表30K-50K
多语言 SentencePiece + byte-level fallback XLM-R, mT5
长文本 Sliding window + sparse attention Longformer
中文 SentencePiece或字符级 LLaMA, BERT中文
效率 预计算+缓存 Hugging Face Fast Tokenizers

10.3 未来趋势

未来方向
当前主流
BPE

GPT系列
WordPiece

BERT系列
SentencePiece

LLaMA系列
Token-free模型

直接处理bytes
动态词表

根据任务调整
多模态分词

统一文本/图像/音频

研究前沿:

  1. Byte-level模型: ByT5, CANINE (无需分词)
  2. 多模态统一: CLIP, Flamingo (统一tokenizer)
  3. 可学习分词: 端到端训练分词器

十一、练习与资源

练习题

1. 手动实现BPE

python 复制代码
# 补全BPE编码函数
def bpe_encode(text, merge_ops):
    # TODO: 实现BPE分词
    pass

2. 对比不同tokenizer

python 复制代码
# 对比GPT vs BERT vs LLaMA在同一文本上的分词结果
text = "I'm learning about transformers and tokenization!"
# TODO: 分析压缩率、token数量、OOV等

3. 训练自定义tokenizer

python 复制代码
# 在自己的数据集上训练BPE tokenizer
# TODO: 使用Hugging Face Tokenizers库
相关推荐
后端小肥肠1 小时前
喂饭级教程!免费部署云端 OpenClaw + 打通飞书,自动抓取 ClawHub 技能并写入飞书表格
人工智能·agent
AI_56782 小时前
Nmap端口扫描:SYN扫描+脚本绕过提升成功率
人工智能·nmap
人工智能培训2 小时前
多模态大模型的统一表征与推理范式
人工智能·深度学习·ai大模型·多模态学习·具身智能·企业ai转型
szxinmai主板定制专家2 小时前
RK3588 8个USB工控解决方案,适用于机器视觉,工业互联等
arm开发·人工智能·fpga开发
mao_feng2 小时前
《AI智脉速递》2026 年 2月16日 - 2月23日
人工智能
2501_943695332 小时前
大专市场调查与统计分析专业,怎么学习市场调研问卷的设计?
人工智能·学习
阿甘编程点滴2 小时前
人声伴奏分离工具5款实测精选
人工智能
小咖自动剪辑2 小时前
豆包AI去水印插件:一键去除图片水印,网页/电脑/手机版通用教程
人工智能
sponge'2 小时前
opencv学习笔记14:transformer
笔记·学习·transformer