注意力机制Attention

学习资料推荐:

强推!!!深度学习中的注意力机制

目前主流的attention方法都有哪些? - 知乎

注意力机制入门_大泽之国-CSDN博客_注意力机制有哪些

attention各种形式总结_向着星辰大海-CSDN博客_attention

nlp中的Attention注意力机制+Transformer详解 - 知乎

详解Transformer中Self-Attention以及Multi-Head Attention_霹雳吧啦Wz-CSDN博客

注意力机制介绍

       注意力机制(Attention Mechanism)是机器学习中的一种数据处理方法,广泛应用在自然语言处理、图像识别及语音识别等各种不同类型的机器学习任务中。注意力机制本质上与人类对外界事物的观察机制相似。通常来说,人们在观察外界事物的时候,首先会比较关注比较倾向于观察事物某些重要的局部信息,然后再把不同区域的信息组合起来,从而形成一个对被观察事物的整体印象。Attention机制最先应用在自然语言处理方面,主要是为了改进文本之间的编码方式,通过编码-解码之后能学习到更好的序列信息。可以参考一篇具有划时代意义的论文:Attention is all you need

注意力计算规则:

它需要三个指定的输入Q(query),K(key),V(value),然后通过计算公式得到注意力的结果,这个结果代表query在key和value作用下的注意力表示.当输入的Q=K=V时,称作自注意力计算规则。

常见的注意力计算规则:

1.将Q,K进行纵轴拼接,做一次线性变化,再使用softmax处理获得结果最后与V做张量乘法。

2.将Q,K进行纵轴拼接,做一次线性变化后再使用tanh函数激活,然后再进行内部求和,最后使用softmax处理获得结果再与V做张量乘法.

3.将Q与K的转置做点积运算,然后除以一个缩放系数再使用softmax处理获得结果最后与V做张量乘法。

说明:当注意力权重矩阵和V都是三维张量且第一维代表为batch条数时, 则做bmm运算.bmm是一种特殊的张量乘法运算。

bmm运算演示:

# 如果参数1形状是(b × n × m), 参数2形状是(b × m × p), 则输出为(b × n × p)
>>> input = torch.randn(10, 3, 4)
>>> mat2 = torch.randn(10, 4, 5)
>>> res = torch.bmm(input, mat2)
>>> res.size()
torch.Size([10, 3, 5])

相似度计算方式

计算query和某个key的分数(相似度),常用方法有:

1)点乘:最简单的方法, 

2)矩阵相乘: 

3)cos相似度: 

4)串联方式:把q和k拼接起来, 

5)用多层感知机也可以: 

注意力机制的作用

在解码器端的注意力机制:能够根据模型目标有效的聚焦编码器的输出结果,当其作为解码器的输入时提升效果,改善以往编码器输出是单一定长张量,无法存储过多信息的情况。

在编码器端的注意力机制:主要解决表征问题,相当于特征提取过程,得到输入的注意力表示。一般使用自注意力(self-attention)。

注意力机制实现步骤

第一步:根据注意力计算规则,对Q,K,V进行相应的计算

第二步:根据第一步采用的计算方法,如果是拼接方法,则需要将Q与第二步的计算结果再进行拼接,如果是转置点积一般是自注意力,Q与V相同,则不需要进行与Q的拼接

第三步:最后为了使整个attention机制按照指定尺寸输出,使用线性层作用在第二步的结果上做个线性变换,得到最终对Q的注意力表示。

常见注意力机制的代码分析:

import torch
import torch.nn as nn
import torch.nn.functional as F

class Attn(nn.Module):
    def __init__(self, query_size, key_size, value_size1, value_size2, output_size):
        """初始化函数中的参数有5个, query_size代表query的最后一维大小
           key_size代表key的最后一维大小, value_size1代表value的导数第二维大小, 
           value = (1, value_size1, value_size2)
           value_size2代表value的倒数第一维大小, output_size输出的最后一维大小"""
        super(Attn, self).__init__()
        # 将以下参数传入类中
        self.query_size = query_size
        self.key_size = key_size
        self.value_size1 = value_size1
        self.value_size2 = value_size2
        self.output_size = output_size

        # 初始化注意力机制实现第一步中需要的线性层.
        self.attn = nn.Linear(self.query_size + self.key_size, value_size1)

        # 初始化注意力机制实现第三步中需要的线性层.
        self.attn_combine = nn.Linear(self.query_size + value_size2, output_size)


    def forward(self, Q, K, V):
        """forward函数的输入参数有三个, 分别是Q, K, V, 根据模型训练常识, 输入给Attion机制的
           张量一般情况都是三维张量, 因此这里也假设Q, K, V都是三维张量"""

        # 第一步, 按照计算规则进行计算, 
        # 我们采用常见的第一种计算规则
        # 将Q,K进行纵轴拼接, 做一次线性变化, 最后使用softmax处理获得结果
        attn_weights = F.softmax(
            self.attn(torch.cat((Q[0], K[0]), 1)), dim=1)

        # 然后进行第一步的后半部分, 将得到的权重矩阵与V做矩阵乘法计算, 
        # 当二者都是三维张量且第一维代表为batch条数时, 则做bmm运算
        attn_applied = torch.bmm(attn_weights.unsqueeze(0), V)

        # 之后进行第二步, 通过取[0]是用来降维, 根据第一步采用的计算方法, 
        # 需要将Q与第一步的计算结果再进行拼接
        output = torch.cat((Q[0], attn_applied[0]), 1)

        # 最后是第三步, 使用线性层作用在第三步的结果上做一个线性变换并扩展维度,得到输出
        # 因为要保证输出也是三维张量, 因此使用unsqueeze(0)扩展维度
        output = self.attn_combine(output).unsqueeze(0)
        return output, attn_weights

调用:

query_size = 32
key_size = 32
value_size1 = 32
value_size2 = 64
output_size = 64
attn = Attn(query_size, key_size, value_size1, value_size2, output_size)
Q = torch.randn(1,1,32)
K = torch.randn(1,1,32)
V = torch.randn(1,32,64)
out = attn(Q, K ,V)
print(out[0])
print(out[1])

输出效果:

tensor([[[ 0.4477, -0.0500, -0.2277, -0.3168, -8.4096, -0.5982, 0.1548,
-8.8771, -8.0951. 8.1833. 8.3128. 8.1260, 8.4420. 8.8495.
-0.7774, -0.0995, 0.2629, 0.4957, 1.0922, 0.1428, 0.3024.
-0.2646, -0.0265, 0.0632, 0.3951, 0.1583, 0.1130, 0.5500,
-0.1887, -0.2816, -0.3800, -0.5741, 0.1342, 0.0244, -0.2217,
0.1544, 0.1865, -0.2019, 0.4090, -0.4762, 0.3677, -0.2553,
-0.5199, 0.2290, -0.4407, 0.0663, -8.0182, -8.2168, 0.0913,
-0.2340, 0.1924, -0.3687, 0.1508, 0.3618, -0.0113, 0.2864.
-0.1929, -0.6821, 0.0951, 0.1335, 0.3560, -0.3215
,0.6461,
0.1532]]],grad_fn=)
tensor([[0.0395, 0.0342, 0.0200, 0.0471, 0.0177, 0.0209, 0.0244, 0.0465, 0.0346,
0.0378, 0.0282, 0.0214, 0.0135, 0.0419, 0.0926, 0.0123, 0.0177, 0.0187,
0.0166, 0.8225, 0.0234, 0.0284, 0.0151, 0.0239, 0.0132, 0.0439, 0.0507,
0.0419, 8.0352, 8.0392, 8.0546, 0.0224]], grad_fn=)

以传统机器翻译为例说明为什么需要Attention

在了解Attention之前,首先应该了解为什么我们需要注意力机制。我们以传统的机器翻译为例子来说明为什么我们需要Attention。

传统的机器翻译,也称机器翻译(Neural machine translation),它是由encoder和decoder两个板块组成。其中Encoder和Decoder都是一个RNN,也可以是LSTM。不熟悉RNN是如何工作的读者,请参考RNN原理。假如现在想要将‘我是一个学生。’翻译成英文‘I am a student.’,传统的机器翻译是如何操作的呢?

在将中文 ‘我是一个学生’ 输入到encoder之前,应首先应该使用一些embedding技术将每一个词语表示成一个向量。encoder的工作原理和RNN类似,将词向量输入到Encoder中之后,我们将最后一个hidden state的输出结果作为encoder的输出,称之为context。Context可以理解成是encoder对当前输入句子的理解。之后将context输入进decoder中,然后每一个decoder中的hidden state的输出就是decoder 所预测的当前位子的单词。从encoder到decoder的过程中,encoder中的第一个hidden state 是随机初始化的且在encoder中我们只在乎它的最后一个hidden state的输出,但是在decoder中,它的初始hidden state 是encoder的输出,且我们关心每一个decoder中的hidden state 的输出。
传统机器翻译的过程可以用下图来表示:
注意力机制Attention_第1张图片

图中的END,是一个结束标志,意味着输入已经结束。从上面的叙述中可以看到,decoder的输出取决于encoder最后一个hidden state 的输出,当输入句子很长的时候,前面的信息可能不能很好的被encoder记录。且decoder在输出的时候,不知道当前位置对应着输入的哪一个位置。此外,就算是将encoder 中所有的hidden state 全部给decoder,仍然存在问题,因为两种语言之间单词之间的位置可能没有一一对应的关系,比如中文的 ‘我是一个学生’ 是5个词翻译成英文之后就只有4个词了。还有一些语言的语法位置也不是一一对应的。我们希望能有一种方式可以让模型关注输入的相关部分。比如还是以 ‘ 我是一个学生。’为例,我们希望模型可以在翻译student的时候,更加的关注 ‘学生’这个词而不是其他位子的词。这种需求下,提出Attention技术。

Attention的基本原理

Attention 的大致过程是这样的。和传统机器翻译不同的是,Attention需要encoder中所有的hidden states的信息都传入decoder中,若encoder中有N个hidden states,则需要将这N个hidden states 的信息全部给decoder。将所有信息传入decoder之前,我们需要为N个hidden states 分别设置一个权重(之后会详细解释如何求得权重),之后将每一个hidden state 根据设置权重加权求和,再将所有加权求和之后的 hidden states 输入到decoder中。

假设现在decoder正在预测句子中的第i个单词,则将decoder中的第i个hidden state 与 每一个encoder的hidden state 做计算,得到一组‘得分’(注意‘’得分‘’是一个向量且长度应该与输入decoder中的hidden states 数量一致),每一个‘得分’代表了模型在预测当前位置的单词时的注意力,得分越高,模型对其的注意力也就越大。然后使用softmax将这个‘得分’向量变成一个概率分布,将其结果作为权重与对应的hidden state做加权求和,将得到的结果与当前时刻decoder的hidden state 相加,作为下一个decoder hidden layer的输入。

上述步骤用示意图进行表示:
注意力机制Attention_第2张图片
那么Attention技术中的权重 是如何求得的呢?
首先先了解所谓的 ‘得分’ 是如何求得的,这里使用Luong的定义:

在这里插入图片描述

其中ht​是第t个时刻decoder的hidden state,而 hs​ˉ​表示的是encoder的hidden states,W是一个需要学习的矩阵,且在整个过程中,都使用同一个W, 在求得了得分之后我们就可以求得Attention的权重了:

注意力机制Attention_第3张图片


然后再将权重与encoder中的hidden states 相乘求得 context vector(也就是图中的C1,C2):

注意力机制Attention_第4张图片

之后就可以计算Attention vector了:

注意力机制Attention_第5张图片

上述方程阐述的是将ct​与ht​结合的过程,对应图中C1 与 H7 和 C2 与 H8相结合的过程。从上述的四个式子中我们可以发现decoder中的hidden state 被被使用了两次,第一次是使用在了求权重 ats​中,第二次使用在了与ct​结合生成at​的步骤中。

自注意力机制(Self-Attention)

从上述的阐述中可以了解到 Attention的产生是依赖于一个权重,它告诉了模型哪些词需要重视,哪些词不太需要重视。我们也可以发现,这个权重的产生是需要encoder的输出和decoder中t时刻hidden state 来产生的。那么所谓的自注意力机制是什么?了解自注意力机制之前,首先先简单了解一下Transformer网络,它也是基于机器翻译推出的,最先出现在论文《Attention is all you need》中,这篇论文提到的是去掉RNN网络,只使用Self-Attention技术,会使网络训练得更快。

Transformer 也是用多个encoder 和 decoder 组合而成的。下图表示的是一个encoder和一个decoder的结构:

注意力机制Attention_第6张图片

从上图可以发现Encoder中包含着两层分别是Self-attention层和一个Feed forward层,decoder中包含着三层,分别是self-attention, encoder-decoder Attention 和 Feed forward 层。 其中所谓的encoder-decoder Attention就和先前讲到的Attention机制一样,需要同时使用encoder和decoder的信息来生成Attention。

在Transformer的encoder结构如下图所示
注意力机制Attention_第7张图片
这个encoder的结构并不太复杂,总的来说就是将上一层的输入xi​;i={1,2,3…},输入到self-attention层中,然后输出一个对应的向量zi​ 并将 每一个zi​ 输入到一个单独的 Feed forward 网络中去,得到对应的输出 ri​,之后再将 ri​ 输入到下一个Self-Attention层中,以此类推。

从上述过程中可以看出,不同的输入唯一发生信息交换的地方就是在self-attention层中。所以self-attention的产生只是依赖于多个输入数据自己产生的,而不是像Attention那样需要encoder和decoder的信息。这也是为什么它叫做self-attention的原因。

那么在self-attention层中到底发生了什么呢?

注意力机制Attention_第8张图片

注意力机制Attention_第9张图片

self-attention的计算过程如下:

  1. 将输入单词转化成嵌入向量;
  2. 根据嵌入向量得到q,k,v三个向量,分别是query 向量,key向量和value向量
  3. 为每个向量计算一个score:score=q×v;
  4. 为了梯度的稳定,Transformer使用了score归一化,即除以sqrt(dk);
  5. 对score施以softmax激活函数;
  6. softmax点乘Value值v,得到加权的每个输入向量的评分v;
  7. 相加之后得到最终的输出结果z。
  8. 计算

注意力计算公式为:

注意力机制Attention_第10张图片

自注意力模型(self-Attention model)中,通常使用缩放点积来作为注意力打分函数,输出向量序列可以写为:

注意力机制Attention_第11张图片

总结

Attention Mechanism可以帮助模型对输入的X每个部分赋予不同的权重,抽取出更加关键及重要的信息,使模型做出更加准确的判断,同时不会对模型的计算和存储带来更大的开销,这也是Attention Mechanism应用如此广泛的原因。

总的来说,注意力机制可分为两种:一种是软注意力(soft attention),另一种则是强注意力(hard attention)。

软注意力(soft attention)与强注意力(hard attention)的不同之处在于:

  • 软注意力更关注区域或者通道,而且软注意力是确定性的注意力,学习完成后直接可以通过网络生成,最关键的地方是软注意力是可微的,这是一个非常重要的地方。可以微分的注意力就可以通过神经网络算出梯度并且前向传播和后向反馈来学习得到注意力的权重。 在计算机视觉中,很多领域的相关工作(例如,分类、检测、分割、生成模型、视频处理等)都在使用Soft Attention,典型代表:SENet、SKNet。

  • 首先强注意力是更加关注点,也就是图像中的每个点都有可能延伸出注意力,同时强注意力是一个随机的预测过程,更强调动态变化。当然,最关键是强注意力是一个不可微的注意力,训练过程往往是通过增强学习(reinforcement learning) 来完成的。

本文记录了Attention以及Self-Attention的基本原理,以及他们是如何做到聚焦输入的局部信息的。Attention的产生需要encoder与decoder的信息结合,而self-attention的产生是输入经过一系列的复杂矩阵运算得到的结构。Self-attention技术可以不用在依赖于RNN。使得训练更加高效。

CV中的注意力机制

近几年来,深度学习与视觉注意力机制结合的研究工作,大多数是集中于使用掩码(mask)来形成注意力机制。掩码的原理在于通过另一层新的权重,将图片数据中关键的特征标识出来,通过学习训练,让深度神经网络学到每一张新图片中需要关注的区域,也就形成了注意力

计算机视觉中的注意力机制的基本思想是让模型学会专注,把注意力集中在重要的信息上而忽视不重要的信息。

attention机制的本质就是利用相关特征图学习权重分布,再用学出来的权重施加在原特征图之上最后进行加权求和。不过施加权重的方式略有差别,大致总结为如下四点:

  • 这个加权可以是保留所有分量均做加权(即soft attention);也可以是在分布中以某种采样策略选取部分分量(即hard attention),此时常用RL来做。
  • 加权可以作用在空间尺度上,给不同空间区域加权
  • 加权可以作用在Channel尺度上,给不同通道特征加权
  • 加权可以作用在不同时刻历史特征上,结合循环结构添加权重,例如机器翻译,或者视频相关的工作。

为了更清楚地介绍计算机视觉中的注意力机制,通常将注意力机制中的模型结构分为三大注意力域来分析。主要是:空间域(spatial domain),通道域(channel domain),混合域(mixed domain)。

  1. 空间域——将图片中的的空间域信息做对应的空间变换,从而能将关键的信息提取出来。对空间进行掩码的生成,进行打分,代表是Spatial Attention Module。

  2. 通道域——类似于给每个通道上的信号都增加一个权重,来代表该通道与关键信息的相关度的话,这个权重越大,则表示相关度越高。对通道生成掩码mask,进行打分,代表是senet, Channel Attention Module。

  3. 混合域——空间域的注意力是忽略了通道域中的信息,将每个通道中的图片特征同等处理,这种做法会将空间域变换方法局限在原始图片特征提取阶段,应用在神经网络层其他层的可解释性不强。

参考:计算机视觉中的注意力机制 - 知乎

未完待续!!!!

你可能感兴趣的:(深度学习,深度学习,神经网络,attention,注意力机制)