Transformer计算量和过程统计

整理一下Transformer相关论文的计算量和计算流程


一、Vision Transformer

在这里插入图片描述
Vision Transformer的结构在大佬 “太阳花的小绿豆” 的博文里面有明确的分析。这里我也是借由这篇博文来写的。
在这里插入图片描述
图片来源:太阳花的小绿豆-Vision Transformer详解


I. Patch Embedding层分析

输入输出

  1. 224 × 224 × 3 224 \times 224 \times 3 224×224×3
  2. 196 × 768 196 \times 768 196×768

运算

  1. Conv2d(卷积核大小 [ O , I , H , W ] → [ 768 , 3 , 16 , 16 ] [O, I, H, W] \rightarrow [768, 3, 16, 16] [O,I,H,W][768,3,16,16],步长是16)
  2. Flatten: [ 14 , 14 , 768 ] → [ 196 , 768 ] [14, 14, 768] \rightarrow [196,768] [14,14,768][196,768]

分析:

在原文中的计算量是主要是一个卷积层的计算量。这个卷积的主要作用就是,将输入图片下采样到 14 × 14 14\times 14 14×14,变成一个个小的patches, 再flatten之后就可以变成transformer的encoder层需要的输入了。


II. Concat & Position Embedding

Class token的插入:

Class token的维度是 1 × 768 1\times 768 1×768,并且是一个可训练的参数。在这里插入之后和Patch Embedding的输出拼接之后得到: C o n c a t ( [ 1 , 768 ] , [ 196 , 768 ] ) → [ 197 , 768 ] Concat([1, 768], [196,768]) \rightarrow [197,768] Concat([1,768],[196,768])[197,768]

Position Embedding:

将每一个patch对应的position信息都要加入进来,所以是和Concat之后的维度是一样的,运算就是一个简单的加法,Position信息也是一个可训练的参数。

  1. 运算: A d d ( [ 197 , 768 ] , [ 197 , 768 ] ) → [ 197 , 768 ] Add([197, 768], [197,768]) \rightarrow [197,768] Add([197,768],[197,768])[197,768]
  2. 维度: [ 197 , 768 ] [197,768] [197,768]

III. Transformer Encoder

请添加图片描述
Encoder Block里面的整体结构大致如上图所示,包含以下几个部分:

  • 第一个Residual结构里面有三个block和一个连线,分别是:
    • Layer Normalization
    • Multi-Head Attention
    • Dropout
  • 第二个Residual结构里面同样也有三个block和一个连线,分别是:
    • Layer Normalization
    • MLP Block
      • Linear
      • GELU
      • Dropout
      • Linear
      • Dropout
    • Dropout

下面分模块讲述。

A. 第一个 Layer Normalization

Layer Normalization的公式与Batch Normalization基本一致,即需要以下操作
在这里插入图片描述
但是不同的是,Batch Normalization是对一个batch内的所有feature map,取它们每个channel的均值和方差进行channel级别的标准化,而Layer Normalization是对一个feature map中某个或某几个维度进行标准化。

运算:

Layer Normalization因为标准化的维度与BN有明显的不同,所以这就导致了它无法像BN一样可以在训练过程中直接获得 r u n n i n g _ m e a n running\_mean running_mean r u n n i n g _ v a r running\_var running_var,从而上面公式中可以作为参数的仅仅只有 γ \gamma γ β \beta β。因此,具体的运算如下:

  • 求取该层的指定维度的期望: E [ x ] E[x] E[x]
  • 求取该层的指定维度的方差: V a r [ x ] Var[x] Var[x]
  • 根据已训练好的 γ \gamma γ β \beta β来运算标准化之后的结果
输入输出维度:
  • 输入: [ 197 , 768 ] [197,768] [197,768]
  • 输出: [ 197 , 768 ] [197,768] [197,768]

B. Multi-Head Attention

1. Self-Attention

在这里插入图片描述
(图片来源于太阳花的小绿豆

上图是非常基本的Self-Attention的运算流程。在上述运算中,可以看到最为重要的运算其实为两步:

  1. 得到 Q , K , V Q,K,V Q,K,V矩阵:即用输入值 a a a与对应的三个权重矩阵进行Matrix-Matrix Multiplication就可以得到三个矩阵。
  2. 计算Attention结果:即运算下面的公式,其中 S o f t m a x Softmax Softmax是对 Q K T / d k QK^T/\sqrt{d_k} QKT/dk 这个结果矩阵的每一行进行运算的。
    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{QK^T}{\sqrt{d_k}})V Attention(Q,K,V)=softmax(dk QKT)V
2. Multi-Head Self-Attention

在这里插入图片描述
(图片来源于太阳花的小绿豆

Multi-head Self-Attention 顾名思义就是在Self-Attention基础上将其中的部分矩阵变成Multi-head的形式,来获得更好的效果,其基本运算与Self-Attention大致无异,主要是多了一步多head的concat以及一个矩阵矩阵乘法:

  1. 得到 Q , K , V Q,K,V Q,K,V矩阵:即用输入值 a a a与对应的三个权重矩阵进行Matrix-Matrix Multiplication就可以得到三个矩阵。(这一步与Self-Attention保持一致)
  2. 将得到的 Q , K , V Q,K,V Q,K,V矩阵按照头的数量进行均匀的按行分配:这一步按照上面的图的方式进行分开。原本原文中是需要乘以对应的 W i Q , W i K , W i V ) W_i^Q, W_i^K, W_i^V) WiQ,WiK,WiV)来进行分开,但是实际操作大可不必
  3. 对分开的 Q i , K i , V i Q_i,K_i,V_i Qi,Ki,Vi运算Self-Attention:这一步与上一节讲的Self-Attention运算基本一致。
  4. 将多头的Attention结果进行拼接,然后乘以 W O W^O WO:这一步就是图中的:
    M u l t i H e a d ( Q , K , V ) = C o n c a t ( h e a d 1 , … , h e a d h ) W O MultiHead(Q,K,V) = Concat(head_1, \dots,head_h)W^O MultiHead(Q,K,V)=Concat(head1,,headh)WO
3. ViT中的Multi-Head Self-Attention分析

原文中ViT-Base取得是12个head,那么下面就以12个head为例来分析一下维度问题。

按照前面几个图的计算流程,我推导出了下面的计算维度变化:

  1. 关键维度
    • d m o d e l = 768 d_{model} = 768 dmodel=768
    • n u m h e a d = 12 num_{head} = 12 numhead=12
    • d k = d m o d e l / n u m h e a d = 64 d_k = d_{model} / num_{head} = 64 dk=dmodel/numhead=64
    • d k = 8 \sqrt{d_k} = 8 dk =8
  2. 输入: a → [ 197 , 768 ] a \rightarrow [197,768] a[197,768]
  3. 计算 Q , K , V Q,K,V Q,K,V Q = a × W q , K = a × W k , V = a × W v Q = a\times W^q, K=a\times W^k, V=a\times W^v Q=a×Wq,K=a×Wk,V=a×Wv
    • W q , Q k , W v → [ 768 , 768 ] W^q, Q^k, W^v \rightarrow [768,768] Wq,Qk,Wv[768,768]
    • Q , K , V → [ 197 , 768 ] Q,K,V \rightarrow [197, 768] Q,K,V[197,768]
  4. 多头均分 Q i = Q W i Q , K i = K W i K , V i = V W i V Q_i = Q W_i^Q, K_i = K W_i^K, V_i = V W_i^V Qi=QWiQ,Ki=KWiK,Vi=VWiV。这个操作其实是将 Q , K , V Q,K,V Q,K,V均分,在实际系统中的倒也可以不用乘以这三个均分矩阵。
    • W i Q , Q i K , W i V → [ 768 , 64 ] W_i^Q, Q_i^K, W_i^V \rightarrow [768,64] WiQ,QiK,WiV[768,64]
    • Q i , K i , V i → [ 197 , 64 ] Q_i,K_i,V_i \rightarrow [197, 64] Qi,Ki,Vi[197,64]
  5. Attention运算
    • Q i K i T = [ 197 , 64 ] × [ 64 , 197 ] → [ 197 , 197 ] Q_i K_i^T = [197,64] \times [64,197] \rightarrow [197,197] QiKiT=[197,64]×[64,197][197,197]
    • s o f t m a x ( Q K T d k ) → [ 197 , 197 ] softmax(\frac{QK^T}{\sqrt{d_k}}) \rightarrow [197,197] softmax(dk QKT)[197,197]
    • h e a d i = s o f t m a x ( Q K T d k ) V = [ 197 , 197 ] × [ 197 , 64 ] → [ 197 , 64 ] head_i = softmax(\frac{QK^T}{\sqrt{d_k}}) V = [197,197] \times [197,64] \rightarrow [197,64] headi=softmax(dk QKT)V=[197,197]×[197,64][197,64]
  6. 多头MultiHead运算
    • C o n c a t ( h e a d 1 , … , h e a d n ) → [ 197 , 768 ] Concat(head_1,\dots, head_n) \rightarrow [197,768] Concat(head1,,headn)[197,768]
    • W O → [ 768 , 768 ] W^O \rightarrow [768,768] WO[768,768]
    • M u l t i H e a d ( Q , K , V ) → [ 197 , 768 ] MultiHead(Q,K,V) \rightarrow [197,768] MultiHead(Q,K,V)[197,768]

将计算流程梳理完之后可以总结一下这个Multi-Head Self-Attention所需要的运算量大致有哪些:

  • 2中的三个M2M
  • 4中Attention运算的M2M,Scaling操作,和按行的Softmax,加一个M2M
  • 6中在Concat结束之后的一个M2M

C. Dropout

这部分在前向传播中不需要额外运算

D. Residual的支线加

这里包含了一个 A d d ( [ 197 , 768 ] , [ 197 , 768 ] ) Add([197,768], [197,768]) Add([197,768],[197,768]) 矩阵矩阵按元素加法

E. 第二个Layer Normalization

运算与A中的运算基本一致,不需要赘述。

F. MLP Block

MLP Block的结构也如III节的第一个图所示,总共包含五层,下面依次讲解

1. 第一个Linear层

这个Linear层就是简单的全连接层,包含矩阵矩阵乘法和bias的加法。其中的维度变化是 [ 197 , 768 ] × [ 768 , 3072 ] → [ 197 , 3072 ] [197,768]\times [768, 3072] \rightarrow [197,3072] [197,768]×[768,3072][197,3072]

全连接层的参数矩阵大小是 [ 3072 , 768 ] [3072, 768] [3072,768]

2. GELU(Gaussian Error Linear Units)

GELU是一个类似于ReLU的计划函数,但是本质上是在ReLU的基础上加入了Dropout的随机性。其数学表达式如下:
G E L U ( x ) = x P ( X ≤ x ) = x Φ ( x ) GELU(x) = xP(X \leq x ) = x \Phi(x) GELU(x)=xP(Xx)=xΦ(x)
这里 Φ ( x ) \Phi(x) Φ(x) 是正态分布的概率函数。对于假设为标准正态分布的 G E L U ( x ) GELU(x) GELU(x),论文中提供了近似计算的数学公式,如下:
G E L U ( x ) = 0.5 x ( 1 + t a n h [ 2 / π ( x + 0.044715 x 3 ) ] ) GELU(x) = 0.5x (1 + tanh[\sqrt{2/\pi} (x + 0.044715x^3)]) GELU(x)=0.5x(1+tanh[2/π (x+0.044715x3)])
这里参考了 alwayschasing博主的博文。

在实际应用中,其实可以利用近似的公式来进行运算。

3. Dropout

在这里不做赘述

4. 第二个Linear层

这个Linear层就是简单的全连接层,包含矩阵矩阵乘法和bias的加法。其中的维度变化是 [ 197 , 3072 ] × [ 3072 , 768 ] → [ 197 , 768 ] [197,3072]\times [3072, 768] \rightarrow [197,768] [197,3072]×[3072,768][197,768]。这个全连接层的主要作用便是将前一个全连接层扩展的特征图维度恢复到初始维度,保证输出的时候可以直接提取结果。

全连接层的参数矩阵大小是 [ 768 , 3072 ] [768,3072 ] [768,3072]

5. Dropout

在这里同样不做赘述。

G. Dropout

不做赘述

H. 第二个Residual Block的支线加

这里包含了一个 A d d ( [ 197 , 768 ] , [ 197 , 768 ] ) Add([197,768], [197,768]) Add([197,768],[197,768]) 矩阵矩阵按元素加法


IV. MLP Head

根据本文参考的博文的描述,在训练中这个MLP Head是由 Linear + tanh激活函数 + Linear组成,但是在实际运算中,其实可以只用一个Linear就可以。


V. Class

在经过MLP Head之后,取第0个位置的token作为分类的特征就可以得到最终的结果。

  • 14
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Swin Transformer模型的运算可以通过计算每个操作的浮点操作数(FLOPs)来估计。FLOPs函数可以通过统计每个操作的计算来实现。 Swin Transformer模型中的关键操作是多头自注意力(multi-head self-attention)和MLP (多层感知机)。对于每个操作,我们可以计算其FLOPs并进行累加。 以下是一个示例代码,用于估计Swin Transformer模型的FLOPs: ```python import torch def count_flops(module, input, output): flops = 0 if hasattr(module, 'weight'): flops += module.weight.numel() if hasattr(module, 'bias') and module.bias is not None: flops += module.bias.numel() if isinstance(module, torch.nn.Linear): flops *= 2 # Linear operations involve both multiplication and addition # Accumulate flops for each operation module.__flops__ += flops def flops(model, input_size): model.eval() model.apply(lambda module: setattr(module, '__flops__', 0)) model.apply(lambda module: module.register_forward_hook(count_flops)) with torch.no_grad(): model(torch.randn(1, *input_size)) total_flops = sum([module.__flops__ for module in model.modules()]) return total_flops ``` 使用该函数,您可以计算Swin Transformer模型的总FLOPs。请确保将正确的输入大小传递给`flops`函数。 ```python import torchvision.models as models model = models.swin_transformer.SwinTransformer() input_size = (3, 224, 224) # Assuming input images of size 224x224 and 3 channels total_flops = flops(model, input_size) print('Total FLOPs:', total_flops) ``` 请注意,这只是一个简单的估计方法,实际的FLOPs可能会有所差异。此外,不同的库和工具可能会提供不同的FLOPs估计结果。这个代码示例可以作为一个起点,您可以根据具体情况进行修改和调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值