BPE、WordPiece、Unigram LM、SentencePiece

date
icon
password
Sub-item
Blocked by
Parent item
type
status
slug
summary
tags
category
Blocking
 

总览

分词方法
特点
被提出的时间
典型模型
BPE
采用合并规则,可以适应未知词
2016年
GPT-2、RoBERTa
WordPiece
采用逐步拆分的方法,可以适应未知词
2016年
BERT
Unigram LM
采用无序语言模型,训练速度快
2018年
XLM
SentencePiece
采用汉字、字符和子词三种分词方式,支持多语言
2018年
T5、ALBERT
 

具体介绍

Byte Pair Encoding (BPE)

理论

BPE是一种基于数据压缩算法的分词方法。它通过不断地合并出现频率最高的字符或者字符组合,来构建一个词表。具体来说,BPE的运算过程如下:
  1. 将所有单词按照字符分解为字母序列。例如:“hello”会被分解为["h","e","l","l","o"]。
  1. 统计每个字母序列出现的频率,将频率最高的序列合并为一个新序列。
  1. 重复第二步,直到达到预定的词表大小或者无法再合并。
举例如下:
假设我们有以下单词:
首先将每个单词按照字符切分:
统计每两个相邻字符序列出现的频率:
将出现频率最高的字符序列"es"进行合并,得到新的词表:
重复上述步骤,将出现频率最高的字符序列"e s"进行合并,直到达到预定的词表大小或者无法再合并。
 
 

代码

 

编码

从最长的token迭代到最短的token,尝试将每个单词中的子字符串替换为token。

理解

  1. 词表大小通常先增加后减小
每次合并后词表可能出现3种变化:
+1,表明加入合并后的新字词,同时原来的2个子词还保留(2个字词不是完全同时连续出现)+0,表明加入合并后的新字词,同时原来的2个子词中一个保留,一个被消解(一个字词完全随着另一个字词的出现而紧跟着出现)
-1,表明加入合并后的新字词,同时原来的2个子词都被消解(2个字词同时连续出现)
 

Word piece

理论

wordpiece算法可以看作是BPE的变种。不同的是,WordPiece基于概率生成新的subword而不是下一最高频字节对。WordPiece算法也是每次从词表中选出两个子词合并成新的子词。BPE选择频数最高的相邻子词合并,而WordPiece选择使得语言模型概率最大的相邻子词加入词表。
 
比如说 P(ed) 的概率比P(e) + P(d)单独出现的概率更大,可能比他们具有最大的互信息值,也就是两子词在语言模型上具有较强的关联性。
 

理解

那wordPiece和BPE的区别在哪儿
我的理解是,在训练阶段其实区别不大(大量数据面前),但是编码阶段区别巨大
BPE: apple 当词表有appl 和 e的时候,apple优先编码为 appl和e(即使原始预料中 app 和 le 的可能性更大)
wordPiece:根据原始语料, app和le的概率更大
 
 

Unigram Language Model (ULM)

 

SentencePiece

它是谷歌推出的子词开源工具包,其中集成了BPE、ULM子词算法。除此之外,SentencePiece还能支持字符和词级别的分词。更进一步,为了能够处理多语言问题,sentencePiece将句子视为Unicode编码序列,从而子词算法不用依赖于语言的表示。
 

理解

中文占3个字节,所以未经中文预训练的词表,解码效率很低,一个中文可能用到多个token,所以是不是重新映射一遍比较好,提升解码速度
💡
回忆一下
作者:禽兽一只
链接:https://www.zhihu.com/question/20451870/answer/391349703
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Unicode定义了所有符号的二进制形式,也就是符号如何在计算机内部存储的,而且每个符号规定都必须使用两个字节来表示,也就是用16位二进制去代表一个符号,这样就导致了一个问题,英文编码的空间浪费,因为在ANSI中的符号都是一个字节来表示的,而使用了UNICODE编码就白白浪费了一个字节。也就代表着Unicode需要使用两倍的空间去存储相应的ANSI编码下的符号。虽然现在硬盘或者内存都很廉价,但是在网络传输中,这个问题就凸显出来了,你可以这样想想,本来1M的带宽在ANSI下可以代表1024*1024个字符,但是在Unicode下却只能代表1024*1024/2个字符。也就是1MB/s的带宽只能等价于512KB/s,这个很可怕啊。
所以为了解决符号在网络中传输的浪费问题,就出现了UTF-8编码,Unicode transfer format -8 ,后面的8代表是以8位二进制为单位来传输符号的,但是这样又导致了一个问题,虽然UTF-8可以使用一个字节来表示ANSI下的符号,但是对于其它类似汉语的符号,得需要两个字节来表示,所以计算机不知道如何去截取一个符号,也就是一个符号对应的二进制的截取开始位置和截取结束位置。
所以为了解决Unicode下的ANSI符号的空间浪费和网络传输下如何截取字符的问题,UTF规定:如果一个符号只占一个字节,那么这个8位字节的第一位就为0。如果为两个字节,那么规定第一个字节的前两位都为1,然后第一个字节的第三位为0,第二个字节的前两位为10,然后如果是三个字节的话,那么第一个字节的前三位为111,第四位为0,剩余的两个字节的前两位都为10。
按照这样的算法去思考一个中文字符的UTF-8是怎么表示的:一个中文字符需要两个字节来表示,两个字节一共是16位,那么UTF-8下,两个字节是不够的,因为两个字节下,第一个字节已经占据了三位:110,然后剩余的一个字节占据了两位:10,现在就只剩下11位,与Unicode下的两个字节,16位去表示任意一个字符是相悖的。所以就使用三个字节去表示非ANSI字符:三个字节下,一共是24位,第一个字节头四位是:1110,后两个字节的前两位都是:10,那么24位-8位=16位,刚好两个字节去表示Unicode下的任意一个非ANSI字符。这也就是为什么UTF-8需要使用三个字节去表示一个非ANSI字符的原因了!
中国的汉字多达10多万,常用的汉字3500左右[08年统计],如果用3个字节来表示,一共只有2^16(65535)种可能,不足以表示10多万的汉字。所以中日韩的超大字符集是采用的4个字节来表示的,多达6万多个。但是平时使用超大字符集的概率0.01%都不到。所以我们一般认为日常的中文在UTF-8中占三个字节 即可!

📎 Reference

 
training LLM from scratchtokenizer