大家好啊,我是电棍。距离上次更新已经过去近两个月。我一直想记录跨年前后发生的事,但任务繁重,这些事情并不十分有趣,故迟迟没有记录。

今天来点大家想看的东西。

我得了一种怪病:看到文档就想睡觉。

因此,我会尽力让我写的东西比文档更有趣。

分词器在做什么?怎么做?

分词器的作用是把一段话分成一系列词语,然后将词语变为编号

把词语变成编号很好理解。但是怎么把一段话分成词语呢?

流派一:基于单词的分词器 Word-based Toknization

这是最符合直觉的方法:按照现有的单词进行编号。

来看英语的例子:

Cock Word 1
loves Word 2
pussy Word 3

来看汉语的例子:

Word 1
鍾 意 Word 2
釣 蟹 Word 3

我们只需要参考辞书进行分词,给词语进行编号即可。

然而这种做法有严重的问题。其一,很多英语单词是由多个词复合而成的。如果以单词为粒度进行分词,词表会膨胀得极其严重。于是一种新的流派也即Subword_based Toknization产生了。这并非我们讨论的重点,故不赘述。

其二,词汇表的空间有限,不可能收纳所有单词。我们在与LLM交流时也免不了会拼错单词。

中国政法大学的郭继承教授有言:

🤓🤏你想想那是个真理,真理是无相的!所以金刚经的一句话,叫凡有所相,皆是虚妄,见所相非相。那是个真理!你不能迷信在这方面。

😠🤚它是个真理!所以,道可道非常道,名可名非常名。

🫳🤓👆但是说那有人说我非得说!你非得说我可以告诉你,老子也没说明白。他是不是语言可以描述的!

🤓👐后来西方的语言哲学家维特根斯坦,把这个事说了一句名言。

😙🤌维特根斯坦说,这个世界上有语言能说的,要说清楚;这个世界上也有超出语言说不明白的,维特根斯坦直接用了俩字:闭嘴。

😫那没法说嘛!

到底来没来?

省流:

闭嘴。

让我们看这样的情况:

Cock Word 1
loves Word 2
pusyy 闭嘴!

遇到词汇表中没有的单词,分词器直接甩了俩字:闭嘴!这是不可被接受的。

流派二:基于字节的分词器 Byte-based Tokenization

顾名思义,我们将一句话编码成一串字节,一个字节就是一个词。

来看英语的例子:

Cock
43 C
6F o
63 c
6B k
loves
6C l
6F o
76 v
65 e
73 s
pussy
70 p
75 u
73 s
73 s
79 y

来看汉语的例子:

Input: 我 鍾 意 釣 蟹
E6 88 91
3 Tokens
E9 8D BE
3 Tokens
E6 84 8F
3 Tokens
E9 87 A3
3 Tokens
E8 9F B9
3 Tokens

这种方法确确实实解决了词表不能涵盖所有单词的问题。但是它带了了新的问题。不难发现,按照Word-based Tokenization的方案,"Cock loves pussy“只被拆成了三个词;而在Byte-based Tokenization中,它被拆成了14个词。一个汉语字符会占用三个字节;因此,之前只需要三3个词表示的“我鍾意釣蟹“在这里被拆成了15个词。

熟悉Transformer的朋友们应该知道,Transformer的计算复杂度和KV Cache规模随着输入量的增长是平方级增长的。这样的代价是我们无法接受的。

你别说,还真有人做了。

流派三:基于字符的分词器 Character-based Tokenization

依然顾名思义,我们以Unicode字符为粒度进行分词。

来看英语的例子:

C
o
c
k
4 Characters
l
o
v
e
s
5 Characters
p
u
s
s
y
5 Characters

来看汉语的例子:

相信聪明的小朋友已经发现了问题:

其一,Unicode字符有约150000个,词表爆炸的问题依然没有得到解决;

其二,大量Unicode字符其实并不常用。

BPE Tokenization及其实现