变换器(Transformer)
变换器(Transformer)是一种用于处理序列数据的深度学习模型,与循环神经网络(RNN)不同,它不依赖于顺序处理数据,而是依靠一种称为注意力机制(Attention Mechanism)的技术来捕捉序列中的依赖关系。Transformer 的核心组件包括 自注意力(Self-Attention)和 多头注意力(Multi-Head Attention),这些机制使 Transformer 能够在自然语言处理、机器翻译等任务中表现出色。
1. 问题描述
假设我们要进行机器翻译任务,将一句话从英文翻译成中文。在这种任务中,传统的 RNN 需要逐字处理输入句子,但 Transformer 可以并行处理整个句子,通过自注意力机制来理解每个单词与其他单词的关系,从而生成更准确的翻译。
2. 自注意力机制(Self-Attention)
自注意力机制是 Transformer 中的关键,它能够让模型在处理序列中的某个元素时,同时关注该序列中的其他元素。这意味着模型可以捕捉到全局的信息,而不是仅仅依赖于邻近的几个元素。
2.1 输入表示
首先,我们将输入序列表示为一个向量列表。假设输入是一个包含 5 个单词的句子,我们将每个单词表示为一个 d 维向量,这样我们就有一个 5 × d 5 \times d 5×d 的矩阵表示整个输入序列。
import torch
# 假设输入序列长度为 5,每个单词的嵌入向量维度为 4
input_seq = torch.rand(5, 4) # 生成随机输入
print(f"输入序列: \n{input_seq}")
2.2 计算注意力得分
自注意力的关键在于计算每个单词对序列中其他单词的“注意力得分”,即每个单词在上下文中的重要性。这个过程通过查询(Query)、键(Key)和值(Value)向量来实现。
- Query: 用于与其他单词的键比较,决定关注哪些部分。
- Key: 提供用于比较的特征向量。
- Value: 包含我们想要从其他单词提取的信息。
通过将输入向量分别乘以三个权重矩阵,我们得到 Query、Key 和 Value 向量。
d_k = 4 # Key和Query的维度
W_q = torch.rand(4, d_k)
W_k = torch.rand(4, d_k)
W_v = torch.rand(4, 4)
queries = torch.matmul(input_seq, W_q)
keys = torch.matmul(input_seq, W_k)
values = torch.matmul(input_seq, W_v)
print(f"Query向量: \n{queries}")
print(f"Key向量: \n{keys}")
print(f"Value向量: \n{values}")
接下来,我们计算每个 Query 与所有 Key 的点积,然后除以 ( d k ) ( \sqrt{d_k}) (dk) 来防止过大的值影响模型稳定性,最后应用 Softmax 函数来得到注意力权重。
attention_scores = torch.matmul(queries, keys.transpose(-2, -1)) / torch.sqrt(torch.tensor(d_k, dtype=torch.float32))
attention_weights = torch.softmax(attention_scores, dim=-1)
print(f"注意力得分: \n{attention_scores}")
print(f"注意力权重: \n{attention_weights}")
2.3 计算加权值(Weighted Sum)
注意力权重用于加权求和 Value 向量,以生成新的序列表示。
weighted_values = torch.matmul(attention_weights, values)
print(f"加权后的输出: \n{weighted_values}")
自注意力机制通过这种方式,使每个单词能够关注序列中的其他所有单词,理解它们之间的关系,从而捕捉全局信息。
3. 多头注意力机制(Multi-Head Attention)
多头注意力是 Transformer 的另一关键特性。它通过将注意力机制应用多次(即多头),使模型能够关注序列中不同位置的关系,并捕捉更多的特征信息。
3.1 多头注意力的原理
在多头注意力中,模型将输入分成多个头(head),每个头独立地执行自注意力操作,最后将这些头的输出拼接起来,得到更丰富的表示。
假设我们使用 2 个头,每个头的 Query、Key 和 Value 向量都具有较小的维度。
num_heads = 2
d_k_per_head = d_k // num_heads
# 为每个头分别生成Query、Key、Value的权重矩阵
W_q_heads = [torch.rand(4, d_k_per_head) for _ in range(num_heads)]
W_k_heads = [torch.rand(4, d_k_per_head) for _ in range(num_heads)]
W_v_heads = [torch.rand(4, 4) for _ in range(num_heads)]
# 计算每个头的Query、Key、Value
head_outputs = []
for i in range(num_heads):
queries = torch.matmul(input_seq, W_q_heads[i])
keys = torch.matmul(input_seq, W_k_heads[i])
values = torch.matmul(input_seq, W_v_heads[i])
attention_scores = torch.matmul(queries, keys.transpose(-2, -1)) / torch.sqrt(torch.tensor(d_k_per_head, dtype=torch.float32))
attention_weights = torch.softmax(attention_scores, dim=-1)
head_output = torch.matmul(attention_weights, values)
head_outputs.append(head_output)
# 拼接多头的输出
multi_head_output = torch.cat(head_outputs, dim=-1)
print(f"多头注意力的输出: \n{multi_head_output}")
3.2 线性变换和输出
最后,多头注意力的输出通过一个线性层进行变换,得到最终的表示。
W_o = torch.rand(8, 4) # 最终线性层的权重
output = torch.matmul(multi_head_output, W_o)
print(f"最终输出: \n{output}")
4. 使用 PyTorch 实现 Transformer 的核心部分
通过 PyTorch 实现多头注意力机制,帮助理解它的实际应用。
4.1 导入必要的库
import torch
import torch.nn as nn
4.2 定义多头注意力层
class MultiHeadAttention(nn.Module):
def __init__(self, d_model, num_heads):
super(MultiHeadAttention, self).__init__()
assert d_model % num_heads == 0
self.d_k = d_model // num_heads
self.num_heads = num_heads
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 = nn.Linear(d_model, d_model)
def forward(self, x):
batch_size = x.size(0)
# 线性变换并拆分成多头
q = self.q_linear(x).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
k = self.k_linear(x).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
v = self.v_linear(x).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
# 计算注意力
scores = torch.matmul(q, k.transpose(-2, -1)) / torch.sqrt(torch.tensor(self.d_k, dtype=torch.float32))
weights = torch.softmax(scores, dim=-1)
context = torch.matmul(weights, v)
# 拼接多头输出
context = context.transpose(1, 2).contiguous().view(batch_size, -1, self.d_k * self.num_heads)
# 最终线性变换
output = self.out(context)
return output
4.3 示例数据输入和输出
# 假设输入序列长度为 5,嵌入向量维度为 8
input_seq = torch.rand(2, 5, 8) # batch_size = 2
multi_head_attn = MultiHeadAttention(d_model=8, num_heads=2)
output = multi_head_attn(input_seq)
print(f"多头注意力层的输出: \n{output}")
5. 完整代码
以下是完整的实现代码:
import torch
import torch.nn as nn
class MultiHeadAttention(nn.Module):
def __init__(self, d_model, num_heads):
super(MultiHeadAttention, self).__init__()
assert d_model % num_heads == 0
self.d_k = d_model // num_heads
self.num_heads = num_heads
# 初始化线性变换矩阵
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 = nn.Linear(d_model, d_model)
def forward(self, x):
batch_size = x.size(0)
# 线性变换并拆分成多头
q = self.q_linear(x).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
k = self.k_linear(x).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
v = self.v_linear(x).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
# 计算注意力
scores = torch.matmul(q, k.transpose(-2, -1)) / torch.sqrt(torch.tensor(self.d_k, dtype=torch.float32))
weights = torch.softmax(scores, dim=-1)
context = torch.matmul(weights, v)
# 拼接多头输出
context = context.transpose(1, 2).contiguous().view(batch_size, -1, self.d_k * self.num_heads)
# 最终线性变换
output = self.out(context)
return output
# 示例数据输入
input_seq = torch.rand(2, 5, 8) # batch_size = 2
multi_head_attn = MultiHeadAttention(d_model=8, num_heads=2)
output = multi_head_attn(input_seq)
print(f"多头注意力层的输出: \n{output}")
代码中的输出是经过多头注意力机制处理后的张量(Tensor),它表示输入序列的加权表示。具体来说,输出内容是一个形状为 [batch_size, sequence_length, d_model]
的张量,它包含了每个序列元素经过多头注意力机制后的最终表示。
运行解释
-
输入数据:
input_seq
是一个形状为[2, 5, 8]
的张量,这表示有 2 个序列(批次大小为 2),每个序列长度为 5,每个序列元素的特征维度(d_model
)为 8。 -
多头注意力机制: 在这个实现中,
num_heads
为 2,因此每个头会处理一部分特征(即每个头处理 4 维度的特征)。
输出内容
-
最终输出:
output
是一个形状为[2, 5, 8]
的张量。这个张量表示经过多头注意力机制处理后的序列,每个序列的长度保持为 5,特征维度(d_model
)为 8。 -
输出示例: 输出的具体值会因输入数据和随机初始化的权重矩阵而异。一般来说,输出会是一个类似于下面这样的张量:
多头注意力层的输出:
tensor([[[ 0.0338, 0.0551, 0.0143, 0.0686, 0.0334, 0.0279, 0.0484, 0.0413],
[ 0.0219, 0.0340, 0.0455, 0.0588, 0.0655, 0.0543, 0.0289, 0.0341],
[ 0.0393, 0.0663, 0.0416, 0.0738, 0.0638, 0.0468, 0.0456, 0.0512],
[ 0.0459, 0.0390, 0.0591, 0.0617, 0.0512, 0.0442, 0.0398, 0.0564],
[ 0.0334, 0.0436, 0.0564, 0.0593, 0.0405, 0.0361, 0.0499, 0.0410]],
[[ 0.0364, 0.0415, 0.0410, 0.0510, 0.0484, 0.0367, 0.0329, 0.0465],
[ 0.0318, 0.0467, 0.0414, 0.0465, 0.0495, 0.0442, 0.0410, 0.0491],
[ 0.0475, 0.0584, 0.0349, 0.0540, 0.0456, 0.0512, 0.0524, 0.0449],
[ 0.0417, 0.0441, 0.0494, 0.0568, 0.0483, 0.0428, 0.0455, 0.0480],
[ 0.0424, 0.0573, 0.0384, 0.0516, 0.0469, 0.0439, 0.0440, 0.0494]]])
此输出张量表示输入序列通过多头注意力机制处理后的结果,每个序列的每个元素的特征表示都被重新计算,捕捉了序列内部的全局依赖关系。
6. 总结
Transformer 的自注意力机制使得模型能够在并行处理序列的同时捕捉全局信息,而多头注意力机制则通过多次应用自注意力,进一步增强了模型的表达能力。这些机制的结合使得 Transformer 能够在许多任务中表现出色,特别是在自然语言处理和机器翻译等领域。