使用Pytorch实现Self-Attention架构

本文详细介绍了Self-Attention机制在自然语言处理中的作用,特别是如何在Pytorch中实现这一机制,包括计算过程和关键组件如查询、键和值的处理。
摘要由CSDN通过智能技术生成

使用Pytorch实现Self-Attention架构

Self-Attention

自注意力(Self-Attention)机制是一种用于建模序列数据中元素之间关系的技术,最初广泛应用于自然语言处理领域,特别是在Transformer模型中。在自注意力机制中,每个元素(例如单词或时间步)都与其他元素进行交互,并且注意力权重决定了每个元素对其他元素的重要性。

在 Self-Attention 中,首先计算一个注意力分数矩阵,其大小为 N × N N \times N N×N,其中 N N N 是序列的长度。注意力分数矩阵的每个元素表示一个元素对另一个元素的注意力权重,这些权重通常通过计算元素之间的相似性得到。

通常 Self-Attention 的计算过程如下:

  1. 计算查询、键和值: 对于给定的序列,首先通过三个独立的线性变换(通常是全连接层)来计算查询向量 Q Q Q、键向量 K K K 和值向量 V V V。这些向量的维度通常是预先定义的,通常是模型的超参数。

  2. 计算注意力分数: 使用查询向量 Q Q Q 和键向量 K K K 之间的点积来计算注意力分数。然后通过应用 s o f t m a x softmax softmax函数将这些分数归一化,以获得注意力权重 A A A
    在这里插入图片描述

  3. 加权求和: 使用注意力权重对值向量 V V V 进行加权求和,以获得注意力加权的输出。

这样,通过将每个元素与其他元素进行交互,并且根据它们之间的关系调整重要性,自注意力机制能够捕捉序列中的长距离依赖关系,并且不受序列长度的限制。

Pytorch 实现

在PyTorch中 Self-Attention 可以通过定义一个自注意力层来完成。

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 must be divisible by number of 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]  # 序列的长度

        # 对查询、键和值进行线性变换
        values = self.values(values)
        keys = self.keys(keys)
        queries = self.queries(query)

        # 将值、键和查询分割为多个头
        values = values.view(N, -1, self.heads, self.head_dim)
        keys = keys.view(N, -1, self.heads, self.head_dim)
        queries = queries.view(N, -1, self.heads, self.head_dim)

        # 将注意力分数计算为查询和键之间的点积,并进行缩放
        energy = torch.einsum("nqhd,nkhd->nhqk", [queries, keys])
        
        # 应用掩码(mask)来忽略无效位置的注意力权重
        # 将掩码中值为0的位置对应的注意力分数设置为一个很大的负数(这里使用了 -1e20)
        # 从而使得在 softmax 操作中这些位置的注意力权重接近于0,从而实现忽略这些位置的效果。
        if mask is not None:
            energy = energy.masked_fill(mask == 0, float("-1e20"))
            
		# 计算注意力权重
        attention = torch.softmax(energy / (self.embed_size ** (1 / 2)), dim=3)

        # 将注意力权重乘以值
        out = torch.einsum("nhql,nlhd->nqhd", [attention, values]).reshape(
            N, -1, self.heads * self.head_dim
        )

        # 将多个头的输出拼接在一起,并通过线性层进行处理
        out = self.fc_out(out)
        return out
  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
首先,我们需要准备数据集。假设我们要预测房价,我们可以使用UCI Machine Learning Repository中的波士顿房价数据集。 接下来,我们可以定义我们的模型。CNN-GRU-Attention模型主要由三部分组成:卷积神经网络层(CNN)、门控循环单元层(GRU)和注意力机制层(Attention)。代码如下: ```python import torch.nn as nn class CNN_GRU_Attention(nn.Module): def __init__(self, input_dim, hidden_dim, output_dim): super(CNN_GRU_Attention, self).__init__() # 定义卷积神经网络层 self.conv_layer = nn.Sequential( nn.Conv1d(in_channels=input_dim, out_channels=32, kernel_size=3), nn.ReLU(), nn.Conv1d(in_channels=32, out_channels=64, kernel_size=3), nn.ReLU(), nn.Conv1d(in_channels=64, out_channels=128, kernel_size=3), nn.ReLU() ) # 定义门控循环单元层 self.gru_layer = nn.GRU(input_size=128, hidden_size=hidden_dim, num_layers=1, batch_first=True) # 定义注意力机制层 self.attention_layer = nn.Sequential( nn.Linear(hidden_dim, 64), nn.Tanh(), nn.Linear(64, 1) ) # 定义输出层 self.output_layer = nn.Linear(hidden_dim, output_dim) def forward(self, x): # 卷积神经网络层 x = self.conv_layer(x) # 将输出转换为GRU的输入格式 x = x.permute(0, 2, 1) # GRU层 output, hidden = self.gru_layer(x) # 注意力机制层 attention_weights = nn.functional.softmax(self.attention_layer(output), dim=1) attention_output = (output * attention_weights).sum(dim=1) # 输出层 output = self.output_layer(attention_output) return output ``` 接下来,我们可以定义损失函数和优化器,并开始训练我们的模型。这里我们使用均方误差(Mean Squared Error)作为损失函数,Adam优化器进行优化。代码如下: ```python import torch.optim as optim # 定义损失函数和优化器 criterion = nn.MSELoss() optimizer = optim.Adam(model.parameters(), lr=0.001) # 开始训练 num_epochs = 100 for epoch in range(num_epochs): # 训练模式 model.train() # 循环批次 for batch_idx, (data, target) in enumerate(train_loader): # 清空梯度 optimizer.zero_grad() # 前向传播 output = model(data) # 计算损失 loss = criterion(output, target) # 反向传播 loss.backward() # 更新参数 optimizer.step() # 测试模式 model.eval() # 计算测试集上的损失 test_loss = 0.0 with torch.no_grad(): for data, target in test_loader: output = model(data) test_loss += criterion(output, target).item() * data.size(0) test_loss /= len(test_loader.dataset) # 打印损失 print('Epoch: {}, Training Loss: {:.6f}, Testing Loss: {:.6f}'.format(epoch+1, loss.item(), test_loss)) ``` 最后,我们可以使用训练好的模型进行预测。代码如下: ```python # 使用模型进行预测 model.eval() with torch.no_grad(): output = model(test_data) # 打印预测结果 print('Prediction:', output.item()) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值