跳到主要内容

注意力机制

注意力机制解决了传统神经网络模型(如CNN和RNN)中需要固定输入大小的挑战。它们提供了一种灵活的方法来处理大小和内容各异的输入,例如长文本序列。这种灵活性是通过能够动态关注输入不同部分的机制来实现的。

数据库与查询类比

  • 数据库模型:数据库使用键和值,其中查询根据键检索值。这个概念类似于神经网络,其中查询从一组数据(键和值)中获取相关信息。
  • 数学公式:神经网络中的查询(qq)根据键的相似性来获取值: Attention(q,D)=i=1mα(q,ki)vi,\textrm{Attention}(\mathbf{q}, \mathcal{D}) = \sum_{i=1}^m \alpha(\mathbf{q}, \mathbf{k}_i) \mathbf{v}_i, 其中α\alpha是注意力权重,表示每个值vi\mathbf{v}_i基于查询q\mathbf{q}和键ki\mathbf{k}_i的重要性。

权重的归一化

  • Softmax 函数:归一化注意力权重,确保它们和为1且保持非负: α(q,ki)=exp(a(q,ki))jexp(a(q,kj)).\alpha(\mathbf{q}, \mathbf{k}_i) = \frac{\exp(a(\mathbf{q}, \mathbf{k}_i))}{\sum_j \exp(a(\mathbf{q}, \mathbf{k}_j))}.

基于相似性的注意力池化

  • Nadaraya-Watson 估计器:利用相似性核函数将查询与键关联起来,展示了现代注意力机制的先驱: f(q)=iviα(q,ki)jα(q,kj).f (\mathbf{q}) = \sum_i \mathbf{v}_i \frac{\alpha (\mathbf{q}, \mathbf{k}_i)}{\sum_j \alpha (\mathbf{q}, \mathbf{k}_j)}.
  • 常用核函数:高斯、Boxcar 和 Epanechikov 核函数展示了根据查询和键之间的距离计算注意力权重的不同方法。

注意力评分函数

  • 点积注意力:使用查询和键的点积来简化注意力权重的计算: a(q,ki)=qkid.a(\mathbf{q}, \mathbf{k}_i) = \frac{\mathbf{q}^\top \mathbf{k}_i}{\sqrt{d}}.
  • 加性注意力:适用于查询和键维度不同的情况,涉及变换和非线性函数的组合: a(q,k)=wvtanh(Wqq+Wkk).a(\mathbf{q}, \mathbf{k}) = \mathbf{w}_v^\top \textrm{tanh}(\mathbf{W}_q\mathbf{q} + \mathbf{W}_k \mathbf{k}).

Bahdanau 注意力机制

  • 动态上下文变量:在每个解码步骤更新上下文变量,使模型能够动态地关注输入序列的不同部分。
  • 数学公式:上下文变量ct\mathbf{c}_{t'}被计算为所有编码器状态的加权和,根据解码器的需要进行调整: ct=t=1Tα(st1,ht)ht.\mathbf{c}_{t'} = \sum_{t=1}^{T} \alpha(\mathbf{s}_{t' - 1}, \mathbf{h}_{t}) \mathbf{h}_{t}.

Seq2Seq 翻译中的注意力机制

PyTorch 关于 Seq2Seq 翻译的教程引入了注意力机制,以增强从法语到英语的翻译模型。以下是注意力如何应用的简明解释:

基本模型结构

  • Seq2Seq 框架: 由编码器和解码器组成,两者都使用 RNN 实现。编码器将输入序列处理成一个上下文向量,解码器使用该向量生成输出序列。

代码示例

class AttnDecoderRNN(nn.Module):
def __init__(self, hidden_size, output_size, dropout_p=0.1, max_length=MAX_LENGTH):
super(AttnDecoderRNN, self).__init__()
self.hidden_size = hidden_size
self.output_size = output_size
self.dropout_p = dropout_p
self.max_length = max_length

self.attn = nn.Linear(self.hidden_size * 2, self.max_length)
self.attn_combine = nn.Linear(self.hidden_size * 2, self.hidden_size)
self.dropout = nn.Dropout(self.dropout_p)
self.gru = nn.GRU(self.hidden_size, self.hidden_size)
self.out = nn.Linear(self.hidden_size, self.output_size)

def forward(self, input, hidden, encoder_outputs):
attn_weights = F.softmax(self.attn(torch.cat((input[0], hidden[0]), 1)), dim=1)
attn_applied = torch.bmm(attn_weights.unsqueeze(0), encoder_outputs.unsqueeze(0))

output = torch.cat((input[0], attn_applied[0]), 1)
output = self.attn_combine(output).unsqueeze(0)

output = F.relu(output)
output, hidden = self.gru(output, hidden)

output = F.log_softmax(self.out(output[0]), dim=1)
return output, hidden, attn_weights

参考资料与有用链接