SentencePiece 是一种无监督的文本分词器和去分词器,主要用于基于神经网络的文本生成系统,在模型训练前需预先确定词汇量大小。SentencePiece 实现了子词单元(例如 byte-pair-encoding (BPE) [Sennrich 等人])和 unigram 语言模型 [Kudo]),并支持直接从原始句子进行训练。SentencePiece 使我们能够构建一个完全端到端的系统,无需依赖特定语言的前处理/后处理。
这不是 Google 的官方产品。
对于不熟悉 SentencePiece (作为软件/算法)的读者,可以阅读这篇温和的介绍。
| 特性 | SentencePiece | subword-nmt | WordPiece |
|---|---|---|---|
| 支持的算法 | BPE, unigram, char, word | BPE | BPE* |
| 开源? | 是 | 是 | Google 内部 |
| 子词正则化 | 是 | 否 | 否 |
| Python 库 (pip) | 是 | 否 | N/A |
| C++ 库 | 是 | 否 | N/A |
| 需要预分割? | 否 | 是 | 是 |
| 可自定义的规范化 (例如 NFKC) | 是 | 否 | N/A |
| 直接生成 ID | 是 | 否 | N/A |
注意:WordPiece 中使用的 BPE 算法与原始 BPE 略有不同。
SentencePiece 是子词单元的一种重实现,是缓解神经机器翻译中开放词汇问题的有效方法。SentencePiece 支持两种分割方式:byte-pair-encoding (BPE) [Sennrich 等人] 和 unigram 语言模型 [Kudo]。以下是与其他实现的主要区别。
神经机器翻译模型通常使用固定词汇量进行操作。与大多数假设无限词汇的无监督词分割算法不同,SentencePiece 训练分割模型,使得最终词汇量大小固定,例如 8k、16k 或 32k。
请注意,SentencePiece 为训练指定了最终词汇量大小,这与 subword-nmt 不同,后者使用的是合并操作的次数。合并操作的次数是 BPE 特有的参数,不适用于其他分割算法,包括 unigram、word 和 char。
以前的子词实现假设输入句子是预先分词的。这种约束对于高效训练是必需的,但使得前处理变得复杂,因为我们必须提前运行依赖语言的分词器。SentencePiece 的实现足够快,可以从原始句子训练模型。这对于训练中文和日语(词之间没有明确空格)的分词器和去分词器非常有用。
自然语言处理的第一步是文本分词。例如,一个标准的英语分词器会将文本 "Hello world." 分割成以下三个标记:
[Hello] [World] [.]
一个观察结果是,原始输入和分词后的序列不是可逆的。例如,"World" 和 "." 之间没有空格的信息会从分词序列中丢失,例如, Tokenize(“World.”) == Tokenize(“World .”)
SentencePiece 将输入文本仅视为 Unicode 字符序列。空格也被视为一个普通符号。为了明确地将空格作为基本标记处理,SentencePiece 首先用一个元符号 "▁" (U+2581) 转义空格,如下所示:
Hello▁World.
然后,将此文本分割成小块,例如:
[Hello] [▁Wor] [ld] [.]
由于空格在分割后的文本中得以保留,我们可以无歧义地还原文本。
detokenized = ''.join(pieces).replace('▁', ' ')
此功能使得在不依赖语言特定资源的情况下执行去分词成为可能。
请注意,在使用标准词分割器分割句子时,我们无法应用相同的无损转换,因为它们将空格视为特殊符号。分词后的序列不保留恢复原始句子所需的信息。
子词正则化 [Kudo] 和 BPE-dropout Provilkov 等人 是简单的正则化方法,通过动态子词采样来虚拟地增强训练数据,有助于提高 NMT 模型的准确性和鲁棒性。
要启用子词正则化,您需要将 SentencePiece 库(C++ / Python)集成到 NMT 系统中,以便每次参数更新时采样一个分割,这与标准的离线数据准备不同。以下是 Python 库 的示例。您会发现 'New York' 在每次 SampleEncode (C++) 或 encode with enable_sampling=True (Python) 调用中都被不同地分割。采样参数的详细信息见 sentencepiece_processor.h。
>>> import sentencepiece as spm
>>> s = spm.SentencePieceProcessor(model_file='spm.model')
>>> for n in range(5):
... s.encode('New York', out_type=str, enable_sampling=True, alpha=0.1, nbest_size=-1)
...
['▁', 'N', 'e', 'w', '▁York']
['▁', 'New', '▁York']
['▁', 'New', '▁Y', 'o', 'r', 'k']
['▁', 'New', '▁York']
['▁', 'New', '▁York']
SentencePiece 提供 Python 封装,支持 SentencePiece 训练和分割。
您可以使用以下命令安装 SentencePiece 的 Python 二进制包:
pip install sentencepiece
更多详情,请参见 Python 模块。
构建 SentencePiece 需要以下工具和库:
在 Ubuntu 上,可以使用 apt-get 安装构建工具:
% sudo apt-get install cmake build-essential pkg-config libgoogle-perftools-dev
然后,您可以按如下方式构建和安装命令行工具:
% git clone https://github.com/google/sentencepiece.git
% cd sentencepiece
% mkdir build
% cd build
% cmake ..
% make -j $(nproc)
% sudo make install
% sudo ldconfig -v
在 OSX/macOS 上,将最后一个命令替换为 sudo update_dyld_shared_cache
您可以使用 vcpkg 依赖管理器下载和安装 sentencepiece:
sudo git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install sentencepiece
vcpkg 中的 sentencepiece 端口由 Microsoft 团队成员和社区贡献者保持最新。如果版本过时,请在 vcpkg 存储库中创建问题或拉取请求。
您可以从 GitHub 发布页面 下载轮子。
在发布过程中,我们使用 OpenSSF 的 slsa-framework/slsa-github-generator 生成 SLSA3 签名。要验证发布二进制文件:
attestation.intoto.jsonl。slsa-verifier -artifact-path <the-wheel> -provenance attestation.intoto.jsonl -source github.com/google/sentencepiece -tag <the-tag>
pip install wheel_file.whl
% spm_train --input=<input> --model_prefix=<model_name> --vocab_size=8000 --character_coverage=1.0 --model_type=<type>
--input:每行一个句子的原始语料文件。无需运行分词器、规范化器或预处理器。默认情况下,SentencePiece 使用 Unicode NFKC 规范化输入。您可以传递以逗号分隔的文件列表。--model_prefix:输出模型名称前缀。会生成 <model_name>.model 和 <model_name>.vocab 文件。--vocab_size:词汇量大小,例如 8000、16000 或 32000。--character_coverage:模型覆盖的字符数量,好的默认值是:对于字符集丰富的语言(如日语或中文)为 0.9995,对于字符集较小的其他语言为 1.0。--model_type:模型类型。从 unigram(默认)、bpe、char 或 word 中选择。使用 word 类型时,输入句子必须预先分词。使用 --help 标志显示训练的所有参数,或参见此处了解概述。
% spm_encode --model=<model_file> --output_format=piece < input > output
% spm_encode --model=<model_file> --output_format=id < input > output
使用 --extra_options 标志插入 BOS/EOS 标记或反转输入序列。
% spm_encode --extra_options=eos (仅添加 </s>)
% spm_encode --extra_options=bos:eos (添加 <s> 和 </s>)
% spm_encode --extra_options=reverse:bos:eos (反转输入并添加 <s> 和 </s>)
SentencePiece 支持使用 --output_format=(nbest|sample)_(piece|id) 标志的 nbest 分割和分割采样。
% spm_encode --model=<model_file> --output_format=sample_piece --nbest_size=-1 --alpha=0.5 < input > output
% spm_encode --model=<model_file> --output_format=nbest_id --nbest_size=10 < input > output
% spm_decode --model=<model_file> --input_format=piece < input > output
% spm_decode --model=<model_file> --input_format=id < input > output
使用 --extra_options 标志逆序解码文本。
% spm_decode --extra_options=reverse < input > output
% spm_train --input=data/botchan.txt --model_prefix=m --vocab_size=1000
unigram_model_trainer.cc(494) LOG(INFO) Starts training with :
input: "../data/botchan.txt"
... <snip>
unigram_model_trainer.cc(529) LOG(INFO) EM sub_iter=1 size=1100 obj=10.4973 num_tokens=37630 num_tokens/piece=34.2091
trainer_interface.cc(272) LOG(INFO) Saving model: m.model
trainer_interface.cc(281) LOG(INFO) Saving vocabs: m.vocab
% echo "I saw a girl with a telescope." | spm_encode --model=m.model
▁I ▁saw ▁a ▁girl ▁with ▁a ▁ te le s c o pe .
% echo "I saw a girl with a telescope." | spm_encode --model=m.model --output_format=id
9 459 11 939 44 11 4 142 82 8 28 21 132 6
% echo "9 459 11 939 44 11 4 142 82 8 28 21 132 6" | spm_decode --model=m.model --input_format=id
I saw a girl with a telescope.
您可以看到原始输入句子从词汇 ID 序列中得到了恢复。
% spm_export_vocab --model=<model_file> --output=<output file>
<output file> 存储词汇表和发射对数概率的列表。词汇 ID 对应于该文件中的行号。
默认情况下,SentencePiece 使用 Unknown (<unk>)、BOS (<s>) 和 EOS (</s>) 标记,其 ID 分别为 0、1 和 2。我们可以在训练阶段如下重新定义此映射:
% spm_train --bos_id=0 --eos_id=1 --unk_id=5 --input=... --model_prefix=... --character_coverage=...
当设置 -1 的 ID 时,例如 bos_id=-1,此特殊标记被禁用。请注意,未知 ID 不能禁用。我们可以将 padding (<pad>) 的 ID 定义为 --pad_id=3。
如果您想分配其他特殊标记,请参见使用自定义符号。
spm_encode 接受 --vocabulary 和 --vocabulary_threshold 选项,以便 spm_encode 仅生成也出现在词汇表中(至少具有一定频率)的符号。此功能的背景在 subword-nmt 页面 中描述。
用法基本上与 subword-nmt 相同。假设 L1 和 L2 是两种语言(源语言/目标语言),训练共享的 spm 模型,并为每种语言获取生成的词汇表:
% cat {train_file}.L1 {train_file}.L2 | shuffle > train
% spm_train --input=train --model_prefix=spm --vocab_size=8000 --character_coverage=0.9995
% spm_encode --model=spm.model --generate_vocabulary < {train_file}.L1 > {vocab_file}.L1
% spm_encode --model=spm.model --generate_vocabulary < {train_file}.L2 > {vocab_file}.L2
使用 shuffle 命令只是为了以防万一,因为 spm_train 默认加载语料库的前 1000 万行。
然后使用 --vocabulary 选项分割训练/测试语料库:
% spm_encode --model=spm.model --vocabulary={vocab_file}.L1 --vocabulary_threshold=50 < {test_file}.L1 > {test_file}.seg.L1
% spm_encode --model=spm.model --vocabulary={vocab_file}.L2 --vocabulary_threshold=50 < {test_file}.L2 > {test_file}.seg.L2
以下是与 SentencePiece 相关的项目。它们独立维护。如果需要添加,请发送拉取请求 (PR)。
- SentencePiece 的 Java 工具/绑定