1.torch.bmm()的用法
先说一般的矩阵乘法torch.mm()。torch.mm()用于将两个二维张量(矩阵)相乘,求它们的叉乘结果。如:
我们创建一个2*3的矩阵A,3*4的矩阵B,它们的值都初始化为均值为0方差为1的标准正态分布,用torch.mm()求它们的叉乘结果:
import torch from torch import nn from d2l import torch as d2l A = torch.normal(0,1,(2,3)) B = torch.normal(0,1,(3,4)) AB = torch.mm(A,B) print(AB)
输出:
torch.mm()是求一个矩阵乘以一个矩阵的结果,它的两个参数都是二维张量。而torch.bmm()是求一个批量的矩阵乘以一个批量的矩阵的结果,它的两个参数都是三维张量,其中第一维表示了这一批矩阵的数量。如:
A = torch.normal(0,1,(3,2,3)) B = torch.normal(0,1,(3,3,4)) AB = torch.bmm(A,B) print(AB)
输出:
注意bmm操作要求两个三维张量维度的第一个参数必须相等:如这里是(3,2,3)的张量和(3,3,4)的张量,它们的第一个参数都是3.
2.掩蔽softmax操作
在有些情况下,并非所有的值都是有意义的,
#@save def masked_softmax(X, valid_lens): """通过在最后一个轴上掩蔽元素来执行softmax操作""" # X:3D张量,valid_lens:1D或2D张量
#如果没有有效长度,即所有值均有效,那么就是直接返回softmax操作的结果 if valid_lens is None: return nn.functional.softmax(X, dim=-1) else: shape = X.shape if valid_lens.dim() == 1: valid_lens = torch.repeat_interleave(valid_lens, shape[1]) else: valid_lens = valid_lens.reshape(-1) # 最后一轴上被掩蔽的元素使用一个非常大的负值替换,从而其softmax输出为0 X = d2l.sequence_mask(X.reshape(-1, shape[-1]), valid_lens, value=-1e6) return nn.functional.softmax(X.reshape(shape), dim=-1)
dim=-1的用法:对于三维张量(C,H,W),做softmax就有三种方式:
1.dim=0就是对每一个维度对应位置的数值构成的向量做softmax运算。
2.dim=1就是对某一个维度的一列数值构成的向量做softmax运算。
3.dim=2就是对某一个维度的一行数值构成的向量做softmax运算。
这里面的dim=-1相当于dim=2. 那为什么不直接写dim=2?应该是为了这个函数能适应不同维度的X输入,若输入X是3维的,则softmax(X,dim=-1)相当于softmax(X,dim=2),若输入X是2维的,则softmax(X,dim=-1)相当于softmax(X,dim=1)。
torch.repeat_interleave()的用法:
a = torch.arange(6).reshape(2,1,3) res = torch.repeat_interleave(a,3,dim = 1) #张量a在第1维(行)上重复3遍 print(a) print(res) print(a.shape) print(res.shape)
运行结果:
看一下这个masked_softmax(X,valid_lens)函数的效果。考虑样本为2个2*4的矩阵,即(2,2,4)的矩阵:
这里面valid_lens为[2,3]时,意思是:第一个矩阵中的每一行数据都是前2个是有效值,第二个矩阵中的每一行数据都是前3个是有效值。
valid_lens为[ [1,3],[2,4] ]时,意思是:第一个矩阵中第一行前1个是有效值,第一个矩阵中第二行前3个是有效值,第二个矩阵第一行前2个是有效值,第二个矩阵中第二行是前4个是有效值。
3.加性注意力
其中可学习的参数是:
它等价于将q和k拼接到一起,丢到一个隐藏层大小为h,输出大小为1的单隐藏层MLP中去。Wq、Wk拼接起来就是隐藏层的参数。
(这里的代码没怎么理解,后面再补充)
4.缩放点积注意力
两个同维度并且模相等的向量,它们的点积越大,就表示它们越相似。当query和key具有相同的长度d时,我们可以使用缩放点积作为评分函数。假设query和key都遵循均值为0,方差为1的标准正态分布,那么q和k的点积就遵循均值为0,方差为d的正态分布。我们为了确保无论向量长度d是多长,q和k点积的方差仍然是1,将点积再除以√d,得到缩放点积注意力的评分函数:
这是一个查询q的情况。考虑有n个查询query,m个key-value pair,其中query和key的长度为d,value的长度为v,那么,
和的缩放点积注意力为:
这里面QKτ / √d (维度为n*m) 的第 i 行第 j 列的值就表示第 i 个query对第 j 个key的注意力分数。再做softmax得到的就是注意力权重,再乘以V得到的就是注意力汇聚的结果。
下面实现的缩放点积注意力使用了dropout进行模型正则化:
#缩放点积注意力 #@save class DotProductAttention(nn.Module): """缩放点积注意力""" def __init__(self, dropout, **kwargs):
#这里的参数暂时没理解 super(DotProductAttention, self).__init__(**kwargs) self.dropout = nn.Dropout(dropout) # queries的形状:(batch_size,查询的个数,d) # keys的形状:(batch_size,“键-值”对的个数,d) # values的形状:(batch_size,“键-值”对的个数,值的维度) # valid_lens的形状:(batch_size,)或者(batch_size,查询的个数) def forward(self, queries, keys, values, valid_lens=None): d = queries.shape[-1] # keys.transpose(1,2)的意思是将key这个三维张量在1和2这两个维度上转置() scores = torch.bmm(queries, keys.transpose(1,2)) / math.sqrt(d) self.attention_weights = masked_softmax(scores, valid_lens) return torch.bmm(self.dropout(self.attention_weights), values)
下面演示一下这个类。在下面的代码中,我们设计了queries维度为(2,1,2),含义是一个批量有两个矩阵,每个矩阵有1个查询,长度为2,那么,key和value也应该一个批量是2个矩阵。我们设计了key-value pair的数量为10,并且key的长度等同于query的长度,value的长度为4.
queries = torch.normal(0, 1, (2, 1, 2)) keys = torch.ones((2, 10, 2)) # values的小批量,两个值矩阵是相同的 #values的维度被repeat成了(2,10,4) values = torch.arange(40, dtype=torch.float32).reshape(1, 10, 4).repeat( 2, 1, 1) valid_lens = torch.tensor([2, 6]) attention = DotProductAttention(dropout=0.5) attention.eval() print(attention(queries, keys, values, valid_lens)) d2l.show_heatmaps(attention.attention_weights.reshape((1, 1, 2, 10)), xlabel='Keys', ylabel='Queries')
运行结果:
总结:
注意力分数是query和key的相似度,是没有被normalize过的,而注意力权重是注意力分数softmax的结果。两种常见的注意力分数计算:
1. 将query和key合并起来进入一个单输出单隐藏层的MLP。(加性注意力)
2.直接将query和key做内积。(点积注意力)
标签:dim,10.3,评分,torch,矩阵,lens,valid,softmax,注意力 From: https://www.cnblogs.com/pkuqcy/p/17689852.html