大家好,我是kaiyuan。前几天在群里讨论,有同学问到了『基于字粒度和词粒度的BERT,效果哪个好?』 这篇文章我们就来深入聊一聊PLM模型『输入粒度』的问题。
划掉划掉,本来想在介绍PLM模型之前简单说下Tokenization的,写完发现已经蛮长了,那模型就下一篇见吧!
关于Tokenization,网上有翻译成"分词"的,但是我觉得不是很准确,容易引起误导。一直找不到合适的中文来恰当表达,所以下文采用原汁原味的英文表达。
在正式进入主题之前,先来看看NLP任务中最基础也最先需要进行的一步:tokenization。简单说,该操作的目地是将输入文本分割成一个个token,和词典配合以让机器认识文本。Tokenization的难点在于如何获得理想的切分,使文本中所有的token都具有正确的表义,并且不会存在遗漏(OOV问题)。
接下来,我们简单梳理下目前主流的tokenization
方法,及其优缺点。
词粒度的切分就跟人类平时理解文本原理一样,常常用一些工具来完成,例如英文的NLTK、SpaCy,中文的jieba、LTP等。举个栗子:
英文: live in New York ------> live / in / New York /
中文: 在纽约生活 -----> 在 / 纽约 / 生活
词粒度的切分能够非常好地保留完整语义信息,但是如果出现拼写错误、英文中的缩写等情况,鲁棒性一般。另一方面,词切分会产生非常巨大的词表,而且这都不能确保不会出现out of vocabulary
问题。
字粒度最早应该是2015年Karpathy[1]提出,简单说英文就是以字母为单位(对于大小写不敏感的任务,甚至可以先转小写再切分),中文就是以字为单位,举个栗子,
英文: live in New York -----> l / i / v /e / i / n / N / e / w / Y / o / r /k
中文: 在纽约生活 -----> 在 / 纽 / 约 / 生 / 活
可以看出,字粒度的切分很好地解决了词粒度的缺陷,鲁棒性增强、词表大大减小。但另一方面,也会带来一些麻烦:
如果词粒度不理想,而且字粒度似乎也有自己的问题,那么还有什么替代方法呢?
Here comes subword tokenization!
我们理想中的tokenization需要满足:
为此,我们需要考虑如何重新利用『小』单词来创建『大』单词。subword tokenization
不转换最常见的单词,而是将稀有单词分解成有意义的子词单元。如果unfriendly
被标记为一个稀有词,它将被分解为un-friendly-ly
,这些单位都是有意义的单位,un
的意思是相反的,friend
是一个名词,ly
则变成副词。这里的挑战是如何进行细分,我们如何获得un-friend-ly
而不是unfr-ien-dly
。
NLP最火的网红 Transformer
和 BERT
就是Subword的带盐人,来看个它们做tokenization的栗子,
I have a new GPU ----> [’i’, ’have’, ’a’, ’new’, ’gp’, ’##u’, ’.’]
subword粒度切分算法又有以下几种:
BPE全称Byte Pair Encoding
,字节对编码,首先在Neural Machine Translation of Rare Words with Subword Units[2] 中提出。BPE 迭代地合并最频繁出现的字符或字符序列,具体步骤:
</ w>
,并统计单词频率。本阶段的subword的粒度是字符。例如,“ low”的频率为5,那么我们将其改写为l o w </ w>:5
举个栗子,我们输入,
{'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w e s t </w>': 6, 'w i d e s t </w>': 3}
第一轮迭代,统计连续的每两个字节出现的次数,发现 e
和s
共现次数最大,合并成es
,有,
{'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w es t </w>': 6, 'w i d es t </w>': 3}
第二轮迭代,统计连续的每两个字节出现的次数,发现 es
和t
共现次数最大,合并成est
,有,
{'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w est </w>': 6, 'w i d est </w>': 3}
依次继续迭代直到达到预设的subword词表大小或下一个最高频的字节对出现频率为1。
以上是BPE的整体流程,关于BPE更多细节可以参考:Byte Pair Encoding[3]
Unigram语言建模首先在Subword Regularization: Improving Neural Network Translation Models with Multiple Subword Candidates[4]中提出,基于所有子词出现是独立的假设,因此子词序列由子词出现概率的乘积生成。算法步骤如下:
unigram-LM模型比BPE更灵活,因为它基于概率LM,并且可以输出具有概率的多个分段。它不是从一组基本符号开始,更具某些规则进行合并,如BPE或WordPiece,而是从一个庞大的词汇量开始,例如所有预处理的单词和最常见的子字符串,并逐步减少。
WordPiece首先在 JAPANESE AND KOREAN VOICE SEARCH[5] 中提出,最初用于解决日语和韩语语音问题。它在许多方面类似于BPE,只是它基于可能性而不是下一个最高频率对来形成一个新的子词。算法步骤如下:
WordPiece更像是BPE和Unigram LM的结合。
简单几句话总结下Subword的三种算法:
到目前为止,可以发现subword结合了词粒度和字粒度方法的优点,并避免了其不足。但是,仔细想会发现上述三种subword算法都存在一些问题:
ok,here comes SentencePiece!来看看是怎么解决上述问题的
快结束了,我想说一下,这真的不是Sentencepiece的软文(谷歌,打钱!)
SentencePiece集成了两种subword算法,BPE和UniLM, WordPiece 则是谷歌内部的子词包,没对外公开。感兴趣的可以去官方开源代码库玩玩:google/sentencepiece[6]
放个栗子:
>>> 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=-1)
...
['▁', 'N', 'e', 'w', '▁York']
['▁', 'New', '▁York']
['▁', 'New', '▁Y', 'o', 'r', 'k']
['▁', 'New', '▁York']
['▁', 'New', '▁York']
最后,如果想尝试WordPiece
,大家也可以试试HuggingFace的Tokenization库[7]
from tokenizers import Tokenizer
from tokenizers.models import BPE
from tokenizers.pre_tokenizers import Whitespace
from tokenizers.trainers import BpeTrainer
tokenizer = Tokenizer(BPE())
tokenizer.pre_tokenizer = Whitespace()
trainer = BpeTrainer(special_tokens=["[UNK]", "[CLS]", "[SEP]", "[PAD]", "[MASK]"])
tokenizer.train(trainer, ["wiki.train.raw", "wiki.valid.raw", "wiki.test.raw"])
output = tokenizer.encode("Hello, y'all! How are you ? ?")
print(output.tokens)
# ["Hello", ",", "y", "'", "all", "!", "How", "are", "you", "[UNK]", "?"]
[1]
2015年Karpathy: https://github.com/karpathy/char-rnn
[2]
Neural Machine Translation of Rare Words with Subword Units: https://arxiv.org/abs/1508.07909
[3]
Byte Pair Encoding: https://leimao.github.io/blog/Byte-Pair-Encoding/
[4]
Subword Regularization: Improving Neural Network Translation Models with Multiple Subword Candidates: https://arxiv.org/abs/1804.10959
[5]
JAPANESE AND KOREAN VOICE SEARCH: https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/37842.pdf
[6]
google/sentencepiece: https://github.com/google/sentencepiece
[7]
HuggingFace的Tokenization库: https://github.com/huggingface/tokenizers
- END -