自注意力和位置编码(含pytorch代码)

本文探讨了自注意力机制如何在处理长序列时捕捉远程信息,以及如何通过位置编码注入绝对和相对位置信息。重点介绍了基于正弦余弦函数的固定位置编码,并提供了代码实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

自注意力和位置编码

自注意力

在这里插入图片描述

自注意力池化层将xi当作key, value, query来对序列特征得到yi

与CNN、RNN进行比较:
在这里插入图片描述

最长路径:信息从序列前端的某个位置传递到末端的某个位置的路径

self-attention在长句子中虽然计算复杂度很好,但能很快地抓取距离很远的信息(适合处理较长的序列,付出了计算复杂度的代价,计算量超大)

位置编码

与CNN/RNN不同,自注意力并没有记录位置信息

在处理词元序列时,循环神经网络是逐个的重复地处理词元的, 而自注意力则因为并行计算而放弃了顺序操作。 为了使用序列的顺序信息,我们通过在输入表示中添加位置编码来注入绝对的或相对的位置信息。 位置编码可以通过学习得到也可以直接固定得到。

想法:不改变本身的自注意力机制,将位置信息加入输入中去。

接下来,我们描述的是基于正弦函数和余弦函数的固定位置编码
在这里插入图片描述

不同的列周期不一样

在位置嵌入矩阵P中, 行代表词元在序列中的位置,列代表位置编码的不同维度。 在下面的例子中,我们可以看到位置嵌入矩阵的第6列和第7列的频率高于第8列和第9列。 第6列和第7列之间的偏移量(第8列和第9列相同)是由于正弦函数和余弦函数的交替。

在这里插入图片描述

绝对位置信息

为了明白沿着编码维度单调降低的频率与绝对位置信息的关系, 让我们打印出0,1,…,7的二进制表示形式。 正如我们所看到的,每个数字、每两个数字和每四个数字上的比特值 在第一个最低位、第二个最低位和第三个最低位上分别交替。

0的二进制是:000
1的二进制是:001
2的二进制是:010
3的二进制是:011
4的二进制是:100
5的二进制是:101
6的二进制是:110
7的二进制是:111

在二进制表示中,较高比特位的交替频率低于较低比特位, 与下面的热图所示相似,只是位置编码通过使用三角函数在编码维度上降低频率。 由于输出是浮点数,因此此类连续表示比二进制表示法更节省空间。

相对位置信息

除了捕获绝对位置信息之外,上述的位置编码还允许模型学习得到输入序列中相对位置信息。 这是因为对于任何确定的位置偏移δ,位置i+δ处 的位置编码可以线性投影位置i处的位置编码来表示。

在这里插入图片描述

小结:

  • 在自注意力chi话层将xi当作key,value和query来对序列抽取特征
  • 卷积神经网络和自注意力都拥有并行计算的优势,而且自注意力的最大路径长度最短。但是因为其计算复杂度是关于序列长度的二次方,所以在很长的序列中计算会非常慢。(完全并行、最长序列为1,长序列计算复杂度高)
  • 为了使用序列的顺序信息,我们可以通过在输入表示中添加位置编码,来注入绝对的或相对的位置信息。
代码实现
导入模块
import torch
from torch import nn
from d2l import torch as d2l
from matplotlib import pyplot as plt
自注意力

根据定义的注意力池化函数f。 下面的代码片段是基于多头注意力对一个张量完成自注意力的计算, 张量的形状为(批量大小,时间步的数目或词元序列的长度,d)。 输出与输入的张量形状相同。

num_hiddens, num_heads = 100, 5
attention = d2l.MultiHeadAttention(num_hiddens, num_hiddens, num_hiddens,
                                   num_hiddens, num_heads, 0.5)
attention.eval()

其中MultiHeadAttention结构如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jZIwXg71-1644311711181)(C:\Users\pc\AppData\Roaming\Typora\typora-user-images\image-20220208154003164.png)]

batch_size, num_queries, valid_lens = 2, 4, torch.tensor([3, 2])
X = torch.ones((batch_size, num_queries, num_hiddens))
attention(X, X, X, valid_lens).shape
位置编码
class PositionalEncoding(nn.Module):
    """位置编码"""
    #num+hiddens:向量长度  max_len:序列最大长度
    def __init__(self, num_hiddens, dropout, max_len=1000):
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(dropout)
        # 创建一个足够长的P : (1, 1000, 32)
        self.P = torch.zeros((1, max_len, num_hiddens))
        #本例中X的维度为(1000, 16)
        X = torch.arange(max_len, dtype=torch.float32).reshape(
            -1, 1) / torch.pow(10000, torch.arange(0, num_hiddens, 2, dtype=torch.float32) / num_hiddens)

        self.P[:, :, 0::2] = torch.sin(X)   #::2意为指定步长为2 为[start_index : end_index : step]省略end_index的写法
        self.P[:, :, 1::2] = torch.cos(X)

    def forward(self, X):
        X = X + self.P[:, :X.shape[1], :].to(X.device)
        return self.dropout(X)

encoding_dim, num_steps = 32, 60
pos_encoding = PositionalEncoding(encoding_dim, 0)
pos_encoding.eval()
X = pos_encoding(torch.zeros((1, num_steps, encoding_dim)))
P = pos_encoding.P[:, :X.shape[1], :]
d2l.plot(torch.arange(num_steps), P[0, :, 6:10].T, xlabel='Row (position)',
         figsize=(6, 2.5), legend=["Col %d" % d for d in torch.arange(6, 10)])

绘图如下所示:

在这里插入图片描述

以下是一个简单的自注意力编码器模型的 PyTorch 代码示例: ```python import torch import torch.nn as nn class SelfAttentionEncoder(nn.Module): def __init__(self, input_size, hidden_size, num_layers): super(SelfAttentionEncoder, self).__init__() self.input_size = input_size self.hidden_size = hidden_size self.num_layers = num_layers self.embedding = nn.Embedding(input_size, hidden_size) self.attention = nn.MultiheadAttention(hidden_size, num_heads=1) self.fc = nn.Linear(hidden_size, hidden_size) self.dropout = nn.Dropout(0.1) def forward(self, input_seq): embedded = self.embedding(input_seq) embedded = embedded.permute(1, 0, 2) # transpose batch and sequence length dimension att_output, _ = self.attention(embedded, embedded, embedded) att_output = att_output.permute(1, 0, 2) att_output = self.fc(att_output) att_output = self.dropout(att_output) return att_output ``` 在这个例子中,我们定义了一个名为 `SelfAttentionEncoder` 的类,它继承自 PyTorch 的 `nn.Module` 类。该编码器接受一个输入序列,其中每个元素都是一个整数,表示输入序列中的一个标记或单词。 该模型的主要组成部分是一个嵌入层 `self.embedding`,用于将输入序列中的整数标记转换为连续向量表示。然后,我们使用 `nn.MultiheadAttention` 类来计算自注意力,该类接受三个相同的输入张量,分别用于查询、键值。在这里,我们使用嵌入层的输出作为所有三个输入张量。 最后,我们将注意力输出传递给一个全连接层 `self.fc`,并使用 `nn.Dropout` 在训练期间对其进行正则化。最终输出是一个形状为 `(batch_size, seq_len, hidden_size)` 的张量,其中 `seq_len` 是输入序列的长度。 这只是一个简单的例子,你可以根据需要更改网络结构超参数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值