动图轻松理解Self-Attention(自注意力机制)


前言


Self - Attention是Transformer中最核心的思想。我们在阅读Transformer论文的过程中,最难理解的可能就是自注意力机制实现的过程和繁杂的公式。本文在Illustrated: Self-Attention这篇文章的基础上,加上了自己对Self-Attention的理解,力求通俗易懂。希望大家批评指正。


一、Self-Attention是什么?

在理解Self-Attention之前,我们先通俗的解释一下什么是Attention。我们首先看一张图:

在这里插入图片描述


我们大部分人第一眼注意到的一定是东方明珠,但是这图其实还有旁边的楼,下面的汽车等等。这其实就是一种Attention,我们关注的是最主要的东西,而刻意“忽视”那些次要的东西。

我们再来讲解一个重要的概念,即query、key和value。这三个词翻译成中文就是查询、键、值,看到这中文的意思,还是迷迷糊糊的。我们来举个例子:小明想在b站搜索深度学习,他把深度学习四个字输入到搜索栏,按下搜索键。搜索引擎就会将他的查询query映射到数据库中相关的标签key,如吴恩达、神经网络等等,然后向小明展示最匹配的结果value

最后我们来说说Self-Attention。和Attention类似,他们都是一种注意力机制。不同的是Attention是source对target,输入的source和输出的target内容不同。例如英译中,输入英文,输出中文。而Self-Attention是source对source,是source内部元素之间或者target内部元素之间发生的Attention机制,也可以理解为Target=Source这种特殊情况下的注意力机制。

下面我们通过一个简单的例子,来了解Self-Attention的计算步骤。

二、计算步骤

1.定义input

在进行Self - Attention之前,我们首先定义3个1×4的input。
pytorch代码如下:

import torch
x = [
	[1, 0, 1, 0],  # input 1
	[0, 2, 0, 2],  # input 2
	[1, 1, 1, 1]   # input 3
	]
x = torch.tensor(x, dtype=torch.float32)

在这里插入图片描述


2.初始化权重

每个input和三个权重矩阵分别相乘会得到三个新的矩阵,分别是key(橙色),query(红色),value(紫色)。我们已经令input的shape为1×4,key、query、value的shape为1×3,因此可以推出与input相乘的权重矩阵的shape为4×3。
代码如下:

w_key = [
  [0, 0, 1],
  [1, 1, 0],
  [0, 1, 0],
  [1, 1, 0]
]  
w_query = [
  [1, 0, 1],
  [1, 0, 0],
  [0, 0, 1],
  [0, 1, 1]
]
w_value = [
  [0, 2, 0],
  [0, 3, 0],
  [1, 0, 3],
  [1, 1, 0]
]
w_key = torch.tensor(w_key, dtype=torch.float32)
w_query = torch.tensor(w_query, dtype=torch.float32)
w_value = torch.tensor(w_value, dtype=torch.float32)

print("Weights for key: \n", w_key)
print("Weights for query: \n", w_query)
print("Weights for value: \n", w_value)

3.计算key,query和value

现在我们计算key,query和value矩阵的值,计算的过程也很简单,运用矩阵乘法即可:
key = input * w_key; query = input * w_query; value = input * w_value;

keys = x @ w_key
querys = x @ w_query
values = x @ w_value

print("Keys: \n", keys)
# tensor([[0., 1., 1.],
#         [4., 4., 0.],
#         [2., 3., 1.]])

print("Querys: \n", querys)
# tensor([[1., 0., 2.],
#         [2., 2., 2.],
#         [2., 1., 3.]])
print("Values: \n", values)
# tensor([[1., 2., 3.],
#         [2., 8., 0.],
#         [2., 6., 3.]])

在这里插入图片描述


4.计算attention scores

例如:为了获得input1的注意力分数(attention scores),我们将input1的query(红色)与input1、2、3的key(橙色)的转置分别作点积,得到3个attention scores(蓝色)。 同理,我们也可以得到input2和input3的attention scores。

attn_scores = querys @ keys.T
print(attn_scores)

# tensor([[ 2.,  4.,  4.],  # attention scores from Query 1
#         [ 4., 16., 12.],  # attention scores from Query 2
#         [ 4., 12., 10.]]) # attention scores from Query 3

在这里插入图片描述


5.对attention scores作softmax

上一步得到了attention scores矩阵后,我们对attention scores矩阵作softmax计算。softmax的作用为归一化,使得其中各项相加后为1。这样做的好处是凸显矩阵中最大的值并抑制远低于最大值的其他分量。

from torch.nn.functional import softmax

attn_scores_softmax = softmax(attn_scores, dim=-1)
print(attn_scores_softmax)
# tensor([[6.3379e-02, 4.6831e-01, 4.6831e-01],
#         [6.0337e-06, 9.8201e-01, 1.7986e-02],
#         [2.9539e-04, 8.8054e-01, 1.1917e-01]])

attn_scores_softmax = [
  [0.0, 0.5, 0.5],
  [0.0, 1.0, 0.0],
  [0.0, 0.9, 0.1]
]
attn_scores_softmax = torch.tensor(attn_scores_softmax)
print(attn_scores_softmax)

在这里插入图片描述


6.将attention scores与values相乘

每个score(蓝色)乘以其对应的value(紫色)得到3个alignment vectors(黄色)。在本教程中,我们将它们称为weighted values(加权值)。

weighted_values = values[:,None] * attn_scores_softmax.T[:,:,None]
print(weighted_values)

在这里插入图片描述


7.对weighted values求和得到output

从图中可以看出,每个input生成3个weighed values(黄色),我们将这3个weighted values相加,得到output(深绿)。图中一共有3个input,所以最终生成3个output。

outputs = weighted_values.sum(dim=0)
print(outputs)

# tensor([[2.0000, 7.0000, 1.5000],  # Output 1
#         [2.0000, 8.0000, 0.0000],  # Output 2
#         [2.0000, 7.8000, 0.3000]]) # Output 3

在这里插入图片描述


三、回到论文

我们在Attention is all you need这篇论文中,可以看到这样一个公式:

A t t e n t i o n ( Q , K , V )   =   s o f t m a x ( Q K T d k ) V Attention(Q,K,V)\, =\, softmax(\frac {Q{K}^{T}} {\sqrt {{d}_{k}}})V Attention(Q,K,V)=softmax(dk QKT)V
其实,这个公式就是描述了我们上面计算的过程。我们首先将Query与Key的转置作点积,然后将结果除以 d k \sqrt {{d}_{k}}{}_{} dk ,再作softmax计算,最后将计算的结果与Value作矩阵乘法得到output。

这里有一个点,就是为什么要除以 d k \sqrt {{d}_{k}}{}_{} dk d k {d}_{k} dk表示的是词向量的维度。我们除以 d k \sqrt {{d}_{k}}{}_{} dk 是为了防止 Q K T Q{K}^{T} QKT值过大,导致softmax计算时上溢出(overflow)。其次,使用 d k {d}_{k} dk可以使 Q K T Q{K}^{T} QKT的结果满足期望为0,方差为1的分布。

四、为什么这样操作?

最后的问题是,为什么要像公式那样计算呢?

我们先从 Q K T Q{K}^{T} QKT看起,从几何角度看,点积是两个向量的长度与它们夹角余弦的积。如果两向量夹角为90°,那么结果为0,代表两个向量线性无关。如果两个向量夹角越小,两向量在方向上相关性也越强,结果也越大。点积反映了两个向量在方向上的相似度,结果越大越相似。

在这里插入图片描述

Q K T Q{K}^{T} QKT进行相似度的计算后,再使用softmax归一化。最后将归一化的结果与 V V V作乘法,计算的结果就是输入经过注意力机制加权求和之后的表示

参考文献

  1. 详解Transformer:https://zhuanlan.zhihu.com/p/48508221
  2. 超详细图解Self-Attention:https://zhuanlan.zhihu.com/p/410776234
  3. Attention机制与Self-Attention机制的区别:http://t.csdn.cn/GFTC2
  4. Illustrated: Self-Attention:https://towardsdatascience.com/illustrated-self-attention-2d627e33b20a
  5. self-attention为什么要除以根号d_k:http://t.csdn.cn/oaOIq
  • 34
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
注意力机制Self-Attention)是一种注意力机制,用于计算同一序列的表示。下面是一个使用自注意力机制的代码示例: ```python import torch import torch.nn as nn class SelfAttention(nn.Module): def __init__(self, input_dim): super(SelfAttention, self).__init__() self.query = nn.Linear(input_dim, input_dim) self.key = nn.Linear(input_dim, input_dim) self.value = nn.Linear(input_dim, input_dim) self.softmax = nn.Softmax(dim=-1) def forward(self, x): q = self.query(x) k = self.key(x) v = self.value(x) scores = torch.matmul(q, k.transpose(-2, -1)) attention_weights = self.softmax(scores) output = torch.matmul(attention_weights, v) return output # 使用自注意力机制 input_dim = 512 seq_length = 10 batch_size = 32 input_data = torch.randn(batch_size, seq_length, input_dim) self_attention = SelfAttention(input_dim) output = self_attention(input_data) print(output.shape) # 输出:torch.Size([32, 10, 512]) ``` 这段代码定义了一个名为`SelfAttention`的自注意力机制模块。在`forward`方法中,输入`x`经过线性变换得到查询(query)、键(key)和值(value)的表示。然后,通过计算查询和键的点积得到注意力分数,再经过softmax函数得到注意力权重。最后,将注意力权重与值相乘得到输出。 在示例中,我们使用了一个随机生成的输入数据`input_data`,维度为(batch_size, seq_length, input_dim),其中`batch_size`表示批次大小,`seq_length`表示序列长度,`input_dim`表示输入维度。通过调用`SelfAttention`模块,我们可以得到输出`output`,其维度为(batch_size, seq_length, input_dim)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值