SentencePiece 子词分词完全指南:语言无关的 BPE / Unigram 分词器实战
2026/9/23 2:32:46 网站建设 项目流程

SentencePiece 子词分词完全指南:语言无关的 BPE / Unigram 分词器实战

【免费下载链接】AI-Research-SKILLsComprehensive open-source library of AI research and engineering skills for any AI model. Package the skills and your claude code/codex/gemini agent will be an AI research agent with full horsepower. Maintained by Orchestra Research.项目地址: https://gitcode.com/gh_mirrors/ai/AI-Research-SKILLs

SentencePiece 是 Google 开源的语言无关(language-independent)子词分词器,它将文本视为原始 Unicode 序列直接处理,无需任何语言专属的预分词规则,因此天然适合多语言模型、CJK(中文/日文/韩文)场景以及需要可复现分词结果的科研与工程任务。在本仓库的 AI Research Skills 体系中,它被定位为「子词分词(Subword tokenization)」任务的指定技能(参见 技能路由表)。读完本文,你将掌握 SentencePiece 的安装、训练、编码/解码全流程,理解 BPE 与 Unigram 两种核心算法及其适用场景,并能在 T5、ALBERT、XLNet、mBART 等主流模型生态中直接落地使用。

何时使用 SentencePiece

优先选择 SentencePiece 的场景

  • 构建多语言模型:无需任何语言专属规则,同一套流程处理所有语言;
  • 处理 CJK 语言(中文、日文、韩文):这些语言没有显式词边界,SentencePiece 直接在原始文本上切分;
  • 需要可复现的分词结果:词表(vocabulary)确定性生成,同一语料产出完全一致的模型;
  • 希望在原始文本上直接训练:不需要预分词(pre-tokenization)环节;
  • 追求轻量部署:加载后的模型仅占用约 6MB 内存,分词吞吐约 5 万句/秒。

性能概览

指标数值
分词速度约 50,000 句/秒
模型加载内存约 6MB
支持语言全部(语言无关)

何时改用替代方案

  • HuggingFace Tokenizers:训练更快、灵活性更高(Rust 核心实现,可追踪 token 与原文字符的对齐关系),详见 02-tokenization/huggingface-tokenizers/SKILL.md;
  • tiktoken:OpenAI GPT-3.5/4 系列模型使用;
  • BERT WordPiece:偏英语中心的任务。

在仓库的 Tokenization 分类下,两个技能是互补关系:HuggingFace Tokenizers 负责「高性能与自定义分词器训练」,SentencePiece 负责「子词分词」(见 技能路由表)。

快速开始

安装

# Python pip install sentencepiece # C++(需要 CMake) git clone https://github.com/google/sentencepiece.git cd sentencepiece mkdir build && cd build cmake .. && make -j $(nproc) sudo make install

Python 环境安装后即可使用spm.SentencePieceTrainer(训练)与spm.SentencePieceProcessor(推理)两个核心类。

训练模型

# 命令行(BPE,8000 词表) spm_train --input=data.txt --model_prefix=m --vocab_size=8000 --model_type=bpe # Python API import sentencepiece as spm spm.SentencePieceTrainer.train( input='data.txt', model_prefix='m', vocab_size=8000, model_type='bpe' )

训练耗时参考:约 100MB 语料 1~2 分钟。

编码与解码

import sentencepiece as spm # 加载模型 sp = spm.SentencePieceProcessor(model_file='m.model') # 编码为子词片段 pieces = sp.encode('This is a test', out_type=str) print(pieces) # ['▁This', '▁is', '▁a', '▁test'] # 编码为 token ID ids = sp.encode('This is a test', out_type=int) print(ids) # [284, 47, 11, 1243] # 解码还原 text = sp.decode(ids) print(text) # "This is a test"

注意out_type参数决定编码结果的形态:str返回可读的子词片段,int返回词表索引 ID。decode接受 ID 列表并还原原始文本。

语言无关的设计核心:▁ 元符号

SentencePiece 最关键的机制是将空白符作为普通字符处理,训练前先把空白替换为元符号(U+2581),解码时再还原为空格。这样分词器无需预先按空格切词,从根源上绕开了「语言是否有词边界」的问题。

text = "Hello world" pieces = sp.encode(text, out_type=str) print(pieces) # ['▁Hello', '▁world'] # 解码保留空格 decoded = sp.decode_pieces(pieces) print(decoded) # "Hello world"

核心原则:把文本当作原始 Unicode 处理,空白 =(元符号)。这也是它在 CJK 场景表现优异的原因——中文句子不需要先分词,直接切出有意义的子词单元。

Tokenization 算法:BPE 与 Unigram

SentencePiece 支持四种model_typeunigram(默认)、bpecharword。其中 BPE 与 Unigram 是最常用的两种,详细原理见 references/algorithms.md。

BPE(Byte-Pair Encoding)

算法流程

  1. 以字符集初始化词表;
  2. 统计相邻 token 对的共现频率;
  3. 合并出现频率最高的对;
  4. 重复直到达到目标词表大小。

示例(语料low:5, lower:2, newest:6, widest:3):

  • 第 1 轮:最高频对为e+s(9 次),合并为es
  • 第 2 轮:最高频为es+t(9 次),合并为est
  • 结果:newestnew|estwidestwid|est
spm.SentencePieceTrainer.train( input='data.txt', model_prefix='bpe_model', vocab_size=16000, model_type='bpe' )

优势:算法简单、训练快、压缩比好。劣势:分词结果是确定性的(无法采样),可能意外拆散常见词。代表模型:mBART(facebook/mbart-large-50,25 万词表)。

Unigram(默认推荐)

算法流程

  1. 从超大种子词表(覆盖所有子串)出发;
  2. 计算每个 token 的概率;
  3. 逐步剔除对整体损失影响最小的 token;
  4. 重复直到达到目标词表大小。

概率分词示例(对 "lowest"):

Option 1: ['low', 'est'] P = 0.02 × 0.03 = 0.0006 ← 概率最高,被选中 Option 2: ['l', 'o', 'w', 'est'] P = 0.01 × 0.015 × 0.01 × 0.03 = 0.000000045
spm.SentencePieceTrainer.train( input='data.txt', model_prefix='unigram_model', vocab_size=8000, model_type='unigram' )

优势:概率式分词(支持采样),对形态丰富的语言效果更好,天然支持子词正则化。劣势:训练较慢、算法更复杂。代表模型:T5、ALBERT、XLNet。

两种算法对比

特性BPEUnigram
训练速度
分词方式确定性概率式
支持采样
常见词表大小16k–32k8k–32k
代表模型mBARTT5, ALBERT, XLNet

训练配置详解

核心参数

spm.SentencePieceTrainer.train( # 必填 input='corpus.txt', # 输入语料 model_prefix='output', # 输出前缀 vocab_size=8000, # 目标词表大小 # 算法 model_type='unigram', # 'unigram'、'bpe'、'char'、'word' # 覆盖率 character_coverage=0.9995, # 多数语言 0.9995,CJK 用 1.0 # 归一化 normalization_rule_name='nmt_nfkc', # 'nmt_nfkc'、'nfkc'、'identity' # 性能 num_threads=16, # 训练线程数 input_sentence_size=10000000 # 最大加载句子数 )

特殊 token 配置

spm.SentencePieceTrainer.train( input='corpus.txt', model_prefix='m', vocab_size=32000, # 控制符号(模型控制用特殊 token) control_symbols=['<s>', '</s>', '<pad>'], # 用户自定义符号(永不切分) user_defined_symbols=['[MASK]', '[SEP]', '[CLS]'], # 特殊 token 的 piece 形式 unk_piece='<unk>', bos_piece='<s>', eos_piece='</s>', pad_piece='<pad>', # 特殊 token 的 ID unk_id=0, bos_id=1, eos_id=2, pad_id=3 )

高级选项

spm.SentencePieceTrainer.train( input='corpus.txt', model_prefix='m', vocab_size=32000, # 字节回退(处理未知字符,如 emoji) byte_fallback=True, # 数字处理 split_digits=True, # 数字逐位切分 # 脚本切分 split_by_unicode_script=True, # 按 Unicode 脚本切分 split_by_whitespace=True, # 按空白切分 # 长度约束 max_sentencepiece_length=16, # 单个 token 最大长度 # 低频词处理 min_frequency=2, # token 最低出现频次 # 训练规模 input_sentence_size=10000000, # 最大句子数 shuffle_input_sentence=True, # 打乱训练数据 # 种子词表 seed_sentencepiece_size=1000000 # 种子词表大小 )

各参数的详细说明与调优建议可参考 references/training.md。

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

import sentencepiece as spm from datasets import load_dataset # 加载数据集 dataset = load_dataset('wikitext', 'wikitext-103-raw-v1', split='train') # 创建迭代器 def corpus_iterator(): for example in dataset: if example['text'].strip(): yield example['text'] # 从迭代器训练 spm.SentencePieceTrainer.train( sentence_iterator=corpus_iterator(), model_prefix='wiki', vocab_size=32000, model_type='unigram' )

train结束后会输出两个文件:m.model(二进制模型)与m.vocab(文本词表),分别用于推理加载和词表检查。

字符覆盖率(Character Coverage)

character_coverage决定训练时覆盖语料中字符的比例,未覆盖的字符将落入未知 token。注意:它只作用于训练时字符级采样,而非切分限制。

语言类型建议覆盖率原因
英语0.9995覆盖 a-z、A-Z、标点及常见重音即可
CJK(中文)1.0汉字体系必须全部覆盖
多语言0.9995–1.0在覆盖率与词表大小间取平衡
# 中文语料:全覆盖 spm.SentencePieceTrainer.train( input='zh_corpus.txt', character_coverage=1.0 )

词表大小选择

任务词表大小依据
英语单语16k–32k标准配置
多语言32k–250k语言越多词表越大
CJK32k–100k汉字字符基数大
代码16k–32k与英语类似

归一化规则(Normalization)

归一化在训练与推理前对文本做统一化处理,直接影响 token 的稳定性:

  • nmt_nfkc(推荐):NFKC Unicode 归一化 + 空白处理,适合绝大多数任务;
  • identity:不做任何归一化,原样保留输入,适用于代码、大小写敏感任务;
  • nfkc:标准 NFKC 归一化,强度低于nmt_nfkc
normalization_rule_name='identity' # 代码 / 大小写敏感场景

编码选项:子词正则化(Subword Regularization)

Unigram 模型支持在编码时对同一文本采样多种合法切分,从而对训练数据做隐式增强:

sp = spm.SentencePieceProcessor(model_file='m.model') # 采样不同切分 for _ in range(3): pieces = sp.encode('tokenization', out_type=str, enable_sampling=True, alpha=0.1) print(pieces) # 输出(每次不同): # ['▁token', 'ization'] # ['▁tok', 'en', 'ization'] # ['▁token', 'iz', 'ation']

alpha参数(正则化强度):

  • 0.0:确定性分词(不采样);
  • 0.1:轻微扰动(推荐平衡点);
  • 0.5:高扰动;
  • 1.0:最大扰动。

收益:① 鲁棒性——模型学到多种合法切分;② 数据增强——训练样本更多样;③ 泛化——减少对特定切分的过拟合。代表应用:mT5、XLM-RoBERTa。实践上推荐训练时开启采样、推理时关闭(确定性模式),以获得一致输出。

# 训练循环中的用法 for batch in dataloader: tokens = sp.encode(batch['text'], enable_sampling=True, alpha=0.1) # 训练模型...

NBest 编码:获取多种切分候选

sp = spm.SentencePieceProcessor(model_file='m.model') # 获取 top-5 切分 nbest = sp.nbest_encode('tokenization', nbest_size=5, out_type=str) for pieces, score in nbest: print(f"{pieces} (log prob: {score:.4f})") # 输出: # ['▁token', 'ization'] (log prob: -2.34) # ['▁tok', 'en', 'ization'] (log prob: -2.41) # ['▁token', 'iz', 'ation'] (log prob: -2.57)

应用场景:① 集成分词——对多种切分结果取平均;② 不确定性估计——观察分数方差;③ 调试——理解分词器行为。

常见模式

T5 风格训练

T5 使用 Unigram + 100 个<extra_id_*>哨兵 token 作为去噪目标:

spm.SentencePieceTrainer.train( input='c4_corpus.txt', model_prefix='t5', vocab_size=32000, model_type='unigram', user_defined_symbols=[f'<extra_id_{i}>' for i in range(100)], unk_id=2, eos_id=1, pad_id=0 )

与 transformers 集成

from transformers import T5Tokenizer # T5 内部使用 SentencePiece tokenizer = T5Tokenizer.from_pretrained('t5-base') inputs = tokenizer('translate English to French: Hello', return_tensors='pt')

性能基准

训练速度

语料BPE (16k)Unigram (8k)
100 MB1–2 分钟3–4 分钟
1 GB10–15 分钟30–40 分钟

分词速度

  • SentencePiece:约 50,000 句/秒;
  • HF Tokenizers:约 200,000 句/秒(约 4 倍)。

若吞吐成为瓶颈,可参考仓库中 HuggingFace Tokenizers 技能(Rust 核心,1GB 语料 <20 秒)。

性能优化实践

多线程训练

spm.SentencePieceTrainer.train( input='large_corpus.txt', num_threads=32 # 使用全部核心 )

加速比:16+ 核心下约 4–8 倍。

超大语料采样

# 大于 10GB 语料:采样 1000 万句 spm.SentencePieceTrainer.train( input='huge_corpus.txt', input_sentence_size=10000000, shuffle_input_sentence=True )

超大规模语料(>10GB)

spm.SentencePieceTrainer.train( input='massive_corpus.txt', train_extremely_large_corpus=True, # 为 >10GB 语料启用 input_sentence_size=100000000 )

支持的主流模型

模型词表算法
T5 系列(t5-baset5-large32kUnigram
ALBERT(albert-base-v230kUnigram
XLNet(xlnet-base-cased32kUnigram
mBART(facebook/mbart-large-50250kBPE

最佳实践清单

  1. 多数任务优先 Unigram:对多语言更友好;
  2. CJK 语料设character_coverage=1.0:保证汉字全覆盖;
  3. 使用nmt_nfkc归一化:通用场景表现稳定;
  4. 为特殊 token 添加user_defined_symbols:如 BERT 风格[CLS]/[SEP]
  5. 开启byte_fallback:增强对 emoji 与生僻字的鲁棒性;
  6. 词表从 32000 起步:多数任务的良好默认值;
  7. 开启多线程训练:显著缩短训练时间;
  8. 多语言场景选 Unigram、追求速度选 BPE:按需求权衡;
  9. 训练开启子词正则化(alpha=0.1)、推理用确定性模式:兼顾鲁棒与一致。

深入阅读

  • SentencePiece 训练指南——完整的训练参数、语料准备与性能调优;
  • Tokenization 算法详解——BPE 与 Unigram 算法对比及子词正则化原理;
  • 技能路由表——了解该技能在自动研究流程中的定位与触发场景;
  • HuggingFace Tokenizers 技能——需要更高吞吐或对齐追踪时的互补方案。

版本说明:以上用法基于 sentencepiece 0.2.0+ 与 transformers 最新稳定版的 Python API;命令行工具spm_train/spm_spm_encode参数与 Python API 一一对应,可放心混用。

【免费下载链接】AI-Research-SKILLsComprehensive open-source library of AI research and engineering skills for any AI model. Package the skills and your claude code/codex/gemini agent will be an AI research agent with full horsepower. Maintained by Orchestra Research.项目地址: https://gitcode.com/gh_mirrors/ai/AI-Research-SKILLs

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询