Transformer是一种基于自注意力机制(Self-Attention Mechanism)的深度学习模型,它在自然语言处理(NLP)领域取得了显著的成就,特别是在机器翻译任务中。以下是Transformer原理的简要介绍以及使用PyTorch实现的代码示例。
Transformer原理:
- 编码器-解码器架构:Transformer模型由编码器(Encoder)和解码器(Decoder)组成,每个部分都由多个相同的层(Layer)堆叠而成。
- 自注意力机制:每个编码器和解码器层都包含自注意力模块,允许模型在处理序列时同时考虑序列内的各个位置。
- 多头注意力:为了捕获不同类型的信息,自注意力机制被分解为多个“头”(Heads),每个头学习序列的不同部分。
- 位置编码:由于Transformer缺乏循环或卷积结构,因此引入位置编码来提供序列中词汇的位置信息。
- 前馈网络:在自注意力之后,每个编码器和解码器层都包含一个前馈网络,用于进一步处理数据。
- 残差连接和层归一化:每个子层(自注意力和前馈网络)的输出都加上其输入,然后进行层归一化,有助于梯度的流动。
- 输出线性层和softmax:在解码器的最后,一个线性层将输出映射到最终的词汇空间,通常伴随着softmax激活函数用于概率分布。
PyTorch代码示例:
import torch
import torch.nn as nn
import torch.nn.functional as F
class TransformerModel(nn.Module):
def __init__(self, src_vocab_size, tgt_vocab_size, d_model, num_heads, num_layers, d_ff, max_len, dropout):
super(TransformerModel, self).__init__()
# 定义词嵌入层
self.src_embedding = nn.Embedding(src_vocab_size, d_model)
self.tgt_embedding = nn.Embedding(tgt_vocab_size, d_model)
# 定义位置编码层
self.pos_encoder = PositionalEncoding(d_model, max_len, dropout)
self.pos_decoder = PositionalEncoding(d_model, max_len, dropout)
# 定义编码器和解码器层
encoder_layer = nn.TransformerEncoderLayer(d_model, num_heads, d_ff, dropout)
self.transformer_encoder = nn.TransformerEncoder(encoder_layer, num_layers)
decoder_layer = nn.TransformerDecoderLayer(d_model, num_heads, d_ff, dropout)
self.transformer_decoder = nn.TransformerDecoder(decoder_layer, num_layers)
# 定义输出线性层
self.linear = nn.Linear(d_model, tgt_vocab_size)
# 定义dropout
self.dropout = nn.Dropout(dropout)
def forward(self, src, tgt):
# 编码器的前向传播
src = self.dropout(self.src_embedding(src) * math.sqrt(d_model))
src = self.pos_encoder(src)
out = self.transformer_encoder(src)
# 解码器的前向传播
tgt = self.dropout(self.tgt_embedding(tgt) * math.sqrt(d_model))
tgt = self.pos_decoder(tgt)
out = self.transformer_decoder(tgt, out)
out = self.linear(out)
return F.softmax(out, dim=-1)
# 假设参数
src_vocab_size = 1000
tgt_vocab_size = 1000
d_model = 512
num_heads = 8
num_layers = 6
d_ff = 2048
max_len = 100
dropout = 0.1
# 实例化模型
model = TransformerModel(src_vocab_size, tgt_vocab_size, d_model, num_heads, num_layers, d_ff, max_len, dropout)
# 随机生成示例输入
src = torch.randint(src_vocab_size, (1, max_len))
tgt = torch.randint(tgt_vocab_size, (1, max_len))
# 前向传播
output = model(src, tgt)
print(output)
自注意力机制
自注意力机制(Self-Attention Mechanism)是Transformer模型的核心组成部分,它允许模型在序列的不同位置间直接计算注意力,从而捕捉序列内部的长距离依赖关系。自注意力机制特别适用于处理序列数据,如自然语言处理任务中的文本序列。
自注意力机制的原理:
- 计算表示:对于输入序列中的每个元素(如单词或字符),模型首先计算其查询(Query)、键(Key)、值(Value)的表示。
- 计算注意力分数:对于序列中的每一对元素,模型计算它们之间的注意力分数。这通常通过计算查询向量和键向量之间的点积来实现,然后通常对结果进行缩放(例如,除以键向量的维度的平方根)。
- 应用softmax函数:将得到的注意力分数通过softmax函数转换为概率分布,这一步确保了对每个元素的注意力权重是归一化的。
- 计算加权和:使用上一步得到的注意力权重对相应的值(Value)向量进行加权求和,得到加权的表示。
- 输出:得到的加权表示可以经过一些后续处理(如线性变换和非线性激活函数),以产生最终的输出。
自注意力机制的优势:
- 捕捉长距离依赖:自注意力机制可以很容易地捕捉序列中任意两个元素之间的关系,无论它们之间的距离有多远。
- 并行化:与循环神经网络(RNN)相比,自注意力机制可以高效地在多个序列元素上并行计算,这使得模型训练更快。
- 灵活性:自注意力机制可以很容易地调整以适应不同的任务和数据类型。
PyTorch中的自注意力实现示例:
import torch
import torch.nn as nn
import torch.nn.functional as F
class SelfAttention(nn.Module):
def __init__(self, embed_size, heads):
super(SelfAttention, self).__init__()
self.embed_size = embed_size
self.heads = heads
self.head_dim = embed_size // heads
assert (
self.head_dim * heads == embed_size
), "Embedding size needs to be divisible by heads"
self.values = nn.Linear(self.head_dim, self.head_dim, bias=False)
self.keys = nn.Linear(self.head_dim, self.head_dim, bias=False)
self.queries = nn.Linear(self.head_dim, self.head_dim, bias=False)
self.fc_out = nn.Linear(heads * self.head_dim, embed_size)
def forward(self, values, keys, query, mask):
N = query.shape[0]
value_len, key_len, query_len = values.shape[1], keys.shape[1], query.shape[1]
# Split the embedding into 'heads' number of heads
values = values.reshape(N, value_len, self.heads, self.head_dim)
keys = keys.reshape(N, key_len, self.heads, self.head_dim)
queries = query.reshape(N, query_len, self.heads, self.head_dim)
values = self.values(values)
keys = self.keys(keys)
queries = self.queries(queries)
# Einsum does matrix multiplication for query*keys for each training example
# with a specific head
attention = torch.einsum("nqhd,nkhd->nhqk", [queries, keys])
if mask is not None:
attention = attention.masked_fill(mask == 0, float("-1e20"))
# Apply softmax activation to the attention scores
attention = F.softmax(attention / (self.embed_size ** (1 / 2)), dim=3)
out = torch.einsum("nhql,nlhd->nqhd", [attention, values]).reshape(
N, query_len, self.heads * self.head_dim
)
# Combine the attention heads together
out = self.fc_out(out)
return out