简化Transformers:使用你理解的单词进行最先进的NLP—第2部分—注意力篇

2023年10月07日 由 alex 发表 268 0

注意力


在Transformer的上下文中,Attention指的是一种机制,使模型能够在处理过程中专注于输入的相关部分。可以将其想象成一个手电筒,照亮句子中特定的部分,根据上下文给予不同程度的重要性。我认为例子比定义更有效,因为它们是一种思维难题,能帮助大脑填补差距并自行理解概念。


当面对句子“那个男人拿走了椅子并消失了”时,你自然而然地对句子的不同部分赋予不同程度的重要性(即注意力)。令人惊讶的是,如果我们去掉特定的词,句子的意思仍然基本保持不变:“男人拿走椅子消失了。”虽然这个版本的英语是破碎的,与原句相比,你仍然可以理解信息的本质。有趣的是,三个词(“The”,“the”和“and”)占据了句子中43%的词数,但对整体意义没有贡献。这个观察对于在柏林生活时接触过我惊人的德语的每一个柏林人来说可能是显而易见的(你可以选择学习德语或者选择快乐,这是你必须做出的决定),但对于机器学习模型来说则不那么明显。


过去,类似于RNN(循环神经网络)的先前结构面临着重大挑战:它们难以“记住”出现在输入序列远处的词语,通常超过20个词。正如你已经知道的,这些模型本质上依靠数学运算来处理数据。不幸的是,早期结构中使用的数学运算不足以充分地将单词表示有效地传递到序列的远期。


这种长期依赖的限制阻碍了RNN在长时间内保持上下文信息的能力,影响到了诸如语言翻译或情感分析等需要理解整个输入序列至关重要的任务。然而,具有注意机制和自注意力机制的Transformer更有效地解决了这个问题。它们可以在输入中有效地捕捉到远距离的依赖关系,使模型能够保持上下文和关联性,即使是在序列中出现得早得多的词语。结果,Transformer已经成为克服先前结构限制并显著提高各种自然语言处理任务性能的突破性解决方案。


为了创建像今天我们所遇到的先进聊天机器人这样的优秀产品,我们需要使模型能够区分高价值和低价值的词语,同时在输入的长距离上保留上下文信息。Transformer架构中引入的用于解决这些挑战的机制被称为注意力。


点积


模型如何在理论上区分不同单词的重要性?在分析一个句子时,我们的目标是识别彼此之间具有较强关系的单词。由于单词被表示为向量(由数字组成),我们需要一种测量数字相似性的方法。衡量向量相似性的数学术语是“点积”。它涉及到两个向量的元素相乘,并产生一个标量值(例如 2、16、-4.43),该值表示它们的相似性。机器学习基于各种数学运算,其中点积具有特殊重要性。


假设我们有5个单词的实际表示(嵌入):“florida”, “california”, “texas”, “politics” 和“truth”。由于嵌入只是数字,我们可以将它们投影到图形上。然而,由于高维度(用于表示单词的数字数量)的存在,这个范围可以轻松从100到1000以上,我们实际上无法直接将它们绘制出来。我们不能在二维的计算机/手机屏幕上绘制一个100维的向量。而且,人脑发现难以理解超过3维的事物。一个4维向量是什么样子的?


为了解决这个问题,我们使用主成分分析(PCA)这个技术,可以减少维度数量。通过应用PCA,我们可以将嵌入投影到二维空间(x,y坐标)。这种维度的降低帮助我们在图形上可视化数据。虽然由于降维而丢失了一些信息,但希望这些降维后的向量仍然保留了足够的与原始嵌入的相似性,使我们能够获得洞察和理解单词之间的关系。


这些数字是基于GloVe嵌入。


florida = [-2.40062016,  0.00478901]
california = [-2.54245794, -0.37579669]
texas = [-2.24764634, -0.12963368]
politics = [3.02004564,  2.88826688]
truth = [4.17067881, -2.38762552]


你可以注意到数字中可能存在某种模式,但我们将绘制这些数字以使生活更轻松。


8


在这个可视化中,我们可以看到五个二维向量(x,y坐标),代表着5个不同的单词。正如你所看到的,这个图表显示出一些单词与其他单词之间有着更密切的关联。


数学


将向量可视化的数学对应可以通过一个简单的方程来表达。如果你对数学没有特别的喜好,并且将Transformer架构描述为一个“简单的网络架构”,你可能会认为对于机器学习的人来说,这就是他们发疯的事情。这可能是真的,但在这种情况下不是这样,这是简单的。我来解释一下:


9


符号||a||表示向量“a”的大小,它表示原点(点0,0)与向量顶点之间的距离。计算向量大小的公式如下:


10


这个计算的结果是一个数字,比如4或12.4。


Theta (θ) 指的是向量之间的夹角(请参考可视化)。对于theta的余弦值,表示为cos(θ),它只是应用余弦函数到该角度得到的结果。


代码


使用GloVe算法,斯坦福大学的研究人员生成了实际单词的嵌入向量,正如我们之前讨论过的。虽然他们有自己创建这些嵌入向量的具体技术,但底层概念与我们在系列的前一部分中讨论的概念是相同的。作为示例,我选取了4个单词,将它们的维度减少到2,并将它们的向量显示为直接的x和y坐标。


为了使这个过程正常运行,下载GloVe嵌入向量是必要的先决条件。


import pandas as pd
path_to_glove_embds = 'glove.6B.100d.txt'
glove = pd.read_csv(path_to_glove_embds, sep=" ", header=None, index_col=0)
glove_embedding = {key: val.values for key, val in glove.T.items()}


words = ['florida', 'california', 'texas', 'politics', 'truth']
word_embeddings = [glove_embedding[word] for word in words]
print(word_embeddings[0]).shape # 100 numbers to represent each word.
---------------------
output:
(100,)


pca = PCA(n_components=2) # reduce dimensionality from 100 to 2.
word_embeddings_pca = pca.fit_transform(word_embeddings)


for i in range(5):
    print(word_embeddings_pca[i])
---------------------
output:
[-2.40062016  0.00478901] # florida
[-2.54245794 -0.37579669] # california
[-2.24764634 -0.12963368] # texas
[3.02004564 2.88826688] # politics
[ 4.17067881 -2.38762552] # truth


我们现在拥有所有5个词的真实表示。我们下一步是进行点乘计算。


向量大小:


import numpy as np
florida_vector = [-2.40062016,  0.00478901]
florida_vector_magnitude = np.linalg.norm(florida_vector)
print(florida_vector_magnitude)
---------------------
output:
2.4006249368060817 # The magnitude of the vector "florida" is 2.4.


两个相似向量之间的点乘积。


import numpy as np
florida_vector = [-2.40062016,  0.00478901]
texas_vector = [-2.24764634 -0.12963368]
print(np.dot(florida_vector, texas_vector))
---------------------
output:
5.395124299364358


两个不相似向量之间的点乘积。


import numpy as np
florida_vector = [-2.40062016,  0.00478901]
truth_vector = [4.17067881, -2.38762552]
print(np.dot(florida_vector, truth_vector))
---------------------
output:
-10.023649994662344


从点积计算中可以明显看出,它似乎捕捉并反映了不同概念之间的相似性理解。


缩放点积注意力


现在我们对点积有了了解,我们可以深入研究注意力机制。特别是自注意力机制。使用自注意力机制使模型能够确定每个单词的重要性,而不考虑其与该单词的“物理”距离。这使得模型能够根据每个单词的情境相关性做出明智的决策,从而实现更好的理解。


为了实现这个雄心勃勃的目标,我们创建了由可学习的参数(Q、K、V)组成的3个矩阵,分别称为查询、键和值矩阵。查询矩阵可以被视为一个包含用户查询或请求的单词的查询矩阵(例如,当你问chatGPT:“上帝今天下午5点可用吗?”时,这就是查询)。键矩阵包含序列中的所有其他单词。通过计算这些矩阵之间的点积,我们可以得到每个单词与我们当前正在考察的单词之间的相关性程度(例如,对查询的翻译或产生答案)。


值矩阵为序列中的每个单词提供“干净”的表示。为什么我把它称为“干净”,而其他两个矩阵以类似的方式形成呢?因为值矩阵保持其原始形式,在乘以另一个矩阵或通过某个值进行归一化后我们不再使用它。这种区别使得值矩阵与众不同,确保它保留了原始嵌入,不受额外计算或变换的影响。


这3个矩阵的大小都是word_embedding(512)。然而,它们被分为“头部”。在论文中,作者使用了8个头部,每个矩阵的大小为sequence_length乘以64。你可能会想为什么要用1/8的数据进行相同的操作8次,而不是一次性使用所有数据。这种方法背后的原理是,通过使用8组不同的可学习权重进行8次相同的操作,我们可以利用数据中的多样性。每个头部可以专注于输入中的特定方面,总体上这可以提高性能。


将Q和K相乘(点积),然后除以维度数的平方根进行归一化。我们通过Softmax函数对结果进行处理,然后将结果乘以矩阵V。


归一化的原因是Q和K是以某种随机方式生成的矩阵。它们的维度可能完全无关(独立),独立矩阵之间的乘法可能会产生非常大的数字,这可能会对学习产生影响,我将在本部分后面解释。


然后,我们使用一个名为Softmax的非线性变换,使所有数字在0和1之间,并且总和为1。结果类似于一个概率分布(因为有从0到1的数字,总和为1)。这些数字展示了序列中每个单词与其他单词的相关性。


最后,我们将结果乘以矩阵V,我们就得到了自注意力分数。


以下是自注意力的可视化。它就像教室里的一群朋友。有些人与某些人联系更紧密。有些人与任何人的联系都不太紧密。


11


整个自注意力层的计算是通过应用以下公式完成的:


12


计算过程如下:


1. 我们将 Q 与 K 转置(翻转)相乘。


2. 我们将结果除以矩阵 K 的维度的平方根。


3. 现在我们有了描述每个单词相互之间相似度的“注意力矩阵分数”。我们将每一行传递给 Softmax(非线性)变换。Softmax执行了三个有趣的关键步骤: 


a. 它将所有数字缩放到0和1之间。 


b. 它使所有数字之和为1。 


c. 它突出显示差距,使得稍微更重要的部分更加重要。


因此,我们现在可以轻松区分模型感知单词 x1 和 x2、x3、x4 等之间连接的不同程度。


4. 我们将分数与 V 矩阵相乘。这是自注意力操作的最终结果。


屏蔽


我们使用虚拟标记来处理句子中的特殊情况,例如句子中的第一个单词、最后一个单词等。其中一个标记被表示为 <PADDING>,表示没有实际数据,但我们需要在整个过程中保持一致的矩阵大小。为了确保模型理解这些是虚拟标记,并且在自注意力计算过程中不考虑它们,我们将这些标记表示为负无穷大(例如一个非常大的负数,如 -153513871339)。屏蔽值被添加到 Q 与 K 相乘的结果中。然后,Softmax将这些数字转换为0。这使我们能够在注意机制中有效地忽略虚拟标记,同时保持计算的完整性。


随机失活


在自注意力层之后,应用了一个随机失活操作。随机失活是机器学习中广泛使用的正则化技术。正则化的目的是在训练过程中对模型施加约束,使其难以过分依赖特定的输入细节。结果,模型可以更加稳健地学习并提高其泛化能力。具体实施方法是随机选择一些激活(来自不同层的数字)并将它们置为零。在同一层的每一次传递中,不同的激活将会被置为零,防止模型找到特定于其接收到的数据的解决方案。实质上,随机失活有助于增强模型处理多样输入的能力,并使模型难以针对数据中的特定模式进行调整。


跳跃连接


Transformer 架构中的另一个重要操作称为跳跃连接。


13


跳过连接是一种无需进行任何转换而传递输入的方法。比如,想象一下我向我的经理报告,然后他再向他的经理报告。即使我的报告意图纯粹是为了使报告更有用,但在另一个人(或者机器学习层)处理输入时,输入现在也会经过一些修改。在这个类比中,跳过连接就像是我直接向我的经理的经理报告。因此,高级经理通过我的经理(经过处理的数据)和直接来自我的报告(未经过处理的数据)都收到输入。然后,高级经理可以更好地做出决策。采用跳过连接的理由是解决可能存在的问题,如梯度消失,我将在下一部分解释。


添加和归一化


“Add & Norm”层执行加法和归一化操作。先从加法开始,因为它比较简单。基本上,我们将自我注意层的输出与原始输入(通过跳过连接接收)相加。这个加和是逐位进行的(每个数字与其相同位置的数字相加)。然后对结果进行归一化操作。


再次进行归一化的原因是,每一层都进行了许多计算。多次相乘数字可能会导致意想不到的情况。例如,如果我拿一个分数,比如0.3,与另一个分数0.9相乘,结果是0.27,比起起始值更小。如果我多次这样做,最终可能会得到非常接近于0的值。这可能会引发深度学习中的梯度消失问题。


相反,另一种现象称为梯度爆炸可能会发生,当非分数的数字与非分数相乘时,导致数值变得过大。结果,模型在学习过程中面临困难,因为权重和激活的巨大变化会导致不稳定和发散。


机器学习模型有点像小孩子,它们需要保护。保护这些模型免受数字过大或过小的影响的方法之一就是归一化。


数学


层归一化操作看起来很可怕,但实际上相对简单。


14


在层归一化操作中,我们对每个输入按照以下简单步骤进行处理:


1. 从输入中减去均值。


2. 除以方差的平方根,并加上一个“epsilon”(一个极小的数),用于避免被零除。


3. 将得到的得分乘以一个可学习的参数叫做gamma (γ)。


4. 加上另一个可学习的参数叫做beta (β)。


这些步骤确保均值接近于0,标准差接近于1。归一化过程可以增强训练的稳定性、速度和整体性能。


代码如下所示:


# x being the input.
(x - mean(x)) / sqrt(variance(x) + epsilon) * gamma + beta


总结


目前为止,我们对编码器的主要内部工作过程有了坚实的理解。此外,我们还探讨了跳连技术(Skip Connections),这是机器学习中一种纯技术(但重要)的技术,可以提高模型的学习能力。


15




文章来源:https://medium.com/towards-data-science/transformers-part-3-attention-7b95881714df
欢迎关注ATYUN官方公众号
商务合作及内容投稿请联系邮箱:bd@atyun.com
评论 登录
热门职位
Maluuba
20000~40000/月
Cisco
25000~30000/月 深圳市
PilotAILabs
30000~60000/年 深圳市
写评论取消
回复取消