HuggingFace分词器应用与训练Skill huggingface-tokenizers

HuggingFace Tokenizers是一个高性能的自然语言处理(NLP)分词工具,基于Rust实现,支持BPE、WordPiece和Unigram算法,可用于训练自定义分词器,处理大规模文本数据,并集成到Transformers库中。适用于需要快速分词或定制分词器的研究和生产环境。关键词:分词、NLP、BPE、WordPiece、Unigram、快速分词、HuggingFace、Transformers、自定义训练、对齐跟踪。

NLP 0 次安装 0 次浏览 更新于 3/21/2026

名称:huggingface-tokenizers 描述:为研究和生产优化的快速分词器。基于Rust的实现,可在<20秒内对1GB文本进行分词。支持BPE、WordPiece和Unigram算法。训练自定义词汇表,跟踪对齐,处理填充/截断。与transformers无缝集成。当您需要高性能分词或自定义分词器训练时使用。 版本:1.0.0 作者:Orchestra Research 许可证:MIT 标签:[分词, HuggingFace, BPE, WordPiece, Unigram, 快速分词, Rust, 自定义分词器, 对齐跟踪, 生产] 依赖项:[tokenizers, transformers, datasets]

HuggingFace Tokenizers - 用于NLP的快速分词

快速、生产就绪的分词器,具有Rust性能和Python易用性。

何时使用HuggingFace Tokenizers

使用HuggingFace Tokenizers当:

  • 需要极快的分词(每GB文本<20秒)
  • 从头开始训练自定义分词器
  • 需要对齐跟踪(token → 原始文本位置)
  • 构建生产NLP流水线
  • 需要高效地对大型语料库进行分词

性能:

  • 速度:在CPU上对1GB文本进行分词<20秒
  • 实现:基于Rust核心,带有Python/Node.js绑定
  • 效率:比纯Python实现快10-100倍

使用替代方案:

  • SentencePiece:语言无关,用于T5/ALBERT
  • tiktoken:OpenAI的BPE分词器,用于GPT模型
  • transformers AutoTokenizer:仅加载预训练(内部使用此库)

快速开始

安装

# 安装tokenizers
pip install tokenizers

# 与transformers集成
pip install tokenizers transformers

加载预训练分词器

from tokenizers import Tokenizer

# 从HuggingFace Hub加载
tokenizer = Tokenizer.from_pretrained("bert-base-uncased")

# 编码文本
output = tokenizer.encode("Hello, how are you?")
print(output.tokens)  # ['hello', ',', 'how', 'are', 'you', '?']
print(output.ids)     # [7592, 1010, 2129, 2024, 2017, 1029]

# 解码回文本
text = tokenizer.decode(output.ids)
print(text)  # "hello, how are you?"

训练自定义BPE分词器

from tokenizers import Tokenizer
from tokenizers.models import BPE
from tokenizers.trainers import BpeTrainer
from tokenizers.pre_tokenizers import Whitespace

# 使用BPE模型初始化分词器
tokenizer = Tokenizer(BPE(unk_token="[UNK]"))
tokenizer.pre_tokenizer = Whitespace()

# 配置训练器
trainer = BpeTrainer(
    vocab_size=30000,
    special_tokens=["[UNK]", "[CLS]", "[SEP]", "[PAD]", "[MASK]"],
    min_frequency=2
)

# 在文件上训练
files = ["train.txt", "validation.txt"]
tokenizer.train(files, trainer)

# 保存
tokenizer.save("my-tokenizer.json")

训练时间:约1-2分钟用于100MB语料库,约10-20分钟用于1GB

批编码与填充

# 启用填充
tokenizer.enable_padding(pad_id=3, pad_token="[PAD]")

# 编码批次
texts = ["Hello world", "This is a longer sentence"]
encodings = tokenizer.encode_batch(texts)

for encoding in encodings:
    print(encoding.ids)
# [101, 7592, 2088, 102, 3, 3, 3]
# [101, 2023, 2003, 1037, 2936, 6251, 102]

分词算法

BPE(字节对编码)

工作原理:

  1. 从字符级词汇表开始
  2. 找到最频繁的字符对
  3. 合并为新token,添加到词汇表
  4. 重复直到达到词汇表大小

用于: GPT-2, GPT-3, RoBERTa, BART, DeBERTa

from tokenizers import Tokenizer
from tokenizers.models import BPE
from tokenizers.trainers import BpeTrainer
from tokenizers.pre_tokenizers import ByteLevel

tokenizer = Tokenizer(BPE(unk_token="<|endoftext|>"))
tokenizer.pre_tokenizer = ByteLevel()

trainer = BpeTrainer(
    vocab_size=50257,
    special_tokens=["<|endoftext|>"],
    min_frequency=2
)

tokenizer.train(files=["data.txt"], trainer=trainer)

优势:

  • 处理OOV词效果好(分解为子词)
  • 词汇表大小灵活
  • 适合形态丰富的语言

权衡:

  • 分词依赖于合并顺序
  • 可能意外拆分常见词

WordPiece

工作原理:

  1. 从字符词汇表开始
  2. 评分合并对:频率(对) / (频率(第一个) × 频率(第二个))
  3. 合并最高评分对
  4. 重复直到达到词汇表大小

用于: BERT, DistilBERT, MobileBERT

from tokenizers import Tokenizer
from tokenizers.models import WordPiece
from tokenizers.trainers import WordPieceTrainer
from tokenizers.pre_tokenizers import Whitespace
from tokenizers.normalizers import BertNormalizer

tokenizer = Tokenizer(WordPiece(unk_token="[UNK]"))
tokenizer.normalizer = BertNormalizer(lowercase=True)
tokenizer.pre_tokenizer = Whitespace()

trainer = WordPieceTrainer(
    vocab_size=30522,
    special_tokens=["[UNK]", "[CLS]", "[SEP]", "[PAD]", "[MASK]"],
    continuing_subword_prefix="##"
)

tokenizer.train(files=["corpus.txt"], trainer=trainer)

优势:

  • 优先有意义合并(高评分 = 语义相关)
  • 在BERT中成功使用(最先进的结果)

权衡:

  • 未知词如果没有子词匹配变为[UNK]
  • 保存词汇表,而非合并规则(文件较大)

Unigram

工作原理:

  1. 从大词汇表开始(所有子字符串)
  2. 计算当前词汇表对语料库的损失
  3. 移除对损失影响最小的token
  4. 重复直到达到词汇表大小

用于: ALBERT, T5, mBART, XLNet(通过SentencePiece)

from tokenizers import Tokenizer
from tokenizers.models import Unigram
from tokenizers.trainers import UnigramTrainer

tokenizer = Tokenizer(Unigram())

trainer = UnigramTrainer(
    vocab_size=8000,
    special_tokens=["<unk>", "<s>", "</s>"],
    unk_token="<unk>"
)

tokenizer.train(files=["data.txt"], trainer=trainer)

优势:

  • 概率性(找到最可能的分词)
  • 适合没有词边界的语言
  • 处理多样化的语言上下文

权衡:

  • 训练计算成本高
  • 更多超参数需要调整

分词流水线

完整流水线:归一化 → 预分词 → 模型 → 后处理

归一化

清理和标准化文本:

from tokenizers.normalizers import NFD, StripAccents, Lowercase, Sequence

tokenizer.normalizer = Sequence([
    NFD(),           # Unicode归一化(分解)
    Lowercase(),     # 转换为小写
    StripAccents()   # 移除重音
])

# 输入:"Héllo WORLD"
# 归一化后:"hello world"

常见归一化器:

  • NFD, NFC, NFKD, NFKC - Unicode归一化形式
  • Lowercase() - 转换为小写
  • StripAccents() - 移除重音(é → e)
  • Strip() - 移除空白
  • Replace(pattern, content) - 正则替换

预分词

将文本拆分为词样单元:

from tokenizers.pre_tokenizers import Whitespace, Punctuation, Sequence, ByteLevel

# 按空白和标点拆分
tokenizer.pre_tokenizer = Sequence([
    Whitespace(),
    Punctuation()
])

# 输入:"Hello, world!"
# 预分词后:["Hello", ",", "world", "!"]

常见预分词器:

  • Whitespace() - 按空格、制表符、换行拆分
  • ByteLevel() - GPT-2风格字节级拆分
  • Punctuation() - 隔离标点
  • Digits(individual_digits=True) - 按数字单独拆分
  • Metaspace() - 用▁替换空格(SentencePiece风格)

后处理

为模型输入添加特殊token:

from tokenizers.processors import TemplateProcessing

# BERT风格:[CLS] 句子 [SEP]
tokenizer.post_processor = TemplateProcessing(
    single="[CLS] $A [SEP]",
    pair="[CLS] $A [SEP] $B [SEP]",
    special_tokens=[
        ("[CLS]", 1),
        ("[SEP]", 2),
    ],
)

常见模式:

# GPT-2:句子 <|endoftext|>
TemplateProcessing(
    single="$A <|endoftext|>",
    special_tokens=[("<|endoftext|>", 50256)]
)

# RoBERTa:<s> 句子 </s>
TemplateProcessing(
    single="<s> $A </s>",
    pair="<s> $A </s> </s> $B </s>",
    special_tokens=[("<s>", 0), ("</s>", 2)]
)

对齐跟踪

跟踪token在原始文本中的位置:

output = tokenizer.encode("Hello, world!")

# 获取token偏移
for token, offset in zip(output.tokens, output.offsets):
    start, end = offset
    print(f"{token:10} → [{start:2}, {end:2}): {text[start:end]!r}")

# 输出:
# hello      → [ 0,  5): 'Hello'
# ,          → [ 5,  6): ','
# world      → [ 7, 12): 'world'
# !          → [12, 13): '!'

用例:

  • 命名实体识别(将预测映射回文本)
  • 问答(提取答案跨度)
  • token分类(将标签对齐到原始位置)

与transformers集成

使用AutoTokenizer加载

from transformers import AutoTokenizer

# AutoTokenizer自动使用快速分词器
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")

# 检查是否使用快速分词器
print(tokenizer.is_fast)  # True

# 访问底层tokenizers.Tokenizer
fast_tokenizer = tokenizer.backend_tokenizer
print(type(fast_tokenizer))  # <class 'tokenizers.Tokenizer'>

将自定义分词器转换为transformers

from tokenizers import Tokenizer
from transformers import PreTrainedTokenizerFast

# 训练自定义分词器
tokenizer = Tokenizer(BPE())
# ... 训练分词器 ...
tokenizer.save("my-tokenizer.json")

# 包装用于transformers
transformers_tokenizer = PreTrainedTokenizerFast(
    tokenizer_file="my-tokenizer.json",
    unk_token="[UNK]",
    pad_token="[PAD]",
    cls_token="[CLS]",
    sep_token="[SEP]",
    mask_token="[MASK]"
)

# 像任何transformers分词器一样使用
outputs = transformers_tokenizer(
    "Hello world",
    padding=True,
    truncation=True,
    max_length=512,
    return_tensors="pt"
)

常见模式

从迭代器训练(大型数据集)

from datasets import load_dataset

# 加载数据集
dataset = load_dataset("wikitext", "wikitext-103-raw-v1", split="train")

# 创建批次迭代器
def batch_iterator(batch_size=1000):
    for i in range(0, len(dataset), batch_size):
        yield dataset[i:i + batch_size]["text"]

# 训练分词器
tokenizer.train_from_iterator(
    batch_iterator(),
    trainer=trainer,
    length=len(dataset)  # 用于进度条
)

性能:处理1GB约10-20分钟

启用截断和填充

# 启用截断
tokenizer.enable_truncation(max_length=512)

# 启用填充
tokenizer.enable_padding(
    pad_id=tokenizer.token_to_id("[PAD]"),
    pad_token="[PAD]",
    length=512  # 固定长度,或None用于批次最大长度
)

# 编码两者
output = tokenizer.encode("This is a long sentence that will be truncated...")
print(len(output.ids))  # 512

多进程

from tokenizers import Tokenizer
from multiprocessing import Pool

# 加载分词器
tokenizer = Tokenizer.from_file("tokenizer.json")

def encode_batch(texts):
    return tokenizer.encode_batch(texts)

# 并行处理大型语料库
with Pool(8) as pool:
    # 将语料库拆分为块
    chunk_size = 1000
    chunks = [corpus[i:i+chunk_size] for i in range(0, len(corpus), chunk_size)]

    # 并行编码
    results = pool.map(encode_batch, chunks)

加速:使用8核时5-8倍

性能基准

训练速度

语料库大小 BPE (30k词汇表) WordPiece (30k) Unigram (8k)
10 MB 15 秒 18 秒 25 秒
100 MB 1.5 分钟 2 分钟 4 分钟
1 GB 15 分钟 20 分钟 40 分钟

硬件:16核CPU,在英文维基百科上测试

分词速度

实现 1 GB语料库 吞吐量
纯Python ~20分钟 ~50 MB/min
HF Tokenizers ~15秒 ~4 GB/min
加速 80× 80×

测试:英文文本,平均句子长度20词

内存使用

任务 内存
加载分词器 ~10 MB
训练BPE (30k词汇表) ~200 MB
编码100万句子 ~500 MB

支持模型

通过from_pretrained()可用的预训练分词器:

BERT系列:

  • bert-base-uncased, bert-large-cased
  • distilbert-base-uncased
  • roberta-base, roberta-large

GPT系列:

  • gpt2, gpt2-medium, gpt2-large
  • distilgpt2

T5系列:

  • t5-small, t5-base, t5-large
  • google/flan-t5-xxl

其他:

  • facebook/bart-base, facebook/mbart-large-cc25
  • albert-base-v2, albert-xlarge-v2
  • xlm-roberta-base, xlm-roberta-large

浏览所有:https://huggingface.co/models?library=tokenizers

参考文献

资源