从零开始的注意力机制

本文是来自翻译Jason Brownlee PhD的文章Machine Learning Mastery

从零开始的注意力机制

引入注意力机制是为了提高用于机器翻译的编码器-解码器模型的性能。注意力机制背后的想法是允许解码器以灵活的方式利用输入序列中最相关的部分,通过所有编码输入向量的加权组合,最相关的向量被赋予最高的权重。
在本教程中,你将了解注意力机制及其实现。
完成本教程后,你将了解:
1、注意机制如何使用所有编码器隐藏状态的加权和来灵活地将解码器的注意力集中到输入序列的最相关部分。
2、如何将注意力机制推广到信息不一定以顺序方式相关的任务中。
3、如何使用Numpy和SciPy在Python中实现一般注意力机制。

教程概述

本教程分为三部分:
1、注意力机制
2、一般注意力机制
3、NumPy和SciPy的一般注意力机制

注意力机制

注意机制是由Bahdanau等人引入的。(2014),解决使用固定长度编码向量出现的瓶颈问题,其中解码器对输入提供的信息的访问有限。这被认为对于长序列和/或复杂序列尤其成问题,在这种情况下,它们的表示的维度将被迫与更短或更简单的序列相同。
我们已经看到Bahdanau等人的注意力机制分为对齐分数、权重和上下文向量的逐步计算:

  • 1、Alignment scores:对齐模型采用编码的隐藏状态,hi,以及之前的解码器输出,S_(t-1),计算分数,e_(t,i),表示输入序列的元素与当前输出位置的对齐程度,t,对齐模型由一个函数表示,a(.),可以通过前馈神经网络实现:
    在这里插入图片描述
  • 2、Weights:权重,α_(t,i),是通过对先前计算的Alignment scores应用softmax操作来计算的:
    在这里插入图片描述
  • 3、Context vector:Ct,在每个时间步被送入到解码器。它由所有的加权和计算,T,编码器隐藏状态:
    在这里插入图片描述
    然而,注意力机制可以重新制定为通用形式,可以应用于任何序列到序列(seq2seq)任务,其中信息不一定以顺序方式相关。

一般注意力机制

一般的注意力机制使用三个主要组件,即查询Q、钥匙K、值V
如果我们必须将这三个组件与Bahdanau等人提出的注意力机制进行比较,那么查询将类似于之前的解码器输出,S_(t-1),虽然这些值类似于编码输入,hi,在Bahdanau注意力机制中,键和值是同一个向量。
一般注意力机制执行以下计算:
1、每个查询向量,q = S_(t-1),与密码数据库匹配以计算分值。该匹配操作被计算为所考虑的特定查询与每个键向量的点积,ki:
在这里插入图片描述
2、分数通过softmax操作来生成权重:
在这里插入图片描述
3、然后通过值向量的加权和来计算广义注意力,Vki,其中每个值向量与相应的键配对:
在这里插入图片描述
在机器翻译的上下文中,输入句子中的每个单词都将归属于自己的查询、键和值向量。这些向量是通过将所考虑的特定单词的编码器表示与训练期间生成的三个不同权重矩阵相乘来生成的。
本质上,当广义注意力机制被呈现为一个词序列时,它会获取归因于序列中某个特定词的查询向量,并根据数据库中的每个键对其进行评分。这样做时,它会捕捉到所考虑的词与序列中的其他词之间的关系。然后它根据注意力权重(根据分数计算)对值进行缩放,以便将注意力集中在与查询相关的词上。这样做时,它会为正在考虑的单词产生注意力输出。

NumPy和SciPy的一般注意力机制

在本节中,我们将探索如何使用Python中的NumPy和SciPy库来实现一般的注意力机制。
为简单起见,我们将首先计算四个序列中第一个单词的注意力。然后我们将概况代码以计算矩阵形式的所有四个单词的注意力输出。
因此,让我们首先定义我们将计算注意力的四个不同单词的词嵌入。在实际实践中,这些词嵌入本来是由编码器生成的,但是对于这个特定的例子,我们将手动定义它们。

import numpy as np

# encoder representations of four different words
word_1 = np.array([1, 0, 0])
word_2 = np.array([0, 1, 0])
word_3 = np.array([1, 1, 0])
word_4 = np.array([0, 0, 1])

下一步生成权重矩阵,我们最终将其与词嵌入相乘以生成查询、键和值。在这里,我们将随机生成这些权重矩阵,但在实际实践中,这些事在训练期间学习的。

# generating the weight matrices
np.random.seed(42) # to allow us to reproduce the same attention values
W_Q = np.random.randint(3, size=(3, 3))
W_K = np.random.randint(3, size=(3, 3))
W_V = np.random.randint(3, size=(3, 3))

注意每个矩阵的行数如何等于词嵌入的维数,以允许我们执行矩阵乘法。
随后,通过将每个词嵌入乘以每个权重矩阵来生成每个词的查询、键和值向量。

# generating the queries, keys and values
query_1 = word_1 @ W_Q
key_1 = word_1 @ W_K
value_1 = word_1 @ W_V
 
query_2 = word_2 @ W_Q
key_2 = word_2 @ W_K
value_2 = word_2 @ W_V
 
query_3 = word_3 @ W_Q
key_3 = word_3 @ W_K
value_3 = word_3 @ W_V
 
query_4 = word_4 @ W_Q
key_4 = word_4 @ W_K
value_4 = word_4 @ W_V

暂时只考虑第一个单词,下一步使用点积运算将其查询向量与所有关键向量进行评分。

# scoring the first query vector against all key vectors
scores = np.array([np.dot(query_1, key_1), np.dot(query_1, key_2), np.dot(query_1, key_3), np.dot(query_1, key_4)])
scores

分值随后通过softmax操作来生成权重。在这样做之前,通常的做法是将得分值除以关键向量的维数的平方根,以保持梯度稳定。

from scipy.special import softmax
# computing the weights by a softmax operation
weights = softmax(scores / key_1.shape[0] ** 0.5)
weights

最后,注意力输出是通过所有四个值向量的加权和来计算的。

# computing the attention by a weighted sum of the value vectors
attention = (weights[0] * value_1) + (weights[1] * value_2) + (weights[2] * value_3) + (weights[3] * value_4)
 
print(attention)
#结果:
[0.98522025 1.74174051 0.75652026]

为了更快的处理,可以以矩阵形式实现相同的计算,一次性为所有四个单词生成一个注意力输出:

import numpy as np
from scipy.special import softmax
 
# encoder representations of four different words
word_1 = np.array([1, 0, 0])
word_2 = np.array([0, 1, 0])
word_3 = np.array([1, 1, 0])
word_4 = np.array([0, 0, 1])
 
# stacking the word embeddings into a single array
words = np.array([word_1, word_2, word_3, word_4])
 
# generating the weight matrices
np.random.seed(42)
W_Q = np.random.randint(3, size=(3, 3))
W_K = np.random.randint(3, size=(3, 3))
W_V = np.random.randint(3, size=(3, 3))
 
# generating the queries, keys and values
Q = words @ W_Q
K = words @ W_K
V = words @ W_V
 
# scoring the query vectors against all key vectors
scores = Q @ K.transpose()
 
# computing the weights by a softmax operation
weights = softmax(scores / K.shape[1] ** 0.5, axis=1)
 
# computing the attention by a weighted sum of the value vectors
attention = weights @ V
 
print(attention)
#结果:
[[0.98522025 1.74174051 0.75652026]
 [0.90965265 1.40965265 0.5       ]
 [0.99851226 1.75849334 0.75998108]
 [0.99560386 1.90407309 0.90846923]]

おすすめ

転載: blog.csdn.net/weixin_50918736/article/details/121317513