大型语言模型(LLM)训练全流程指南
ChatGPT 面世以来,各种大模型相继出现。本文将详细梳理一个完整的 LLM 训练流程,包括模型预训练(Pretrain)、Tokenizer 训练、指令微调(Instruction Tuning)等环节。
1. 预训练阶段(Pretraining Stage)
当前,不少工作选择在一个较强的基座模型上进行微调,且通常效果不错(如 Alpaca、Vicuna 等)。这种成功的前提在于:预训练模型和下游任务的差距不大,预训练模型中通常已经包含微调任务中所需要的知识。
但在实际情况中,我们通常会遇到一些问题,使得我们无法直接使用一些开源 backbone:
- 语言不匹配: 大多数开源基座对中文的支持都不太友好,例如 Llama、mpt、falcon 等,这些模型在英文上效果都很优秀,但在中文上却差强人意。
- 专业知识不足: 当我们需要一个专业领域的 LLM 时,预训练模型中的知识就尤为重要。由于大多数预训练模型都是在通用训练语料上进行学习,对于一些特殊领域(金融、法律等)中的概念和名词无法具备很好的理解。我们通常需要在训练语料中加入一些领域数据(如轩辕 2.0),以帮助模型在指定领域内获得更好的效果。
基于上述原因,我们在进行 SFT 步骤之前,先来看看预训练任务是如何做的。
1.1 Tokenizer Training
在进行预训练之前,我们需要先选择一个预训练的模型基座。一个较为普遍的问题是:大部分优秀的语言模型都没有进行充分的中文预训练,因此,许多工作都尝试将在英语上表现比较优秀的模型用中文语料进行二次预训练,期望其能够将英语上的优秀能力迁移到中文任务中来。
但在进行正式的训练之前,我们还有一步很重要的事情去做:词表扩充。通俗来讲,tokenizer 的目的就是将一句话进行切词,并将切好词的列表喂给模型进行训练。
例如:
输入句子 >>> 你好世界
切词结果 >>> ['你', '好', '世', '界']
通常,tokenizer 有 2 种常用形式:WordPiece 和 BPE。
WordPiece WordPiece 很好理解,就是将所有的「常用字」和「常用词」都存到词表中,当需要切词的时候就从词表里面查找即可。BERT 就使用的这种切词法。当我们输入句子:你好世界,BERT 就会依次查找词表中对应的字,并将句子切成词的组合。当遇到词表中不存在的字词时,tokenizer 会将其标记为特殊的字符 [UNK]。
Byte Pair Encoder(BPE) WordPiece 的方式很有效,但当字词数目过于庞大时这个方式就有点难以实现了。对于一些多语言模型来讲,要想穷举所有语言中的常用词(穷举不全会造成 OOV),既费人力又费词表大小,为此,人们引入另一种方法:BPE。BPE 不是按照中文字词为最小单位,而是按照 unicode 编码作为最小粒度。对于中文来讲,一个汉字是由 3 个 unicode 编码组成的,因为平时我们不会拆开来看(毕竟中文汉字是不可拆分的),所以我一开始对这个概念也不太熟悉。
我们来看看 LLaMA 的 tokenizer(BPE)对中文是如何进行 encode 的。可以看到,「编码」两个字能够被正常切成 2 个字,但「待」却被切成了 3 个 token,这里的每个 token 就是 1 个 unicode 编码。通过 token 查找功能,我们可以发现「编」「码」在词表中,但「待」不在词表中。但任何 1 个汉字都是可以由 unicode 表示(只是组合顺序不同),因此「待」就被切成了 3 个 token。通常在模型训练不够充足的时候,模型会输出一些乱码(不合法的 unicode 序列)。
词表扩充 为了降低模型的训练难度,人们通常会考虑在原来的词表上进行「词表扩充」,也就是将一些常见的汉字 token 手动添加到原来的 tokenizer 中,从而降低模型的训练难度。Chinese LLaMA 在原始 tokenizer 上新增了 17953 个 tokens,且加入 token 的大部分为汉字。而在 BELLE 中也有同样的做法:在 120w 行中文文本上训练出一个 5w 规模的 token 集合,并将这部分 token 集合与原来的 LLaMA 词表做合并,最后再在 3.2B 的中文语料上对这部分新扩展的 token embedding 做二次预训练。
1.2 Language Model PreTraining
在扩充完 tokenizer 后,我们就可以开始正式进行模型的预训练步骤了。Pretraining 的思路很简单,就是输入一堆文本,让模型做 Next Token Prediction 的任务,这个很好理解。我们主要来讨论几种预训练过程中所用到的方法:数据源采样、数据预处理、模型结构。
数据源采样 在 GPT3 的训练过程中,存在多个训练数据源,论文中提到:对不同的数据源会选择不同采样比例。通过「数据源」采样的方式,能够缓解模型在训练的时候受到「数据集规模大小」的影响。从上图中可以看到,相对较大的数据集(Common Crawl)会使用相对较大的采样比例(60%),这个比例远远小于该数据集在整体数据集中所占的规模(410 / 499 = 82.1%),因此,CC 数据集最终实际上只被训练了 0.44 个 epoch。而对于规模比较小的数据集(Wikipedia),则将多被训练几次(3.4 个 epoch)。这样一来就能使得模型不会太偏向于规模较大的数据集,从而失去对规模小但作用大的数据集上的学习信息。


