大语言模型的训练分为两个阶段:(1)在海量文本语料上的无监督预训练,学习通用的语义表示和世界知识。(2)在小规模数据上,进行指令微调和基于人类反馈的强化学习,更好地对齐最终任务和人类偏好。LIMA[1] 证明了 LLM 的几乎所有知识都是在预训练过程中学习到的,只需要有限的指令微调数据就可以生成高质量的回复。因此,基座模型的性能是至关重要的,如果基座模型的性能不够好,指令微调和强化学习也难以取得很好的效果。
目前,主流的开源大语言模型主要有三个:LLaMA、ChatGLM 和 BLOOM。基于这三个开源模型,业界进行了指令微调或强化学习,衍生出了许多不同的大模型。下面从训练数据、tokenizer 和模型结构上对这三个大语言模型进行比较。
模型 | 训练数据 | 训练数据量 | 模型参数量 | 词表大小 |
---|---|---|---|---|
LLaMA | 以英语为主的拉丁语系,不包含中日韩文 | 1T/1.4T tokens | 7B、13B、33B、65B | 32000 |
ChatGLM-6B | 中英双语,中英文比例为 1:1 | 1T tokens | 6B | 130528 |
Bloom | 46 种自然语言和 13 种编程语言,包含中文 | 350B tokens | 560M、1.1B、1.7B、3B、7.1B、176B | 250880 |
模型 | 模型结构 | 位置编码 | 激活函数 | layer norm |
---|---|---|---|---|
LLaMA | Casual decoder | RoPE | SwiGLU | Pre RMS Norm |
ChatGLM-6B | Prefix decoder | RoPE | GeGLU | Post Deep Norm |
Bloom | Casual decoder | ALiBi | GeLU | Pre Layer Norm |
LLaMA[2] 是 Meta 提出的大语言模型。训练数据是以英语为主的拉丁语系,另外还包含了来自 GitHub 的代码数据。训练数据以英文为主,不包含中韩日文,所有训练数据都是开源的,分词之后大约有 1400B 的 tokens。
按照模型参数量,LLaMA 模型有 7B、13B、33B、65B 这四个不同参数规模的模型版本。7B 和 13B 版本使用了 1T 的 tokens 进行训练,33B 和 65B 的版本使用了 1.4T 的 tokens 进行训练。[3] 证明了在给定训练预算的情况下,即使减少模型参数量,只要增加预训练的数据大小和训练时长(更多的训练 tokens 数),可以达到甚至超过原始大小模型的效果。作为对比,280B 的 Gopher 模型只训练了 300B 的 tokens,176B 的 BLOOM 模型只训练了 350B 的 tokens,GLM-130B 只训练了 400B 的 tokens,LLaMA 模型则训练了 1T/1.4T 的 tokens,显著增大了训练数据量。从结果来看,虽然 LLaMA-13B 模型参数量只有 GPT3 的不到 1/10,但在大部分任务上效果都超过了 GPT3。
模型结构上,与 GPT 相同,LLaMA 采用了 causal decoder-only 的 transformer 模型结构。在模型细节上,做了以下几点改动:
在训练目标上,LLaMA 的训练目标是语言模型,即根据已有的上文去预测下一个词。
关于 tokenizer,LLaMA 的训练语料以英文为主,使用了 Sentence Piece 作为 tokenizer,词表大小只有 32000。词表里的中文 token 很少,只有几百个,LLaMA tokenizer 对中文分词的编码效率比较低。
下面是一些基于 LLaMA 衍生出来的大模型:
词表扩充的必要性。 LLaMA 原模型的词表大小是 32000,tokenizer 主要是在英文语料上进行训练的,在中文上和多语种上效果比较差。LLaMA 在中文上效果差,一方面是由于 LLaMA 模型是在以英文为主的拉丁语系语料上进行训练的,训练语料不包含中文;另一方面,与 tokenizer 有关,词表规模小,可能将一个汉字切分为多个 token,编码效率低,模型学习难度大。LLaMA 词表中只包含了很少的中文字符,在对中文文本进行分词时,会将中文切分地更碎,需要多个 token 才能表示一个汉字,编码效率很低。扩展中文词表后,单个汉字倾向于被切分为 1 个 token,避免了一个汉字被切分为多个 token 的问题,提升了中文编码效率。
如何扩展词表呢?[6] 尝试扩展词表,将中文 token 添加到词表中,提升中文编码效率,具体方式如下。
在中文语料上使用 Sentence Piece 训练一个中文 tokenizer,使用了 20000 个中文词汇。然后将中文 tokenizer 与原始的 LLaMA tokenizer 合并起来,通过组合二者的词汇表,最终获得一个合并的 tokenizer,称为 Chinese LLaMA tokenizer。词表大小为 49953。
为了适应新的 tokenizer,将 transformer 模型的 embedding 矩阵从 V × h V\times h V×hV\times h 扩展到 V ′ × h V^{'}\times h V′×hV^{'}\times h ,新加入的中文 token 附加到原始 embedding 矩阵的末尾,确保原始词表表的 embedding 矩阵不受影响。
在中文语料上进一步预训练,冻结和固定 transformer 的模型参数,只训练 embedding 矩阵,学习新加入中文 token 的词向量表示,同时最小化对原模型的干扰。
在指令微调阶段,可以放开全部模型参数进行训练。
扩展词表的效果。 从 Chinese-LLaMA-Alpaca 和 BELLE 的结果来看,扩充中文词表,可以提升中文编码效率,并提升模型性能。
ChatGLM-6B 是清华大学提出的支持中英双语问答的对话语言模型。ChatGLM-6B 采用了与 GLM-130B[4] 相同的模型结构。截止到 2022 年 7 月,GLM-130B 只训练了 400B 的 tokens,中英文比例为 1:1。ChatGLM-6B 则使用了更多的训练数据,多达 1T 的 tokens,训练语料只包含中文和英文,中英文比例为 1:1。
模型结构上,ChatGLM-6B 采用了 prefix decoder-only 的 transformer 模型框架,在输入上采用双向的注意力机制,在输出上采用单向注意力机制。在模型细节上,做了以下几点改动:
在训练目标上,ChatGLM-6B 的训练任务是自回归文本填空。相比于采用 causal decoder-only 结构的大语言模型,采用 prefix decoder-only 结构的 ChatGLM-6B 存在一个劣势:训练效率低。causal decoder 结构会在所有的 token 上计算损失,而 prefix decoder 只会在输出上计算损失,而不计算输入上的损失。在有相同数量的训练 tokens 的情况下,prefix decoder 要比 causal decoder 的效果差,因为训练过程中实际用到的 tokens 数量要更少。另外,ChatGPT 的成功已经证明了 causal decoder 结构的大语言模型可以获得非常好的 few-shot 和 zero-shot 生成能力,通过指令微调可以进一步激发模型的能力。至于 prefix decoder 结构的大语言模型能否获得相当的 few-shot 和 zero-shot 能力还缺少足够的验证。
关于 tokenizer,ChatGLM 在 25GB 的中英双语数据上训练了 SentencePiece 作为 tokenizer,词表大小为 130528。
下面是一些基于 ChatGLM 衍生出来的大模型应用:
BLOOM[5] 系列模型是由 BigScience 团队训练的大语言模型。训练数据包含了英语、中文、法语、西班牙语、葡萄牙语等共 46 种语言,另外还包含 13 种编程语言。1.5TB 经过去重和清洗的文本,转换为 350B 的 tokens。训练数据的语言分布如下图所示,可以看到中文语料占比为 16.2%。
按照模型参数量,BLOOM 模型有 560M、1.1B、1.7B、3B、7.1B 和 176B 这几个不同参数规模的模型。BLOOMZ 系列模型是在 xP3 数据集上微调得到的,推荐用于英语提示的场景。BLOOMZ-MT 系列模型是在 xP3mt 数据集上微调得到的,推荐用于非英语提示的场景。
模型结构上,与 GPT 相同,BLOOM 采用了 causal decoder-only 的 transformer 模型结构。在模型细节上,做了以下几点改动:
在训练目标上,BLOOM 的训练目标是语言模型,即根据已有的上文去预测下一个词。
关于 tokenizer,BLOOM 在多语种语料上使用 Byte Pair Encoding(BPE) 算法进行训练得到 tokenizer,词表大小为 250880。
下面是一些基于 BLOOM 衍生出来的大模型应用:
以上几个基座模型的 tokenizer 的词表大小不同,对同一个中文文本的分词结果会产生不同的结果。在 news_commentary 的 6.9 万条中英文平行语料上进行分词处理,对比分词结果和分词耗时,结果如下。“中文平均 token 数” 表示了 tokenizer 分词后,每个中文字符对应的平均 token 数。
模型 | 词表大小 | 中文平均 token 数 | 英文平均 token 数 | 中文处理时间 (s) | 英文处理时间 (s) |
---|---|---|---|---|---|
LLaMA | 32000 | 1.45 | 0.25 | 12.60 | 19.40 |
Chinese LLaMA | 49953 | 0.62 | 0.249 | 8.65 | 19.12 |
ChatGLM-6B | 130528 | 0.55 | 0.19 | 15.91 | 20.84 |
Bloom | 250880 | 0.53 | 0.22 | 9.87 | 15.60 |
从结果来看,
从两个例子上,来直观对比不同 tokenizer 的分词结果。“男儿何不带吴钩,收取关山五十州。” 共有 16 字。几个 tokenizer 的分词结果如下:
[ '男', '<0xE5>', '<0x84>', '<0xBF>', '何', '不', '<0xE5>', '<0xB8>', '<0xA6>', '<0xE5>', '<0x90>', '<0xB4>', '<0xE9>', '<0x92>', '<0xA9>', ',', '收', '取', '关', '山', '五', '十', '州', '。']
[ '男', '儿', '何', '不', '带', '吴', '钩', ',', '收取', '关', '山', '五十', '州', '。']
[ '男儿', '何不', '带', '吴', '钩', ',', '收取', '关山', '五十', '州', '。']
['男', '儿', '何不', '带', '吴', '钩', ',', '收取', '关', '山', '五十', '州', '。']
“杂申椒与菌桂兮,岂维纫夫蕙茝。” 的长度为 15 字。几个 tokenizer 的分词结果如下:
[ '<0xE6>', '<0x9D>', '<0x82>', '<0xE7>', '<0x94>', '<0xB3>', '<0xE6>', '<0xA4>', '<0x92>', '与', '<0xE8>', '<0x8F>', '<0x8C>', '<0xE6>', '<0xA1>', '<0x82>', '<0xE5>', '<0x85>', '<0xAE>', ',', '<0xE5>', '<0xB2>', '<0x82>', '<0xE7>', '<0xBB>', '<0xB4>', '<0xE7>', '<0xBA>', '<0xAB>', '夫', '<0xE8>', '<0x95>', '<0x99>', '<0xE8>', '<0x8C>', '<0x9D>', '。']
[ '杂', '申', '椒', '与', '菌', '桂', '兮', ',', '岂', '维', '纫', '夫', '蕙', '<0xE8>', '<0x8C>', '<0x9D>', '。']
[ '杂', '申', '椒', '与', '菌', '桂', '兮', ',', '岂', '维', '纫', '夫', '蕙', '<0xE8>', '<0x8C>', '<0x9D>', '。']
['杂', '申', '椒', '与', '菌', '桂', '兮', ',', '岂', '维', '�', '�', '夫', '蕙', '�', '�', '。']
从上面的例子可以看到,LLaMA 词表中包含了极少数的中文字符,常见字 “儿” 也被切分为了 3 个 token。Chinese LLaMA、ChatGLM-6B 和 Bloom 的词表中则覆盖了大部分中文常见字,另外也包含了一些中文常用词,比如都把 “收取” 这个词切分为了一个 token;对于一些生僻词,比如 “茝” 也会切分为 2-3 个 token。总的来说,LLaMA 通常会将一个中文汉字切分为 2 个以上的 token,中文编码效率低;Chinese LLaMA、ChatGLM-6B 和 Bloom 对中文分词的编码效率则更高。
如下图所示,按照 layer normalization 的位置不同,可以分为 post layer norm 和 pre layer norm。
post layer norm。在原始的 transformer 中,layer normalization 是放在残差连接之后的,称为 post LN。使用 Post LN 的深层 transformer 模型容易出现训练不稳定的问题。如下图所示,post LN 随着 transformer 层数的加深,梯度范数逐渐增大,导致了训练的不稳定性。
pre layer norm。[7] 改变 layer normalization 的位置,将其放在残差连接的过程中,self-attention 或 FFN 块之前,称为 “Pre LN”。如下图所示,Pre layer norm 在每个 transformer 层的梯度范数近似相等,有利于提升训练稳定性。相比于 post LN,使用 pre LN 的深层 transformer 训练更稳定,可以缓解训练不稳定问题。但缺点是 pre LN 可能会轻微影响 transformer 模型的性能。大语言模型的一个挑战就是如何提升训练的稳定性。为了提升训练稳定性,GPT3、PaLM、BLOOM、OPT 等大语言模型都采用了 pre layer norm。
layer normalization 的计算过程如下:
$$\mu = E(X) \leftarrow \frac{1}{d}\sum_{i=1}^{d}x_i\$$
\mu = E(X) \leftarrow \frac{1}{d}\sum_{i=1}^{d}x_i\
$$\sigma=\leftarrow \sqrt{\frac{1}{d}\sum_{i=1}{d}(x_i-\mu)2+\epsilon}\$$
\sigma=\leftarrow \sqrt{\frac{1}{d}\sum_{i=1}{d}(x_i-\mu)2+\epsilon}\
$$Y=\frac{X-E(X)}{\sqrt{Var(X)+\epsilon}}\cdot \gamma+\beta\$$
Y=\frac{X-E(X)}{\sqrt{Var(X)+\epsilon}}\cdot \gamma+\beta\ 其中, γ \gamma γ\gamma 和 β \beta β\beta 是可训练的模型参数。 γ \gamma γ\gamma 是缩放参数,新分布的方差为 γ 2 \gamma^2 γ2\gamma^2 ; β \beta β\beta 是平移参数,新分布的均值为 β \beta β\beta 。 ϵ \epsilon ϵ\epsilon 是一个很小的值,添加到方差上,避免分母为 0。如下图所示,红线部分为可训练的缩放参数 γ \gamma γ\gamma 和平移参数 β \beta β\beta 。
layer normalization 重要的两个部分是平移不变性和缩放不变性。 [8] 认为 layer normalization 取得成功重要的是缩放不变性,而不是平移不变性。因此,去除了计算过程中的平移,只保留了缩放,进行了简化,提出了 RMS Norm(Root Mean Square Layer Normalization),即均方根 norm。计算过程如下:
$$RMS(x) = \sqrt{\frac{1}{d}\sum_{i=1}{d}x_i2}\$$
RMS(x) = \sqrt{\frac{1}{d}\sum_{i=1}{d}x_i2}\
$$x=\frac{x}{RMS(x)} \cdot \gamma\$$
x=\frac{x}{RMS(x)} \cdot \gamma\
相比于正常的 layer normalization,RMS norm 去除了计算均值进行平移的部分,计算速度更快,效果基本相当,甚至略有提升。Gopher、LLaMA、T5 等大语言模型都采用了 RMS norm。
[9] 提出了 Deep Norm 可以缓解爆炸式模型更新的问题,把模型更新限制在常数,使得模型训练过程更稳定。具体地,Deep Norm 方法在执行 Layer Norm 之前,up-scale 了残差连接( α > 1 \alpha>1 α>1\alpha>1 );另外,在初始化阶段 down-scale 了模型参数 ( β < 1 \beta<1 β<1\beta<1 )。ChatGLM-6B 采用了基于 Deep Norm 的 post LN。
每个 transformer 层分为 self attention 块和 FFN 块两部分。FFN 通常先将向量从维度 d 升维到中间维度 4d,再从 4d 降维到 d。FFN 的计算公式如下:
$$FFN(x)=f(xW_1+b_1)W_2+b_2\$$
FFN(x)=f(xW_1+b_1)W_2+b_2\ 其中, f ( ) f() f()f() 为非线性激活函数。广泛使用的激活函数有 gelu(Gaussian Error Linear Unit) 函数和 swish 函数。swish 函数是一种自门控激活函数。
$$swish(x) = x\cdot\sigma(\beta x)\$$
swish(x) = x\cdot\sigma(\beta x)\ gelu 也是一种通过门控机制调整输出值的激活函数,与 swish 函数类似,可以用 tanh 函数或 σ ( ) \sigma() σ()\sigma() 函数近似。
$$gelu(x) \approx 0.5x(1+tanh(\sqrt{\frac{2}{\pi}}(x+0.044715x^3)))\$$
gelu(x) \approx 0.5x(1+tanh(\sqrt{\frac{2}{\pi}}(x+0.044715x^3)))\
$$gelu(x) \approx x\cdot\sigma(1.702 x)\$$
gelu(x) \approx x\cdot\sigma(1.702 x)\ [10] 提出了门控线形单元 GLU(Gated Linear Units),相比于正常的 FFN 只有两个权重矩阵,使用 GLU 的 FFN 额外增加了一个权重矩阵,即下式中的 V,共有三个权重矩阵,获得了更好的模型性能。
$$FFN_{GLU}=(f(xW_1)\otimes xV)W_2\$$
FFN_{GLU}=(f(xW_1)\otimes xV)W_2\
使用 gelu 激活函数的 GLU 计算公式为:
$$GeGLU(x) = GeLU(xW) \otimes xV\$$
GeGLU(x) = GeLU(xW) \otimes xV\ 使用 swish 激活函数的 GLU 计算公式为:
$$SwiGLU = Swish_{\beta}(xW) \otimes xV\$$
SwiGLU = Swish_{\beta}(xW) \otimes xV\
对于 transformer 模型,位置编码是必不可少的。因为 attention 模块是无法捕捉输入顺序的,无法区分不同位置的 token。位置编码分为绝对位置编码和相对位置编码。
最直接的方式是训练式位置编码,将位置编码当作可训练参数,训练一个位置编码向量矩阵。GPT3 就采用了这种方式。训练式位置编码的缺点是没有外推性,即若训练时最大序列长度为 2048,在推断时最多只能处理长度为 2048 的序列,超过这个长度就无法处理了。
苏神 [11] 提出了旋转位置编码 RoPE。训练式的位置编码作用在 token embedding 上,而旋转位置编码 RoPE 作用在每个 transformer 层的 self-attention 块,在计算完 Q/K 之后,旋转位置编码作用在 Q/K 上,再计算 attention score。旋转位置编码通过绝对编码的方式实现了相对位置编码,有良好的外推性。值得一提的是,RoPE 不包含可训练参数。LLaMA、GLM-130B、PaLM 等大语言模型就采用了旋转位置编码 RoPE。
ALiBi(Attention with Linear Biases)[12] 也是作用在每个 transformer 层的 self-attention 块,如下图所示,在计算完 attention score 后,直接为 attention score 矩阵加上一个预设好的偏置矩阵。这里的偏置矩阵是预设好的,固定的,不可训练。这个偏置根据 q 和 k 的相对距离来惩罚 attention score,相对距离越大,惩罚项越大。相当于两个 token 的距离越远,相互贡献就越小。ALiBi 位置编码有良好的外推性。BLOOM 就采用了这种位置编码。
随着大语言模型的参数量越来越大,进行大模型的全量微调成本很高。高成本主要体现在硬件资源要求高,显存占用多;训练速度慢,耗时长;存储成本高。高效参数微调(parameter-efficient finetuning techniques,PEFT)在微调大模型时只训练一小部分参数,而不是训练全量多模型参数。高效参数微调方法有以下几方面优点:
prompt tuning[13] 原本的含义指的是通过修改输入 prompt 来获得更好的模型效果。这里的提示是 “硬提示(hard prompt)”。我们直接修改输入 prompt,输入 prompt 是不可导的。
与 “硬提示” 相对应,“软提示微调(soft prompt tuning)”将一个可训练张量与输入文本的 embeddings 拼接起来,这个可训练张量可以通过反向传播来优化,进而提升目标任务的模型效果。这里的可训练张量可以理解为 prompt 文本对应的 embedding,是一个 soft prompt。如下图所示,这个可训练张量的形状是 [ v i r t u a l _ t o k e n s _ n u m , e m b e d _ s i z e ] [virtual\_tokens\_num, embed\_size] [virtual_tokens_num,embed_size][virtual_tokens_num, embed_size] 。
prompt tuning 冻结大模型原始的参数,只训练这个新增加的 prompt 张量。prompt tuning 随着基座模型参数量的增大效果会变好。
prefix tuning[14] 与 prompt tuning 相似,将一个特定任务的张量添加到输入,这个张量是可训练的,保持预训练模型的参数不变。主要区别如下:
prefix tuning 将 prefix 参数(可训练张量)添加到所有的 transformer 层,而 prompt tuning 只将可训练矩阵添加到输入 embedding。具体地,prefix tuning 会将 prefix 张量作为 past_key_value 添加到所有的 transformer 层。
用一个独立的 FFN 来编码和优化 prefix 参数,而不是直接优化 soft prompt,因为它可能造成不稳定并损害性能。在更新完 soft prompt 后,就不再使用 FFN 了。
prefix tuning 与 prompt tuning 的作用位置不同,有点类似于可训练式位置编码和旋转位置编码 RoPE。前者是直接作用在输入 embedding 上,后者是作用在所有 transformer 层的 self-attention 块,在计算得到 K 和 V 后,与可训练的 prefix 张量拼接起来。
如下图所示,prefix tuning 可训练张量的形状是 [ v i r t u a l _ t o k e n s _ n u m , 2 × l a y e r _ n u m × h i d d e n _ s i z e ] [virtual\_tokens\_num, 2\times layer\_num \times hidden\_size] [virtual_tokens_num,2×layer_num×hidden_size][virtual_tokens_num, 2\times layer_num \times hidden_size] 。下图是 LLaMA-7B 进行 prefix tuning 的例子,LLaMA-7B 有 32 个 transformer 层,隐藏维度为 4096,有 $262144=2\times 32\times 4096$262144=2\times 32\times 4096 。这里的 2 对应的是 K 和 V。
adapter[16] 在某种程度上与 prefix tuning 是类似的,二者都是把额外的可训练参数添加到每个 transformer 层。不同之处是:prefix tuning 是把 prefix 添加到输入 embedding;而 adapter 在两个位置插入了 adapter 层,如下图所示。
LLaMA-adapter[16] 结合了 prefix tuning 和 adapter。与 prefix tuning 类似,LLaMA-adapter 在输入 embed 上添加了可训练的 prompt 张量。需要注意的是:prefix 是以一个 embedding 矩阵学习和保持的,而不是外部给出的。每个 transformer 层都有各自不同的可学习 prefix,允许不同模型层进行更量身定制的适应。
如上图所示,LLaMA-adapter 引进了零初始化的注意力机制和门控机制。动机是 adapter 和 prefix tuning 结合了随机初始化的张量(prefix prompts 和 adapter layers)很大可能会损害预训练语言模型的语义学知识,导致在训练初始阶段的微调不稳定和很高的性能损失。
另一个重要的区别是,LLaMA-adapter 只给 L 个深层 transformer 层添加了可学习的 adaption prompts,而不是给所有的 transformer 层都添加。作者认为,这种方法可以更有效的微调专注于高级语义信息的语言表示。
LLaMA-adapter 与 prefix tuning 的基本想法是相关的,都是添加可训练的 soft prompts。二者之间存在一些细微差别,添加 soft prompt 时只修改输入的 key 和 value 序列,而不修改 query。另外,取决于门控因子(在训练开始阶段设置为 0),决定是否使用 prefix 修改的注意力机制。
总的来说,LLaMA-adapter 与 prefix tuning 的主要区别是:LLaMA-adapter 只修改深层的 L 个 transformer 层,并且引入了门控机制来实现稳定训练。尽管 LLaMA-adapter 是在 LLaMA 上进行实验的,但它适用于 GPT 结构的其他大模型。
如下图所示,LLaMA-adapter 每个 transformer 层都有各自不同的可学习参数,一个 prefix 张量和一个门控因子。prefix 张量的形状是 [ 1 , v i r t u a l _ t o k e n s _ n u m , h i d d e n _ s i z e ] [1, virtual\_tokens\_num, hidden\_size] [1,virtual_tokens_num,hidden_size][1, virtual_tokens_num, hidden_size] 。
transformer 模型中包含了很多进行矩阵乘法的稠密层。有论文认为预训练语言模型存在一个低秩,即使映射到更小的子空间中,依然可以高效地学习。基于这个理论,LoRA(Low-Rank Adaptation)[18] 假设模型在适应 / 微调过程中的权重参数更新也存在一个低的内在维度。
对于一个预训练好的模型权重参数 W 0 ∈ R d × k W_0 \in R^{d\times k} W0∈Rd×kW_0 \in R^{d\times k} ,我们可以将权重更新限制为 低秩矩阵分解。即:
$$W_0 + \Delta W = W_0 + BA,B\in R^{d\times r}; A\in R^{r \times k}\$$
W_0 + \Delta W = W_0 + BA,B\in R^{d\times r}; A\in R^{r \times k}\ 其中, r ≪ m i n ( d , k ) r \ll min(d,k) r≪min(d,k)r \ll min(d,k) 。在微调训练过程中, W 0 W_0 W0W_0 是冻结的,不需要进行参数更新; A , B A,B A,BA,B 才是需要训练的参数。在训练开始前,用随机高斯分布初始化 A A AA ,将 B B BB 初始化为全 0 矩阵,保持训练开始时的旁路矩阵 A B AB ABAB 为全 0 矩阵。
如下图所示,给定 h = W 0 x h = W_0x h=W0xh = W_0x ,更新之后有: h = W 0 x + Δ W x = W 0 x + B A x h = W_0x +\Delta W x = W_0x +BAx h=W0x+ΔWx=W0x+BAxh = W_0x +\Delta W x = W_0x +BAx 。
对于 LLaMA-6B,采用 LoRA 在每个 transformer 层的 self-attention 块的 query 和 value 上注入旁路模块,可训练参数如下。
huggingface 的 PEFT 库已经实现了上述的这几种高效参数微调方法。在 LLaMA-7B、ChatGLM-6B 和 BLOOM-7B 这三个基座模型上尝试不同的高效参数微调方法,对比它们的微调效果。
在单机 8 卡的 40GB A100 上进行训练。为了节省显存和加速训练,采用 deepspeed 框架的 ZeRO stage3 和 CPU offload,开启 float16 的混合精度训练,不进行激活重计算。设置单卡 batch_size 为 4,最大序列长度为 512,梯度累加步数为 4,学习率设置为 1e-4。在 alpaca chinese 数据上训练 3 个 epoch。
下图是 LLaMA-7B 进行高效参数微调和全量微调的损失变化曲线。acc 指标衡量了语言模型预测下一个词的准确率。从损失变化曲线来看,对于 LLaMA-7B 基座模型,prompt tuning 和 prefix tuning 的效果是比较差的,在训练损失上,prompt tuning 甚至要好于 prefix tuning。这可能是因为 prefix tuning 在每个 transformer 层结合了随机初始化的张量,很大可能会损害预训练语言模型的语义学知识,导致微调效果很差。相比于 prompt tuning 和 prefix tuning,LLaMA-adapter 的效果有明显提升,这可能得益于 LLaMA-adapter 引入了门控机制来实现稳定训练。在高效参数微调方法中,LoRA 的效果是最好的,从验证损失来看,LoRA 的效果只略微逊色于全量参数微调。总的来说,对于 LLaMA-7B,全量参数微调 > LoRA > LLaMA-adapter >prompt tuning > prefix tuning。
下图是 Chinese LLaMA-7B 进行高效参数微调和全量微调的损失变化曲线。Chinese LLaMA-7B 是在 LLaMA-7B 基座模型上进一步预训练得到的。几种轻量化微调方法的效果对比与 LLaMA-7B 是基本一致的。prompt tuning 和 prefix tuning 的效果是比较差,LLaMA-adapter 的效果有明显提升,LoRA 的效果与全量参数微调基本相当。
下图是 ChatGLM-6B 进行高效参数微调和全量微调的损失变化曲线。
从损失变化曲线来看,LoRA 的效果甚至要好于全量参数微调。
下图是 BLOOM 进行高效参数微调和全量微调的损失变化曲线。对于 BLOOM-7B 基座模型,prefix tuning 的效果要略微好于 prompt tuning。在验证损失上,prompt tuning 和 prefix tuning 的效果甚至与全量参数微调基本相当。LoRA 则取得了比全量参数微调更低的验证损失。
总的来看,汇总四个基座模型的微调损失变化曲线,对于不同的基座模型,全量参数微调的方法都容易出现过拟合问题,随着训练 epoch 增大,训练损失呈现出阶梯式下降,但验证损失从第三个 epoch 开始上升。而高效参数微调方法只训练极少数新增参数,冻结预训练语言模型的参数,有效避免了过拟合问题。对于这几种高效参数微调方法,验证损失曲线和训练损失曲线基本是重叠的,走势相同。
对比了不同参数高效微调方法的效果,那哪个基座模型的微调效果要更好呢?不同基座模型的 tokenizer 和词表大小不同,导致它们之间的训练损失是不可以比较的。下图是不同基座模型上进行 LoRA 微调的损失变化曲线,由于不可比,没有实际意义。
如下图所示,可以尝试从具体的例子来比较不同基座模型和不同参数高效微调方法的效果。对于 LLaMA 和 Chinese LLaMA 模型,prompt tuning 和 prefix tuning 的效果都很差,不能生成流畅通顺的中文回复。虽然 LLaMA 的预训练数据中不包含中文,但经过指令微调后,仍然能生成不错的中文回复。另外,LoRA 微调生成的回复要明显好于 LLaMA-adapter 的回复。
相比于 LLaMA,扩展了词表的 Chinese LLaMA 微调后生成的回复要更好。这也验证了扩展词表有助于提升 LLaMA 在中文上的效果。
对于 BLOOM,prompt tuning 和 prefix tuning 都可以生成流畅的中文回复,prefix tuning 生成的回复要好于 prompt tuning。ChatGLM-6B 模型生成回复偏短,比较简单。
本文首先从训练数据、tokenizer 和模型结构细节上对比了 LLaMA、ChatGLM 和 BLOOM 这三个主流的开源大语言模型,并介绍了这三个基座模型的衍生模型;接着详细介绍了不同大语言模型在 tokenizer、layer normalization、激活函数和位置编码的模型细节;然后讲述了 prompt tuning、prefix tuning、LLaMA- adapter 和 LoRA 这些参数高效微调方法;最后对比了不同基座语言模型和不同微调方法的效果。
Zhou C, Liu P, Xu P, et al. Lima: Less is more for alignment[J]. arXiv preprint arXiv:2305.11206, 2023.
Touvron H, Lavril T, Izacard G, et al. Llama: Open and efficient foundation language models[J]. arXiv preprint arXiv:2302.13971, 2023.
Hoffmann J, Borgeaud S, Mensch A, et al. Training compute-optimal large language models[J]. arXiv preprint arXiv:2203.15556, 2022.
Zeng A, Liu X, Du Z, et al. Glm-130b: An open bilingual pre-trained model[J]. arXiv preprint arXiv:2210.02414, 2022.
Scao T L, Fan A, Akiki C, et al. Bloom: A 176b-parameter open-access multilingual language model[J]. arXiv preprint arXiv:2211.05100, 2022.
Cui Y, Yang Z, Yao X. Efficient and Effective Text Encoding for Chinese LLaMA and Alpaca[J]. arXiv preprint arXiv:2304.08177, 2023.
Xiong R, Yang Y, He D, et al. On layer normalization in the transformer architecture[C]//International Conference on Machine Learning. PMLR, 2020: 10524-10533.
Zhang B, Sennrich R. Root mean square layer normalization[J]. Advances in Neural Information Processing Systems, 2019, 32.
Wang H, Ma S, Dong L, et al. Deepnet: Scaling transformers to 1,000 layers[J]. arXiv preprint arXiv:2203.00555, 2022.
Shazeer N. Glu variants improve transformer[J]. arXiv preprint arXiv:2002.05202, 2020.
Su J, Lu Y, Pan S, et al. Roformer: Enhanced transformer with rotary position embedding[J]. arXiv preprint arXiv:2104.09864, 2021.
Press O, Smith N A, Lewis M. Train short, test long: Attention with linear biases enables input length extrapolation[J]. arXiv preprint arXiv:2108.12409, 2021.
Lester B, Al-Rfou R, Constant N. The power of scale for parameter-efficient prompt tuning[J]. arXiv preprint arXiv:2104.08691, 2021.
Li X L, Liang P. Prefix-tuning: Optimizing continuous prompts for generation[J]. arXiv preprint arXiv:2101.00190, 2021.
Houlsby N, Giurgiu A, Jastrzebski S, et al. Parameter-efficient transfer learning for NLP[C]//International Conference on Machine Learning. PMLR, 2019: 2790-2799.
Zhang R, Han J, Zhou A, et al. Llama-adapter: Efficient fine-tuning of language models with zero-init attention[J]. arXiv preprint arXiv:2303.16199, 2023.
Hu E J, Shen Y, Wallis P, et al. Lora: Low-rank adaptation of large language models[J]. arXiv preprint arXiv:2106.09685, 2021.