跳到主要内容Transformer 架构原理与自注意力机制详解 | 极客日志PythonAI算法
Transformer 架构原理与自注意力机制详解
综述由AI生成Transformer 是一种基于自注意力机制的深度学习模型,用于序列到序列任务。文章介绍了其核心组件,包括多头注意力、位置编码、残差连接及层归一化。详细阐述了编码器与解码器的结构差异,特别是 Masked Multi-Head Attention 的作用。通过 PyTorch 代码示例演示了自注意力机制中 Q、K、V 矩阵的计算流程,涵盖从输入嵌入到输出概率分布的全过程。此外,还分析了 Transformer 的优缺点及潜在应用场景。
花里胡哨20 浏览 Transformer 架构原理与自注意力机制详解
前言
Transformer 是一种用于自然语言处理(NLP)和其他序列到序列(sequence-to-sequence)任务的深度学习模型架构,它在 2017 年由 Vaswani 等人首次提出。Transformer 架构引入了自注意力机制(self-attention mechanism),这是一个关键的创新,使其在处理序列数据时表现出色,彻底改变了 NLP 领域的研究范式。
一、Transformer 是什么?
Transformer 是一种用于自然语言处理(NLP)和其他序列到序列(sequence-to-sequence)任务的深度学习模型架构,它在 2017 年由 Vaswani 等人首次提出。Transformer 架构引入了自注意力机制(self-attention mechanism),这是一个关键的创新,使其在处理序列数据时表现出色。
以下是 Transformer 的一些重要组成部分和特点:
- 自注意力机制(Self-Attention):这是 Transformer 的核心概念之一,它使模型能够同时考虑输入序列中的所有位置,而不是像循环神经网络(RNN)或卷积神经网络(CNN)一样逐步处理。自注意力机制允许模型根据输入序列中的不同部分来赋予不同的注意权重,从而更好地捕捉语义关系。
- 多头注意力(Multi-Head Attention):Transformer 中的自注意力机制被扩展为多个注意力头,每个头可以学习不同的注意权重,以更好地捕捉不同类型的关系。多头注意力允许模型并行处理不同的信息子空间。
- 堆叠层(Stacked Layers):Transformer 通常由多个相同的编码器和解码器层堆叠而成。这些堆叠的层有助于模型学习复杂的特征表示和语义。
- 位置编码(Positional Encoding):由于 Transformer 没有内置的序列位置信息,它需要额外的位置编码来表达输入序列中单词的位置顺序。
- 残差连接和层归一化(Residual Connections and Layer Normalization):这些技术有助于减轻训练过程中的梯度消失和爆炸问题,使模型更容易训练。
- 编码器和解码器:Transformer 通常包括一个编码器用于处理输入序列和一个解码器用于生成输出序列,这使其适用于序列到序列的任务,如机器翻译。
1.1 Transformer 的结构
在原始论文中,Nx = 6,Encoder block 由 6 个 encoder 堆叠而成。图中的一个框代表的是一个 encoder 的内部结构,一个 Encoder 是由 Multi-Head Attention 和全连接神经网络 Feed Forward Network 构成。
简略结构(每一个编码器都对应上图的一个 encoder 结构):
Transformer 的编码组件是由 6 个编码器叠加在一起组成的,解码器同样如此。所有的编码器在结构上是相同的,但是它们之间并没有共享参数。
从编码器输入的句子首先会经过一个自注意力层,这一层帮助编码器在对每个单词编码的时候时刻关注句子的其它单词。解码器中的解码注意力层的作用是关注输入句子的相关部分,类似于 seq2seq 的注意力。原结构中使用到的是多头注意力机制(Multi-Head Attention),我们先从基础——自注意力机制开始讲起。
1.2 自注意力机制
自注意力的作用:随着模型处理输入序列的每个单词,自注意力会关注整个输入序列的所有单词,帮助模型对本单词更好地进行编码。在处理过程中,自注意力机制会将对所有相关单词的理解融入到我们正在处理的单词中。更具体的功能如下:
- 序列建模:自注意力可以用于序列数据(例如文本、时间序列、音频等)的建模。它可以捕捉序列中不同位置的依赖关系,从而更好地理解上下文。这对于机器翻译、文本生成、情感分析等任务非常有用。
- 并行计算:自注意力可以并行计算,这意味着可以有效地在现代硬件上进行加速。相比于 RNN 和 CNN 等序列模型,它更容易在 GPU 和 TPU 等硬件上进行高效的训练和推理。(因为在自注意力中可以并行的计算得分)
- 长距离依赖捕捉:传统的循环神经网络(RNN)在处理长序列时可能面临梯度消失或梯度爆炸的问题。自注意力可以更好地处理长距离依赖关系,因为它不需要按顺序处理输入序列。
自注意力的计算流程:
- 输入向量:从每个编码器的输入向量(每个单词的词向量,即 Embedding,可以是任意形式的词向量,比如说 word2vec,GloVe,one-hot 编码)。
查询、键、值矩阵:将以上所得到的查询向量、键向量、值向量组合起来就可以得到三个向量矩阵 Query、Keys、Values。计算得分:假设我们在为这个例子中的第一个词'Thinking'计算自注意力向量,我们需要拿输入句子中的每个单词对'Thinking'打分。这些分数是通过所有输入句子的单词的键向量与'Thinking'的查询向量相点积来计算的。缩放(Scaling):将分数除以 $\sqrt{d_k}$(8 是论文中使用的键向量的维数 64 的平方根,这会让梯度更稳定。这里也可以使用其它值,8 只是默认值,这样做是为了防止内积过大导致 softmax 进入梯度极小的区域)。Softmax 归一化:通过 softmax 传递结果。softmax 的作用是使所有单词的分数归一化,得到的分数都是正值且和为 1。这个 softmax 分数决定了每个单词对编码当下位置('Thinking')的贡献。加权求和:将每个值向量乘以 softmax 分数(这是为了准备之后将它们求和)。这里的直觉是希望关注语义上相关的单词,并弱化不相关的单词(例如,让它们乘以 0.001 这样的小数)。输出:对加权值向量求和,然后即得到自注意力层在该位置的输出。对应整体结构图中的 Multi-Head Attention。
- 扩展了模型专注于不同位置的能力。
- 有多个查询/键/值权重矩阵集合,(Transformer 使用八个注意力头)并且每一个都是随机初始化的。和上边一样,用矩阵 X 乘以 WQ、WK、WV 来产生查询、键、值矩阵。
- Self-Attention 只是使用了一组 WQ、WK、WV 来进行变换得到查询、键、值矩阵,而 Multi-Head Attention 使用多组 WQ,WK,WV 得到多组查询、键、值矩阵,然后每组分别计算得到一个 Z 矩阵。
前馈层只需要一个矩阵,则把得到的 8 个矩阵拼接在一起,然后用一个附加的权重矩阵 $W^O$ 与它们相乘。
编码 it 一词时,不同注意力的头集中在哪里,当我们编码 it 这个单词时:(图中只列举出了两个注意力):
- 其中一个注意力头集中在 The animal
- 另一个注意力头集中在 tire 上。即形象解释 it 代指的是 animal 和 tire。
1.3 使用位置编码表示序列的顺序
- 如果不添加位置编码,那么无论单词在什么位置,它的注意力分数都是确定的。这不是我们想要的。
- 为了理解单词顺序,Transformer 为每个输入的词嵌入添加了一个向量,这样能够更好的表达词与词之间的关系。词嵌入与位置编码相加,而不是拼接,他们的效率差不多,但是拼接的话维度会变大,所以不考虑。
为了让模型理解单词的顺序,我们添加了位置编码向量,这些向量的值遵循特定的模式。位置编码公式如下:
$$PE_{(pos, 2i)} = \sin(pos / 10000^{2i/d_{model}})$$
$$PE_{(pos, 2i+1)} = \cos(pos / 10000^{2i/d_{model}})$$
其中 pos 是位置,i 是维度。这种设计使得模型能够学到相对位置信息。
1.4 Add&Normalize
在经过多头注意力机制得到矩阵 Z 之后,并没有直接传入全连接神经网络,而是经过了一步 Add&Normalize。
Add & Norm 层由 Add 和 Norm 两部分组成,其计算公式如下:
$$LayerNorm(x + Sublayer(x))$$
其中 X 表示 Multi-Head Attention 或者 Feed Forward 的输入,MultiHeadAttention(X) 和 FeedForward(X) 表示输出 (输出与输入 X 维度是一样的,所以可以相加)。
Add(残差连接)
Add,就是在 z 的基础上加了一个残差块 X,加入残差块的目的是为了防止在深度神经网络的训练过程中发生退化的问题,退化的意思就是深度神经网络通过增加网络的层数,Loss 逐渐减小,然后趋于稳定达到饱和,然后再继续增加网络层数,Loss 反而增大。
为了了解残差块,我们引入 ResNet 残差神经网络,神经网络退化指的是在达到最优网络层数之后,神经网络还在继续训练导致 Loss 增大,对于多余的层,我们需要保证多出来的网络进行恒等映射。只有进行了恒等映射之后才能保证这多出来的神经网络不会影响到模型的效果。残差连接主要是为了防止网络退化。
上图就是构造的一个残差块,X 是输入值,F(X)是经过第一层线性变换后并且激活的输出,在第二层线性变化之后,激活之前,F(X)加入了这一层输入值 X,然后再进行激活后输出。
要恒等映射,我们只需要让 F(X)=0 就可以了。x 经过线性变换(随机初始化权重一般偏向于 0),输出值明显会偏向于 0,而且经过激活函数 Relu 会将负数变为 0,过滤了负数的影响。
这样当网络自己决定哪些网络层为冗余层时,使用 ResNet 的网络很大程度上解决了学习恒等映射的问题,用学习残差 F(x)=0 更新该冗余层的参数来代替学习 h(x)=x 更新冗余层的参数。
使用到的归一化方法是 Layer Normalization。
LN 是在同一个样本中不同神经元之间进行归一化,而 BN 是在同一个 batch 中不同样本之间的同一位置的神经元之间进行归一化。
BN 是对于相同的维度进行归一化,但是咱们 NLP 中输入的都是词向量,一个 300 维的词向量,单独去分析它的每一维是没有意义地,在每一维上进行归一化也是适合地,因此这里选用的是 LN。
1.5 全连接层 Feed Forward
全连接层是一个两层的神经网络,先线性变换,然后 ReLU 非线性,再线性变换。
这两层网络就是为了将输入的 Z 映射到更加高维的空间中然后通过非线性函数 ReLU 进行筛选,筛选完后再变回原来的维度。
经过 6 个 encoder 后输入到 decoder 中。
1.6 Decoder 整体结构
和 Encoder Block 一样,Encoder 也是由 6 个 decoder 堆叠而成的,Nx=6。包含两个 Multi-Head Attention 层。第一个 Multi-Head Attention 层采用了 Masked 操作。第二个 Multi-Head Attention 层的 K, V 矩阵使用 Encoder 的编码信息矩阵 C 进行计算,而 Q 使用上一个 Decoder block 的输出计算。
Masked Multi-Head Attention
与 Encoder 的 Multi-Head Attention 计算原理一样,只是多加了一个 mask 码。mask 表示掩码,它对某些值进行掩盖,使其在参数更新时不产生效果。Transformer 模型里面涉及两种 mask,分别是 padding mask 和 sequence mask。为什么需要添加这两种 mask 码呢?
padding mask
什么是 padding mask 呢?因为每个批次输入序列长度是不一样的也就是说,我们要对输入序列进行对齐。具体来说,就是给在较短的序列后面填充 0。但是如果输入的序列太长,则是截取左边的内容,把多余的直接舍弃。因为这些填充的位置,其实是没什么意义的,所以我们的 attention 机制不应该把注意力放在这些位置上,所以我们需要进行一些处理。
具体的做法是,把这些位置的值加上一个非常大的负数 (负无穷),这样的话,经过 softmax,这些位置的概率就会接近 0!
sequence mask
sequence mask 是为了使得 decoder 不能看见未来的信息。对于一个序列,在 time_step 为 t 的时刻,我们的解码输出应该只能依赖于 t 时刻之前的输出,而不能依赖 t 之后的输出。因此我们需要想一个办法,把 t 之后的信息给隐藏起来。这在训练的时候有效,因为训练的时候每次我们是将 target 数据完整输入进 decoder 中地,预测时不需要,预测的时候我们只能得到前一时刻预测出的输出。
那么具体怎么做呢?也很简单:产生一个上三角矩阵,上三角的值全为 0。把这个矩阵作用在每一个序列上,就可以达到我们的目的。
- 在 Encoder 中的 Multi-Head Attention 也是需要进行 mask 的,只不过 Encoder 中只需要 padding mask 即可,而 Decoder 中需要 padding mask 和 sequence mask。
- Encoder 中的 Multi-Head Attention 是基于 Self-Attention 地,Decoder 中的第二个 Multi-Head Attention 就只是基于 Attention,它的输入 Quer 来自于 Masked Multi-Head Attention 的输出,Keys 和 Values 来自于 Encoder 中最后一层的输出。
1.7 输出
Output 如图中所示,首先经过一次线性变换(线性变换层是一个简单的全连接神经网络,它可以把解码组件产生的向量投射到一个比它大得多的,被称为对数几率的向量里),然后 Softmax 得到输出的概率分布(softmax 层会把向量变成概率),然后通过词典,输出概率最大的对应的单词作为我们的预测输出。
1.8 Transformer 的优缺点
- 效果好:在多项基准测试中表现优异。
- 可以并行训练,速度快:相比 RNN 的串行处理,Transformer 能充分利用 GPU 并行计算能力。
- 很好的解决了长距离依赖的问题:通过全局注意力机制,直接连接序列两端。
- 完全基于 self-attention,对于词语位置之间的信息有一定的丢失,虽然加入了 positional encoding 来解决这个问题,但也还存在着可以优化的地方。
- 计算复杂度较高:对于超长序列,自注意力机制的计算量随序列长度呈二次方增长,内存消耗较大。
- 缺乏归纳偏置:相比 CNN 或 RNN,Transformer 对局部特征的提取能力较弱,通常需要更多数据进行预训练。
二、Self-Attention 的实现
2.0 过程
- 准备输入
- 初始化参数
- 获取 key,query 和 value
- 给 input1 计算 attention score
- 计算 softmax
- 给 value 乘上 score
- 给 value 加权求和获取 output1
- 重复步骤 4-7,获取 output2,output3
2.1 准备输入(词嵌入向量)
import torch
x = [
[1, 0, 1, 0],
[0, 2, 0, 2],
[1, 1, 1, 1]
]
x = torch.tensor(x, dtype=torch.float32)
print("Input Tensor:", x)
输出:
tensor([[1., 0., 1., 0.],
[0., 2., 0., 2.],
[1., 1., 1., 1.]])
2.2 初始化参数(Q、K、V 矩阵)
Note:Q、K、V 矩阵在神经网络初始化的过程中,一般都是随机采样完成并且比较小,可以根据想要输出的维度来确定 Q、K、V 矩阵的维度。
w_key = [
[0, 0, 1],
[1, 1, 0],
[0, 1, 0],
[1, 1, 0]
]
w_query = [
[1, 0, 1],
[1, 0, 0],
[0, 0, 1],
[0, 1, 1]
]
w_value = [
[0, 2, 0],
[0, 3, 0],
[1, 0, 3],
[1, 1, 0]
]
w_key = torch.tensor(w_key, dtype=torch.float32)
w_query = torch.tensor(w_query, dtype=torch.float32)
w_value = torch.tensor(w_value, dtype=torch.float32)
print("Weights for key: \n", w_key)
print("Weights for query: \n", w_query)
print("Weights for value: \n", w_value)
输出:
Weights for key:
tensor([[0., 0., 1.],
[1., 1., 0.],
[0., 1., 0.],
[1., 1., 0.]])
Weights for query:
tensor([[1., 0., 1.],
[1., 0., 0.],
[0., 0., 1.],
[0., 1., 1.]])
Weights for value:
tensor([[0., 2., 0.],
[0., 3., 0.],
[1., 0., 3.],
[1., 1., 0.]])
2.3 获取 key,query 和 value
keys = x @ w_key
querys = x @ w_query
values = x @ w_value
print("Keys: \n", keys)
print("Querys: \n", querys)
print("Values: \n", values)
下图为得到的 key,query 和 value:
(此处省略图片,实际应用中应查看矩阵数值变化)
2.4 计算注意力分数
attn_scores = querys @ keys.T
print(attn_scores)
输出:
tensor([[ 2., 4., 4.],
[ 4., 16., 12.],
[ 4., 12., 10.]])
2.5 计算 softmax
from torch.nn.functional import softmax
attn_scores_softmax = softmax(attn_scores, dim=-1)
print(attn_scores_softmax)
attn_scores_softmax = [
[0.0, 0.5, 0.5],
[0.0, 1.0, 0.0],
[0.0, 0.9, 0.1]
]
attn_scores_softmax = torch.tensor(attn_scores_softmax)
print(attn_scores_softmax)
输出:
tensor([[0.0000, 0.5000, 0.5000],
[0.0000, 1.0000, 0.0000],
[0.0000, 0.9000, 0.1000]])
2.6 给 value 乘上 score
weighted_values = values[:,None] * attn_scores_softmax.T[:,:,None]
print(weighted_values)
输出:
tensor([[[0.0000, 0.0000, 0.0000],
[0.0000, 0.0000, 0.0000],
[0.0000, 0.0000, 0.0000]],
[[1.0000, 4.0000, 0.0000],
[2.0000, 8.0000, 0.0000],
[1.8000, 7.2000, 0.0000]],
[[1.0000, 3.0000, 1.5000],
[0.0000, 0.0000, 0.0000],
[0.2000, 0.6000, 0.3000]]])
2.7 给 value 加权求和获取 output(得到 input1 的结果向量)
(此处省略图片,实际计算为对 weighted_values 沿特定维度求和)
重复步骤 4-7,获取到 input2、input3 的结果向量
至此,我们完成了 Self-Attention 的基本实现逻辑。在实际工程中,通常会结合 PyTorch 的 nn.MultiheadAttention 模块来优化性能,并处理 Batch 维度及 Padding 掩码等细节。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
- 随机西班牙地址生成器
随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- curl 转代码
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online