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库
相关推荐
lijianhua_971215 小时前
国内某顶级大学内部用的ai自动生成论文的提示词
人工智能
EDPJ15 小时前
当图像与文本 “各说各话” —— CLIP 中的模态鸿沟与对象偏向
深度学习·计算机视觉
蔡俊锋15 小时前
用AI实现乐高式大型可插拔系统的技术方案
人工智能·ai工程·ai原子能力·ai乐高工程
自然语15 小时前
人工智能之数字生命 认知架构白皮书 第7章
人工智能·架构
大熊背15 小时前
利用ISP离线模式进行分块LSC校正的方法
人工智能·算法·机器学习
eastyuxiao16 小时前
如何在不同的机器上运行多个OpenClaw实例?
人工智能·git·架构·github·php
诸葛务农16 小时前
AGI 主要技术路径及核心技术:归一融合及未来之路5
大数据·人工智能
光影少年16 小时前
AI Agent智能体开发
人工智能·aigc·ai编程
ai生成式引擎优化技术16 小时前
TSPR-WEB-LLM-HIC (TWLH四元结构)AI生成式引擎(GEO)技术白皮书
人工智能
帐篷Li16 小时前
9Router:开源AI路由网关的架构设计与技术实现深度解析
人工智能