文章目录
- 直观理解
- 分词方式
- 词粒度-Word
- 字粒度-Character
- 子词粒度-Subword(目前最常使用)
- 词表大小的影响
- 参考资料
直观理解
在理解Transformer或者大模型对输入进行tokenize之前,需要理解什么是token?
理工科的兄弟姐妹们应该都知道,计算机内部的所有数据,最终都是以二进制形式(0和1)表示。文本数据也不例外,需要被转换成计算机能够理解和处理的形式。但是,如果直接处理原始文本字符串会非常低效,因为每个字符都需要占用一定的存储空间。
那么聪明的科学家就想可以通过将文本压缩成更紧凑的表示形式(tokens)来减少存储和传输的开销,这就是Tokenization的过程。将文本处理成的紧凑表示形式,就是Token,也就是一系列离散的字符(单词,单词的一部分,或者是字母),这些符号可以进一步被编码成二进制形式。
举个例子:
假设我们可以把We love AI
表示为We
,love
,AI
这三个token,并分别表示为13, 456,7
,以便于获取每个token的embedding表示。
- 其中,将
We love AI
表示为We
,love
,AI
这三个token的过程,就是tokenization过程,主要是对输入序列进行分词,获取tokens - 将不同的token表示为不同的整数序列,也就是每个token都有一个单独的token ID,这个过程叫做编码(encoding),比如我们可以用
7
来表示AI
- 一般后面还会加上一层embedding层,将每个token ID用更高维度的embedding来进行表示
Tokenization的难点[1]:
经过上述分析,我们可以大概猜到tokenization的一些难点:
1. 切分的粒度:比如 北京大学 是作为一个词,还是“北京” + “大学”,还是“北” +“京” +“大” +“学”?
2. 切分导致的歧义:武汉市/长江/大桥 还是 武汉/市长/江大桥
3. 数字怎么处理:日期数字 03/09/2024,电话号码 17866666666等
4. 不同语言的特性:拉丁语系可以用空格来区分,但是中文没有空格
分词方式
常见的分词方式主要有三种:
- 词粒度-Word
- 字粒度-Character
- 子词粒度-Subword
词粒度-Word
顾名思义,对于拉丁语系来说,每个单词,就是一个token,按照空格或者标点来分割即可。
但是对于中文来说,则需要专门进行分词,比如中文分词一般都用 jieba(结巴分词),哈工大的 LTP等。中文的分词可以抽象为一个序列标注问题,所以这些分词方法会使用词典索引,或者 HMM,双向 GRU 等模型来进行分词。
优点:
- 符合人类的直觉,词的含义完备
- 对于英文来说实现很简单
缺点:
- 如何构造一份好的词典,包括内容和长度等因素都需要考虑。
- OOV (Out of Vocabulary)问题,对于词典中没有词一般会分配一个统一来处理,信息丢失。
- 同义词和误拼会被认为是不同的词。比如
do not
和don't
虽然含义一样,但是最终的 id 不一样。类似的还有词的不同形态,比如have, has, had, having
等。 - 词典中的词没有任何关联,低频词不会得到充分的训练。
字粒度-Character
顾名思义,对于拉丁语系来说,每个字母或者标点符号,就是一个token。对于中文来说,每个汉字,就是一个token。
优点:
- 实现简单,且词典较小。
- 几乎没有 OOV (Out of Vocabulary)问题。
缺点:
- 对于拉丁语系来说,单个字母是没有语义的。对于中文来说,一个字是有一定的含义,但是也很难学习到文本真正的语义信息。
- 同时会让输入变得很长,极大影响训练和推理的效率。
子词粒度-Subword(目前最常使用)
基于Subword的分词方法,旨在实现基于Word和基于Character的方法的优点,同时最大限度地减少它们的缺点。基于Subword的方法采取中间立场,通过在单词中分割文本来尝试创建具有语义意义的标记,即使它们不是完整的单词。例如,标记 ing
和 ed
具有语法意义,尽管它们本身不是单词。
Subword方法产生的词汇量小于基于Character方法中的词汇量,但大于基于Word方法中的词汇量。每个token内存储的信息量也是如此,介于前两种方法生成的token之间。
Subword方法使用以下两个准则:
-
频繁使用的word不应拆分为Subword,而应作为一整个token标记
-
不常用的word应该分成Subword,比如
interdisciplinary
,可以被分为inter
,discipline
和ary
,那么就可以大概猜到其含义为跨学科的
。
从上面这个例子可以看出,理想的Subword分词方法应该是把不常见的单词,按照词形变化、复数形式等拆分为不同的token,同时保留token之间的关系,这样就可以最大可能保留句法和语义相似性。因此,Subword方法在当今的大模型领域中非常常用的分词方法。比如,常见大模型使用的Subword方法如下:
- GPT:Byte-level BPE
- LLaMA: sentencepiece BPE
- DeepSeek: Byte-level BPE
- Qwen: Byte-level BPE
- Yi: sentencepiece BPE
具体的Subword方法的讲解大家可以参考这篇博客:Tokenization — A Complete Guide
词表大小的影响
词表(vocabulary),通俗来说,是指我们提供给模型可以进行分词的字典,也就是模型能够理解和使用的词汇集合。具体来说,vocabulary通常包含成千上万个单词或词组,这些词汇是通过对大量文本数据的学习得到的。模型在学习过程中,会根据这些词汇出现的频率和上下文关系,来理解每个词汇的含义和用法。这样,当模型在处理新的文本或任务时,就能够利用它学到的vocabulary来生成合适的回答或文本。
不同大模型,使用的vocabulary的大小不一样,那么vocabulary的大小会有什么影响呢?
回答这个问题之前,我们首先理解两个概念:token压缩率和内存对齐:
- token压缩率:模型将原始文本转换为token的过程中,token的数量相对于原始文本字符数量的比例,比如100个文本经过tokenization之后,压缩成了20个token,那么这个tokenizer的压缩率就是20%。这个比例反映了模型在处理文本时对信息的压缩程度。
- 内存对齐:内存对齐是指将数据存储在内存中时,按照一定的规则来安排数据的位置,使得每个数据项的起始地址都是某个特定数值(通常是数据项大小的倍数)。例如,一个4字节大小的整数,它的起始地址最好是4的倍数。因为计算机硬件在读取内存时,通常会以固定大小的块(比如32位或64位)为单位来操作。如果数据没有对齐,即它的起始地址不是块大小的倍数,那么在读取这个数据时,可能需要额外的一次内存访问来拼凑出完整的数据,这样就会降低效率。
现在,我们来回答上述问题:[2]
- 数据量够大的情况下,vocabulary 越大,压缩率越高(即 token数/文本长度 越小),模型效果越好
- 计算效率的提升:相同的文本,转换为token后越短越好。更高的压缩率代表了相同数量的token能够表达更多的信息,相同的信息 token 越短则训练效率更高
- 有助于更好理解文本:更多的词汇能够减少 OOV (Out of Vocabulary)的影响, 训练的信息不会丢失,推理的时候泛化能力也更强。同时更多的词汇可以减少词汇分解后的歧义,从而更好地理解和生成文本
- 更长的上下文:预训练阶段往往都有最大序列长度的限制,压缩率更高代表着能接收更长序列的token,也就可以看到更多的上下文。
- 太大的 vocabulary 需要做一些训练和推理的优化,所以要平衡计算和效果
- 虽然 vocabulary 越大越好,但是也不能无限扩大。因为 vocabulary 变大后,Embedding 层变大,最后输出的 Head layer 也会变大。比如 Llama3 将 vocabulary 从 Llama2 的 32000 扩展到 128256,参数量就变大了,Llama2 还不到7B,但是 Llama3 有 8B了(当然这里面还有其他参数的改动)。参数更大,更占内存,而且输出的时候 softmax 也更大,计算就更慢。
- 虽然大多数情况下 token 量的减少,整体上是算得更快的。所以也不能设置得过大,目前业界普遍设置在 10万 到 20万左右。比如 Qwen 的 词表大小为 152064,baichuan2为125696,llama3 为128256,deepseek 为 102400。多模态的会更大一些。
- 要考虑内存对齐
- vocabulary 的大小一般要设置成 8 的倍数,在 A100 上则是 64 的倍数。(不同的GPU可能不一样)
- 比如 qwen 的 readme 和 训练代码中 vocabulary 的数量不一样, readme 中为 151643,但是实际上代码里写的是 152064。这就是为了内存对齐。所以很多时候模型上的一些设置其实跟硬件息息相关。
- 最终 Embedding 层和 Head Layer 最终都会转换成矩阵放到 GPU 的 Tensor Core 中计算。而根据英伟达的开发手册,矩阵运算最好根据 GPU 和计算类型满足如下的条件:
图片来源:[2]
参考资料
- [1] https://note.mowen.cn/note/detail?noteUuid=waAeRtCgZXLO62f9RhUWa
- [2] https://note.mowen.cn/note/detail?noteUuid=-PBW4yH2X9bLLRNL26VKp