大语言模型实战

分词器在大型语言模型中的作用是将原始文本转换为模型可理解的输入形式。它负责将文本分割成基本单位,并将其转换成模型词汇表中的索引或向量表示。分词器能够标准化输入、处理不同语言、处理特殊文本形式,并控制输入长度,直接影响模型的性能和效果。


1 分词器概述


在深入介绍分词器之前,我们需要先回答一个重要问题:为何需要对文本进行分词?词在文本中是最小的独立单元,携带了一定的语义信息。在模型训练过程中,采用分词方法能够有效降低文本数据的维度,进而提高训练效率。分词器针对不同的粒度也有不同的分词方式,如字符级分词、单词级分词、子词级分词等。例如针对以下文本:

 


1)字符级分词:按照单字符进行分词,就是以char为最小粒度。针对上述文本,可以分割为:

 

显然,该方法的词表中仅需存储字符级别的token,能够最大限度地缩减词表的大小,但分词后的结果仅是单个字符,会导致严重的缺失,如英文中仅采用字母表示,几乎失去了所有的词汇语义信息,使得在后续训练阶段失去重要意义。


2)单词级分词:按照词进行分词,在英文中可以采用空格进行分割。针对上述文本,可以分割为:

 

Transformer XL论文链接:https://arxiv.org/pdf/1901.02860.pdf。

该方法能够用有效地为每个单词进行分词,从语义层面保留了词语信息,但是分词结果中的“Let's”“tomorrow!”等词的分词结果,存在由于大小写、缩写或标点符号导致的冗余,且单词众多,导致词表不断扩大,如Transformer XL 使用基于空格和标点符号的规则分词方式,其词表中的词汇量超过250000个,这使得训练的代价非常高昂。同时,即使扩充了词表的规模,也依然存在词表外词(Out-Of Vocabulary,OOV,又称未登录词)难以表征的情况。


3)子词级分词:按照词的子词进行分词,类似于利用词根和词源来学习一系列单词。通过这些共通的基本单元可以构造更多的词汇,每一个基本单元即是词表的组成部分。针对上述文本,可以分割为:

 

需要指出的是,我们的分词器属于基于统计意义的分词器,而非基于语言学的分词器。然而,一旦经过大量训练数据的学习,这些基本单元也能够掌握语言学上的含义。子词级分词能够有效平衡字符级分词器与单词级分词的优缺点,在大模型预训练中的分词器中得到广泛应用,如BPE、WordPiece和Unigram等。


2 BPE分词器

BPE是由爱丁堡大学的Rico Sennrich等学者于2015年在论文“Neural machine translation of rare words with subword units”中提出的一种简单而高效的数据压缩形式。在研究神经网络机器翻译时,传统的处理方式是为机器翻译任务设定一个固定的词汇表。然而,机器翻译面临的是未登录词问题,传统应对方法是对词汇表进行频繁更新,这无疑增加了处理的复杂性和工作量。为了解决这一难题,Rico Sennrich等人提出了一种更为简单且有效的解决方法,即通过将未登录词编码为子单词单元序列,使神经机器学习模型能够处理未登录词问题。这种方法便是BPE。其核心思想是在迭代过程中将原始序列中最频繁出现的字节替换为单个未使用过的字节。具体的算法逻辑如下。

1)采用任意分词方法(如空格分割方法)对所有文本进行分词。

2)统计所有词语的词频,将所有词语以字母形式进行分割,得到相关字符,并设置词库的上限。

3)统计任意两个字符连续出现的总次数。

4)选取出现次数最高的一组字符对,替换2)中获得的字符统计,并将该字符对加入词库中。

5)重复3)和4),直到词库规模达到预设上限。

下面以实际数据为例,当前获得以下词语及其词频:

 

第一步:得到相应字母组成的词库,当前词库规模为11,设定词的上限为13。词库 及词频信息如下:

第二步:结合上述词频及字母,可以将其表示为如下形式。

 

第三步:经过统计,字母组合<c,a>在“car”中出现5次,在“cabbage”中出现3次,共出现8次,当前最高,因此将“ca”加入词库中,此时词库及词频信息如下。

 

第四步:进一步地,更新当前统计的词频,并将字母组合<c,a>进行替换,此时词频可以统计如下。

 

重复上述步骤,字母组合<c,h>在“detch”中出现2次,在“chair”中出现5次,共出现7次,当前最高,因此将“ch”加入词库中,由于字母“c”的所有组合均已出现,删除“c”,并更新词库,此时词库及词频信息如下:

 

继续迭代直至词库规模达到上限13,完成词库构建。

代码实现如下:

 

执行上述代码,我们发现经过4次迭代,词表规模达到了上限13,此时词库及词频信息如下:

 

当我们遇到新词“card”时,结合上述词库,可以得到分词结果为[′car′,

′d′]。

当然Hugging Face也提供了分词器包,可以快速进行模型分词器的训练,以下是BPE训练的代码实现。

 

 

 

BPE方法是一种高效的分词方法,可以显著缩短训练时间,在大型语言模型的训练中体现尤为明显。例如,OpenAI开源的GPT-2和Meta的RoBERTa都采用BPE作为分词方法并构建相应的词库。下面结合RoBERTa模型来测试BPE分词的效果。我们将利用Hugging Face仓库中哈工大公布的RoBERTa模型进行BPE方法的测试,相关代码如下。

 

 

针对文本“我爱北京天安门,天安门上太阳升。”得到的分词结果如下。

 

 

针对文本“A large language model(LLM)is a language model consisting of a neural network with many parameters.”得到的分词结果如下。

 


3 WordPiece分词器

WordPiece分词器最初由Google提出,旨在解决神经机器翻译中的未登录词问题。该分词方法被广泛关注,特别是在深度学习领域得到了应用,如常用的预训练语言模型BERT就采用了WordPiece分词。WordPiece可以视作BPE的一种变体。它首先将所有字符添加到词库中,并需要预先设定词库的规模。在不断添加子词的过程中,WordPiece与BPE最大的区别在于子词加入词库的方式。WordPiece选择最大化训练数据的可能词对,而不考虑词频。这也意味着从初始构建的词库开始,在语言模型的训练过程中,不断更新词库,直至词库达到所设规模为止。具体操作步骤如下。

1)对待训练语料进行字符拆分,并将拆分的字符加入初始词库中。

2)设置词库上限。

3)开始训练语言模型。

4)结合训练的语言模型,将能够最大化训练数据概率的子词作为新的部分加入词库。

5)重复3)和4)直至词库的规模达到上限。

Hugging Face也提供了分词器包,可以快速进行模型分词器的训练,以下是WordPiece训练的代码实现。

 

例如,针对文本“A large language model(LLM)is a language model

consisting of a neural network with many parameters.”,利用我们训练的分词器可以得到以下结果。

 

 

这里的“##”表示一个附加标记。

正如前文所述,在2018年由Google公司提出的BERT中就使用了WordPiece作为分词器,下面使用BERT来进行分词测试。我们可以利用Hugging Face仓库中公布的BERT模型文件来测试WordPiece分词。具体测试代码如下:

 

针对文本“我爱北京天安门,天安门上太阳升。”得到的分词结果如下。

 

针对文本“A large language model(LLM)is a language model consisting of a neural network with many parameters.”得到的分词结果如下。

 


4 Unigram分词器


众多研究已经验证,子词单元(Subword)是缓解神经机器翻译中开放词汇问题的一种有效方法。尽管通常会将句子转换为独特的子词序列,但基于子词的分词结果可能存在潜在的歧义。即使在使用相同词汇的情况下,也可能会出现多种分词结果。为了应对这一挑战,Google公司的Kudo提出了基于Unigram语言模型的基于子词的分词算法。该算法利用简单的正则化方法来分割歧义,并将其视为噪声,以提高神经机器翻译的鲁棒性。在训练过程中,该算法使用多个概率抽样的基于子词的分词来训练模型。这种分词方法被称为Unigram分词方法。Unigram分词方法也是一

种经常被采用的分词方式,与BPE、Word-Piece两种分词方式的主要区别是,Unigram在构建词库时基本包含了所有词语和符号,然后采用的逐步删除的方式得到最终的词库。具体步骤如下。

1)构建基本包含所有词语和符号的词库。

2)设置词库规模。

3)开始训练语言模型。

4)删除具有最高损失x%的词对。

5)重复3)和4)直至词库规模达到预定规模。

Hugging Face也提供了分词器包,可以快速进行模型分词器的训练,以下是Unigram分词器训练的代码实现。

 

 


5 SentencePiece分词器


大多数的分词方法都基于空格对词语进行切分。然而,对于中文、日文、泰文等语言来说,简单的空格分割并不是最高效的方法。针对这种情况,学者们展开了深入研究。Taku Kudo提出了一种创新的分词方式,被称为SentencePiece。

SentencePiece是一种简单且不依赖特定语言的文本分词方法。它不仅可以作为分词器,还可以作为逆分词器(Detokenizer)。逆分词器的作用是将已经分割的单词、标点等标记恢复为原始文本形式。SentencePiece主要用于基于神经网络的文

本生成系统。与现有的子词分割工具相比,SentencePiece可以直接从原始句子训练子词模型,假定输入以pre-to-kenized为单词序列,这时我们可以构建一个纯粹的端到端且语言层面相对独立的系统。

为了实现所谓的端到端和与语言无关,SentencePiece设计了4个主要组件。

·规范化器。规范化器是一个模块,用于将语义上等效的Unicode字符标准化为规范形式。

·训练器。训练器从经过标准化的语料库中训练子词分割模型。

·编码器。编码器在内部执行规范化器来标准化输入文本,并使用训练器训练的子词模型将其标记为子词序列。

·解码器。解码器则负责将子词序列转换为标准化文本。

SentencePiece实现了两种子词分割算法,即前文提到的BPE和Unigram,并且扩展了直接从原始句子训练的能力。这个工具以输入流的方式处理输入文本,包括字符、空格、标点等内容,而分词方式可以采用BPE、Unigram等方法。

利用开源工具SentencePiece包可以快速进行模型分词器的训练,以下是利用分词SentencePiece包并使用BPE进行分词器训练的代码实现。

 

 

 

 

例如针对文本“A large language model(LLM)is a language model

consisting of a neural network with many parameters.”,利用我们训练的分词器可以得到以下结果。

 

在由Hugging Face提供的Transformers库中,多数基于Transformer架构的模型(如ALBert、XLNet、T5等)的分词都采用了SentencePiece与Unigram结合的方法作为分词器。其中,XLNet是一种基于自回归语言模型的预训练方法,由CMU和谷歌83的研究人员联合提出。这里我们采用在Hugging Face上哈工大公布的一个基于中文XLNet模型架构提供的模型,利用如下代码进行测试。

 

针对文本“我爱北京天安门,天安门上太阳升。”得到的分词结果如下。

 

针对文本“A large language model(LLM)is a language model consisting of a neural network with many parameters.”得到的分词结果如下。

 

 

 

 

 

6 词表融合


若想对大型语言模型词表进行扩充,可以有多种方式,这里给出一个面向中文词表扩充的方法,相关步骤如下,相关流程如图2-2所示。

 

 

1)使用SentencePiece工具在中文预训练语料上进行训练,生成一个包含中文词单词的词表。

2)从上述获取的中文词表中删除包含词表(如LLaMA)的部分。

3)将原版词表与经过去重后的中文词表进行拼接,得到最终的扩展后的中文词表。

在针对LLaMA进行中文词表扩充的任务中,通过上述流程将LLaMA词表的大小得以扩充至49953个单词,而二代中文LLaMA则进一步扩充至55396个单词。这个词表扩充的过程旨在丰富中文词汇,提高模型对多样化语言表达的理解能力,从而更好地满足语言模型在不同应用场景下的需求。