1 致谢
感谢paddle开源的Vit系列教程《飞桨——从零开始学视觉Transformer》;
2 Vit模型介绍
2.2 Attention模块(Multi-head Attention)
Attention模块是一种seq2seq的变换模块,其输入输出的特征维数相同; 参数:
num_heads : attention head的数量,默认为8 ;将向量分成8份的话,计算效率也比较高。qkv_bias : 在qkv 映射时使用偏置参数,默认为False ,但是qkv_bias 默认需要开启。attn_drop : self.attn_drop 的丢弃率,默认为0 。proj_drop : self.proj_drop 的丢弃率,默认为0 。
Note:
qkv_bias 在一般情况下必须开启,这样才符合逻辑回归的数学原理,我们在timm 的Vit实现中也可以看到此参数默认是开启的,这里我们引用timm 中Vit模型的参数解释来说明:
qkv_bias: bool, If True, enable qkv(nn.Linear) layer with bias, default: True
可知qkv_bias 默认需要开启。
class Attention(nn.Module):
def __init__(self, dim, num_heads=8, qkv_bias=False, attn_drop=0., proj_drop=0.):
super().__init__()
self.num_heads = num_heads
head_dim = dim // num_heads
self.scale = head_dim ** -0.5
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
self.attn_drop = nn.Dropout(attn_drop)
self.proj = nn.Linear(dim, dim)
self.proj_drop = nn.Dropout(proj_drop)
def forward(self, x):
B, N, C = x.shape
qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
q, k, v = qkv.unbind(0)
attn = (q @ k.transpose(-2, -1)) * self.scale
attn = attn.softmax(dim=-1)
attn = self.attn_drop(attn)
x = (attn @ v).transpose(1, 2).reshape(B, N, C)
x = self.proj(x)
x = self.proj_drop(x)
return x
复杂度分析
可以看到,Attention 模块的计算复杂度为
3
C
2
+
B
N
2
C
+
B
N
2
?
n
h
e
a
d
s
+
B
N
2
3C^2 + BN^2C+BN^2\cdot nheads + BN^2
3C2+BN2C+BN2?nheads+BN2 我们将复杂度按照算子分开列出:
- FC算子
self.qkv :
3
C
2
3C^2
3C2 - 乘法注意力
q@k' :
B
N
2
C
BN^2C
BN2C - Softmax算子:
B
N
2
?
n
h
e
a
d
s
BN^2\cdot nheads
BN2?nheads
- 注意力加权运算:
B
N
2
BN^2
BN2
2.2 Vit模型为什么要使用Multi-head-Attention?
这里我们针对仅原始Vit的Multi-head-Attention进行讨论,并没有扩展到所有SOTA的vit类型算法; 由Attention 模块的复杂度分析可知,仅有Softmax算子的复杂度
B
N
2
?
n
h
e
a
d
s
BN^2\cdot nheads
BN2?nheads与注意力head的数量有关;于是我们可以知道使用Multi-head-Attention会使得Self-Attention算法的复杂度增加,且其增加的复杂度主要是来源于softmax 算子的运算量增加了,且其运算量增加了
n
h
e
a
d
s
nheads
nheads倍;
|