#大语言模型 #基础 #结构 #GPT
==导言:这部分代码太多了,只提到编码器结构,本人仅作爱好和初学者看不懂,,不知道用什么编译器,所以机械性复刻。==
语言模型的目标是对自然语言的概率分布建模,在自然语言处理研究中具有重要的作用,是 自然语言处理的基础任务之一。大量的研究从n元语言模型(n-gramLanguageModels)、神经 语言模型以及预训练语言模型等不同角度开展了一系列工作。这些研究在不同阶段对自然语言处理任务有重要作用。随着基于Transformer的各类语言模型的发展及预训练微调范式在自然语言处理各类任务中取得突破性进展,从2020年OpenAI发布GPT-3开始,对大语言模型的研究逐渐深入。虽然大语言模型的参数量巨大,通过有监督微调和强化学习能够完成非常多的任务,但是其基础理论仍然离不开对语言的建模。
本章介绍了Transformer结构,在此基础上介绍生成式预训练语言模型GPT、大语言模型网
络结构和注意力机制优化及相关实践。
## 2.1 Transformer结构
Transformer结构是由Google在2017年提出并首先应用于机器翻译的神经网络模型架构。机器翻译的目标是从源语言(SourceLanguage)转换到目标语言(Target Language)。Transformer结构完全通过注意力机制完成对源语言序列和目标语言序列全局依赖的建模。如今,几乎全部大语言模型都是基于Transformer结构的。本节以应用于机器翻译的基于Transformer
的编码器和解码器为例介绍该模型。基于Transformer的编码器和解码器结构如图2.1所示,左侧和右侧分别对应着编码器(En-coder)和解码器(Decoder)结构,它们均由若干个基本的Transformer块(Block)组成(对应图中的灰色框)。这里N×表示进行了N次堆叠。每个Transformer块都接收一个向量序列![[Pasted image 20250123184814.png]]作为输入,并输出一个等长的向量序列作为输出![[Pasted image 20250123184836.png]]。这里的x ᵢ和yᵢ分别对应文本序列中 的一个词元(Token)的表示。yᵢ是当前Transformer块对输入xᵢ进一步整合其上下文语义后对应的输出。在从输入{xᵢ}t²=1到输出{yᵢ}t²=1的语义抽象过程中,主要涉及如下几个模块。
- **注意力层**:使用多头注意力(Multi-HeadAttention)机制整合上下文语义,它使得序列中
任意两个单词之间的依赖关系可以直接被建模而不基于传统的循环结构,从而更好地解决
文本的长程依赖问题。
- **位置感知前馈网络层**(Position-wiseFeed-ForwardNetwork):通过全连接层对输入文本序
列中的每个单词表示进行更复杂的变换。
- **残差连接**:对应图中的Add部分。它是一条分别作用在上述两个子层中的直连通路,被用于连接两个子层的输入与输出,使信息流动更高效,有利于模型的优化。
- **层归一化**:对应图中的Norm部分。它作用于上述两个子层的输出表示序列,对表示序列进行层归一化操作,同样起到稳定优化的作用。
### 2.1.1 嵌入表示层
对于输入文本序列,先通过输入嵌入层(InputEmbedding)将每个单词转换为其相对应的
向量表示。通常,直接对每个单词创建一个向量表示。Transformer结构不再使用基于循环的方式建模文本输入,序列中不再有任何信息能够提示模型单词之间的相对位置关系。在送入编码器端
![[P8}N(1[ZE@_OUBMZ7KMRHD1_tmb.jpg]]
建模其上下文语义之前,一个非常重要的操作是在词嵌入中加入位置编码(PositionalEncoding)
这一特征。具体来说,序列中每一个单词所在的位置都对应一个向量。这一向量会与单词表示对应相加并送入后续模块中做进一步处理。在训练过程中,模型会自动地学习到如何利用这部分位置信息。
为了得到不同位置所对应的编码, Transformer结构使用不同频率的正余弦函数,如下所示。 ![[Pasted image 20250123185340.png]]
其中,pos表示单词所在的位置,2i和2i+1表示位置编码向量中的对应维度,d则对应位置编码
的总维度。通过上面这种方式计算位置编码有以下两个好处:第一,正余弦函数的范围是`[-1,+1]`,
导出的位置编码与原词嵌入相加不会使得结果偏离过远而破坏原有单词的语义信息;第二,依据 三角函数的基本性质,可以得知第pos+k个位置编码是第pos个位置编码的线性组合,这就意味着位置编码中蕴含着单词之间的距离信息。
使用PyTorch实现的位置编码参考代码如下:
```
```class PositionalEncoder(nn.Module):
def _init_(self,d_model, max_seq_len=80):
super()._init_()
self.d_model=d_model
#根据pos和i创建一个常量PE矩阵
pe=torch.zeros(max_seq_len,d_model)
forpos in range(max_seq_len):
for i in range(0,d_model,2):
pe[pos,i]=math.sin(pos/(10000**(i/d_model)))
pe[pos,i+1]= math.cos(pos/(10000**(i/d_model)))
pe=pe.unsqueeze(0)
self.register_buffer('pe', pe)
def forward(self,x):
#使得单词嵌入表示相对大一些
x=x*math.sqrt(self.d_model)
#增加位置常量到单词嵌入表示中
seq_len=x.size(1)
x=x+Variable(self.pe[:,:seq_len], requires_grad=False).cuda()
return x
```
![[Pasted image 20250123185754.png]]
### 2.1.2 注意力层
自注意力(Self-Attention)操作是基于Transformer的机器翻译模型的基本操作,在源语言
的编码和目标语言的生成中频繁地被使用,以建模源语言、目标语言任意两个单词之间的依赖关系。将由单词语义嵌入及其位置编码叠加得到的输入表示为{xi∈Rd}ξ=1,为了实现对上下文语义依赖的建模,引入自注意力机制涉及的三个元素:查询qi(Query)、键ki(Key)和值vi
(Value)。在编码输入序列的每一个单词的表示中,这三个元素用于计算上下文单词对应的权重得分。直观地说,这些权重反映了在编码当前单词的表示时,对于上下文不同部分所需的关注程度。
为了得到编码单词xᵢ时所需要关注的上下文信息,通过位置i查询向量与其他位置的键向
量做点积得到匹配分数qᵢ·k₁,qᵢ·k₂,…,qᵢ·kt。为了防止过大的匹配分数在后续Softmax计算
过程中导致的梯度爆炸及收敛效率差的问题,这些得分会除以放缩因子√d以稳定优化。放缩后的得分经过Softmax归一化为概率,与其他位置的值向量相乘来聚合希望关注的上下文信息,并 最小化不相关信息的干扰。上述计算过程可以被形式化地表述如下:
![[Pasted image 20250123190101.png]]
其中Q∈RL×d₄,K∈RL×dk,V∈RL×dv分别表示输入序列中的不同单词的q,k,v向量拼接组
成的矩阵,L表示序列长度,Z∈RL×dv表示自注意力操作的输出。为了进一步增强自注意力机制聚合上下文信息的能力,提出了多头自注意力(Multi-head Attention)机制,以关注上下文的不同侧面。具体来说,上下文中每一个单词的表示 xi 经过多组线性![[Pasted image 20250123190326.png]]映射到不同的表示子空间中![[Pasted image 20250123190346.png]]![[Pasted image 20250123190401.png]]
![[Pasted image 20250123185815.png]]
![[Pasted image 20250123190425.png]]![[Pasted image 20250123190436.png]]
### 2.1.3 前馈层
前馈层接收自注意力子层的输出作为输入,并通过一个带有ReLU激活函数的两层全连接|
络对输入进行更复杂的非线性变换。实验证明,这一非线性变换会对模型最终的性能产生重要影响。
![[Pasted image 20250123191249.png]]
实验结果表明,增大前馈子层隐状态的维度有利于i 高最终翻译结果的质量,因此,前馈子层隐状态的维度一般比自注意力子层要大。
![[Pasted image 20250123191315.png]]
### 2.1.4 残差连接与层归一化
由Transformer结构组成的网络结构通常都非常庞大。编码器和解码器均由很多层基本的
Transformer块组成,每一层中都包含复杂的非线性映射,这就导致模型的训练比较困难。因此,研究人员在Transformer块中进一步引入了残差连接与层归一化技术,以进一步提升训练的稳定性。具体来说,残差连接主要是指使用一条直连通道直接将对应子层的输入连接到输出,避免在优化过程中因网络过深而产生潜在的梯度消失问题:
![[Pasted image 20250123191536.png]]
其中 xl 表示第 l 层的输入,f(.) 表示一个映射函数。此外,为了使每一层的输入/输出稳定在一个合理的范围内,层归一化技术被进一步引入每个Transformer 块中:
![[Pasted image 20250123191606.png]]
其中μ和σ分别表示均值和方差,用于将数据平移缩放到均值为0、方差为1的标准分布,α和
b是可学习的参数。层归一化技术可以有效地缓解优化过程中潜在的不稳定、收敛速度慢等问题。
使用PyTorch实现的层归一化参考代码如下:
![[Pasted image 20250123191635.png]]
### 2.1.5 编码器和解码器结构
基于上述模块,根据图2.1给出的网络架构,编码器端较容易实现。相比于编码器端,解码器端更复杂。具体来说,解码器的每个Transformer块的第一个自注意力子层额外增加了注意力掩码,对应图中的掩码多头注意力(MaskedMulti-HeadAttention)部分。这主要是因为在翻译的过程中,编码器端主要用于编码源语言序列的信息,而这个序列是完全已知的,因而编码器仅需要考虑如何融合上下文语义信息。解码器端则负责生成目标语言序列,这一生成过程是自回归的,即对于每一个单词的生成过程,仅有当前单词之前的目标语言序列是可以被观测的,因此这一额 外增加的掩码是用来掩盖后续的文本信息的,以防模型在训练阶段直接看到后续的文本序列,进 而无法得到有效的训练。
此外,解码器端额外增加了一个多头交叉注意力(Multi-HeadCross-Attention)模块,使用交叉注意力(Cross-Attention)方法,同时接收来自编码器端的输出和当前Transformer块的前一个
掩码注意力层的输出。查询是通过解码器前一层的输出进行投影的,而键和值是使用编码器的输出进行投影的。它的作用是在翻译的过程中,为了生成合理的目标语言序列,观测待翻译的源语言序列是什么。基于上述编码器和解码器结构,待翻译的源语言文本经过编码器端的每个Transformer 块对其上下文语义进行层层抽象,最终输出每一个源语言单词上下文相关的表示。解码器端以自回归的方式生成目标语言文本,即在每个时间步t,根据编码器端输出的源语言文本表示,以及前t-1个时刻生成的目标语言文本,生成当前时刻的目标语言词。
**编码器**
![[Pasted image 20250123192203.png]]
![[Pasted image 20250123192212.png]]
**解码器**
![[Pasted image 20250123192227.png]]
![[Pasted image 20250123192235.png]]
## 2.2 生成式预训练语言模型GPT
受到计算机视觉领域采用ImageNet[13]对模型进行一次预训练,使得模型可以通过海量图像 充分学习如何提取特征,再根据任务目标进行模型微调的范式影响,自然语言处理领域基于预训 练语言模型的方法也逐渐成为主流。以ELMo[3]为代表的动态词向量模型开启了语言模型预训练的大门,此后,以GPT4和BERT为代表的基于Transformer的大规模预训练语言模型的出 现,使得自然语言处理全面进入了预训练微调范式新时代。利用丰富的训练数据、自监督的预训练任务及Transformer等深度神经网络结构,预训练语言模型具备了通用且强大的自然语言表示能力,能够有效地学习到词汇、语法和语义信息。将预训练模型应用于下游任务时,不需要了解太多的任务细节,不需要设计特定的神经网络结构,只需要“微调”预训练模型,即使用具体任务的标注数据在预训练语言模型上进行监督训练,就可以取得显著的性能提升。
OpenAI公司在2018年提出的生成式预训练语言模型(GenerativePre-Training,GPT)
是典型的生成式预训练语言模型之一。GPT的模型结构是由多层Transformer
组成的单向语言模型,主要分为输入层、编码层和输出层三部分。
![[Pasted image 20250123192252.png]]
本节将重点介绍GPT无监督预训练、有监督下游任务微调及基于HuggingFace的预训练语
言模型实践。
### 2.2.1 无监督预训练
GPT采用生成式预训练方法,单向意味着模型只能从左到右或从右到左对文本序列建模,所
采用的Transformer结构和解码策略保证了输入文本每个位置只能依赖过去时刻的信息。
给定文本序列w=w₁,w₂,…,wn,GPT首先在输入层中将其映射为稠密的向量:
![[Pasted image 20250123192346.png]]
其中, vt是词wi的词向量,vpi;是词wi的位置向量, vi为第i个位置的单词经过模型输入层
(第0层)后的输出。GPT模型的输入层与前文中介绍的神经网络语言模型的不同之处在于其需
要添加位置向量,这是Transformer结构自身无法感知位置导致的,因此需要来自输入层的额外位置信息。
经过输入层编码,模型得到表示向量序列υ=υ1,…,υn,随后将ν送入模型编码层。编码
层由L个Transformer模块组成,在自注意力机制的作用下,每一层的每个表示向量都会包含之前位置表示向量的信息,使每个表示向量都具备丰富的上下文信息,而且,经过多层编码,GPT能得到每个单词层次化的组合式表示,其计算过程表示为
![[Pasted image 20250123192416.png]]
其中h(L)∈Rd×n表示第L层的表示向量序列,n为序列长度,d为模型隐藏层维度,L为模型
总层数。
GPT模型的输出层基于最后一层的表示h(L),预测每个位置上的条件概率,其计算过程可
以表示为
![[Pasted image 20250123192456.png]]
其中,We∈R|V|×d为词向量矩阵,|V|为词表大小。
单向语言模型按照阅读顺序输入文本序列ω,用常规语言模型目标优化w的最大似然估计,
使之能根据输入历史序列对当前词做出准确的预测:
![[Pasted image 20250123192511.png]]
其中θ代表模型参数。也可以基于马尔可夫假设,只使用部分过去词进行训练。预训练时通常使 用随机梯度下降法进行反向传播,优化该负对数似然函数。
### 2.2.2有监督下游任务微调
通过无监督语言模型预训练,使得GPT模型具备了一定的通用语义表示能力。下游任务微
调(DownstreamTaskFine-tuning)的目的是在通用语义表示的基础上,根据下游任务的特性进
行适配。下游任务通常需要利用有标注数据集进行训练,数据集使用D进行表示,每个样例由输
入长度为n的文本序列x=x₁x₂···xn和对应的标签y构成。
先将文本序列x输入GPT模型,获得最后一层的最后一个词所对应的隐藏层输出h⁽ˣ⁾,在
此基础上,通过全连接层变换结合Softmax函数,得到标签预测结果。
P(y|x₁…xn)=Softmax(h⁽L)Wv)
(2.11)
其中Wv∈Rd×k为全连接层参数,k为标签个数。通过对整个标注数据集D优化如下目标函数
精调下游任务:
LFT(D)=-∑logP(y|x₁…xn)
(2.12)
在微调过程中,下游任务针对任务目标进行优化,很容易使得模型遗忘预训练阶段所学习的
通用语义知识表示,从而损失模型的通用性和泛化能力,导致出现灾难性遗忘(CatastrophicFor-
getting)问题。因此,通常采用混合预训练任务损失和下游微调损失的方法来缓解上述问题。在
实际应用中,通常采用如下公式进行下游任务微调:
L=LFT(D)+λLPT(D)
其中λ的取值为[0,1],用于调节预训练任务的损失占比。
### 2.2.3 基于HuggingFace的预训练语言模型实践
HuggingFace是一个开源自然语言处理软件库,其目标是通过提供一套全面的工具、库和模
型,使自然语言处理技术对开发人员和研究人员更易于使用。HuggingFace最著名的贡献之一是
transformers库,基于此,研究人员可以快速部署训练好的模型,以及实现新的网络结构。除此
之外,HuggingFace提供了Dataset库,可以非常方便地下载自然语言处理研究中经常使用的基
准数据集。本节将以构建BERT模型为例,介绍基于HuggingFace的BERT模型的构建和使用
方法。
1.数据集准备
常见的用于预训练语言模型的大规模数据集都可以在Dataset库中直接下载并加载。例如,
如果使用维基百科的英文数据集,可以直接通过如下代码完成数据获取:
2.训练词元分析器
BERT采用WordPiece分词算法,根据训练数据中的词频决定是否将一个完整的词切分为多
个词元。因此,需要先训练词元分析器(Tokenizer)。可以使用transformers库中的BertWord-
PieceTokenizer类来完成任务,代码如下所示:
|