LLM 大模型学习必知必会系列 (四):LLM 训练理论篇以及 Transformer 结构模型详解
LLM 大模型学习必知必会系列(四):LLM 训练理论篇以及 Transformer 结构模型详解
1.模型/训练/推理知识介绍
深度学习领域所谓的“模型”,是一个复杂的数学公式构成的计算步骤。为了便于理解,我们以一元一次方程为例子解释:
该方程意味着给出常数 a、b 后,可以通过给出的 x 求出具体的 y。比如:
这个根据 x 求出 y 的过程就是模型的推理过程。在 LLM 中,x 一般是一个句子,如“帮我计算 23+20 的结果”,y 一般是:“等于 43”。
基于上面的方程,如果追加一个要求,希望 a=1,b=1,x=3 的时候 y=10 呢?这显然是不可能的,因为按照上面的式子,y 应该是 4。然而在 LLM 中,我们可能要求模型在各种各样的场景中回答出复杂的答案,那么这显然不是一个线性方程能解决的场景,于是我们可以在这个方程外面加上一个非线性的变换:
这个非线性变换可以理解为指数、对数、或者分段函数等。
在加上非线性部分后,这个公式就可以按照一个复杂的曲线(而非直线)将对应的 x 映射为 y。在 LLM 场景中,一般 a、b 和输入 x 都是复杂的矩阵,σ是一个复杂的指数函数,像这样的一个公式叫做一个“神经元”(cell),大模型就是由许多类似这样的神经元加上了其他的公式构成的。
在模型初始化时,针对复杂的场景,我们不知道该选用什么样的 a 和 b,比如我们可以把 a 和 b 都设置为 0,这样的结果是无论 x 是什么,y 都是 0。这样显然是不符合要求的。但是我们可能有很多数据,比如:
我们客观上相信这些数据是正确的,希望模型的输出行为能符合这些问题的回答,那么就可以用这些数据来训练这个模型。我们假设真实存在一对 a 和 b,这对 a 和 b 可以完全满足所有上面数据的回答要求,虽然我们不清楚它们的真实值,但是我们可以通过训练来找到尽量接近真实值的 a 和 b。
训练(通过 x 和 y 反推 a 和 b)的过程在数学中被称为拟合。
模型需要先进行训练,找到尽量符合要求的 a 和 b,之后用 a 和 b 输入真实场景的 x 来获得 y,也就是推理。
1.1 预训练范式
在熟悉预训练之前,先来看几组数据:
第一组:
第二组:
第三组:
我们会发现:
第一组数据是没有问题答案的(未标注),这类数据在互联网上比比皆是
第二组数据包含了问题和答案(已标注),是互联网上存在比例偏少的数据
第三组数据不仅包含了正确答案,还包含了错误答案,互联网上较难找到
这三类数据都可以用于模型训练。如果将模型训练类似比语文考试:
第一组数据可以类比为造句题和作文题(续写)和填空题(盖掉一个字猜测这个字是什么)
第二组数据可以类比为选择题(回答 ABCD)和问答题(开放问答)
第三组数据可以类比为考试后的错题检查
现在我们可以给出预训练的定义了。
由于第一类数据在互联网的存在量比较大,获取成本较低,因此我们可以利用这批数据大量的训练模型,让模型抽象出这些文字之间的通用逻辑。这个过程叫做预训练。
第二类数据获得成本一般,数据量较少,我们可以在预训练后用这些数据训练模型,使模型具备问答能力,这个过程叫做微调。
第三类数据获得成本很高,数据量较少,我们可以在微调后让模型了解怎么回答是人类需要的,这个过程叫人类对齐。
一般我们称做过预训练,或预训练结合通用数据进行了微调的模型叫做 base 模型。这类模型没有更专业的知识,回答的答案也可能答非所问或者有重复输出,但已经具备了很多知识,因此需要进行额外训练才能使用。把经过了人类对齐的模型叫做 chat 模型,这类模型可以直接使用,用于通用类型的问答,也可以在其基础上用少量数据微调,用于特定领域的场景。
预训练过程一般耗费几千张显卡,灌注数据的量达到几个 TB,成本较高。
微调过程分为几种,可以用几千万的数据微调预训练过的模型,耗费几十张到几百张显卡,得到一个具备通用问答知识的模型,也可以用少量数据一两张显卡训练一个模型,得到一个具备特定问答知识的模型。
人类对齐过程耗费数张到几百张显卡不等,技术门槛比微调更高一些,一般由模型提供方进行。
1.2 如何确定自己的模型需要做什么训练?
Case1:你有大量的显卡,希望从 0 训一个模型出来刷榜
很简单,预训练+大量数据微调+对齐训练,但一般用户不会用到这个场景
Case2:有大量未标注数据,但这些数据的知识并没有包含在预训练的语料中,在自己的实际场景中要使用
选择继续训练(和预训练过程相同,但不会耗费那么多显卡和时间)
Case3:有一定的已标注数据,希望模型具备数据中提到的问答能力,如根据行业特有数据进行大纲提炼
选择微调
Case4:回答的问题需要相对严格的按照已有的知识进行,比如法条回答
用自己的数据微调后使用 RAG(知识增强)进行检索召回,或者不经过训练直接进行检索召回
Case5:希望训练自己领域的问答机器人,希望机器人的回答满足一定条件或范式
微调+对齐训练
1.3 模型推理的一般过程
现在有一个句子,如何将它输入模型得到另一个句子呢?
我们可以这样做:
先像查字典一样,将句子变为字典中的索引。假如字典有 30000 个字,那么“我爱张学”可能变为[12,16,23,36]
像[12,16,23,36]这样的标量形式索引并不能直接使用,因为其维度太低,可以将它们映射为更高维度的向量,比如每个标量映射为 5120 长度的向量,这样这四个字就变为:
我们就得到了 4x5120 尺寸的矩阵(这四个字的矩阵表达)。
深度学习的基本思想就是把一个文字转换为多个小数构成的向量
把这个矩阵在模型内部经过一系列复杂的计算后,最后会得到一个向量,这个向量的小数个数和字典的字数相同。
下面我们把这些小数按照大小转为比例,使这些比例的和是 1,通常我们把这个过程叫做概率化。把值(概率)最大的索引找到,比如使 51,那么我们再把 51 通过查字典的方式找到实际的文字:
下面,我们把“我爱张学友”重新输入模型,让模型计算下一个文字的概率,这种方式叫做自回归。即用生成的文字递归地计算下一个文字。推理的结束标志是结束字符,也就是 eos_token,遇到这个 token 表示生成结束了。
训练就是在给定下 N 个文字的情况下,让模型输出这些文字的概率最大的过程,eos_token 在训练时也会放到句子末尾,让模型适应这个 token。
2. PyTorch 框架
用于进行向量相乘、求导等操作的框架被称为深度学习框架。高维度的向量被称为张量(Tensor),后面我们也会用 Tensor 代指高维度向量或矩阵。
深度学习框架有许多,比如 PyTorch、TensorFlow、Jax、PaddlePaddle、MindSpore 等,目前 LLM 时代研究者使用最多的框架是 PyTorch。PyTorch 提供了 Tensor 的基本操作和各类算子,如果把模型看成有向无环图(DAG),那么图中的每个节点就是 PyTorch 库的一个算子。
参考链接:超全安装教程
conda 配置好后,新建一个虚拟环境(一个独立的 python 包环境,所做的操作不会污染其它虚拟环境):
之后:
打开 python 命令行:
可以看到,a 的梯度是 2.0,b 的梯度是 1.0,这是因为 c 对 a 的偏导数是 b,对 b 的偏导数是 a 的缘故。backward 方法非常重要,模型参数更新依赖的就是 backward 计算出来的梯度值。
torch.nn.Module 基类:所有的模型结构都是该类的子类。一个完整的 torch 模型分为两部分,一部分是代码,用来描述模型结构:
state_dict 存下来就是 pytorch_model.bin,也就是存在于modelhub中的文件
config.json:用于描述模型结构的信息,如上面的 Linear 的尺寸(4, 4)
tokenizer.json: tokenizer 的参数信息
vocab.txt: nlp 模型和多模态模型特有,描述词表(字典)信息。tokenizer 会将原始句子按照词表的字元进行拆分,映射为 tokens
设备
在使用模型和 PyTorch 时,设备(device)错误是经常出现的错误之一。
tensor 和 tensor 的操作(比如相乘、相加等)只能在两个 tensor 在同一个设备上才能进行。要不然 tensor 都被存放在同一个显卡上,要不然都放在 cpu 上。一般最常见的错误就是模型的输入 tensor 还在 cpu 上,而模型本身已经被放在了显卡上。PyTorch 驱动 N 系列显卡进行 tensor 操作的计算框架是 cuda,因此可以非常方便地把模型和 tensor 放在显卡上:
2.1 PyTorch 基本训练代码范例
3.Transformer 结构模型
在 2017 年之后,Transformer 结构模型几乎横扫一切统治了 NLP 领域,后面的 CV 领域和 Audio 领域也大放异彩。相比 LSTM 和 CNN 结构,Transformer 结构好在哪里呢?
这是 LLaMA2 的模型结构。
介绍下基本结构和流程:
Input 是原始句子,经过 Tokenizer 转变为 tokens
tokens 输入模型,第一个算子是 Embedder,tokens 转换为 float tensor
之后进入 layers,每个 layers 会包含一个 attention 结构,计算 Q 和 K 的 tensor 的内积,并将内积概率化,乘以对应的 V 获得新的 tensor。
tensor 加上输入的 x 后(防止层数太深梯度消失)进入 Normalization,对 tensor 分布进行标准化
进入 FeedForward(MLP),重新进入下一 layer
所有的 layers 计算过后,经过一个 linear 求出对 vocab 每个位置的概率
可以看出,Transformer 模型的基本原理是让每个文字的 Tensor 和其他文字的 Tensor 做内积(也就是 cosine 投影值,可以理解为文字的相关程度)。之后把这些相关程度放在一起计算各自占比,再用占比比例分别乘以对应文字的 Tensor 并相加起来,得到了一个新的 Tensor(这个 Tensor 是之前所有 Tensor 的概率混合,可以理解为对句子所有文字的抽象)。每个文字都进行如上动作,因此生成的新的 Tensor 和之前输入的 Tensor 长度相同(比如输入十个字,计算得到的 Tensor 还是十个),在层数不断堆叠的情况下,最后的 Tensor 会越来越抽象出文字的深层次意义,用最后输出的 Tensor 去计算输出一个新的文字或分类。
3.1 Transformer 对比 CNN 和 LSTM
CNN 有局部性和平移不变性,促使模型关注局部信息。CNN 预设了归纳偏差,这使得小样本训练可以取得较好效果,但在充分数据训练下这一效果也被 transformer 所掩盖。并且局部性会忽略全局关系,导致某些条件下效果不佳
LSTM 的长距离记忆会导致最早的 token 被加速遗忘,并且其只能注意单侧信息导致了对句子的理解存在偏差。后来虽然引入了双向 LSTM,但其大规模分布式训练仍然存在技术问题
Transformer 结构并不预设归纳偏差,因此需要大数据量训练才有较好效果。但其对于 token 的并行计算大大加速了推理速度,并且对分布式训练支持较好,因此在目前数据量充足的情况下反而异军突起。由于内置了 positional-embedding,因此较好地解决了 attention 结构中的位置不敏感性
3.2 Encoder 和 Decoder
如上图所示,左边是 encoder,右边是 decoder。我们可以看到目前的 LLM 模型几乎都是 decoder 结构,为什么 encoder-decoder 结构模型消失了呢?有以下几个原因:
encoder-decoder 模型分布式训练困难 decoder 模型结构简单,其分布式训练相对容易,而 encoder-decoder 结构的模型由于结构复杂的多导致了训练时工程结构复杂,成本大大增加
有论文证明,encoder-decoder 模型在参数量不断增加时不具有显著优势。在模型较小时,由于中间隐变量的存在,decoder 部分进行交叉注意力会获得更好的效果,但随着模型增大,这些提升变得不再明显。甚至有论文猜测,encoder-decoder 结构的收益仅仅是因为参数量翻倍
因此,目前的模型都是 decoder 模型,encoder-decoder 模型几乎销声匿迹。
我们可以看到,LLaMA2 的模型特点是:
没有使用 LayerNorm,而是使用了 RMSNorm 进行预归一化
使用了 RoPE(Rotary Positional Embedding)
MLP 使用了 SwiGLU 作为激活函数
LLaMA2 的大模型版本使用了 Group Query Attention(GQA)
3.2.1 RMSNorm
LayerNorm 的公式是:
RMSNorm 的开发者发现,减去均值做中心偏移意义不大,因此简化了归一化公式,最终变为:
$$\begin{align} \begin{split} & \bar{a}i = \frac{a_i}{\text{RMS}(\mathbf{a})} g_i, \quad \text{where}~~ \text{RMS}(\mathbf{a}) = \sqrt{\frac{1}{n} \sum{i=1}^{n} a_i^2} \end{split}\nonumber \end{align}$$
最终在保持效果不变的情况下,计算时间提升了 40%左右。
3.2.2 RoPE
BERT 模型使用的原始位置编码是 Sinusoidal Position Encoding。该位置编码的原理非常简单:
该设计的主要好处在于:
在位置编码累加到 embedding 编码的条件下,基本满足不同位置编码的内积可以模拟相对位置的数值
随着相对位置增大,其位置编码的内积趋近于 0
具备一定的外推特性
LLM 常用的位置编码还有 AliBi(注意力线性偏置)。该方法不在 embedding 上直接累加位置编码,而选择在 Q*K 的结果上累加一个位置矩阵:
ALiBi 的好处在于:
具备良好的外推特性
相对位置数值很稳定
RoPE 的全称是旋转位置编码(Rotary Positional Embedding),该编码的推导过程和 Sinusoidal Position Encoding 的推导过程比较类似,不同之处在于后者是加性的,而前者是乘性的,因此得到的位置编码类似于:
或者也可以简化为:
该位置编码表示相对位置的几何意义比较明显,也就是两个向量的角度差。
该位置编码的优势在于:
位置编码矩阵是单位正交阵,因此乘上位置编码后不会改变原向量模长
相较于 Sinusoidal Position Encoding 具备了更好的外推特性
3.2.3 SwiGLU
SwiGLU 是 GLU 结构的变种。GLU 是和 LSTM 原理类似,但不能接受时序数据,只能处理定长数据。而且省略了遗忘门与记忆门,只保留了输入门,SwiGLU 是将其中的激活函数替换为了 SiLU:
其中
的表达式为:
在 SwiGLU 的论文中,作者论证了 SwiGLU 在 LOSS 收益上显著强于 ReLU、GeLU、LeakyGeLU 等其他激活方法。
3.2.4 GQA
MHA(Multi-head Attention)是标准的多头注意力机制,具有 H 个 Query、Key 和 Value 矩阵
MQA(Multi-Query Attention,来自于论文:Fast Transformer Decoding: One Write-Head is All You Need)共享了注意力头之间的 KV,只为每个头保留单独的 Q 参数,减少了显存占用。
GQA(Grouped-Query Attention,来自于论文:GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints)在 MQA 的基础上分成了 G 个组,组内共享 KV。
在 Llama2 模型中,70B 参数为了提升推理性能使用了 GQA,其他版本没有使用这项技术。
3.3 ChatGLM2 的模型结构
ChatGLM2 模型结构和 Llama2 的结构有一定相似之处,主要不同之处在于:
在开源的 ChatGLM2 代码中没有使用 GQA,而是使用了 MQA
QKV 为单一矩阵,在对 hidden_state 进行整体仿射后拆分为 Query、Key、Value
MLP 结构中没有使用 Up、Gate、Down 三个 Linear 加上 SwiGLU,而是使用了 hidden_size -> 2 * ffn_hidden_size 的 Up Linear 进行上采样,对 tensor 进行拆分为两个宽度为 ffn_hidden_size 的 tensor 后直接输入 SiLU,然后经过 ffn_hidden_size -> hidden_size 的 Down Linear 进行下采样
更多优质内容请关注公号:汀丶人工智能;会提供一些相关的资源和优质文章,免费获取阅读。
版权声明: 本文为 InfoQ 作者【AI课程】的原创文章。
原文链接:【http://xie.infoq.cn/article/6b8d060bd1676d0516f75ac45】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论