Attention
复习一下上一篇文章,现有一组数据集,其中作为key, 作为value,对于新来的作为query,估计,就是attention的基本模型。对于一个窗口内的全部,可以通过加权的方式估计: 其中,就可以看作attention。注意到上一篇的注意力权重为: 可以单独把softmax的输入抽象出来,作为注意力评分函数,有: 后面各种attention只需要在注意力评分函数上面做文章即可。注意力评分函数输入向量和,输出一个实数,一个与多个求得的注意力评分通过softmax后得到注意力权重,与加权求和便可预测。
可以看出,注意力评分其实本质上可以看作一种相似度的度量,与越接近则评分越大。
Additive attention
对于query()和key()是不同维度矢量时,可以使用additive attention,通过参数化矩阵分别与和相乘变换到相同维度,然后便可求和(理解成做差也可以),最后通过与向量求积得到一个标量作为权重。给定和,注意力评分函数为: 其中,参数尺寸为:,乘起来就是
参数量为。矩阵相乘可以等价于偏置为0的单个Linear层,这也是常见的工程实现方式。
import torch
from torch import nn
class AdditiveAttention(nn.Module):
def __init__(self, q_size, k_size, h, dropout=0.5, **kwargs):
super().__init__(**kwargs)
self.W_q = nn.Linear(q_size, h, bias=False)
self.W_k = nn.Linear(k_size, h, bias=False)
self.w_v = nn.Linear(h, 1, bias=False)
self.dropout = nn.Dropout(dropout)
def forward(self, query, key, value):
# query: [batch_size, n_q, q_size]
# key: [batch_size, n_kv, k_size]
# value: [batch_size, n_kv, v_size]
query, key = self.W_q(query), self.W_k(key)
# query: [batch_size, n_q, h]
# key: [batch_size, n_kv, h]
features = torch.tanh(query.unsqueeze(2) + key.unsqueeze(1))
# features: [batch_size, n_q, n_kv, h] = [batch_size, n_q, 1, h] + [batch_size, 1, n_kv, h]
scores = self.w_v(features).squeeze(-1)
# scores: [b, n_q, n_kv]
self.attention = nn.functional.softmax(scores, dim=-1) # softmax on each key-value pair
# attention: [b, n_q, n_kv]
result = torch.bmm(self.dropout(self.attention), value)
# result: [batch_size, n_q, v_size]
return result
Scaled dot-product attention
如果和维度同时为,直接使用向量内积即可作为注意力评分函数,我们知道内积本质上就是余弦相似度。为了规范化方差,将内积除以即可,有: 使用矩阵批量计算,则对于 query: ,key: 和 value: ,注意力为: 这种注意力机制没有参数,运算过程也很简单,比较快,实现容易。
import torch
from torch import nn
class DotProductAttetnion(nn.Module):
def __init__(self, dropout=0.5, **kwargs):
super().__init__(**kwargs)
self.dropout = nn.Dropout(dropout)
def forward(self, query, key, value):
# query: [batch_size, n_q, d]
# key: [batch_size, n_kv, d]
# value: [batch_size, n_kv, v_size]
d = torch.tensor(query.shape[-1])
scores = torch.bmm(query, key.transpose(-1,-2)) / torch.sqrt(d)
# scores: [b, n_q, n_kv] = [b, n_q, d] @ [b, d, n_kv]
self.attention = nn.functional.softmax(scores, dim=-1) # softmax on each key-value pair
# attention: [b, n_q, n_kv]
result = torch.bmm(self.dropout(self.attention), value)
# result: [batch_size, n_q, v_size]
return result
推广
只要能构造出相同形状的注意力评分,要么参数化要么使用相似性度量,就可以构造出一种注意力机制。比方说使用参数化矩阵,设计如下注意力评分函数: 就可以用与内积注意力相似的计算方式,在和维度不同的情况下计算注意力了。