1、Attention机制的研究进展
Attention机制最早是应用于图像领域,九几年就提出来的思想。在2014年,Google Mind团队发表的《Recurrent Models of Visual Attention》[2]论文使Attention机制开始火了起来,该论文提出在RNN模型上使用Attention机制来进行图像分类,结果取得了很好的性能。随后,在Bahdanau等人发表论文《Neural Machine Translation by Jointly Learning to Align and Translate》[8]中提出在机器翻译任务上使用Attention机制将翻译和对齐同时进行,他们的工作是第一个将Attention机制应用在NLP领域中的。接着,在Xu等人发表的论文《Show, Attend and Tell: Neural Image Caption Generation with Visual Attention》[10]中,成功的将Attention机制应用在Image Caption领域。从此,Attention机制就被广泛应用在基于RNN神经网络模型的各种深度学习任务中。随后,如何在CNN中使用Attention机制也成为研究的热点。2017年,Google发表的论文《Attention is all you need》[12]中提出在机器翻译上大量使用自注意力(self-attention)机制来学习文本表示。图1展示了Attention机制研究进展的大概趋势。
2、基本原理
注意力机制认为,网络中每层不同的特征的重要性不同,后面的层应该更注重其中重要的信息,抑制不重要的信息。
3、Encoder-Decoder框架
目前大多数注意力模型都附着在Encoder-Decoder框架下,其实注意力机制也可以作为一种通用的思想。
Encoder-Decoder框架可以看作是一种深度学习领域的研究模式,应用场景异常广泛。图2是文本处理领域里常用的Encoder-Decoder框架最抽象的一种表示。
Encoder就是对输入句子Source进行编码,将输入句子通过非线性变换转化为中概念语义表示 C C C,公式如下:
C = F ( x 1 , x 2 , . . , x m ) (3-1) C=F(x_1,x_2,..,x_m)\tag{3-1} C=F(x1,x2,..,xm)(3-1)
解码器Decoder的任务是根据句子Source的中间于一表示 C C C和之前已经生成的历史信息 y 1 , y 2 , . . . , y i − 1 y_1,y_2,...,y_{i-1} y1,y2,...,yi−1来生成 i i i时刻要生成的单词 y i y_i yi,公式如下:
y i = G ( C , y 1 , y 2 , . . . , y i − 1 ) (3-2) y_i=G(C,y_1,y2,...,y_{i-1})\tag{3-2} yi=G(C,y1,y2,...,yi−1)(3-2)
图2中展示的Encoder-Decoder框架是没有体现出注意力模型的,所以可以把它看作是注意力不集中的分心模型。请观察下目标句子Target中每个单词的生成过程如下:
y 1 = f ( C ) (3-3) y_1=f(C)\tag{3-3} y1=f(C)(3-3)
y 2 = f ( C , y 1 ) (3-4) y_2=f(C,y_1)\tag{3-4} y2=f(C,y1)(3-4)
y 3 = f ( C , y 1 , y 2 ) (3-5) y_3=f(C,y_1,y_2)\tag{3-5} y3=f(C,y1,y2)(3-5)
其中 f f f是Decoder的非线性变换函数。我们可以看出无论生成哪个单词,他们使用的输入句子Source的语义编码C都是一样的。句子Source中任意单词对生成某个目标单词 y i y_i yi来说影响力都是相同的。
4、软注意力机制
目标句子中每个单词都应该学会其对应的Source中单词的注意力分配改了信息。这意味着在生成每个单词
y
i
y_i
yi的时候,原先都是相同的中间语义表示
C
C
C会被替换成根据当前生成单词而不断变化的
C
i
C_i
Ci。理解Attention模型的关键就是这里,即由固定的中间语义表示
C
C
C换成了根据当前输出单词来调整成加入注意力模型的变化的
C
i
C_i
Ci。增加了注意力模型的Encoder-Decoder框架理解起来如图3所示。
即生成目标句子单词的过程成了下面的形式:
y 1 = f ( C 1 ) (4-1) y_1=f(C_1)\tag{4-1} y1=f(C1)(4-1)
y 2 = f ( C 2 , y 1 ) (4-2) y_2=f(C_2,y_1)\tag{4-2} y2=f(C2,y1)(4-2)
y 3 = f ( C 3 , y 1 , y 2 ) (4-3) y_3=f(C_3,y_1,y_2)\tag{4-3} y3=f(C3,y1,y2)(4-3)
而每个 C i C_i Ci可能对应着不同的Source句子单词的注意力分配概率分布。
5、注意力机制的本质思想
我们可以这样看注意力机制:将数据源(Source)中的构成元素想象成是由一系列的<Key,Value>数据对组成,此时给定目标中某个元素的Query,通过计算Query和各个Key的相似性或者相关性,得到每个Key对应Value的权重系数,然后对Value进行加权求和,即得到了最终的注意力数值。所以本质上注意力机制是对数据源(Source)中元素的Value值进行加权求和,而Query和Key用来计算对应Value的权重系数。
即可以将其本质思想改写为如下公式:
A
t
t
e
n
t
i
o
n
(
Q
u
e
r
y
,
S
o
u
r
c
e
)
=
∑
i
=
1
L
x
S
i
m
i
l
a
r
i
t
y
(
Q
u
e
r
y
,
K
e
y
i
)
∗
V
a
l
u
e
i
(5-1)
Attention(Query,Source)=\sum_{i=1}^{L_x}{Similarity(Query,Key_i) \ast Value_i} \tag{5-1}
Attention(Query,Source)=i=1∑LxSimilarity(Query,Keyi)∗Valuei(5-1)
其中, L x = ∣ ∣ S o u r c e ∣ ∣ L_x=||Source|| Lx=∣∣Source∣∣代表 S o u r c e Source Source的长度,公式含义即如上所述。
6、注意力机制的具体计算过程
对目前大多数方法进行抽象,可以将其归纳为两个过程
根据Query和Key计算权重系数,可以细分为两个阶段:
第一个阶段根据Query和Key计算两者的相似性或者相关性;
在第一个阶段,可以引入不同的函数和计算机制,根据Query和某个
K
e
y
i
Key_i
Keyi,计算两者的相似性或者相关性,最常见的方法包括:求两者的向量点积、求两者的向量Cosine相似性或者通过在引入额外的神经网络来求值,如下公式:
点积:
S
i
m
i
l
a
r
i
t
y
(
Q
u
e
r
y
,
K
e
y
i
)
=
Q
u
e
r
y
⋅
K
e
y
i
(6-1)
Similarity(Query,Key_i)=Query \cdot Key_i\tag{6-1}
Similarity(Query,Keyi)=Query⋅Keyi(6-1)
Cosine相似性:
S i m i l a r i t y ( Q u e r y , K e y i ) = Q u e r y ⋅ K e y i ∥ Q u e r y ∥ ⋅ ∥ K e y i ∥ (6-2) Similarity(Query,Key_i)=\frac{Query \cdot Key_i}{\parallel Query \parallel \cdot \parallel Key_i \parallel}\tag{6-2} Similarity(Query,Keyi)=∥Query∥⋅∥Keyi∥Query⋅Keyi(6-2)
MLP网络:
S
i
m
i
l
a
r
i
t
y
(
Q
u
e
r
y
,
K
e
y
i
)
=
M
L
P
(
Q
u
e
r
y
,
K
e
y
i
)
(6-3)
Similarity(Query,Key_i)=MLP(Query,Key_i)\tag{6-3}
Similarity(Query,Keyi)=MLP(Query,Keyi)(6-3)
第二个阶段对第一阶段的原始分值进行归一化处理;
第二阶段引入类似Softmax的计算方式对第一阶段的得分进行数值转换,一方面可以进行归一化,将原始计算分值整理成所有元素权重之和为1的概率分布;另一方面也可以通过Softmax的内在机制更加突出重要元素的权重。即一般采用如下公式计算:
a
i
=
S
o
f
t
m
a
x
(
S
i
m
i
)
=
e
S
i
m
i
∑
j
=
1
L
x
e
S
i
m
i
(6-4)
a_i=Softmax(Sim_i)=\frac{e^{Sim_i}}{\sum_{j=1}^{L_x}e^{Sim_i}} \tag{6-4}
ai=Softmax(Simi)=∑j=1LxeSimieSimi(6-4)
根据权重系数对Value进行加权求和
之后进行加权求和即可得到Attention数值:
A
t
t
e
n
t
i
o
n
(
Q
u
e
r
y
,
S
o
u
r
c
e
)
=
∑
i
=
1
L
x
a
i
⋅
V
a
l
u
e
i
(6-5)
Attention(Query,Source)=\sum_{i=1}^{L_x} a_i \cdot Value_i\tag{6-5}
Attention(Query,Source)=i=1∑Lxai⋅Valuei(6-5)
7、自注意力机制
什么是自注意力机制
Self Attention也经常被称为intra Attention。
在一般任务的Encoder-Decoder框架中,输入Source和输出Target内容是不一样的,Attention 机制发生在Target的元素Query和Source中的所有元素之间。Self Attention机制指的不是Target和Source之间的Attention机制,而是Source内部元素治安或者Target内部元素之间发生的Attention机制,也可以理解为Target=Source这种特殊情况下的注意力计算机制。其具体计算过程一样的,只是计算对象发生了变化而已。
为什么要引入自注意力机制
引入Self Attention后会更容易捕获句子中长距离的相互依赖的特征,因为如果是RNN或者LSTM,需要依次序序列计算,对于远距离的相互依赖的特征,要经过若干时间步步骤的信息累计才能将两者联系起来,而距离越远,有效捕获的可能性越小。但是Self Attention在计算过程中会直接将句子中任意两个单词的联系通过一个计算步骤直接联系起来,所以远距离依赖特征之间的距离被极大缩短,有利于有效地利用这些特征。此外Self Attention对于增加计算地并行性也有直接帮助作用。
8、非局部神经网络(Non-local Neural Networks)
网络结构
在深度学习中非局部操作可以表达为:
y
i
=
1
C
(
x
)
∑
∀
j
f
(
x
i
,
x
j
)
g
(
x
j
)
(8-1)
y_i=\frac{1}{C(x)} \sum_{\forall j} f(x_i,x_j)g(x_j)\tag{8-1}
yi=C(x)1∀j∑f(xi,xj)g(xj)(8-1)
i
i
i是输出特征图的其中一个位置,通用来说这个位置可以是时间、空间和时空。
j
j
j是所有可能位置的索引,
x
x
x是输入信号,可以是图像、序列和视频,通常是特征图。
y
y
y是和
x
x
x尺度一样的输出图,f是配对计算函数,计算第
i
i
i个位置和其他所有位置的相关性,
g
g
g是一元输入函数,目的是进行信息变换,
C
(
x
)
C(x)
C(x)是归一化函数,保证变换前后整体信息不变。以上是一个非常泛化的公式,具体细节见下面。在局部卷积算子中,一般的
i
−
1
⩽
j
⩽
i
+
1
i-1 \leqslant j \leqslant i+1
i−1⩽j⩽i+1。
由于
f
f
f和
g
g
g都是通式,故结合神经网络特定,需要考虑其具体形式。
首先g由于是一元输出,比较简单,我可以采用1x1卷积,代表线性嵌入,其形式为:
g ( x j ) = W g x j (8-2) g(x_j)=W_gx_j\tag{8-2} g(xj)=Wgxj(8-2)
对于
f
f
f,前面我们说过其实就是计算两个位置的相关性,那么第一个非常自然的函数是Gaussian。
(1) Gaussian
f ( x i , x j ) = e x i T x j (8-3) f(x_i,x_j)=e^{x_{i}^Tx_j}\tag{8-3} f(xi,xj)=exiTxj(8-3)
对两个位置进行点乘,然后通过指数映射,放大差异。
(2) Embedded Gaussian
f
(
x
i
,
x
j
)
=
e
θ
(
x
i
)
T
ϕ
(
x
j
)
(8-4)
f(x_i,x_j)=e^{\theta(x_i)^T\phi(x_j)}\tag{8-4}
f(xi,xj)=eθ(xi)Tϕ(xj)(8-4)
前面的gaussian形式是直接在当前空间计算,而(2)更加通用,在嵌入空间中计算高斯距离。这里:
θ
(
x
i
)
=
W
θ
x
i
(8-5)
\theta(x_i)=W_{\theta}x_i\tag{8-5}
θ(xi)=Wθxi(8-5)
ϕ ( x j ) = W ϕ x j (8-6) \phi(x_j)=W_{\phi}x_j\tag{8-6} ϕ(xj)=Wϕxj(8-6)
前面两个:
C
(
x
)
=
∑
∀
j
f
(
x
i
,
x
j
)
(8-7)
C(x)=\sum_{\forall j}f(x_i,x_j)\tag{8-7}
C(x)=∀j∑f(xi,xj)(8-7)
仔细观察,如果把*C(x)*考虑进去,那么
1
C
(
x
)
f
(
x
i
,
x
j
)
\frac{1}{C(x)}f(x_i,x_j)
C(x)1f(xi,xj)其实就是Softmax形式,完整考虑就是:
y
=
s
o
f
t
m
a
x
(
x
T
W
θ
T
W
ϕ
x
)
g
(
x
)
(8-8)
y=softmax(x^T W_{\theta}^T W_{\phi} x)g(x)\tag{8-8}
y=softmax(xTWθTWϕx)g(x)(8-8)
最终地结果为:
z i = W z y i + x i (8-9) z_i=W_zy_i+x_i\tag{8-9} zi=Wzyi+xi(8-9)
可以看出,上面构造了残差形式。上面地做法地好处就是可以随意嵌入到任何一个预训练好的网络中,因为只要设置 W z W_z Wz初始化为0,那么就没有任何影响,然后在迁移学习中学习新的权重。这样就不会因为引入新的模块而导致预训练权重无法使用。
9、PAM
场景理解的本质是判别特征的表示,它能捕获长范围上下文信息。然而一些工作表明通过传统的FCN网络产生的局部特征会导致目标或物体的错分。为了相对于局部特征丰富上下文的信息,我们介绍了一个位置注意力模型。位置注意力模型编码了广泛的上下文信息进入局部特征,因此增强了网络的表示能力。接下来,我们详细阐述自适应的聚合空间关系。
阐述图片2,给了一个局部特征 A ∈ ℜ C × H × W A \in \Re^{C \times H \times W} A∈ℜC×H×W,为了产生两个独立的新的特征图B和C,我们将它输入到卷积层,这里 B , C ∈ ℜ C × H × W {B,C} \in \Re^{C \times H \times W} B,C∈ℜC×H×W。之后我们将它们变形到 ℜ C × N \Re^{C \times N} ℜC×N,这里的 N = H × W N=H \times W N=H×W是像素的数量。之后我们将C和B的转置矩阵相乘,并且应用softmax层为了计算空间注意力图 S ∈ ℜ N × N S \in \Re^{N \times N} S∈ℜN×N。
s
j
i
=
e
x
p
(
B
i
⋅
C
j
)
∑
i
=
1
N
e
x
p
(
B
i
⋅
C
j
)
B,C
∈
ℜ
C
×
H
×
W
,
S
∈
ℜ
N
×
N
(9-1)
s_ji=\frac {exp(B_i \cdot C_j)}{\sum_{i=1}^N exp(B_i \cdot C_j)} \quad \text {B,C}\in \Re^{C \times H \times W},S \in \Re^{N \times N} \tag{9-1}
sji=∑i=1Nexp(Bi⋅Cj)exp(Bi⋅Cj)B,C∈ℜC×H×W,S∈ℜN×N(9-1)
这里的
s
j
i
s_{ji}
sji是i位置对j位置的影响的测量。两个位置的更相似的特征表示有助于更好的相关性。
同时,我们将A输入卷积层产生了一个新的特征图 D ∈ ℜ C × H × W D \in \Re^{C \times H \times W} D∈ℜC×H×W重塑为 ℜ C × N \Re^{C \times N} ℜC×N。然后我们将D和转置的S矩阵相乘并且结果重塑为 R C × H × W R^{C\times H\times W} RC×H×W。最后我们将它与尺度参数 α \alpha α相乘并且与特征A逐元素相加,获得最终的输出结果 E ∈ ℜ C × H × W E \in \Re^{C \times H \times W} E∈ℜC×H×W,公式如下所示:
E j = α ∑ i = 1 N ( s j i D i ) + A j (9-2) E_j=\alpha \sum_{i=1}^{N} (s_{ji} D_i)+A_j \tag{9-2} Ej=αi=1∑N(sjiDi)+Aj(9-2)
这里的 α \alpha α是初始值为0并且逐渐分配更多的权重。它能够从公式6中推断出,结果特征E的每个位置是这个特征通过所有位置和原始特征的权重之和。所以,它有一个全局上下文视野和选择性聚集上下文关于空间注意力图。相似的语义特征实现了互惠因此改善了类间的影响和语义一致性。
代码
class PAM_Module(Module):
""" Position attention module"""
#Ref from SAGAN
def __init__(self, in_dim):
super(PAM_Module, self).__init__()
self.chanel_in = in_dim
self.query_conv = Conv2d(in_channels=in_dim, out_channels=in_dim//8, kernel_size=1)
self.key_conv = Conv2d(in_channels=in_dim, out_channels=in_dim//8, kernel_size=1)
self.value_conv = Conv2d(in_channels=in_dim, out_channels=in_dim, kernel_size=1)
self.gamma = Parameter(torch.zeros(1))
self.softmax = Softmax(dim=-1)
def forward(self, x):
"""
inputs :
x : input feature maps( B X C X H X W)
returns :
out : attention value + input feature
attention: B X (HxW) X (HxW)
"""
m_batchsize, C, height, width = x.size()
proj_query = self.query_conv(x).view(m_batchsize, -1, width*height).permute(0, 2, 1)
proj_key = self.key_conv(x).view(m_batchsize, -1, width*height)
energy = torch.bmm(proj_query, proj_key)
attention = self.softmax(energy)
proj_value = self.value_conv(x).view(m_batchsize, -1, width*height)
out = torch.bmm(proj_value, attention.permute(0, 2, 1))
out = out.view(m_batchsize, C, height, width)
out = self.gamma*out + x
return out
10、CAM
高阶特征的每个通道图可以看作是对特定类的响应,并且不同的语义响应与其他相结合。利用通道图之间的相互依存,我们能够突出特征图之间的相互依存和改善特定语义的特征表示。因此,我们建立了通道注意力模型为了明确通道间的模型相互依存。
通道注意力模型的结构阐述了在图3。不同于位置注意力模型,我们直接计算通道注意力图 X ∈ ℜ C × C X \in \Re^{C \times C} X∈ℜC×C从原始的特征 A ∈ ℜ C × H × W A \in \Re^{C \times H \times W} A∈ℜC×H×W特别的,我们将A重塑为 ℜ C × N \Re^{C \times N} ℜC×N,并且将A与A的转置矩阵相乘。最后,我们应用softmax层获得通道注意力图 X ∈ ℜ C × C X \in \Re^{C \times C} X∈ℜC×C,公式如下:
x j i = e x p ( A i ⋅ A j ) ∑ i = 1 C e x p ( A i ⋅ A j ) A ∈ ℜ C × H × W , X ∈ ℜ C × C (10-1) x_ji=\frac {exp(A_i \cdot A_j)}{\sum_{i=1}^C exp(A_i \cdot A_j)} \quad \text {A}\in \Re^{C \times H \times W},X \in \Re^{C \times C} \tag{10-1} xji=∑i=1Cexp(Ai⋅Aj)exp(Ai⋅Aj)A∈ℜC×H×W,X∈ℜC×C(10-1)
这里的
x
j
i
x_{ji}
xji是i通道对j通道的影响的测量。另外,我们将X的转置与A矩阵相乘并且重塑他们的结果到
ℜ
C
×
H
×
W
\Re^{C \times H \times W}
ℜC×H×W。然后我们将结果与
β
\beta
β相乘并且与A逐元素相加,获得最终的输出结果
E
∈
ℜ
C
×
H
×
W
E \in \Re^{C \times H \times W}
E∈ℜC×H×W,公式如下所示:
E
j
=
β
∑
i
=
1
N
(
x
j
i
A
i
)
+
A
j
(10-2)
E_j=\beta \sum_{i=1}^{N} (x_{ji} A_i)+A_j \tag{10-2}
Ej=βi=1∑N(xjiAi)+Aj(10-2)
这里的
β
\beta
β的权重从0开始学习。公式8展示了是每个通道最终的特征是所有通道特征和原始特征的和,模型能够捕捉特征图之间的长距离的语义依赖。它帮助改善特征辨别。
代码
class CAM_Module(Module):
""" Channel attention module"""
def __init__(self, in_dim):
super(CAM_Module, self).__init__()
self.chanel_in = in_dim
self.gamma = Parameter(torch.zeros(1))
self.softmax = Softmax(dim=-1)
def forward(self,x):
"""
inputs :
x : input feature maps( B X C X H X W)
returns :
out : attention value + input feature
attention: B X C X C
"""
m_batchsize, C, height, width = x.size()
proj_query = x.view(m_batchsize, C, -1)
proj_key = x.view(m_batchsize, C, -1).permute(0, 2, 1)
energy = torch.bmm(proj_query, proj_key)
energy_new = torch.max(energy, -1, keepdim=True)[0].expand_as(energy)-energy
attention = self.softmax(energy_new)
proj_value = x.view(m_batchsize, C, -1)
out = torch.bmm(attention, proj_value)
out = out.view(m_batchsize, C, height, width)
out = self.gamma*out + x
return out
参考
https://zhuanlan.zhihu.com/p/37601161
https://arxiv.org/pdf/1809.02983.pdf
http://openaccess.thecvf.com/content_cvpr_2018/papers/Wang_Non-Local_Neural_Networks_CVPR_2018_paper.pdf
https://arxiv.org/pdf/1811.05544.pdf