一、定义
1、随意线索与不随意线索:随意=刻意
2、注意力机制
(1)卷积、全连接、池化层都只考虑不随意线索,“是否包含自主性提示”将注意力机制与全连接层或汇聚层区别开来;注意力机制考虑随意线索
(2)在注意力机制中,感官输入被称为值(value)每个值都与一个键(key)配对, 这可以想象为感官输入的非自主提示。
(3)可以通过设计注意力汇聚的方式, 便于给定的查询(自主性提示)与键(非自主性提示)进行匹配, 这将引导得出最匹配的值(感官输入)
3、非参注意力池化层
(1)平均汇聚
(2)非参数注意力汇聚
a、Nadaraya-Watson核回归
b、注意力汇聚(attention pooling)公式
其中𝑥是查询,(𝑥𝑖,𝑦𝑖)是键值对, 注意力汇聚是𝑦𝑖的加权平均;阿尔法称之为距离权重
对需要查询的x,找到与xi相关的权重,与yi相乘得到随意的值;即非参注意力池化层,拿到了新值与很近的一个值做权重计算
非参更需要大量的数据支持
(3)举例高斯核,并代入
a、softmax作用:映射为非负数、归一化
b、使用softmax对权重做处理
4、参数化的注意力机制(引入可学习的w)
(1)通过学习W控制高斯核大小进而控制曲线是否平滑
二、热图(平均汇聚层举例)
1、注意力机制中的热图用于可视化显示不同查询(query)和键(key)之间的注意力权重分配。
2、横轴代表的是注意力机制中的键(key),这些键通常是输入序列中的各个元素的表示;在热图上,每个键对应一个位置,从左到右依次排列。
3、纵轴代表的是查询(query)。这些查询也是输入序列中的各个元素的表示,或者是在解码过程中生成的序列元素; 在热图上,每个查询对应一个位置,从上到下依次排列。
4、热图中的颜色表示注意力权重的大小。颜色越深,表示权重越大,即查询(query)对相应的键(key)给予的注意力越多;通常使用从浅到深的颜色渐变来表示权重,从白色或浅色(低权重)到深色(高权重)。
5、 通过观察整个热图,可以看到不同查询对不同键的注意力分布情况,从而理解模型在不同输入之间是如何分配注意力的
6、比如平均汇聚层热图
三、总结
1、使用非自主性和自主性提示有选择性地引导注意力。前者基于突出性,后者则依赖于意识。
2、注意力机制与全连接层或者汇聚层的区别源于增加的自主提示;由于包含了自主性提示,注意力机制与全连接的层或汇聚层不同。
3、注意力机制通过注意力汇聚使选择偏向于值(感官输入),其中包含查询(自主性提示)和键(非自主性提示)。
4、键和值是成对的。
5、可视化查询和键之间的注意力权重是可行的(热图)。
6、Nadaraya-Watson核回归是具有注意力机制的机器学习范例。
7、Nadaraya-Watson核回归的注意力汇聚是对训练数据中输出的加权平均。从注意力的角度来看,分配给每个值的注意力权重取决于将值所对应的键和查询作为输入的函数。
8、注意力汇聚可以分为非参数型和带参数型。
四、注意力汇聚Nadaraya-Watson 核回归实现
1、生成数据集
非线性函数生成一个人工数据集, 其中加入的噪声项为𝜖:
import torch from torch import nn from d2l import torch as d2l n_train = 50 # 训练样本数 x_train, _ = torch.sort(torch.rand(n_train) * 5) # 排序后的训练样本 def f(x): return 2 * torch.sin(x) + x**0.8 y_train = f(x_train) + torch.normal(0.0, 0.5, (n_train,)) # 训练样本的输出 x_test = torch.arange(0, 5, 0.1) # 测试样本 y_truth = f(x_test) # 测试样本的真实输出 n_test = len(x_test) # 测试样本数 n_test
2、绘制函数
绘制所有的训练样本(样本由圆圈表示), 不带噪声项的真实数据生成函数𝑓(标记为“Truth”), 以及学习得到的预测函数(标记为“Pred”)。
#这个就是画图便于更好的理解 def plot_kernel_reg(y_hat): d2l.plot(x_test, [y_truth, y_hat], 'x', 'y', legend=['Truth', 'Pred'], xlim=[0, 5], ylim=[-1, 5]) d2l.plt.plot(x_train, y_train, 'o', alpha=0.5);
3、平均汇聚
y_hat = torch.repeat_interleave(y_train.mean(), n_test) plot_kernel_reg(y_hat)
4、非参数注意力汇聚
# X_repeat的形状:(n_test,n_train), # 每一行都包含着相同的测试输入(例如:同样的查询) X_repeat = x_test.repeat_interleave(n_train).reshape((-1, n_train)) # x_train包含着键。attention_weights的形状:(n_test,n_train), # 每一行都包含着要在给定的每个查询的值(y_train)之间分配的注意力权重 attention_weights = nn.functional.softmax(-(X_repeat - x_train)**2 / 2, dim=1) # y_hat的每个元素都是值的加权平均值,其中的权重是注意力权重 y_hat = torch.matmul(attention_weights, y_train) plot_kernel_reg(y_hat)
#热图 d2l.show_heatmaps(attention_weights.unsqueeze(0).unsqueeze(0), xlabel='Sorted training inputs', ylabel='Sorted testing inputs')
5、小批量矩阵乘法
#这个是对每一项批量分开做乘法所以是2个批量,1x4矩阵*4x6矩阵 X = torch.ones((2, 1, 4)) Y = torch.ones((2, 4, 6)) torch.bmm(X, Y).shape
6、定义模型
class NWKernelRegression(nn.Module): def __init__(self, **kwargs): super().__init__(**kwargs) #w是要学习的参数 self.w = nn.Parameter(torch.rand((1,), requires_grad=True)) def forward(self, queries, keys, values): # queries和attention_weights的形状为(查询个数,“键-值”对个数) queries = queries.repeat_interleave(keys.shape[1]).reshape((-1, keys.shape[1])) self.attention_weights = nn.functional.softmax( -((queries - keys) * self.w)**2 / 2, dim=1) # values的形状为(查询个数,“键-值”对个数) return torch.bmm(self.attention_weights.unsqueeze(1), values.unsqueeze(-1)).reshape(-1)
7、训练
# X_tile的形状:(n_train,n_train),每一行都包含着相同的训练输入 X_tile = x_train.repeat((n_train, 1)) # Y_tile的形状:(n_train,n_train),每一行都包含着相同的训练输出 Y_tile = y_train.repeat((n_train, 1)) # keys的形状:('n_train','n_train'-1) keys = X_tile[(1 - torch.eye(n_train)).type(torch.bool)].reshape((n_train, -1)) # values的形状:('n_train','n_train'-1) values = Y_tile[(1 - torch.eye(n_train)).type(torch.bool)].reshape((n_train, -1)) net = NWKernelRegression() loss = nn.MSELoss(reduction='none') trainer = torch.optim.SGD(net.parameters(), lr=0.5) animator = d2l.Animator(xlabel='epoch', ylabel='loss', xlim=[1, 5]) for epoch in range(5): trainer.zero_grad() l = loss(net(x_train, keys, values), y_train) l.sum().backward() trainer.step()
8、预测结果绘制的线不如之前非参数模型的平滑
# keys的形状:(n_test,n_train),每一行包含着相同的训练输入(例如,相同的键) keys = x_train.repeat((n_test, 1)) # value的形状:(n_test,n_train) values = y_train.repeat((n_test, 1)) y_hat = net(x_test, keys, values).unsqueeze(1).detach() plot_kernel_reg(y_hat)
9、热图(因为训练了w,权重更加集中了)
d2l.show_heatmaps(net.attention_weights.unsqueeze(0).unsqueeze(0), xlabel='Sorted training inputs', ylabel='Sorted testing inputs')