SentencePiece 是一种无监督文本分词器和去分词器,主要用于基于神经网络的文本生成系统,其中词汇量大小在神经模型训练之前预先确定。 SentencePiece 通过扩展原始句子的直接训练来实现子字单元(例如,字节对编码(BPE) [Sennrich 等人])和一元语言模型[Kudo.])。 SentencePiece 允许我们制作一个纯粹的端到端系统,不依赖于特定于语言的预处理/后处理。
这不是 Google 官方产品。
对于那些不熟悉 SentencePiece 软件/算法的人,可以在这里阅读一篇温和的介绍。
特征 | 句子片段 | 子词-nmt | 词片 |
---|---|---|---|
支持的算法 | BPE、一元语法、字符、单词 | BPE | BPE* |
开源软件? | 是的 | 是的 | 谷歌内部 |
子字正则化 | 是的 | 不 | 不 |
Python 库 (pip) | 是的 | 不 | 不适用 |
C++ 库 | 是的 | 不 | 不适用 |
需要预先分段吗? | 不 | 是的 | 是的 |
可定制的标准化(例如,NFKC) | 是的 | 不 | 不适用 |
直接生成id | 是的 | 不 | 不适用 |
请注意,WordPiece 中使用的 BPE 算法与原始 BPE 略有不同。
SentencePiece 是子词单元的重新实现,是缓解神经机器翻译中开放词汇问题的有效方法。 SentencePiece 支持两种分段算法:字节对编码 (BPE) [Sennrich et al.] 和一元语言模型[Kudo.]。以下是与其他实现的高级差异。
神经机器翻译模型通常使用固定词汇进行操作。与大多数假设无限词汇量的无监督分词算法不同,SentencePiece 训练分词模型,使得最终词汇量是固定的,例如 8k、16k 或 32k。
请注意,SentencePiece 指定了训练的最终词汇量,这与使用合并操作次数的 subword-nmt 不同。合并操作的数量是 BPE 特定的参数,不适用于其他分段算法,包括一元语法、单词和字符。
以前的子词实现假设输入句子是预先标记化的。此约束是有效训练所必需的,但使预处理变得复杂,因为我们必须提前运行语言相关的分词器。 SentencePiece 的实现速度足够快,足以从原始句子训练模型。这对于训练中文和日文的分词器和解分词器非常有用,因为单词之间不存在明确的空格。
自然语言处理的第一步是文本标记化。例如,标准英语分词器会对文本“Hello world”进行分段。分为以下三个标记。
[你好世界] [。]
一项观察是原始输入和标记化序列不可逆地转换。例如,“World”和“.”之间没有空格的信息。从标记化序列中删除,因为例如Tokenize(“World.”) == Tokenize(“World .”)
SentencePiece 将输入文本视为 Unicode 字符序列。空格也被作为普通符号处理。为了明确地将空格作为基本标记处理,SentencePiece 首先使用元符号“” (U+2581) 转义空格,如下所示。
你好世界。
然后,该文本被分割成小块,例如:
[你好][世界][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 提供了支持 SentencePiece 训练和分割的 Python 包装器。您可以安装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 依赖管理器下载并安装句子:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install sentencepiece
vcpkg 中的句子端口由 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 安装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 (add </s> only)
% spm_encode --extra_options=bos:eos (add <s> and </s>)
% spm_encode --extra_options=reverse:bos:eos (reverse input and add <s> and </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 对应于该文件中的行号。
默认情况下,句子分别使用ID,分别为0、1和2的IDS句子,BOS(<s>)和EOS(</s>)令牌。我们可以在训练阶段重新定义这个映射,如下所示。
% spm_train --bos_id=0 --eos_id=1 --unk_id=5 --input=... --model_prefix=... --character_coverage=...
当设置 -1 id 例如bos_id=-1
时,此特殊令牌被禁用。请注意,未知 ID 无法被禁用。我们可以将填充 (<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
默认加载语料库的前 10M 行。
然后使用--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