深度学习学习经验——变换器(Transformer)

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

你可能感兴趣的:(深度学习学习经验,深度学习,学习,transformer)