赞
踩
目录
Transformer的原理是基于自注意力机制(Self-Attention),该机制可以直接计算序列中各个位置之间的依赖关系。与传统的循环神经网络(RNN)和长短期记忆网络(LSTM)不同,Transformer完全依赖注意力机制,没有使用循环结构。这允许模型并行处理整个序列的数据。
Transformer 模型由编码器(Encoder)和解码器(Decoder)两部分组成,每部分由若干个相同的层叠加而成。
每个编码器层包括两个子层:
这两个子层都有残差连接,并且其输出通过层归一化(Layer Normalization)。
每个解码器层也包括三个子层:
解码器每层的这三个子层也都有残差连接,并且其输出通过层归一化。
自注意力机制的关键是以下三个向量:
对于给定的输入 X,我们首先将其映射到 Q、K、V 三个向量上。然后,计算 Q 与 K 的点积,得到注意力权重,接着对该权重进行 softmax 操作,最后用这个 softmax 权重与 V 相乘得到最终的输出。
Transformer 使用了多头注意力机制,将 Q、K、V 映射到不同的表示空间,计算多组注意力,并将这些注意力的结果拼接起来,最后再次映射得到最终的输出。
- import torch
- import torch.nn as nn
-
- class SelfAttention(nn.Module):
- def __init__(self, d_model, num_heads, mask=True, scale=True):
- super(SelfAttention, self).__init__()
-
- self.num_heads = num_heads
- self.d_model = d_model
- self.d_k = d_model // num_heads
- self.mask = mask
- self.scale = scale
-
- self.q_linear = nn.Linear(d_model, d_model)
- self.k_linear = nn.Linear(d_model, d_model)
- self.v_linear = nn.Linear(d_model, d_model)
- self.out_linear = nn.Linear(d_model, d_model)
-
- def forward(self, x):
- batch_size, seq_len, _ = x.size()
-
- # 线性变换,通过线性层对输入进行变换,得到查询(Q)、键(K)和值(V)的表示。
- q = self.q_linear(x)
- k = self.k_linear(x)
- v = self.v_linear(x)
-
- # 多头分割和转置,将查询、键和值进行多头分割,并进行维度的转置操作。
- q = q.view(batch_size, seq_len, self.num_heads, self.d_k)
- k = k.view(batch_size, seq_len, self.num_heads, self.d_k)
- v = v.view(batch_size, seq_len, self.num_heads, self.d_k)
-
- q = q.permute(2, 0, 1, 3).contiguous().view(-1, seq_len, self.d_k)
- k = k.permute(2, 0, 1, 3).contiguous().view(-1, seq_len, self.d_k)
- v = v.permute(2, 0, 1, 3).contiguous().view(-1, seq_len, self.d_k)
-
- # 注意力得分计算
- scores = torch.matmul(q, k.transpose(1, 2))
-
- if self.scale:
- scores = scores / torch.sqrt(torch.tensor(self.d_k).float())
-
- # 掩码处理
- if self.mask:
- mask = torch.triu(torch.ones(seq_len, seq_len), diagonal=1)
- mask = mask.unsqueeze(0).unsqueeze(0).to(x.device)
- scores = scores.masked_fill(mask == 0, float('-inf'))
-
- # 注意力权重计算
- attention_weights = torch.softmax(scores, dim=-1)
- attention_output = torch.matmul(attention_weights, v)
-
- # 多头拼接和转置
- attention_output = attention_output.view(self.num_heads, batch_size, seq_len, self.d_k)
- attention_output = attention_output.permute(1, 2, 0, 3).contiguous().view(batch_size, seq_len, -1)
-
- # 线性变换
- output = self.out_linear(attention_output)
-
- return output
在自注意力机制中,mask和scale是重要的步骤。Masking用于避免在注意力计算中不必要或者不合适的信息流,而scale(缩放)则是用来稳定训练过程。
Masking:
在序列生成任务中(如语言模型或者机器翻译中的解码器),mask用来确保模型不会看到未来的信息。这通常通过将注意力权重矩阵中对应于未来位置的条目设置为非常小的负数(例如,-1e9
)来实现。这在softmax之前完成,所以这些位置的权重在softmax后趋近于0。在处理填充(padding)的序列时,mask也用于忽略填充的位置,从而不影响注意力权重的计算。Scale(缩放):
缩放的目的是为了控制内积的大小,防止过大的内积值使得softmax函数的梯度几乎为0,从而导致梯度消失问题。缩放操作通常是在计算注意力权重之前,将内积除以一个缩放因子,这个因子通常是embed_size
的平方根。在Transformer模型中使用LayerNorm而不使用其他归一化方法,如BatchNorm,主要由以下原因:
独立性: LayerNorm在每一个样本内对所有的特征进行归一化,而不像BatchNorm那样依赖于一个batch中的其他样本。这样的特性使得LayerNorm在处理不同长度的输入序列时更加灵活,因为序列处理任务中的批次中每个样本的长度可能不同。
批次大小不敏感: 由于LayerNorm不依赖于批次维度,它对批次大小不敏感。这意味着即使在批次大小为1时也可以正常工作,这对于在线学习、强化学习或者资源受限的情况很有用。
便于并行计算: 在Transformer模型中,自注意力层允许并行处理整个序列。使用LayerNorm而不是BatchNorm可以确保模型中每个时间步的归一化操作是独立的,这使得整个模型更适合并行计算。
序列处理适用性: LayerNorm特别适用于序列处理任务,因为它在每个时间步独立处理激活值。对于序列数据,尤其是当序列长度变化或者模型需要记住长距离依赖时,LayerNorm更有优势。
计算稳定性: LayerNorm可以减少训练过程中的梯度消失和梯度爆炸问题,这对于训练深度网络尤其重要。在Transformer中,由于存在大量的残差连接和自注意力操作,维持梯度的稳定性是非常关键的。
避免潜在的批次效应: BatchNorm可能会引入批次之间的相互依赖,这可能导致模型在训练时与实际应用时表现不一致,特别是当实际应用中的批次大小不同或者数据分布有变化时。LayerNorm避免了这些问题。
多头注意力机制(Multi-Head Attention)是Transformer模型中的一个关键创新。它允许模型在不同的表示子空间中并行地学习信息。以下是使用多头注意力机制的主要原因和优势:
捕捉不同子空间的信息: 每个“头”可以被看作是一个独立的注意力学习机制,该机制关注输入的不同部分并从不同的角度解读信息。这样可以让模型捕捉到更加丰富和多样化的信息。
提高表示能力: 各个头学习到的注意力表示被合并起来,提供了一个综合的表示,这可能比任何单个头学到的表示包含更多信息,从而增强了模型的表示能力。
并行计算: 多头注意力可以并行计算各个头,这样在训练和推断时可以显著提高效率,尤其是在使用现代硬件(如GPU或TPU)时。
模型稳健性: 多头注意力使模型更加稳健,因为即使一些头可能学习到不太有用的信息,其他头仍然可以学习到有价值的特征。这种多元化的学习策略提高了模型的泛化能力。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。