深度学习---------------------------------自注意力和位置编码

ops/2024/10/9 11:07:01/

目录

  • 自注意力
  • 跟CNN、RNN对比
  • 位置编码
    • 位置编码矩阵
  • 绝对位置信息
  • 相对位置信息
  • 总结
  • 自注意力和位置编码
    • 自注意力
      • 该部分总代码
    • 位置编码
      • 该部分总代码
    • 二进制表示
      • 在编码维度上降低频率
      • 该部分总代码

自注意力

给定一个由词元组成的输入序列 x 1 x_1 x1,…, x n x_n xn,∀ x i x_i xi R d R^d Rd

自注意力池化层 x i x_i xi当作key、value、query来对序列抽取特征得到 y 1 y_1 y1,… y n y_n yn,这里

在这里插入图片描述

在这里插入图片描述




跟CNN、RNN对比

在这里插入图片描述

CNN:k:窗口的大小(每次看一个k大小的东西)
     n:序列长度
     d:输入和输出的通道数量

并行度:每一个输出可以自己并行做运算.

最长路径:O(n/k)

在这里插入图片描述



RNN:
时间复杂度:序列长度为为n,当更新循环神经网络的隐状态时, d×d的权重矩阵和d维隐状态的乘法计算复杂度为O( d 2 d^2 d2),所以复杂度为O(n d 2 d^2 d2)

并行度:每一个 y i y_i yi的输出要等 y i − 1 y_{i-1} yi1算完

在这里插入图片描述

最长路径 x 1 x_1 x1的信息要一直传递到 x n x_n xn,需要经过一个O(n)的一个序列。

在这里插入图片描述



自注意力在自注意力中,查询、键和值都是n×d矩阵。其中n×d矩阵乘以d×n矩阵,之后输出的n×n矩阵乘以n×d矩阵。因此,自注意力具有O( n 2 d n^2d n2d)计算复杂度。

并行度:O(n)

最长路径:O(1),也就是说任何一个地方的信息要到任何一个输出的话,是可以直接过去的。

在这里插入图片描述
自注意力适合比较长的文本,因为能看的比较宽。但计算复杂度比较高。




位置编码

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

如果纯用自注意力机制来做序列模型的话,那么没有位置信息,那肯定是有问题的。所以一个加入位置信息的办法是位置编码。
它不是把位置信息加入到模型里面,因为一旦位置信息加入到模型里面总会有各种问题,CNN的会导致每一次得看一个比较长的序列,RNN的话并行度就低了,所以不是改变注意力机制本身,然后就把位置编码信息放到输入里,让输入有位置信息。

    假设长度为n的序列是X∈ R n × d R^{n×d} Rn×d,那么使用位置编码矩阵P∈ R n × d R^{n×d} Rn×d来输出X+P作为自编码输入。

P的元素计算:

在这里插入图片描述


位置编码矩阵

在这里插入图片描述
X坐标是它的一个行数,就是说对第i个序列加的那个值是什么?然后四根曲线对应的是第6、7、8、9列

对每一个样本,它的维度每一个加的值是不一样的。样本和样本之间也是不一样的。

在这里插入图片描述
在这里插入图片描述




绝对位置信息

在这里插入图片描述




相对位置信息

在这里插入图片描述




总结

①自注意力池化层将 x i x_i xi当做key、value、query来对序列抽取特征。

②完全并行、最长序列为1、但对长序列计算复杂度高。

③位置编码在输入中加入位置信息,使得自注意力能够记忆位置信息。




自注意力和位置编码

import math
import torch
from torch import nn
from d2l import torch as d2l



自注意力

from d2l import torch as d2lnum_hiddens, num_heads = 100, 5
# 创建多头注意力实例
# 输入参数为隐藏单元数量、查询维度、键维度、值维度、头的数量和dropout率
attention = d2l.MultiHeadAttention(num_hiddens, num_hiddens, num_hiddens,num_hiddens, num_heads, 0.5)
# 将多头注意力设置为评估模式,不进行训练
print(attention.eval())

在这里插入图片描述


# 设置批量大小、查询数和有效长度
batch_size, num_queries, valid_lens = 2, 4, torch.tensor([3, 2])
X = torch.ones((batch_size, num_queries, num_hiddens))
# 对输入张量X应用多头注意力机制,并获取输出的形状
print(attention(X, X, X, valid_lens).shape)

在这里插入图片描述


该部分总代码

from d2l import torch as d2lnum_hiddens, num_heads = 100, 5
# 创建多头注意力实例
# 输入参数为隐藏单元数量、查询维度、键维度、值维度、头的数量和dropout率
attention = d2l.MultiHeadAttention(num_hiddens, num_hiddens, num_hiddens,num_hiddens, num_heads, 0.5)
attention.eval()
# 设置批量大小、查询数和有效长度
batch_size, num_queries, valid_lens = 2, 4, torch.tensor([3, 2])
X = torch.ones((batch_size, num_queries, num_hiddens))
# 对输入张量X应用多头注意力机制,并获取输出的形状
print(attention(X, X, X, valid_lens).shape)



位置编码

class PositionalEncoding(nn.Module):# 初始化函数,接收隐藏单元数量、dropout率和最大序列长度作为输入def __init__(self, num_hiddens, dropout, max_len=1000):super(PositionalEncoding, self).__init__()self.dropout = nn.Dropout(dropout)# 创建一个形状为(batch_size, max_len, num_hiddens)的位置编码张量P,初始化为全0self.P = torch.zeros((1, max_len, num_hiddens))# 生成位置编码矩阵X,其中每一行表示一个位置的编码,编码方式采用sin和cos函数# 编码公式:X[i, j] = sin(i / 10000^(2j / num_hiddens)) 或 cos(i / 10000^(2j / num_hiddens))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)# 第一维度、第二维的所有元素,第三维将位置编码矩阵中的偶数维度的元素替换为sin函数的结果self.P[:, :, 0::2] = torch.sin(X)# 将位置编码矩阵中的奇数维度的元素替换为cos函数的结果self.P[:, :, 1::2] = torch.cos(X)def forward(self, X):# 将位置编码张量P与输入张量X相加,并将结果移动到与X相同的设备上# self.P[:, :X.shape[1], :]第二维从索引0到 X 的第二维大小的所有元素➡确保P的第二维和X的第二维一致,因为可能是不同的序列长度,为了动态的适应不同长度的输入X = X + self.P[:, :X.shape[1], :].to(X.device)# 对相加后的结果应用dropout,并返回结果return self.dropout(X)



该部分总代码

import torch
from torch import nn
from d2l import torch as d2l# 位置编码
class PositionalEncoding(nn.Module):# 初始化函数,接收隐藏单元数量、dropout率和最大序列长度作为输入def __init__(self, num_hiddens, dropout, max_len=1000):super(PositionalEncoding, self).__init__()self.dropout = nn.Dropout(dropout)# 创建一个形状为(batch_size, max_len, num_hiddens)的位置编码张量P,初始化为全0self.P = torch.zeros((1, max_len, num_hiddens))# 生成位置编码矩阵X,其中每一行表示一个位置的编码,编码方式采用sin和cos函数# 编码公式:X[i, j] = sin(i / 10000^(2j / num_hiddens)) 或 cos(i / 10000^(2j / num_hiddens))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)# 第一维度、第二维的所有元素,第三维将位置编码矩阵中的偶数维度的元素替换为sin函数的结果self.P[:, :, 0::2] = torch.sin(X)# 将位置编码矩阵中的奇数维度的元素替换为cos函数的结果self.P[:, :, 1::2] = torch.cos(X)def forward(self, X):# 将位置编码张量P与输入张量X相加,并将结果移动到与X相同的设备上# self.P[:, :X.shape[1], :]第二维从索引0到 X 的第二维大小的所有元素➡确保P的第二维和X的第二维一致,因为可能是不同的序列长度,为了动态的适应不同长度的输入X = X + self.P[:, :X.shape[1], :].to(X.device)# 对相加后的结果应用dropout,并返回结果return self.dropout(X)num_hiddens, num_heads = 100, 5
# 创建多头注意力实例
# 输入参数为隐藏单元数量、查询维度、键维度、值维度、头的数量和dropout率
attention = d2l.MultiHeadAttention(num_hiddens, num_hiddens, num_hiddens,num_hiddens, num_heads, 0.5)
# 将多头注意力设置为评估模式,不进行训练
attention.eval()# 设置批量大小、查询数和有效长度
batch_size, num_queries, valid_lens = 2, 4, torch.tensor([3, 2])
X = torch.ones((batch_size, num_queries, num_hiddens))
# 对输入张量X应用多头注意力机制,并获取输出的形状
print(attention(X, X, X, valid_lens).shape)



行代表标记在序列中的位置,列代表位置编码的不同维度。

import torch
from torch import nn
from d2l import torch as d2l# 位置编码
class PositionalEncoding(nn.Module):# 初始化函数,接收隐藏单元数量、dropout率和最大序列长度作为输入def __init__(self, num_hiddens, dropout, max_len=1000):super(PositionalEncoding, self).__init__()self.dropout = nn.Dropout(dropout)# 创建一个形状为(batch_size, max_len, num_hiddens)的位置编码张量P,初始化为全0self.P = torch.zeros((1, max_len, num_hiddens))# 生成位置编码矩阵X,其中每一行表示一个位置的编码,编码方式采用sin和cos函数# 编码公式:X[i, j] = sin(i / 10000^(2j / num_hiddens)) 或 cos(i / 10000^(2j / num_hiddens))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)# 第一维度、第二维的所有元素,第三维将位置编码矩阵中的偶数维度的元素替换为sin函数的结果self.P[:, :, 0::2] = torch.sin(X)# 将位置编码矩阵中的奇数维度的元素替换为cos函数的结果self.P[:, :, 1::2] = torch.cos(X)def forward(self, X):# 将位置编码张量P与输入张量X相加,并将结果移动到与X相同的设备上# self.P[:, :X.shape[1], :]第二维从索引0到 X 的第二维大小的所有元素➡确保P的第二维和X的第二维一致,因为可能是不同的序列长度,为了动态的适应不同长度的输入X = X + self.P[:, :X.shape[1], :].to(X.device)# 对相加后的结果应用dropout,并返回结果return self.dropout(X)# 设置位置编码的维度和序列的长度
encoding_dim, num_steps = 32, 60
pos_encoding = PositionalEncoding(encoding_dim, 0)
pos_encoding.eval()
# 应用位置编码器到全0张量上,得到位置编码后的张量X
X = pos_encoding(torch.zeros((1, num_steps, encoding_dim)))
# 获取位置编码器中的位置编码张量P,截取与X相同长度的部分
P = pos_encoding.P[:, :X.shape[1], :]
# 绘制位置编码张量P中特定维度的子集
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)])
d2l.plt.show()

在这里插入图片描述




二进制表示

for i in range(8):# 打印当前数字的二进制表示,使用字符串格式化进行对齐和补零print(f'{i} in binary is {i:>03b}')

在这里插入图片描述


在编码维度上降低频率

# 在编码维度上降低频率
# 从位置编码张量P中获取第一个样本的编码部分,并添加两个维度
P = P[0, :, :].unsqueeze(0).unsqueeze(0)
# 显示热力图,以编码维度为x轴,位置为y轴
d2l.show_heatmaps(P, xlabel='Column (encoding dimension)',ylabel='Row (position)', figsize=(3.5, 4), cmap='Blues')

在这里插入图片描述



该部分总代码

import torch
from torch import nn
from d2l import torch as d2l# 位置编码
class PositionalEncoding(nn.Module):# 初始化函数,接收隐藏单元数量、dropout率和最大序列长度作为输入def __init__(self, num_hiddens, dropout, max_len=1000):super(PositionalEncoding, self).__init__()self.dropout = nn.Dropout(dropout)# 创建一个形状为(batch_size, max_len, num_hiddens)的位置编码张量P,初始化为全0self.P = torch.zeros((1, max_len, num_hiddens))# 生成位置编码矩阵X,其中每一行表示一个位置的编码,编码方式采用sin和cos函数# 编码公式:X[i, j] = sin(i / 10000^(2j / num_hiddens)) 或 cos(i / 10000^(2j / num_hiddens))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)# 第一维度、第二维的所有元素,第三维将位置编码矩阵中的偶数维度的元素替换为sin函数的结果self.P[:, :, 0::2] = torch.sin(X)# 将位置编码矩阵中的奇数维度的元素替换为cos函数的结果self.P[:, :, 1::2] = torch.cos(X)def forward(self, X):# 将位置编码张量P与输入张量X相加,并将结果移动到与X相同的设备上# self.P[:, :X.shape[1], :]第二维从索引0到 X 的第二维大小的所有元素➡确保P的第二维和X的第二维一致,因为可能是不同的序列长度,为了动态的适应不同长度的输入X = X + self.P[:, :X.shape[1], :].to(X.device)# 对相加后的结果应用dropout,并返回结果return self.dropout(X)# 设置位置编码的维度和序列的长度
encoding_dim, num_steps = 32, 60
pos_encoding = PositionalEncoding(encoding_dim, 0)
pos_encoding.eval()
# 应用位置编码器到全0张量上,得到位置编码后的张量X
X = pos_encoding(torch.zeros((1, num_steps, encoding_dim)))
# 获取位置编码器中的位置编码张量P,截取与X相同长度的部分
P = pos_encoding.P[:, :X.shape[1], :]
# 在编码维度上降低频率
# 从位置编码张量P中获取第一个样本的编码部分,并添加两个维度
P = P[0, :, :].unsqueeze(0).unsqueeze(0)
# 显示热力图,以编码维度为x轴,位置为y轴
d2l.show_heatmaps(P, xlabel='Column (encoding dimension)',ylabel='Row (position)', figsize=(3.5, 4), cmap='Blues')
d2l.plt.show()

http://www.ppmy.cn/ops/123149.html

相关文章

低质量数据的多模态融合方法

目录 多模态融合 低质量多模态融合的核心挑战 噪声多模态数据学习 缺失模态插补 平衡多模态融合 动态多模态融合 启发式动态融合 基于注意力的动态融合 不确定性感知动态融合 论文 多模态融合 多模态融合侧重于整合多种模态的信息,以实现更准确的预测,在自动驾驶、…

SSM 浅汐物品交易平台-计算机毕业设计源码48127

摘 要 近年来,信息网络迅猛推进,其具有灵活方便、传递消息速度快等优点,这一新兴媒体日渐兴盛,已经成为人们日常生活获取信息一个重要手段。从08开始,电商行业如春风吹过后的小草,呈现出一片蓬勃地发展态…

创新的火花:日常小发明中的专利智慧

在日常生活中,我们常常被一些看似微不足道的小发明所惊喜。这些小发明虽然简单,却往往能解决大问题,提高生活质量。而这些发明背后,专利的智慧和力量不容小觑。本文将带您一探究竟,看看这些小发明是如何通过专利保护&a…

docker compose入门3—docker compose yaml字段详解

这是一个整合了几乎所有主要字段的 Docker Compose 示例文件,包括服务定义、卷、网络、Secrets、配置等所有你可能用到的配置项。这个文件模拟了一个 Web 服务、数据库服务、以及如何使用卷、网络、Secrets 和配置。 综合示例 docker-compose.yml version: 3.8 #…

基于SpringBoot+Vue+MySQL的中医院问诊系统

系统展示 用户前台界面 管理员后台界面 医生后台界面 系统背景 随着信息技术的迅猛发展和医疗服务需求的不断增加,传统的中医院问诊流程已经无法满足患者和医院的需求。纸质病历不仅占用大量存储空间,而且容易丢失和损坏,同时难以实现信息的快…

YOLOv10改进策略【注意力机制篇】| GAM全局注意力机制: 保留信息以增强通道与空间的相互作用

一、本文介绍 本文记录的是基于GAM注意力模块的YOLOv10目标检测改进方法研究。GAM注意力模块通过3D排列和重新设计的子模块,能够在通道和空间方面保留信息,避免了先前方法中由于信息减少和维度分离而导致的全局空间-通道交互丢失的问题。本文利用GAM改进…

微信重大更新来袭,用户体验全面升级

在经过了两个月的漫长等待之后,微信终于迎来了新版本的更新。 无论是安卓还是iOS用户,都能感受到这次更新带来的全新体验。 微信团队在细节上的精心打磨,无疑让这款国民应用更加贴合用户的需求,从界面的调整到功能的优化&#x…

软件I2C-基于江科大源码进行的原理解析和改造升级

一、软件I2C的作用 软件I2C可以不用特定的端口,可以在I2C外设不够的时候使用,虽然没有硬件I2C的速度快,但是在一些要求低的工作中不足为谈 数据有效性: I2C总线进行数据传送时,时钟信号为高电平期间,数据…