变换器(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 能够在许多任务中表现出色,特别是在自然语言处理和机器翻译等领域。










