AI大模型系列之七:Transformer架构讲解
Transformer模型设计之初,用于解决机器翻译问题,是完全基于注意力机制构建的编码器-解码器架构,编码器和解码器均由若干个具有相同结构的层叠加而成,每一层的参数不同。编码器主要负责将输入序列转化为一个定长的向量表示,解码器则将这个向量解码为输出序列。Transformer总体架构可分为四个部分:输入部分、编码器、解码器、输出部分。
目录
Transformer网络是什么?
是一种基于注意力机制(attention mechanism)的神经网络架构,最初由Vaswani等人在论文《Attention Is All You Need》中提出。它在自然语言处理(NLP)领域取得了巨大成功,特别是在机器翻译任务中。
传统的循环神经网络(RNNs)和长短时记忆网络(LSTM)在处理长距离依赖关系时存在一些问题,而Transformer引入了自注意力机制来解决这些问题。自注意力机制允许模型在处理序列数据时关注输入序列中的不同位置,而不仅仅是当前位置。这使得Transformer能够并行处理输入序列,加速训练。
Transformer模型设计之初,用于解决机器翻译问题,是完全基于注意力机制构建的编码器-解码器架构,编码器和解码器均由若干个具有相同结构的层叠加而成,每一层的参数不同。编码器主要负责将输入序列转化为一个定长的向量表示,解码器则将这个向量解码为输出序列。Transformer总体架构可分为四个部分:输入部分、编码器、解码器、输出部分。
如何理解Transformer 架构?
第一,Transformer 架构。它是模型的底座,但 Transformer 不等于大模型,但大模型的架构可以基于 Transformer;第二,GPT。严格意义上讲,GPT 可能不算是一个模型,更像是一种预训练范式,它本身模型架构是基于 Transformer,但 GPT 引入了“预测下一个词”的任务,即不断通过前文内容预测下一个词。之后,在大量的数据上进行学习才达到大模型的效果。
输入模块结构:
- 源文本嵌入层及其位置编码器
- 目标文本嵌入层及其位置编码器
编码器模块结构:
- 由N个编码器层堆叠而成
- 每个编码器层由两个子层连接结构组成
- 第一个子层连接结构包括一个多头自注意力子层、规范化层和一个残差连接
- 第二个子层连接结构包括一个前馈全连接子层、规范化层和一个残差连接
编码器encoder,包含两层,一个self-attention层和一个前馈神经网络,self-attention能帮助当前节点不仅仅只关注当前的词,从而能获取到上下文的语义。
解码器模块:
- 由N个解码器层堆叠而成
- 每个解码器层由三个子层连接结构组成
- 第一个子层连接结构包括一个多头自注意力子层、规范化层和一个残差连接
- 第二个子层连接结构包括一个多头注意力子层、规范化层和一个残差连接
- 第三个子层连接结构包括一个前馈全连接子层、规范化层和一个残差连接
解码器decoder也包含encoder提到的两层网络,但是在这两层中间还有一层attention层,帮助当前节点获取到当前需要关注的重点内容。
输出模块结构:
- 线性层
- softmax层
Transformer 具体是如何工作的?
首先,是对输入进行标识符化,基于单词形式,或字母,或字符子串,将输入文本切分成几个 token,对应到字典中的 ID 上,并对每个 ID 分配一个可学习的权重作为向量表示,之后就可以针对做训练,这是一个可学习的权重。
在输入 Transformer 结构之后,其核心的有自注意力模块和前向传播层。而在自注意力模块中,Transformer 自注意力机制建模能力优于 RNN 序列建模能力。因此,有了 Transformer 架构后,基本上就解决了运行效率和训练很大模型的问题。
Transformer核心思想是什么?
自注意力机制(Self-Attention): 模型能够同时考虑输入序列中的所有位置,而不是像传统的固定窗口大小的卷积或循环神经网络一样逐步处理。 传统的神经网络在处理序列数据时,对每个位置的信息处理是固定的,而自注意力机制允许模型在处理每个位置时关注输入序列的其他部分,从而更好地捕捉全局信息。
位置编码(Positional Encoding): 由于Transformer没有显式的顺序信息,为了保留输入序列中元素的位置信息,需要添加位置编码。
多头注意力(Multi-Head Attention): 将自注意力机制应用多次,通过多个注意力头来捕捉不同的关系。
前馈神经网络(Feedforward Neural Network): 每个注意力子层后接一个前馈神经网络,用于学习非线性关系。
Transformer的成功不仅限于NLP领域,还在计算机视觉等领域取得了重要进展。由于其并行计算的优势,Transformer已成为深度学习中的经典模型之一,被广泛用于各种任务。
Transformer的代码架构
因为它涉及到自注意力机制、位置编码、多头注意力等多个关键概念。
import torch
import torch.nn as nn
class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_len=512):
super(PositionalEncoding, self).__init__()
self.encoding = torch.zeros(max_len, d_model)
position = torch.arange(0, max_len).unsqueeze(1).float()
div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(torch.log(torch.tensor(10000.0)) / d_model))
self.encoding[:, 0::2] = torch.sin(position * div_term)
self.encoding[:, 1::2] = torch.cos(position * div_term)
self.encoding = self.encoding.unsqueeze(0)
def forward(self, x):
return x + self.encoding[:, :x.size(1)].detach()
class TransformerModel(nn.Module):
def __init__(self, vocab_size, d_model, nhead, num_encoder_layers, num_decoder_layers):
super(TransformerModel, self).__init__()
self.embedding = nn.Embedding(vocab_size, d_model)
self.positional_encoding = PositionalEncoding(d_model)
self.transformer = nn.Transformer(d_model, nhead, num_encoder_layers, num_decoder_layers)
self.fc = nn.Linear(d_model, vocab_size)
def forward(self, src, tgt):
src = self.embedding(src)
src = self.positional_encoding(src)
tgt = self.embedding(tgt)
tgt = self.positional_encoding(tgt)
output = self.transformer(src, tgt)
output = self.fc(output)
return output
# 定义模型
vocab_size = 10000 # 词汇表大小
d_model = 512 # 模型维度
nhead = 8 # 多头注意力的头数
num_encoder_layers = 6 # 编码器层数
num_decoder_layers = 6 # 解码器层数
model = TransformerModel(vocab_size, d_model, nhead, num_encoder_layers, num_decoder_layers)
# 定义输入
src = torch.randint(0, vocab_size, (10, 32)) # 10个序列,每个序列长度为32
tgt = torch.randint(0, vocab_size, (20, 32)) # 20个序列,每个序列长度为32
# 前向传播
output = model(src, tgt)
自注意力机制是什么?
Self Attention
先看例子,下列句子是我们想要翻译的输入句子:
The animal didn’t cross the street because it was too tired
这个“it”在这个句子是指什么呢?它指的是street还是这个animal呢?这对于人类来说是一个简单的问题,但是对于算法则不是。
当模型处理这个单词“it”的时候,自注意力机制会允许“it”与“animal”建立联系。
随着模型处理输入序列的每个单词,自注意力会关注整个输入序列的所有单词,帮助模型对本单词更好地进行编码。
如果你熟悉RNN(循环神经网络),回忆一下它是如何维持隐藏层的。RNN会将它已经处理过的前面的所有单词/向量的表示与它正在处理的当前单词/向量结合起来。而自注意力机制会将所有相关单词的理解融入到我们正在处理的单词中
当我们在编码器#5(栈中最上层编码器)中编码“it”这个单词的时,注意力机制的部分会去关注“The Animal”,将它的表示的一部分编入“it”的编码中。
自注意力机制(Self-Attention Mechanism)是Transformer模型的核心组成部分之一,用于处理输入序列中各个位置之间的依赖关系。以下是对自注意力机制的详细解读:
注意力的概念:
注意力机制模拟了人类视觉系统的工作原理,即在处理某个任务时,我们不是对所有信息一视同仁,而是在某个时刻关注一部分信息,而另一时刻可能关注其他信息。在自注意力机制中,模型通过学习到的权重分配给输入序列中的不同位置,以便在生成输出时更加关注相关的部分。
自注意力机制的基本原理:
Query(查询): 通过将输入序列与权重矩阵相乘,得到每个位置的查询向量。查询向量用于衡量每个位置与其他位置的相关性。
Key(键): 通过将输入序列与权重矩阵相乘,得到每个位置的键向量。键向量用于被查询向量衡量,从而计算注意力分布。
Value(值): 通过将输入序列与权重矩阵相乘,得到每个位置的值向量。值向量将根据注意力分布加权求和,形成最终的输出。
注意力分布计算:
计算相似度: 通过查询向量和键向量的点积,计算每个位置的相似度得分。
缩放: 为了避免相似度过大导致的梯度消失或梯度爆炸问题,一般会对相似度进行缩放,常用的缩放因子是输入维度的平方根。
Softmax: 对缩放后的相似度应用Softmax函数,得到注意力权重分布。Softmax确保所有权重的总和为1,使其成为有效的概率分布。
注意力加权求和: 将值向量按照得到的注意力权重进行加权求和,得到最终的自注意力输出。
多头注意力(Multi-Head Attention):
为了增强模型的表达能力,自注意力机制通常会使用多个独立的注意力头。每个头学习不同的查询、键、值权重矩阵,最后将多个头的输出拼接在一起并通过线性映射进行融合。
位置编码(Positional Encoding):
自注意力机制没有直接考虑序列中元素的顺序,为了捕捉序列的位置信息,常常会在输入序列中添加位置编码。位置编码是一个与位置有关的可学习参数,使得模型能够更好地处理序列的顺序信息。
多头注意力有什么用?
多头注意力机制的引入具有以下几个优势:
多头并行计算: 不同注意力头可以并行计算,提高了计算效率。
学习不同表示: 不同头关注输入序列的不同部分,有助于模型学习更丰富、更复杂的特征表示。
提高模型泛化能力: 多头注意力可以使模型在处理不同类型的信息时更加灵活,提高了模型的泛化能力。
通过这种方式,多头注意力机制在Transformer模型中起到了至关重要的作用,使得模型能够更好地捕捉输入序列中的关系,提高了模型的表达能力。
前馈神经网络
前馈神经网络(Feedforward Neural Network)是一种最基本的神经网络结构,也被称为多层感知机(Multilayer Perceptron,MLP)。在深度学习中,前馈神经网络被广泛应用于各种任务,包括图像分类、语音识别、自然语言处理等。下面是对前馈神经网络的详细解读:
1. 基本结构
前馈神经网络由输入层、隐藏层和输出层组成。每一层都包含多个神经元(或称为节点),每个神经元与上一层的所有神经元都有连接,连接上带有权重。每个连接上都有一个权重,表示连接的强度。
输入层(Input Layer): 接受输入特征的层,每个输入特征对应一个输入层神经元。
隐藏层(Hidden Layer): 在输入层和输出层之间的一层或多层神经元,负责学习输入数据中的复杂模式。
输出层(Output Layer): 提供网络的输出,输出的维度通常与任务的要求相匹配,例如,对于二分类任务,可以有一个输出神经元表示两个类别的概率。
2. 激活函数
每个神经元在接收到输入后,会通过激活函数进行非线性变换。常用的激活函数包括:
Sigmoid 函数: 将输入映射到范围 ((0, 1)),适用于二分类问题。
Hyperbolic Tangent(tanh)函数: 将输入映射到范围 ((-1, 1)),具有零中心性,有助于减少梯度消失问题。
Rectified Linear Unit(ReLU)函数: 对于正数输入,输出等于输入;对于负数输入,输出为零。ReLU 是目前最常用的激活函数之一。
Softmax 函数: 用于多分类问题的输出层,将输出转化为概率分布。
3. 前向传播
前馈神经网络的训练过程中,信息从输入层传播到输出层的过程称为前向传播。具体步骤如下:
输入层接收输入特征。
每个神经元接收来自上一层神经元的输入,计算加权和。
加权和经过激活函数进行非线性变换,得到每个神经元的输出。
输出传递到下一层作为输入,重复以上步骤。
最终,网络的输出被用于任务的预测。
编码器+解码器
编码器-解码器结构是深度学习中常用的一种网络架构,特别在图像分割和生成任务中得到广泛应用。以下是对编码器-解码器结构的详细解读:
编码器(Encoder)
特征提取: 编码器的主要作用是从输入数据中提取关键特征。对于图像任务,输入通常是图像,编码器通过一系列卷积层(Convolutional Layers)进行特征提取。这些卷积层可以捕捉图像中的低级别和高级别特征,例如边缘、纹理和对象形状。
降维: 随着网络深度的增加,编码器通常会进行降维操作,通过池化层(Pooling Layers)或步幅较大的卷积层减小特征图的尺寸。这有助于减少计算复杂性和内存需求,并提高网络对输入的抽象表示能力。
语义信息提取: 在编码器的高层级特征表示中,网络通常能够捕捉到更抽象的语义信息,例如图像中的物体类别、结构等。这些特征通常被称为“语义特征”。
解码器(Decoder):
上采样: 解码器负责将编码器提取的特征映射还原为输入数据的尺寸。这通常涉及到上采样操作,其中通过插值或反卷积操作将特征图的尺寸放大。
特征融合: 解码器通常需要与编码器的相应层进行特征融合,以保留从输入到编码器的层次结构中学到的语义信息。这可以通过连接编码器和解码器的相应层来实现,形成所谓的“跳跃连接”(Skip Connections)。
重建输出: 解码器的最终目标是生成与输入数据相匹配的输出。对于图像分割任务,输出通常是一个与输入图像尺寸相同的特征图,其中每个像素或区域对应一个类别的概率或标签。
基于卷积神经网络(CNN)的编码器-解码器结构
import torch
import torch.nn as nn
class EncoderDecoder(nn.Module):
def __init__(self):
super(EncoderDecoder, self).__init__()
# 编码器部分
self.encoder = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(64, 128, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2)
# 添加更多卷积层和池化层...
)
# 解码器部分
self.decoder = nn.Sequential(
# 添加上采样层和特征融合...
nn.Conv2d(128, 64, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Upsample(scale_factor=2, mode='bilinear', align_corners=False),
nn.Conv2d(64, 32, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Upsample(scale_factor=2, mode='bilinear', align_corners=False),
nn.Conv2d(32, 1, kernel_size=3, padding=1),
nn.Sigmoid() # 用于二分类任务时添加Sigmoid激活函数
)
def forward(self, x):
# 编码器前向传播
x = self.encoder(x)
# 解码器前向传播
x = self.decoder(x)
return x
# 创建模型实例
model = EncoderDecoder()
# 打印模型结构
print(model)
编码器和解码器的结构可能会更加复杂,具体的设计取决于任务的要求和数据集的特点。上述代码中使用的是PyTorch的简单卷积层、池化层和上采样层,实际场景中可能需要更深的网络结构和更复杂的组件。
基于Transformer架构的主流语言模型有哪些?
一是,自编码模型,如 BERT,简单讲就是给到一句话,然后把这句话的内容挖空,当问及挖空的内容时,就把内容填回去,这其实是典型地用来做一个自然语言理解的任务,但做生成任务是非常弱的;
二是,自回归模型,如 GPT,它是通过不断地预测下一个词,特点是只能从左到右生成,而看不到后面的内容。GPT-1 最后接了一个 Linear 层做分类或选题题等任务,到了 GPT-2 ,已经将一些选择任务或者分类任务全部都变成文本任务,统一了生成的范式;
三是,编码器-解码器模型,如 T5,它的输入和输出是分为比较明显的两块内容,或者是问答式,或者序列到序列的转换型的任务;
四是,通用语言模型,如 GLM,该模型结合了自回归和自编码两种形式的模型,举个例子,“123456”是一串输入的序列,现在把 “3”、“5”、“6” 挖空,让模型去学习,那么,挖空以后换成一个 “ mask token” 告诉模型这个地方遮掉了一些内容,现在需要去预测出来遮掉的内容。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)