10.1节介绍了框架下的注意力机制主要组件,查询和键之间的交互形成了注意力汇聚,注意力汇聚有选择性地汇聚了值以生成最终的输出,本节将介绍注意力汇聚的更多细节。以便从宏观上了解注意力机制在实践中的运作方式。具体来说,1964年提出的Ndaraya-Watson核回归模型是一个简单但是完整的例子,可以用于演示具体有注意力机制的机器学习。
import torch
from torch import nn
from d2l import torch as d2l
10.2.1 生成数据集
为简单期间,考虑下面回归问题,给定成对的输入-输出 数据集(X1, Y1) 如何学习f来预测任意新输入x的输出y^ = f(x)?
根据下面的非线性函数生成一个人工数据集
Yi = 2 sin(Xi) + Xi^0.8 + t
其中, t为加入的噪声项,服从均值为0和标准差为0.5的正态分布,在这里生成了50个训练样本和50个测试样本,为了更好的可视化之后的注意力模式,需要将训练样本进行排序。
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
下面的函数将绘制所有的训练样本,不带噪声项的真实数据生成函数f(标记为Truth),以及学习得到的预测函数
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)
10.2.2 平均汇聚
先使用简单的估计器来解决回归问题,基于平均汇聚来计算所有训练样本输出值的平均值。
f(x) = 1/n * Sigma i=1,yi
如下图所示,这个估计器确实不够聪明,真实函数f(Truth)和预测函数Pred相差很大。
y_hat = torch.repeat_interleave(y_train.mean(), n_test)
plot_kernel_reg(y_hat)
10.2.3 非参数注意力汇聚
平均汇聚忽略了输入xi, 于是Nadaraya和Watson提出了一个更好的想法,根据输入的位置对输出yi进行加权。
f(x) = Sigma i=1,
K是核Kernel, 所描述的估计器被称为Nadaraya-Watson核回归。这里不会深入讨论核函数的细节,受此启发,可以从图10-3中的注意力机制框架的角度充血,使之成为一个更加通用的注意力汇聚公式。
f(x) = Sigma i=1 a(x, xi)yi
x是查询,(xi,yi)是键值对。
值得注意是,Nadaray-Waston核回归是个非参数模型,注意力汇聚模型,接下来,我们将基于这个非参数的注意力汇聚模型来绘制预测结果。从绘制的结果会发现新的模型预测线式平滑的。并且比平均汇聚的预测更接近真实情况。
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 trainning inputs',
ylabel = 'Sorted testint inputs')
10.2.4 带参数注意力汇聚
非参数的Nadaraya_Watson核回归具有一致性的优点,如果有足够的数据,此模型会收敛最优结果,尽管如此,还是可以轻松的将科学系的参数集成到注意力汇聚中。
与10.6 略有不同,将下面的查询x和键xi之间的距离乘以可学习参数w:
本节的剩余部分将通过训练这个模型来学习注意力汇聚的参数。
1 批量矩阵乘法
为了更有效的计算小批量数据的注意力,可以利用深度学习开发框架中提供的批量矩阵乘法
假设第一个小批量数据包含n个矩阵Xl,,,Xn。形状为axb,第二个小批量数据包含n个矩阵Yl,,Yn,形状为bxc,批量矩阵乘法得到n个矩阵X1,Y1,,,Xn,Yn形状为axc,假设两个张量的形状分别为(n,a,b)和(n,b,c) 批量矩阵乘法输出的形状为(n,a,c)
X=torch.ones(2,1,4)
Y = torch.ones(2,4,6)
torch.bmm(X,Y).shape
在主利益机制背景下,可以使用小批量矩阵乘法来计算小批量数据中的加权平均值。
weights = torch.ones(2, 10) * 0.1
values = torch.arange(20.0).reshape(2, 10)
torch.bmm(weights.unsqueeze(1), values.unsqueeze(-1))
2 定义模型
基于式10.7中的带参数注意力汇聚,使用小批量矩阵乘法,定义Nadaraya-Watson核回归的带参数版本
class NWKernelRegression(nn.Module):
def __init__(self, **kwargs):
super().__init__(**kwargs)
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[l]).reshape(-1, keys.shape[l])
self.attention_weights = nn.functional.sofmax(--(queries - keys) self.w) *2 / 2, dim=1)
#values的形状为查询数,键-值对数
return torch.bmm(self.attention_weights_unsqueeze(1), values_unsqueeze(-1).reshape(-1))
3 训练
将训练数据集变换为键和值用于训练注意力模型。在带参数和注意力汇聚模型中,任何一个训练样本的输入都会和除自身以外的所有训练样本的键-值进行计算,从而得到其对应的预测输出。
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_trian, -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)
- sum().backward()
- trainer.step()
animator.add(epoch + 1, float(l.sum()))
训练带参数的注意力汇聚模型后可以发现,在尝试拟合带噪声的训练数据时,预测结果绘制的曲线不如之前非参数模型的平滑。
keys的形状为(n_test, n_train) 每一行包含相同的训练输入
keys = x_train.repeat(n_test, l)
value的形状为n_test, n_train
values = y_train.repeat(n_test, l)
y_hat = net(x_test, keys, values).unsqueeze(1).detach()
plot_kernel_reg(y_hat)
新的模型更不平滑,下面看一下输出结果的绘制图,与非参数的注意力汇聚模型相比,带参数的模型加入可学习的参数后,曲线的注意力权重较大的区域变得更不平滑。
小结
Nadaray-Watson核回归是具有注意力机制的机器学习范例
Nadaray-Watson 核回归的注意力汇聚时对训练数据输出的加权平均,从注意力的角度来看。
注意力汇聚可以分为非参数型核带参数型。
1745

被折叠的 条评论
为什么被折叠?



