【RepVGG】 RepVGG- Making VGG style ConvNets Great Again



RepVGG

0. Abstract

  1. 3 × 3 convolution와 ReLU 만을 사용하였다.
  2. Training과 Inference과정의 Architecture를 decoupling하였다. 이를 위해서 Training parameter를 VGG구조로 바꾸기 위한 re-parameterization technique를 사용했다.
  3. RepVGG의 결과 : (1080Ti) 80% top1 accuracy. (plain model에서는 최초로) ResNet보다 83% 빠르고, ResNet 101보다 101% 빠르다.
  4. favorable accuracy-speed trade-off 를 가진 모델이다.

image-20210426163041272

PS. 여기서, full precision(fp32): half precision이 fp16(floating point)을 Training 과정에서 효율적으로 사용하고 fp32도 중간중간에 사용하는 것이라면, full precision은 항상 fp32만 사용하는 것.


1. Instruction

  1. 과거의 유명한 Backbone들 complicated ConvNets : Inception, ResNet, DensNet, EfficientNet
  2. 이런 모델들의 drawbacks
    • complicated multi-branch designs (ex, residual addition, branch-concatenation in Inception): 구현하기가 어렵다. Inference하는데 느리다. 메모리 사용을 더 많이 한다.
    • depthwise conv(inverted residual), channel shuffle: various devices에서의 사용이 힘들다.
    • 이 모델들의 FLOP(floating-point operations)과 Inference speed가 비례하지 않는다.
  3. 반면에 RepVGG는 VGG 같은 plain without any branch 모델이다. 하지만 multi-branch 들과 유사한 성능을 내기에는 매우 challenging했다.
  4. ResNet의 장점이자 단점: avoids the gradient vanishing problem을 위해서 residual Block을 사용한다. 하지만! 이 모듈은 Inference 에서 필요가 없다.
  5. 반면에 RepVGG는 Training과 Inference를 decouple했다. Training때 학습한 파라미터를 Inference모델로 ` equivalently replace하기 위해서 re-parameterization` 사용했다. 전체 구조를 미리 대강 살펴보면 아래와 같다.
  6. Only 3x3 conv and ReLU로 구성되어 있기 때문에 빠르다. 만약 하나의 유닛이 fewer operator만을 수행한다면, 더 많은 computing 유닛을 가진 GPU를 만들어 낼 수 있다. 만약 3x3-Relu만을 수행하는 GPU가 존재한다면, 엄청난 수의 Unit을 가진 GPU가 될 것이다. 이 GPU에 특화될 수 있다. (특별한 GPU만들 수 있다면 RepVGG는 개꿀 모델이다.)
  7. Contributions
    • favorable speed-accuracy trade-off
    • re-parameterization
    • effectiveness at classification and sementic segmentation

image-20210426163834045

2. Related Work

2.3. Model Re-parameterization

아래에 4.2 내용 참조

2.4 Winograd Convolution

  • RepVGG는 only 3x3 conv로만 구성되어 있는데, GPU라이브러리는 이 연산에 매우 최적화 되어있다. 위의 표에서 주목해야할 점을 기록해 놓았다.
  • Winograd Algorithm을 사용하면 3x3 conv연산을 더욱 가속할 수 있다. Original 3x3 conv연산보다 the amount of multiplications (MULs)의 양이 4/9로 줄어든다. 우리는 이 알고리즘을 통한 연산을 딥러닝 프레임워크에서 default로 사용하고 있다.
  • TFLOPS: Tera FLoating-point Operations Per Second GPU 제작사에서 사용하는 지표로써 actual running time and computational density의 정도를 의미한다. 높을수록 좋다.
  • (Table4,5에서 MULs을 기준으로 한 비교를 할 예정이다. Additions연산보다 multiplication연산이 더 많은 time-consuming이 필요하기 때문이다.)

image-20210426170022719


3. Building RepVGG

3.1. simple ConvNets의 장점

  1. Fast
    • FLOP과 Speed는 비례하지 않는다. 아래의 비교를 보면 VGG16이 FLOPs가 8배 높지만, 2배 빠르다. 즉 computational density가 대략 15배 차이 난다는 것을 의미한다.
    • Simple Conv는 FLOP이 높아도 Speed는 훨씬 빠르다. 그 이유는 (1) the memory access cost (MAC) (2) ` degree of parallelism ` 때문이다.
    • MAC는 다른 연산에 비해서 높은 time-consuming이 필요하고, degree of parallelism이 높으면 빠른 연산이 가능하다.
    • plain conv는 MAC가 거의 없고, parallelism이 매우 높다.
    • NASNET-A(the number of individual conv or pooling operations in one building block): few (paralleism을 사용한) large operators이 아닌 multiple small operators을 사용하는 정도를 의미한다. 높을 수록 degree of parallelism이 안 좋은 것이다.
  2. Memory-economica
    • multi-branch topology들은 memory-inefficient 하다. 예시는 아래의 그림에 있다.
  3. Flexible
    • multi-branch topology에는 몇가지 제약이 있다.
      1. 제약1: the last conv layers of every residual block have to produce tensors of the same shape
      2. 제약2 : channel pruning to remove some unimportant channels 사용하지 못한다. (channel pruning)
    • 반면에 RepVGG는 자유로는 shape 구성이 가능하고, channel pruning 또한 가능하다.

image-20210426170709145

3.2 Training-time Multi-branch Architecture

  • Plain conv = the poor performance. 따라서 Training과정에서는 multi-branch 모델의 장점을 최대한 이용한다.
  • ResNet의 Residual block에 의해서 implicit (sum) ensemble model이라고 할 수 있다. n개의 block은 an ensemble of 2^n models이라고 표현가능하다. RepVGG는 an ensemble of 3^n models 모델을 만들었다.
    image-20210426173456044

3.3. Re-param for Plain Inference-time Model

  • 코드를 바탕으로 제대로 다시 제대로 그려놓은 그림

image-20210426175447877

  • 하지만 굳이 논문 내용으로 이해하고 싶다면, 아래와 같이 해석할 수 있다.
  • (한 채널의 2차원 평면을 단위 행렬을 곱하여, 똑같은 2차원 평면이 나오게 만든다.)

image-20210427100758563

3.4. Architectural Specification

  • VGG에서는max pooling를 사용하지만, RepVGG에서는 최소한의 Operator만을 사용하기 위해서 사용하지 않는다.
  • 5 stage, the first layer of stage with stride=2
  • classification위해서 RepVGG를 통과한 후, Global average pooling -> fully connected layer
  • 몇가지 guidelines을 따라서 아래와 같은 Architecture models을 만들었다.
    image-20210426230732874
  • 특별히 b를 사용하는 이유는, 5 stage에 어차피 1개의 layer만을 사용하니, channel을 최대한으로 늘려도 모델 전체 파라미터 양에 큰 영향을 주지 않으므로 b를 사용한다.
  • further 파라미터를 줄이기 위해서, Groupwise conv layer를 사용한다.
    • 3rd, 5th, 7th, …, 21st layer of RepVGG-A
    • 3rd, 5th, 7th, …, 21st, 23rd, 25th and 27th layers of RepVGG-B
    • (channel을 몇등분으로 나눌지) g = 1 or 2 or 4

4. Experiments

4.1. RepVGG for ImageNet Classification

image-20210426231324078

  • data augmentation: random cropping and left-right flipping
  • batch size of 256 on 8 GPUs
  • 위의 Architecture에서 a와 b를 다르게 설정하여, middleweight and heavyweight models을 만들어 낸다.

image-20210426231653182

4.2. Structural Re-parameterization is the Key

  • Ablation studies
    image-20210426231808974
  • Re-parameterizaition 성능 비교: 각각에 대한 자세한 설명은 논문에서 잘 분류해 놓았으니 참고
    image-20210426231933911
  • 추가 실험
    1. ResNet50에서 모든 3x3 conv를 RepVGG block으로 바꾸고 학습시킨 후 re-parameterization을 통해서 다시 simple 3x3 conv로 바꿨을 때, 성능이 0.03% 상승해다. -> 강력한 plain convNet을 학습시키기 위해, 우리의 방법이 매우 유용하다!
    2. RepVGG에 추가적인 Skip-connection을 추가했을때, 성능이 0.58% 상승하였다. residual로써 ensemble 모델이 내제적으로 구현된 것이기 때문에 당연하다.
  • 보충 설명
    1. DiracNet
      • 비슷하면서 조금 다른 모듈이다.
      • RepVGG에서는 scaling vector 없음, BN 고려, Bias 고려, 1x1 conv 추가
        image-20210427102504970
      • 논문에서 이야기 하는 차이점
        1. RepVGG는 actual dataflow through a concrete structure 를 따른다. 반면에 DiracNet은 easier optimization를 위해서 ` another mathematical expression of conv kernels`을 사용했을 뿐이다.
        2. 성능이 RepVGG보다, 다른 SOTA 모델보다 떨어진다.
    2. Trivial Re-param
      • 위의 식에서 a = 1,b = 1
    3. ACB
      • 아래와 같이 3x3 conv를 more parameter를 사용하는 conv로 바꿔서 학습시킨다. 그리고 다시 3x3conv를 아래와 같이 변환한다.
        image-20210427100934484

4.3. Semantic Segmentation

  • ImageNetpretrained RepVGG 사용했다
  • PSPNet 사용
    image-20210426233353421
  • PSPNet는 stage 3,4에서 dilated conv를 적용한다. RepVGG에서도 dilated conv 구현하였지만, 속도가 많이 줄어들었다. (5*5로 padding한 conv를 사용해야하기 때문?)
  • 그래서 last 5 layers에서만 dilated conv를 적용한 것이 fast 모델이다.
  • 결과 분석
    • ResNet101과 성능이 비슷한 RepVGG-B1g2-fast의 속도가 62%나 빠르다.
    • middleweight 모델에서는 dilation이 그리 효율적이지 않다.

4.4 Limitation

  • RepVGG models은 단순 연산을 하는 unit이 많은 GPU에 매우 효율적이다.
  • 하지만 GPU가 없는 mobile-regime에서는 효율적은 모델은 아니다.
  • CPU를 위한 모델 -> MobileNet // low-power -> ShuffleNet (연산량이 매우 적다)


Code

  • Equation (3): 여기서 else가 Identity(only nn.BatchNorm2d) To W’+b’로 변환하는 코드이다.
    image-20210427095426340
    • kernel_value = np.zeros((self.in_channels, input_dim, 3, 3), dtype=np.float32)에서 self.in_channelsself.out_channels이 아닌 이유는, Skip_connection 연산은 input, output channel이 같기 때문이다.
    • 특히 kernel_value[i, i % input_dim, 1, 1] = 1 코드가 위의 나의 그림 중 노란색으로 수정한 코드부분이다.
  • Sum
    image-20210427095836596

【Python】Visualize torch.tensor or numpy.ndarray

In this short guide, you’ll see how to Visualize torch.tensor or numpy.ndarray

how to Visualize torch.tensor or numpy.ndarray

Visualize torch.tensor or numpy.ndarray

# from via import via; via(x)
import numpy as np
import torch
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

def via(arr, save_txt:bool = True, size:tuple = (20,20), 
                out:str = 'array_out.txt', Normalize:bool = False):
    dim = arr.ndim 
    if isinstance(arr, np.ndarray):
        # (#Images, #Chennels, #Row, #Column)
        if dim == 4:
            arr = arr.transpose(3,2,0,1)
        if dim == 3:
            arr = arr.transpose(2,0,1)
    if isinstance(arr, torch.Tensor):
        arr = arr.numpy()
    fig = plt.figure(figsize=size)

    if save_txt:
        with open(out, 'w') as outfile:    
            outfile.write('# Array shape: {0}\n'.format(arr.shape))
            
            if dim == 1 or dim == 2:
                np.savetxt(outfile, arr, fmt='%-7.3f')

            elif dim == 3:
                for i, arr2d in enumerate(arr):
                    outfile.write('# {0}-th channel\n'.format(i))
                    np.savetxt(outfile, arr2d, fmt='%-7.3f')

            elif dim == 4:
                for j, arr3d in enumerate(arr):
                    outfile.write('\n\n# {0}-th Image\n'.format(j))
                    for i, arr2d in enumerate(arr3d):
                        outfile.write('# {0}-th channel\n'.format(i))
                        np.savetxt(outfile, arr2d, fmt='%-7.3f')
            else:
                print("Out of dimension!")

    
    if Normalize:
        arr -= np.min(arr)
        arr /= max(np.max(arr),10e-7)
    if dim == 1 or dim == 2:
        if dim==1: arr = arr.reshape((1,-1))
        fig.suptitle('Array shape: {0}\n'.format(arr.shape), fontsize=30)
        plt.imshow(arr, cmap='jet')
        plt.colorbar()
        plt.savefig('array_out.png')

    elif dim == 3:
        x_n = int(np.ceil(np.sqrt(arr.shape[0])))
        fig.suptitle('Array shape: {0}\n'.format(arr.shape), fontsize=30)
        for i, arr2d in enumerate(arr):
            ax = fig.add_subplot(x_n,x_n,i+1)
            im = ax.imshow(arr2d, cmap='jet')
            plt.colorbar(im)
            ax.set_title('{0}-channel'.format(i))
        fig.savefig('array_out.png')

    elif dim == 4:
        img_n = arr.shape[0]
        x_n = int(np.ceil(np.sqrt(arr.shape[1])))
        outer = gridspec.GridSpec(img_n, 1)
        fig.suptitle('Array shape: {0}\n'.format(arr.shape), fontsize=30)
        for j, arr3d in enumerate(arr):
            inner = gridspec.GridSpecFromSubplotSpec(x_n, x_n, subplot_spec=outer[j],wspace=0.1,hspace=0.3)
            for i, arr2d in enumerate(arr3d):
                ax = plt.subplot(inner[i])
                im = ax.imshow(arr2d, cmap='jet')
                plt.colorbar(im)
                ax.set_title('{0}-Image {1}-channel'.format(j,i))
        
        fig.suptitle('Array shape: {0}\n'.format(arr.shape), fontsize=30)
        fig.savefig('array_out.png')

    else:
        print("Out of dimension!")

    

arr = torch.rand(2,28,35)
via(arr, size=(20,20))

How to use

from pythonfile import Visualarr; Visualarr(x)

【Transformer】DeiT-Training data-efficient image transformers & distillation



1. DeiT 논문 핵심 정리

  • ViT의 문제점 : JFT, ImageNet 매우매우 많은 데이터셋을 사용해서 학습시켜야, ImageNet SOTA를 찍을 수 있다. Transformer를 사용해서 좋은 성능이 나오려면, 일단 무조건 데이터셋이 아주아주아주 많으면 많을 수록 좋다.
  • Motivation : ImageNet 데이터셋만 가지고 학습시켜서 좋은 성능이 나오게 할 수 없을까?
  • 해결방법 : – Knowledge Distillation 을 사용하자!! Knowledge Distillation을 사용하면 Data Agumentation하기 이전의 class를 그대로 예측해야 할 필요가 없다. Teacher 또한 data augmentation한 결과를 보고 예측한 결과를 Stutent가 따라하면 되기때문에, 잘못된 모델학습이 되는 것을 막아준다. (PPT 2번 참조)
  • Transformer 만의 특장점: inductive biases가 매우 낮다. (inductive biases: [모집단(=전세계 모든)의 이미지의 centroids를 중심으로한 분포(+ decision boundary) VS 표본이미지(ImageNet)의 centroids와 분포]의 차이를 이야기 한다.) Conv와 fc에서는 weight가 Fixed되어 있지만, self-attention에서는 들어온 이미지에 대해서 서로 다른 weight[softmax(query x key) ]값을 가진다. (하지만 ImageNet에 대해서 좋은 Validation 결과가 나오려면.. inductive biases도 어느정도 필요하다.)
  • DeiT는 Hard Distillation을 사용해서 적은 데이터 만으로 빠르게 성능을 수렴시켰다.
  • 추가 기법 : Distillation Token (이거 추가해서 성능이 더 높아졌다. 아래 보충 내용 참조하기)
  • 아니그러면! Distillation Token 추가하면 Test 할 때, 뭐를 이용해서 Prediction하는가? Distillation Embeding, Class Embeding 값을 적절히 융합해서 Prediction한다. (PPT 7번 참조)
  • 실험결과를 보아 Convolution Network에 의해서 나오는 softmax결과를 Teacher로 이용했을때 가장 좋은 성능 나왔다, (inductive biases가 들어갔다!)
  • Transformer는 fc를 주력으로 이용한다. conv는 파라미터를 이용하면서 연산해야하므로, 병렬연산이 fc보단 좋지 않다. 따라서 파라미터를 BeiT가 더 많이 사용하기는 하지만, 더 빠른 인퍼런스 시간이 나온다.
  • 추가 기법들! bag of tricks :
    1. Transformer는 Initialization이 매우매우 중요하다. “The effect of initialization and architecture”, NIPS 2018 논문 내용을 사용했다.
    2. Transformer는 위에서 말했듯이, 데이터셋이 많으면 많을수록 좋기 떄문에, 여러여러여러가 data augmentation 기법을 사용했다. (그 기법들은 PPT 9번 참조)
    3. 이미지 resoltuion을 갑자기 늘려서 조금만 Fine-tuning 해주면 성능이 높아진다. resoltuion을 키우고 patch사이즈를 그대로 하면, patch의 갯수가 늘어나고, positional embeding의 갯수가 늘어나야한다. 이때 기존의 positional embedding을 interpolates으로 늘려주는데, bicubic 을 사용해야 그나마 성능이 늘어난다.

2. DeiT 보충 내용

  • Distillation token : Class token과 같은 역할을 한다. 하지만, the (hard) label predicted by the teacher 만으로 학습된다. (Its target objective is given by the distillation component of the loss) = 아래 loss함수의 (Fun) + (Fun)에서 오른쪽 항 만을 사용해서 학습시킨다.

  • Distillation token으로 만들어 지는 Distillation Embeding의 결과도 Classfication Prediction 결과가 나와야 하는데, Teacher모델이 예측하는 결과가 나오면 된다. (이미지 자체 Label에 대해서는 학습되지 않는다.)
    image-20210415204857822


3. DeiT Youtube 필기 내용

img01 img02 img03 img04 img05 img06 img07 img08 img09 img10 img11 img12 img13 img14 img15 img16 img17 img18 img19 img20 img21 img22 img23 img24 img25 img26 img27 img28 img29 img30 img31 img32

【Self】Self-Supervised-Learning & BYOL(Bootstrap Your Own Latent)

【DA】ADVENT-Entropy Minimization for DA



ADVENT

1. Conclusion, Abstract, Instruction

  • Task: unsupervised domain adaptation for semantic segmentation
  • Motivation: Real world와 비교해보면, Data distribution(데이터 분포, 이미지의 통계적 성질)의 많은 차이가 존재한다.
  • Simple observation
    • (1) Source이미지에 대해서, over-confident, low-entropy predictions
    • (2) Target이미지에 대해서, under-confident, high-entropy predictions
    • 과거에는 Pseudo label을 사용해서 Self-Training을 하는 방법, Feature output에 대해서 Adversarial Learning을 적용하는 방법을 사용했지만, 우리는 Target도 high-confident를 예측하게 만듬으로써, domain gap을 줄이는 방법을 고안했다.
      image-20210412193432212
  • 핵심 모듈
    • (1) an entropy loss: target 예측 결과가 high-confident 혹은 low-confident 가 되도록 강제한다. 에매하게 [0,1] 중간 값인 0.5 confident를 가지면 Loss를 준다.
    • (2) entropy-based adversarial training approach : AdaptSeg가 Output을 이용했다면, 여기서는 Output에 대해서 entropy를 계산한 feature map을 이용해서 Adversarial learning을 적용한다. 이로써, Target 예측결과도 Source 예측 결과처럼 적은 Entropy를 갖도록 유도한다.
    • 추가적인 성능향상을 위해서, 아래의 기법을 적용했다.
      • (i) training on specific entropy ranges (Entropy_x_target ∈ [0, 1] (H×W)
      • (ii) incorporating class-ratio priors. (그저 자주 나오는 class가 1 confident가 나와 버리는 문제를 막는다)
  • 성능
    • SOTA / Two main synthetic-2-real benchmarks / GTA5 → Cityscapes / SYNTHIA → Cityscapes.

3. Approaches

image-20210412193417344


3.0 Prerequisite

image-20210412194233568

  • 위와 같은 Segmentation Network를 Source data로 학습시킨다,

3.1.1 Direct entropy minimization

  • 위 전체 Architecture에서 상단부분에 있는 모듈이다.
  • 간접적으로는 Domain adaptation이 되도록 만들지만, 엔트로피 관점의 직접적인 최소화 과정이 수행된다.
  • Target Image에 대해서 high confidence를 추축해내는 F(Network)를 만들어내는 것이 목적이다.
  • prediction output에 대해서, the Shannon Entropy [36]라는 Entropy를 계산하는 공식을 적용해서 Entropy map을 만들어 낸다.
    image-20210412200526378
  • 계산되는 E를 구체적으로 예시를 통해서 알아보자. 아래와 같이 아에 높은 confident, 아에 낮은 confident이여야 낮은 Loss를 얻는다.
    image-20210412195920203
  • 위의 그래프에서 x값과 y값의 범위는 각각, [0,1], [0,0.16] 이다. 위 Entropy를 계산해준 후 y값을 [0,1] 사이의 값으로 정규화해준다. 즉 [0, 0.16] -> [0,1]
  • entropy loss는 soft-assignment version of the pseudo-label이다. 따라서 hard Image dataset에 대해서 좋은 성능을 보여준다. 그리고 threshold를 고르거나 One-hot encoding을 하는 a complex scheduling procedure가 필요없다.
  • 아래의 과정을 통해서 성능을 더욱 끌어올린다.

3.1.2 Self-training = Pseudo Label

image-20210412200649683


3.2 Minimizing entropy with adversarial learning

  • 3.1.1을 보면 c방향으로 sum을 해서 E (W,H)를 구하는 것을 알 수 있다. 이 sum 때문에 자칫하면, 지역에 담긴 구조적인 의존적 의미(? the structural dependencies between local semantics)를 무시할 수 있다. (?)
  • AdaptSeg가 Output을 이용했다면, 여기서는 Output의 각각의 pixel 스스로의 entropy를 계산한 map(weighted self-information space)을 이용해서 Adversarial learning을 적용한다.
  • weighted self-information space=I_x : Shape (C x H x W). image-20210412234837163 (단순하게 생각해서, 그냥 각 픽셀마다 이 연산이 수행됐다고 생각하면 되겠다.) 이것을 사용하는 장점은 아래와 같다.
    • Source에서 출력되는 I_x 와 Target에서 출력되는 I_x가 유사해지도록 만든다.
    • 간접적으로 Entropy를 낮추게 만드는 방법이라고 할 수 있다.
  • Discriminator, Adversarial Loss, Total Loss이다.
    image-20210412235416458

3.3. Incorporating class-ratio priors

  • 위의 방법들 처럼 단순하게 Entropy minimization만 하다보면 한가지 문제점이 생긴다.
  • 그 문제점은 발생 빈도가 높은 class, 쉬운 class를 우선적으로 1로 만들어 버리는 문제이다.
  • ProDA의 degeneration issue라고 생각하면 된다. 따라서 우리는 regularization 을 하는 과정이 필요하다. (ProDA의 방법이 더 좋을 거다. 아래의 방법은 구식인 것 같다)
  • 아래의 수식을 통해서, Class 발생 빈도 Histogram이 Source와 유사하도록 만든다. 여기서 Mu는 얼마나 유사하게 만들것 인가? 를 나타내는 강도 값이다. 0=약하게, 1=강하게. image-20210413000253218
  • the class-prior vector p_s: 각 클래스당 픽셀의 갯수의 L_1 histogram 이다.

4. Experiments

  • Dataset
    • Source: GTA5 or SYNTHIA
    • Traget: Cityscape
    • DA: 2, 975 unlabeled Cityscapes images are used for trainin
    • DA test: Evaluation is done on the 500 validation images
  • Network architecture
    • Deeplab-V
    • VGG-16 and ResNet101
    • Discriminator in DCGAN [28] : 4conv, leaky-ReLU laye
  • Implementation detail
    • single NVIDIA 1080TI GPU with 11 GB memory
    • discriminator: Adam optimizer [19] with learning rate 10−4 to train the discriminator
    • 나머지 모델들: SGD with learning rate 2.5 × 10-4 , momentum 0.9 and weight decay 10-4
  • Weighting factors
    • λ_ent = 0.001
    • λ_adv = 0.001


5. Results

image-20210413000352485



【Transformer+Video】VisTR & TrackFormer & 3D conv



1. VisTR 핵심 및 보충설명

1.1 논문 핵심 정리

  1. Instance segmentation은 하나의 이미지내에서 Pixel-level similarity를 찾내는 Task이다. Instance tracking는 다른 이미지에서 Instance-level similarity를 찾아내는 Task이다. Transformer는 similarity learning에 매우 특화되어 있다. (Why? softmax(Key*Query) 연산을 통해서 구해지는 Attention 값이 결국에는 query와 얼마나 유사한가를 계산하는 것이다)
  2. 전체 Architecture는 아래와 같다. 자세한 내용은 PPT에서 찾아보도록 하자.
    image-20210410222103658
    • 주의할 점으로는, instance queries가 decoder로 들어가서 나오는 최종 결과에 대해서, 각 프레임의 같은 Instance는 같은 i번쨰 query에 의해서 나와야하는 결과들이다. (corresponding indices)
  3. Sequential Network 이기 때문에 발생하는 특징
    • Temporal positional encoding (spatial positional encoding 뿐만 아니라, 이것을 3개 뭉친)
    • 3D Convolution
  4. 36개의 frame이 한꺼번에 들어가므로, 하나의 Sequence of images가 들어가서 총 연산에 걸리는 시간은 조금 걸릴지라도, FPS로 계산하면 상당히 높은 FPS가 나온다.(?)
  5. VisTR가 가지는 특장점들
    • instances overlapping를 매우 잘 잡는다 (DETR에서 코끼리 성능 결과 처럼)
    • changes of relative positions에도 강하다. (카메라가 고정되지 않아도 된다)
    • confusion by the same category instances (비슷한 고릴라, 코끼리 2개 붙어있어도.. 잘 분류하는 편이다)
    • instances in various poses 에 대해서 강하다. (사람이 무슨 포즈를 취하든..)
  6. Summary
    • a much simpler and faster
    • Vedio에 대한 Tracking은 Detection과 Tracking이 따로 진행되기 때문에 이음새가 반듯이 필요한데, VisTR은 seamlessly and naturally 한 모델이다.

1.2. 보충 내용

  • 3D conv란 ??
    image-20210412141401875
  • Dice Loss란? (mask loss를 Focal Loss와 Dice Loss의 합으로 계산된다.)
    image-20210412141200111
  • 굳이 Hungarian Loss?? -> 논문을 봐도 내가 생각한게 맞는 것 같다. 최적의 Assignment를 찾기 위해서 Hungarian algorithm을 쓰고, 찾아진 최적의 matching에 대해서만 Loss를 계산하고 backpro 하는 것이다.
  • Train/Inference Setting
    • AdamW, learning rate 10−4, trained for 18 epochs (by 10x at 12 epochs 마다 decays 되는 비율로)
    • initialize our backbone networks with the weights of DETR
    • 8 V100 GPUs of 32G RAM
    • frame sizes are downsampled to 300×540
  • Faster ??
    • 8개 V100 GPU
    • ResNet-101 backbone -> 27.7 FPS, Image Process 작업 제외하면 57.7 FPS
    • 높은 FPS 원인 (1) data loading을 쉽게 병렬화 처리 할 수 있고 (2) parallel decoding의 가능하고 (3) no post-processing

1.3 코드로 공부해야 할 것들

  • Temporal Positional Encoding
  • Mask를 생성하기 위한, the self-attention module.
  • Self-attetion(O,E)가 B와 Fusion 될때의 연산
  • Deform_conv

2. VisTR PPT 공부자료

img img img img img img img img img img img img img img img img img img img img

3. TrackFormer 핵심 및 보충설명

3.1 논문 핵심 정리

image-20210412155207024

  • Key Idea는 track query embeddings을 적용해서, 위의 그림처럼 auto-regressive manner을 사용하는 것이다.
  • Transformer의 수학적 수식은 위 VisTR 논문 내용과 같다.
  • 전체 순서
    1. DETR을 그대로 사용해서, 첫번째 Frame에 대해서 Object Detection을 수행한다.
    2. 첫번째 Frame에 대해, Object query에 의해서 나온 Object decoder output 중 object로 판정되는 output을, 다음 Frame의 Object query로 사용한다. (N_track) 뿐만 아니라, 추가적인 N_object query도 당연히 추가한다.
    3. 새로운 Frame에 대해서 2번 과정을 반복 수행한다.
    4. 학습은 2개의 Frame씩만 묶어서 학습한다.
  • Track query attention block(= a separate self-attention block) 은 Track queries 끼리 서로 소통하게 만드는 모듈이다. (위 이미지 아래 모듈) 이 모듈을 통해서 t-1 Frame의 Object 위치와 구조에 대한 정보를 그대로 사용하는게 아니게 만들어서, 새로운 Frame에 대한 적응을 좀더 잘하게 만들어 주었다.
  • 신박한 학습 기법들
    1. t-1 Frame을 t 주변의 아무 다른 Frame으로 대체해서 학습시킨다. 객체의 포즈나 위치가 확확 바뀌는 상황에도 잘 적응하게 만들기 위함이다.
    2. 어떠한 query type(track, object)에서도 잘 반응할 수 있게 만들기 위해서, 가끔 track query를 제거해서 다음 Frame Decoder에 넣어준다. 학습 밸런싱을 맞추는 과정이다. 새로 나타난 객체에 대해서도 잘 Detecion 하게 만들어 주는 방법이다.
    3. Track queries에 Background에 대한 output도 넣어준다. (내 생각으로 이 과정을 통해서, Track query attention block 과정을 통해서 object occlusion의 문제점을 완화시켜준다.)
  • DETR에서 Panoptic Sementation 구조를 그대로 차용해서 Instance에 대한 Mask 예측도 수행하고, MOTS 결과도 출력해보았다.

3.2. 보충 내용

image-20210412163825650

  1. Bipartite track query matching에 대한 부분
  2. MOT dataset, Environment Setting에 대해서는 추후에 필요하면 공부해보자.

4. TrackFormer PPT 공부자료

img img img img img img img img img img img img img

【DA】AdaptSegNet-Learning to Adapt Structured Output Space



AdaptSegNet

  • Deeplab V2를 사용했다고 한다. V2의 architecture 그림은 없으니 아래의 V3 architecture 그림 참조
    image-20210410152157113

  • AdaptSegNet 전체 Architecture
    image-20210410152244470

  • 코드를 통해 배운, 가장 정확한 학습과정 그림은 해당 나의 포스트 참조.


1. Conclusion, Abstract

  • DA semantic segmentation Task를 풀기 위해, adversarial learning method을 사용한다.
  • 방법1 : Adversarial learning을 사용한다. in the output space(last feature map, not interval feature map)
  • 방법2 : 위의 그림과 같이 a multi-level에 대해서 adversarial network 을 사용한다. multi-level을 어떻게 사용하는지에 대해서는 위의 Deeplab V3 architecture를 참조할 것.
  • 방법1의 Motivation : 아래 그림과 같이, 이미지 사이에는 많은 차이가 있지만, CNN을 모두 통과하고 나온 Output feature map은 비슷할 것이다.
    • 따러서 Sementic segmentation의 예측결과 output을 사용하여, Adversarial DA과정을 진행함으로써, 좀더 효과적으로 scene layout과 local contect를 align할 수 있다.
      image-20210410152644326

2. Instruction, Relative work

  • pass

3. Method

  • the discriminator D_i (level i): source 이미지로 부터온 prediction Ps인지 Target, 이미지로 부터온 prediction Pt인지 구별한다.
  • target prediction에 대한 Adversarial loss를 적용해서, G(Generator=Feature encoder)가 similar segmentation distribution를 생성하게 만든다.
  • Output Space Adaptation에 대한 Motivation
    1. 과거의 Adversarial 방법들은, 그냥 feature space(맨 위 이미지에서 빨간색 부분=complex representations)를 사용해서 Adversarial learning을 적용했다.
    2. 반면에, segmentation outputs(맨위 이미지에서, hard labeling 작업 이전의 최종 예측 결과값)은low-dimension이지만 rich information을 담고 있다.
    3. 우리의 직관으로 이미지가 source이든 target이든 상관없이, segmentation outputs은 굉장히 강한 유사성을 가지고 있을 것이다.
    4. 따라서 우리는 이러한 직관과 성질을 이용해서, low-dimensional softmax outputs을 사용한 Adversarial Learning을 사용한다.
  • Single-level Adversarial Learning: 아래 수식들에 대해서 간단히 적어보자
    1. Total Loss
    2. Discriminator Loss: Discriminator 만들기
    3. Sementic segmentation prediction network
    4. Adversarial Loss: Generator(prediction network)가 target 이미지에 대해서도 비슷한 출력이 나오게 만든다
      image-20210410155549796
  • Multi-level Adversarial Learning
    • Discriminator는 feature level마다 서로 다른 Discriminator를 사용한다
    • i level feature map에 대해서 ASPP module(pixel-level classification model)을 통과시키고 나온 output을 위 이미지의 (2),(3),(4)를 적용한다.
      image-20210410161814692
  • Network Architecture and Training
    1. Discriminator
      • all fully-convolutional layers, convolution layers with kernel 4 × 4 and stride of 2, channel number is {64, 128, 256, 512, 1},
      • leaky ReLU parameterized by 0.2
      • do not use any batch-normalization layers: small batch size를 놓고 각 source batch, target batch 번갈아가면서 사용했기 때문이다.
    2. Segmentation Network
      • e DeepLab-v2 [2] framework with ResNet-101 [11] model
      • modify the stride of the last two convolution layers from 2 to 1 함으로써, 최종결과가 이미지 H,W 각각이 H/8, W/8이 되도록 만들었다.
      • an up-sampling layer along with the softmax output
    3. Multi-level Adaptation Model
      • we extract feature maps from the conv4 layer and add an ASPP module as the auxiliary classifier
      • In this paper, we use two levels.
      • 위 그림을 보면 이해가 쉽니다.
    4. Network Training(학습 과정)
      1. (Segmentation Network와 Discriminators를 효율적으로 동시에 학습시킨다.)
      2. 위의 Segmentation Network에 Source 이미지를 넣어서 the output Ps를 생성한다.
      3. L_seg를 계산해서 Segmentation Network를 Optimize한다.
      4. Segmentation Network에 Target 이미지를 넣어서 the output Pt를 생성한다.
      5. Optimizing L_d in (2). 위에서 만든 Ps, Pt를 discriminator에 넣고 학습시킨다.
      6. Adversarial Learning : Pt 만을 이용해서 the adversarial loss를 계산하여, Segmentation Network를 Adaptation 시킨다.
    5. Hardware
      • a single Titan X GPU with 12 GB memory
      • the segmentation network
        • Stochastic Gradient Descent (SGD) optimizer
        • momentum is 0.9 and the weight decay is 5×10−4
        • initial learning rate is set as 2.5×10−4
      • the discriminator
      • Adam optimizer
      • learning rate as 10−4 and the same polynomial decay
      • The momentum is set as 0.9 and 0.99

4. Results

  • 성능 결과 및 Ablation study에 대한 내용은 Pass
    image-20210410163443274

【Pytorch】 mmclassification teardown reports & Ubuntu re-install

Ubuntu re-install & mmclassification teardown reports

1. New ubuntu setting List

  1. 우분투 설치
  2. gpu graphic driver 설치
  3. docker, docker-nvidia 설치
  4. Vscode 설치
  5. anaconda 설치
  6. docker image, container 필요한거 다운 및 설치 (MLworkspace, pytorch-cuda)
  7. dataset 다운로드 (coco, imagenet)

2. Docker setting

  • Docker hub naming (참고 사이트)

    • devel : 속편하지만 용량이 너무 큼
    • base : 가장 낮은 용량
    • runtime : 사용하기에 적절한 용량
    • 이유는 모르겠지만 pytorch-1.5.1-cuda10.1-runtime에서는 cat /usr/local/cuda/version.txt가 동작하지 않는다. 그럼에도 불구하고 예전에 detr을 cuda로 돌렸었다. mmclf에서는 cuda문제가 발생했으므로 속편하게 pytorch-1.5.1-cuda10.1-devel 사용해야겠다.
  • docker run –shm-size

    • shared memory 사용 공간 ML-workspace에서 512MB를 주니, 대충 그정도 사용하면 될 듯하다.
  • docker run

    • detr에서 준 dockerfile을 build해서 사용했다. 하지만 cuda 문제가 발생했다.
    • detr에서 준 docker image 부터가 잘못 됐다. 재설치 필요
    • docker hub - pytorch 1.5.0 - devel 을 사용한다. runtime에는 cat /usr/local/cuda/version.txt 가 동작하지 않는다. 맘편하게 devel사용한다.
    • 아래와 같이 -v를 2번 가능하다. <my path>/<container path> 에서, <container path>는 path가 존재하지 않으면 자동으로 mkdir된다.
    $ sudo docker run -d -it      \
    --gpus all         \
    --restart always     \
    -p 8000:8080         \
    --name "mmcf"          \
    --shm-size 2G      \
    -v ./home/junha/docker/mmclf/mmclassification   \ # 가능하면 무조건 절대경로 사용할 것
    -v /hdd1T:/dataset   \
    pytorch/pytorch:1.5.1-cuda10.1-cudnn7-devel
      
    $ sudo docker run -d -it      \
    --gpus all         \
    --restart always     \
    -p 8080:8080         \
    --name "mmcl"          \
    --shm-size 2G      \
    -v /home/junha/docker:/workspace  \
    -v /hdd1T:/dataset   \
    sb020518/mmcl:0.1
    

3. mm installation

  • mmcv installation

    $ apt-get update
    $ apt-get install ffmpeg libsm6 libxext6  -y
    $ pip install mmcv-full==1.3.0 -f https://download.openmmlab.com/mmcv/dist/cu101/torch1.5.0/index.html
    
  • mmclasification install

    $ git clone https://github.com/open-mmlab/mmclassification.git
    $ cd mmclassification
    $ pip install -e .  # or "python setup.py develop"
    
  • 이런 식으로 install하면, 이 공간의 mmcls 파일을 수정하면, import mmcls.util.collect_env 이것을 새로운 py파일에 적든, 터미널에서 python을 실행해서 적든, 모두 수정된 코드 내용이 적용 된다.

  • mmclasification install Test

    python demo/image_demo.py \
    /workspace/checkpoints/dog.jpg  \
    /workspace/configs/resnet/resnet50_b32x8_imagenet.py  \
    /workspace/checkpoints/resnet50_batch256_imagenet_20200708-cfb998bf.pth 
    

4. 코드 공부 흐름 필수

  1. Github 패키지 이곳저곳 돌아다니면서, 전체적인 구조 눈으로 익히기(좌절하기 말기. 핵심만 파악하려 노력하기)
  2. Interence 흐름 따라가기
  3. Tranin 흐름 따라가기
  4. Dataloader 공부하기
  5. Loss, Optimizer, Scheduler 분석하기
  6. Model save load, check point 분석하기
  7. Batch 구성방법, GPU 분산학습 분석하기

[코드에서 다 봐야하는 것] (순서대로 보지 말고. 코드 따라가면서 눈에 보이는 것 부터 부셔버리기)

  • Github로 전체적인 구조 눈으로 익히기
  • Interence 흐름 따라가기
  • Tranin 흐름 따라가기
  • Dataloader 공부하기
  • Loss, Optimizer, Scheduler 분석하기
  • Model save load, check point 분석하기
  • Batch 구성방법, GPU 분산학습 분석하기

5. mmclassification github

  1. Python argparse 사용법

    • parser.add_argument에 대해서, –가 붙여 있는 것은 optional 이다. –가 없는 것은 필수입력인자(positional) 이다.
    • add_argument()에 들어가는 인자가 많다.
    • 그 인자 중, type= 설정해주지 않으면 str이 default
    • 그 인자 중, default= 값이 지정되지 않았을 때, 사용할 디폴트값
  2. config 파일 분석하기 (아래 팁 참조)

    • configs/resnet/resnet50_b32x8_imagenet.py에는 또 다른 config 파일의 path만 기록 되어 있다.
    • 어려울 거 없이, 그 path의 config 파일만 다~ 모아서 cfg에 저장된다.
    • 이쁘게 출력해보고 싶으면, print(f'Config:\n{cfg.pretty_text}')
  3. test.py이든 train.py이든 핵심 호출법은 아래와 같다.

    • model: model = build_classifier(cfg.model)
    • dataset: dataset = build_dataset(cfg.data.test), datasets = [build_dataset(cfg.data.train)]

6. mmclassification/getting_started.md

  • Prepare datasets
    • ImageNet 다운로드. 각 클래스는 같은 폴더에 넣어주기 위한 sh 실행 (참조사이트).
    • MNIST, CIFAR10 and CIFAR100와 같은 데이터 셋은, 만약 데이터셋이 없다면 자동으로 다운 받아진다.
  • Inference a dataset
    • Inference는 이미지를 모델이 넣고 나오는 결과를 직접 눈으로 확인해보는게 목적이다.
    • mmclassification에서는 inference.py 파일 없다.
    • 굳이 하고 싶다면, demo/image_demo.py를 이용하자.
  • Test a datatset
    • Test는 val_img들에 대해서 예측결과를 추론해보는 것이 목표이다.
    • tools/test.py, tools/dist_test.sh
    • dist_test.sh는 결국 tools/test.py 실행한다.
    • $ python tools/test.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] 이거로 먼저 debuging하고 그 다음에 dist_test.sh 파일을 이용해서 multi-gpu testing을 진행하자.
    • $ python tools/test.py configs/resnet/resnet50_b32x8_imagenet.py checkpoints/resnet50_batch256_imagenet_20200708-cfb998bf.pth --out result/imagenet-val.json
  • Train a dataset
    • $ python tools/train.py configs/resnet/resnet50_b32x8_imagenet.py --load-from checkpoints/resnet50_batch256_imagenet_20200708-cfb998bf.pth
      image-20210415100049662

7. tools/test.py 디버깅

  • 이제부터 아래의 과정은 디버깅을 통해 알아낸 내용이다,
  • [Vscode Debugging] : tools/test.py + "args" : ["configs/resnet/resnet50_b32x8_imagenet.py", "checkpoints/resnet50_batch256_imagenet_20200708-cfb998bf.pth "], or "args" : ["configs/resnet/resnet50_b32x8_imagenet.py", "checkpoints/resnet50_batch256_imagenet_20200708-cfb998bf.pth", "--metrics", "mAP", "CP", "CR"]
  • [Terminal] : python tools/test.py configs/resnet/resnet50_b32x8_imagenet.py checkpoints/resnet50_batch256_imagenet_20200708-cfb998bf.pth --out ./result/imagenet-val-resnet50.json --metrics "accuracy" "precision" "recall" "f1_score" "support"
  1. build_dataset/classifier 호출 순서

    # 아래로 내려갈 수록 define으로 들어가는 것
       
    ## 1. dataset
    from mmcls.datasets import build_dataloader, build_dataset
    from mmcv.utils import Registry, build_from_cfg
    return <class 'mmcls.datasets.imagesnet.ImagesNet'> # cfg.dataset_type 에 적힌 class
    # return이 완벽하게 되기 이전에 __init__ 가 실행되면서, 맴버 변수가 채워진다. 이때 pipeline(augmentation)이 정의된다.
       
    ## 2. classifier
    from mmcls.models import build_classifier
    from mmcv.utils import build_from_cfg
    return <class 'mmcls.models.classifiers.image.ImageClassifier'> # cfg.model.type 에 적힌 class
    # return이 완벽하게 되기 이전에 __init__ 가 실행되면서, 맴버 변수가 채워진다. 이떄 backbone, neck, head가 정의된다. 
    
  2. model.eval() 하고, 테드스 결과 뽑아내기

    # tools/test.py
    model = MMDataParallel(model, device_ids=[0])
    outputs = single_gpu_test(model, data_loader)
    
    • mmcv/paralled/MMDataParalle == from torch.nn.parallel import DataParallel
    • 여기서 model = <class 'mmcv.paralled.MMDataParalled' object> 로 바뀐다.
    • dataloader = <class 'torch.utils.data.dataloader.DataLoader' object>
  3. result 결과 뽑기

    # from mmcls.apis import single_gpu_test
    for i, data in enumerate(data_loader):
            with torch.no_grad():
                result = model(return_loss=False, **data)
                   
    """
    type(result) = list 
    len(result) = 32 = batch size = cfg.data.samples_per_gpu
    type(result[0]) = numpy.ndarray
    result[0].shape = (1000,)
    """
    

8.tools/train.py 디버깅

  1. argparse, config 읽고, 에러 미리 확인.

  2. build_ 수행하기

    model = build_classifier(cfg.model)
    datasets = [build_dataset(cfg.data.train)]
    # cpu worker 처리만 해주고
    train_model(
            model,
            datasets,
            cfg,
            distributed=distributed,
            validate=(not args.no_validate),
            timestamp=timestamp,
            meta=meta)
    
  3. from mmcls.apis import train_model

    # /mmcls/apis/train_model
    """
    1. dataloader = `<class 'torch.utils.data.dataloader.DataLoader' object>
    2. model = `<class 'mmcv.paralled.MMDataParalled' object>
    """
    runner = build_runner(
            cfg.runner,
            default_args=dict(
                model=model,
                batch_processor=None,
                optimizer=optimizer,
                work_dir=cfg.work_dir,
                logger=logger,
                meta=meta))
       
    runner.run(data_loaders, cfg.workflow)
    
  4. from mmcv.runner import epoch_based_runner

    • 아래 코드는, 아래로 차근차근 내려가면서 실행된다고 보면 된다.
    # /mmcls/runner/epoch_based_runner
    class EpochBasedRunner(BaseRunner):
        def run(self, data_loaders, workflow, max_epochs=None, **kwargs): # kwargs = {None}, self.model 이미 저장되어있음
            epoch_runner = getattr(self, mode) 
        	epoch_runner(data_loaders[i], **kwargs) 
            # == self.train(data_loaders[i], **kwargs)
        def train()
            for i, data_batch in enumerate(self.data_loader):
                self.run_iter(data_batch, train_mode=True)
        def run_iter(self, data_batch, train_mode, **kwargs):
             outputs = self.model.train_step(data_batch, self.optimizer, **kwargs)
                   
    """
    여기서 model은 
    <class 'mmcls.models.classifiers.image.ImageClassifier' object> 이자
    <class 'mmcv.paralled.MMDataParalled' object> 이다. 
    따라서 아래의 파일에 들어가면 ""train_step"" 함수 찾기 가능!
    """   
    
  5. mmclassification/mmcls/models/classifiers/base.py

    # mmclassification/mmcls/models/classifiers/base.py
    class BaseClassifier(nn.Module, metaclass=ABCMeta):
        def train_step(self, data, optimizer):
            losses = self(**data) #== BaseClassifier.fowerd 실행!
            outputs = dict(loss=loss, log_vars=log_vars, num_samples=len(data['img'].data))
        def forward(self, img, return_loss=True, **kwargs):
            if return_loss:
                return self.forward_train(img, **kwargs)
           
    def forward_train(self, imgs, **kwargs):
    	pass
        # 즉! torch.nn.Module.forward 그대로 실행된다.
    

중요하지만, 사소한 추가 팁들 모음

1. config 파일 넌 무엇인가?!

# test.py에서 디버깅 하면서, cfg파일 읽어보기 
print(f'Config:\n{cfg.pretty_text}')

########## 예를 들어서 이 파일을 봐보자. configs/resnet/resnet50_b32x8_imagenet.py  ##########
_base_ = [
    '../_base_/models/resnet50.py', '../_base_/datasets/imagenet_bs32.py',
    '../_base_/schedules/imagenet_bs256.py', '../_base_/default_runtime.py'
]
########## '../_base_/models/resnet50.py'  ##########
model = dict(
    type='ImageClassifier',
    backbone=dict(
        type='ResNeSt',
        depth=50,
        num_stages=4,
        out_indices=(3, ),
        style='pytorch'),
    neck=dict(type='GlobalAveragePooling'),
    head=dict(
        type='LinearClsHead',
        num_classes=1000,
        in_channels=2048,
        loss=dict(type='CrossEntropyLoss', loss_weight=1.0),
        topk=(1, 5),
    ))


########## '../_base_/datasets/imagenet_bs32.py'  ##########
dataset_type = 'ImageNet'
img_norm_cfg = dict(
    mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
# 원하는 Transformer(data agumentation)은 아래와 같이 추가하면 된다.
train_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(type='RandomResizedCrop', size=224),
    dict(type='RandomFlip', flip_prob=0.5, direction='horizontal'),
    dict(type='Normalize', **img_norm_cfg),
    dict(type='ImageToTensor', keys=['img']),
    dict(type='ToTensor', keys=['gt_label']),
    dict(type='Collect', keys=['img', 'gt_label'])
]
test_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(type='Resize', size=(256, -1)),
    dict(type='CenterCrop', crop_size=224),
    dict(type='Normalize', **img_norm_cfg),
    dict(type='ImageToTensor', keys=['img']),
    dict(type='Collect', keys=['img'])
]
# 위에 내용은 이 아래를 정의하기 위해서 사용된다. cfg.data.test, cfg.data.val 이 사용된다.
data = dict(
    samples_per_gpu=32,
    workers_per_gpu=2,
    train=dict(
        type=dataset_type,
        data_prefix='data/imagenet/train',
        pipeline=train_pipeline),
    val=dict(
        type=dataset_type,
        data_prefix='data/imagenet/val',
        ann_file='data/imagenet/meta/val.txt',
        pipeline=test_pipeline),
    test=dict(
        # replace `data/val` with `data/test` for standard test
        type=dataset_type,
        data_prefix='data/imagenet/val',
        ann_file='data/imagenet/meta/val.txt',
        pipeline=test_pipeline))
 evaluation = dict(interval=1, metric='accuracy')


########## '../_base_/schedules/imagenet_bs256.py'  ##########
optimizer = dict(type='SGD', lr=0.1, momentum=0.9, weight_decay=0.0001)
optimizer_config = dict(grad_clip=None)
# learning policy
lr_config = dict(policy='step', step=[30, 60, 90])
runner = dict(type='EpochBasedRunner', max_epochs=100)


########## '../_base_/default_runtime.py'  ##########
# checkpoint saving
# 1번 epoch씩 모델 파라미터 저장
checkpoint_config = dict(interval=1)
# yapf:disable
# 100번 iteration에 한번씩 log 출력 
log_config = dict(
    interval=100,
    hooks=[
        dict(type='TextLoggerHook'),
        # dict(type='TensorboardLoggerHook')
    ])
# yapf:enable
dist_params = dict(backend='nccl')
log_level = 'INFO'
load_from = None
resume_from = None
workflow = [('train', 1)] # [('train', 2), ('val', 1)] means running 2 epochs for training and 1 epoch for validation, iteratively.

2. Registry는 모두 안다. 난 고르기만 하면 된다.

  1. mmcv.utils.Registry 사실 맴버 변수는 2개 뿐이다,

  2. self._name = ‘backbone’

  3. self._module_dict = 아래 그림 4번처럼 모든 backbone을 다 알고 있다!! 어떻게 알지??

  4. 아래 그림 2번과 같이 정의된 class를 모두 담고 있는 것이다.
    image-20210413222450328

  5. @BACKCONES.register_modules()* 은 언제 실행되는 것 일까? 초반에 import할때 모두 실행된다. 여기*에다가 Breakpoint 걸고 디버깅한 결과는 아래와 같다.

    # test.py
    import mmcls.models
    import mmcls.models.__init__
    import mmcls.models.backbone.__init__
    from .regnet import RegNet
    

3. checkpoint = load_checkpoint(model, args.checkpoint) 결과는?

  • type( checkpoint ) = dictionary
  • Netron 이용해서 Visualization 하자

image-20210414120459711

4. 모델 파라미터 가져오기 - “어차피 텐서다 쫄지말자”

image

  • How to load part of pre trained model?

    pretrained_dict = ...
    model_dict = model.state_dict()
      
    # 1. filter out unnecessary keys
    pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict}
    # 2. overwrite entries in the existing state dict
    model_dict.update(pretrained_dict) 
    # 3. load the new state dict
    model.load_state_dict(pretrained_dict)
    

5. mmcls/models/backbones/ResNet.py forward 를 실행하기 까지의 과정은?

  • tools/test.py의 가장 마지막이, single_gpu_test 함수에 들어가는 것이었다.
  • mmcls/models/backbones/ResNet.py 에다가 breakpoint 걸어서 디버깅 해본 결과, 아래와 같은 코드 흐름을 확인할 수 있었다.
  • 알아만 두자.
    image-20210415092626508

6. python getattr()

# mmcv.runner.epoch_based_runner.py
# ** 아래의 2 코드는 같은 말이다. ** if mode == str('train') **
epoch_runner = getattr(self, mode) 
epoch_runner = self.train()
  • 따라서 디버깅으로 EpochBasedRunner을 확인해보면 train 맴버함수 (Github link)를 가지고 있는 것을 알 수 있다.

【Pytorch】 DETR code teardown reports / 핵심 배운점 2개

  • DETR github : https://github.com/facebookresearch/detr
  • Remarks
    1. Dockerfile 제공 (사용하지 말기)
    2. Demo code 제공
  • 한글 주석 포함 github 링크
  • 의문 정리 (검색하기)
    • demo에서 0.1을 왜 곱해주지??

    • coco_evaluator 를 어떻게 사용하는건지 패스했다. 나중에 공부해보자.

    • multihead attention에서 width height가 계속 다르게 들어가는데, W(linear)를 어떻게 학습시키지? linear도 input, output dimension을 고정시켜줘야 할 텐데…

      • 굳이 이해하려 하지말고, torch.nn.MULTIHEADATTENTION 그대로 사용하자.

      • vector의 갯수는 상관없다. 그냥 key, value 적당한 차원으로 넣어주면 된다.

      • 예를 들어서 아래와 같은 코드가 있다. 아래에 주석 참조!

        class TransformerEncoderLayer(nn.Module):
            def __init__(self, d_model=512, nhead=8, dim_feedforward=2048, dropout=0.1,
                         activation="relu", normalize_before=False):
                super().__init__()
                self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
                self.linear1 = nn.Linear(d_model, dim_feedforward) 
                # (512 -> 2048) x (feature_map width x height) 이 모든 것에 같은 linear 연산이 이뤄진다.
                # self.linear1의 weight.shape = 512x2048 
                      
            def forward_post(self,
                q = k = self.with_pos_embed(src, pos)
                src2, score = self.self_attn(q, k, value=src, attn_mask=src_mask,
                                      key_padding_mask=src_key_padding_mask)
                src = src + self.dropout1(src2)
                src = self.norm1(src)
                # 2. Feed Forward
                src2 = self.linear2(self.dropout(self.activation(self.linear1(src))))
        
  • 의문 해결

    • detr.py에 num_classes = 91 if args.dataset_file != 'coco' else 20num_classes = 20 if args.dataset_file != 'coco' else 91 로 바꾸면, 왜 main.py 에서 디버깅 실행하면 from models import build_model 에서 에러가 나는 거지? 에러내용은 아래 사진 (해결 - 반성하자)
  • 추신

    • 아래의 목차 순서 잘 분리해놓았으니, 무시하지 말고 꼭 신경쓰기

0. 핵심 배운점

0.1 디버깅 배운점 (1)

  • 나의 현재 문제점
    1. 코드(main.py)를 처음부터 똑바로 안봤다. torch.utils.data.dataset이 아니라, build_dataset이었는데.. 아무 생각 없이 패스했다.
    2. dataloader의 파라미터 중에서 collate_fu이 정말 중요했는데, 이것도 무시했다.
    3. 이건* 나중에 보자!라는 마음으로 패스하고, 나중에 그걸* 안봐서 너무 힘들었다.
    4. 내가 지금 뭐하지는 모르고 계속 했던거 또하고 했던거 또했다.
  • 해결책
    1. 정말 하나하나! 꼼꼼히! 순서대로! 부셔야 한다.
    2. 그냥 아무 생각없이 디버깅 하기 전에 아래의 방법** 중 지금의 에러를 해결하기 위해 사용해야하는 방법이 무엇인지 진지하게 생각하고! 들어가자. 손부터 움직이지 말고 머리를 쓰자.
    3. 좌절하지 말자. 포기하지 말자. 할 수 있다. 아자아자
  • 최적의 디버깅 방법**이 무엇인가?
    • 코드 직접 수정하기
    • pdb 이용하기 / pdb의 interact 이용하기
    • python debuger 이용하기 / Watch, Debug console이용하기
    • 코드 전체에서 (특정 클래스, 함수) 검색해보기 / VScode의 define(F12)과 reference(shift+F12) 잘 이용하기
    • (위의 방법 섞기)
    • 코드를 직접 수정하기 + pdb이용하기
    • 코드를 직접 수정하기 + Python debuger를 이용하기
  • 신경망에 있어서 디버깅 방법
    • __init__ 과 forward가 실행되는 시간 차는 매우 다르다. 따라서 분리해서 생각하도록 하자.
    • forward는 for x,y in data_loader: 이후에 실행이 된다. 그 이전에 model을 build하는 과정에서는 모두 __init__만 실행된다.
  • 추가 꿀팁
    1. 뭔가 안된다면, worker = 1 로 해놓고 디버깅 할 것.
    2. 왜 안되는지 앞으로 따라가 보고, 앞으로 따라가도 모르겠으면
    3. 뒤로 가야한다. 뒤로가서 무엇을 하고 나서 이상이 생긴건지.

0.2 개발자로써 배운점 (2)

  • 4일 동안 DETR 코드를 하나하나 씹어먹으며 공부하다가, 결국 나는 아래와 같은 큰~~!! 깨달음을 얻었다.
  • 코딩 공부는 고등학교 시험공부, 대학교 시험공부 처럼 하는게 아니다.
  • Just Do It. 뭐든 만들어 보고, 뭐든 시도해봐야한다. (by nomadcoders 니꼬쌤 . 나의 동기, 선배님!)
  • 따라서! 아래와 같은 생각은 틀렸다. 그냥 100프로 틀렸다.
    • 모든 것 하나하나 다 부셔버리고, 다 알고 넘어가야 한다.
    • 기초부터 차근차근 모두 다 알아야 한다.
    • 나중에 내가 스스로 만들 수 있게 충분히 이해해야 한다.
    • 언젠간 내가 처음부터 끝까지 만들 수 있어야 한다.
  • 이게 아니고! 아래와 같이 생각하는게 정답이다. 이게 무조건 맞다. 100프로 이다.
    • 그냥 사용할 수 있으면 장땡이다. 대충 그런것 같은데… 라고 input 넣어서 원하는 output 얻었으면 끝! 더 이상 볼 필요도 없다.
    • 왜냐면 어차피 그 함수, 프레임워크 미친듯이 공부하고 완벽히 이해했다고 해도, 곧! 언젠가! 그 함수 프레임워크 보다 좋은게 나온다.
    • 그럼 내가 기초부터 쌓았다고 좋아했던 모든 시간들이 쓰레기가 된다.
    • 개발자는 기초 따위는 중요하지 않다. 파이썬 쓰면서 C언어 안 보잖아. numpy쓰면서 그 안에 어떻게 돌아가는지 몰라도 되잖아. 기초가 중요하다는 소리는 이제 나에게 필요없는 소리다.
    • 빨리 무언가 만들어보고 실험해보는 자세가 앞으로 개발자로써 내가 가져야 하는 진짜 자세이다.
    • 패키지는 내가 만드는게 아니다. 내가 해야할 것은 남이 만든 패키지를 이리저리 섞는 것이다. 남이 만든 패키지도 결국 누군가가 만들어 놓은 것을 융합해놓은 것일 뿐이다.
    • ‘이러한 기능을 하는 함수/클래스가 필요한데..’ 라는 생각이 들면, 패키지를 처음부터 끝까지 공부하면서 그 함수/클래스를 찾아야 하는게 아니라, 많은 패키지에서 그 함수와 클래스를 (랜덤적으로 뒤져보면서) 결국 찾아내서 사용하기만 하면 끝이다.
    • 정신 차려라 개발자가 기초를 생각하기엔 쇠퇴 및 발전 속도는 미치도록 빠르다.

1. detr_demo.py. Inference 흐름 따라가기

  • Github/facebookresearch/detr Code link

  • Error 해결

    $ pip install torch==1.5.0+cu101 torchvision==0.6.0+cu101 -f https://download.pytorch.org/whl/torch_stable.html 
    # docker Image 그대로 사용했어야 했다. requirements.txt가 개세끼다
    $ conda install -c conda-forge ipywidgets
    $ pip install pdbpp
    
  • 주요 코드 설명

    1. h = self.transformer(pos + 0.1 * h.flatten(2).permute(2, 0, 1), self.query_pos.unsqueeze(1)).transpose(0, 1)
      • 위의 transformer 함수 내부 : ((625, 1, 256) + 0.1 * (625(갯수), 1, 256(백터) , (100, 1 ,256)) : 0.1을 왜 곱해주지??
      • transformer ouput : (100, 1 ,256)
      • h.shape : (by transpose) : (1, 100, 256)
  • 이 코드는 nn.transformer를 그대로 사용하기 때문에.. 여기서 그만 공부..

SmartSelect_20210418-161421_Samsung Notes.jpg


2. Evaluation. Inference 흐름 따라가기

  • main.py는 train.py 코드도 되고, test.py 코드도 된다. --eval 옵션만 넣고 빼주면 된다.
[Terminal]
$ python main.py \
		--batch_size 1\
        --no_aux_loss\
        --eval\
        --resume https://dl.fbaipublicfiles.com/detr/detr-r50-e632da11.pth
        --coco_path /workspace/coco # /path/to/coco

$ python main.py \
       	--batch_size 2\
        --no_aux_loss\
        --eval\
        --resume checkpoints/detr-r50-e632da11.pth\
        --num_workers 4\
        --world_size 2\
        --coco_path /dataset/coco\
        --output_dir result

[Vscode Debugging] 
# ./vscode/launch.json
"args" : ["--batch_size", "2", 
        "--no_aux_loss", 
        "--eval", 
        "--resume", "checkpoints/detr-r50-e632da11.pth", 
        "--num_workers", "4",
        "--world_size", "2",
        "--coco_path", "/dataset/coco",
        "--output_dir", "result"]

2.0 main.py 분석해보기

  • print(args)

    - Namespace(aux_loss=False, backbone='resnet50', batch_size=1, bbox_loss_coef=5, clip_max_norm=0.1, coco_panoptic_path=None, coco_path='/workspace/coco', dataset_file='coco', dec_layers=6, device='cuda', dice_loss_coef=1, dilation=False, dim_feedforward=2048, dist_url='env://', distributed=False, dropout=0.1, enc_layers=6, eos_coef=0.1, epochs=300, eval=True, frozen_weights=None, giou_loss_coef=2, hidden_dim=256, lr=0.0001, lr_backbone=1e-05, lr_drop=200, mask_loss_coef=1, masks=False, nheads=8, num_queries=100, num_workers=2, output_dir='', position_embedding='sine', pre_norm=False, remove_difficult=False, resume='https://dl.fbaipublicfiles.com/detr/detr-r50-e632da11.pth', seed=42, set_cost_bbox=5, set_cost_class=1, set_cost_giou=2, start_epoch=0, weight_decay=0.0001, world_size=1)
    
  • 코드 순서 요약하기

    1. args 설정 및 seed 설정
    2. model, criterion, postprocessors = build_model(args)
    3. 총 파라메터 갯수 계산 하기
    4. backbone / Transformer-encoder, decoder / detector head 각각의 learning rate를 다르게 주기
    5. optimizer와 lr_scheduler 설정
    6. data_loader 만들어 주기 - 1. dataset 정의 , 2. batchSampler 정의
    7. coco_api 를 사용한 dataset validation = evaluate AP 계산하는 방법
    8. Model Parameters load 하기(1) - frozen_weights 경로가 있을 경우 (= panoptic segmentaion 모듈만 학습시키고 싶은 경우)
    9. Model Parameters load 하기(2.1) - Evaluate 하기 위한 파라메터
    10. Model Parameters load 하기(2.2) - Train 하기 위한 파라메터
    11. Evaluation만 하고 코드 종료(return) (1) model infernece하기 (2) coco api로 AP 구하기
    12. If args.eval=False 이라면, 바로 위 코드 안하고, Traiining 시작하기

2.1 datasets/coco.py /build_dataset()

  • torchvision.datasets.CocoDetection 를 상속한다. (링크) 여기서! target_transform 가 의미하는 것은, (Image, label) 쌍에서 image augmentation만해주는게 아니라, label에 대한 변형(agumentation)까지도 해주는 것을 의미한다.

  • dataset/coco.py 안의 make_coco_transforms함수에서 Transforms(agumentation)을 정의한다. 이때 dataset/transforms.py 에 정한 Transformer만을 사용한다. 여기서 Transforms(agumentation)을 정의하는 것을 보면, 다 공통적으로 아래와 같은 구조이다. 따라서 내가 구현하고 싶은 Agumentation이 존재한다면 참고해서 만들도록 하자.

    class New_Transformer_Name(object):
        def __init__(self, p=0.5):
            # just 맴버 변수 정의
    	def __call__(self, img, target=None):
            return img, target
    
  • Backbone에 이미지가 들어가기 전에, DataLoader에서 나오는 변수의 타입이 type(Image, label) = (torch.tensor, dict) 이었다가 (NestedTensor, dict) 으로 바뀐다. 그 이유는 torch.utils.dataloader의 파라메터인 collate_fn=utils.collate_fn 덕분이다.

  • dataset_val에는 build_dataset()으로 부터 받은 torch.utils.dataset클래스이다. 따라서 transforms가 적용된 후! collate_fu이 적용되는 것이다.

    ## main.py 아래의 collate_fn=utils.collate_fn !! 덕분이다.
    data_loader_val = DataLoader(dataset_val, args.batch_size, sampler=sampler_val,
                                     drop_last=False, collate_fn=utils.collate_fn, num_workers=args.num_workers)
    ## util/misc.py
    def collate_fn(batch):
        # batch[0] : len이 batch_size인 tuple. Image 정보 들어가 있다. batch[0][0]에는 torch tensor가 들어가 있다.
        # batch[1] : len이 batch_size인 tuple. label 정보 들어가 있다. batch[1][0]에는 dict()가 들어가 있다. dict_keys(['boxes', 'labels', 'image_id', 'area', 'iscrowd', 'orig_size', 'size'])
        batch = list(zip(*batch))
        batch[0] = nested_tensor_from_tensor_list(batch[0])
        return tuple(batch)
      
    def nested_tensor_from_tensor_list(tensor_list: List[Tensor]):
        return NestedTensor(tensor, mask)
      
    ## datasets/transforms.py
    def resize(image, target, size, max_size=None):
        """
        이미지에서 (w,h 중) 작은 쪽을 size(val일때 800) 크기로 변환해준다. 
        작은 쪽을 800으로 변한해서 큰쪽이 max_size보다 커치면, 큰쪽이 max_size가 되도록 이미지를 변환한다.
        """
    
  • collate_fn 함수는 misc.py의 Nested_tensor_from_tensor_list() 함수에 의해서 정의 되어있다.
    SmartSelect_20210418-161447_Samsung Notes.jpg

2.2 models/detr.py

  • 아래로 내려가면서, 함수가 호출된다. build에서 핵심적인 내용반 뽑았다.
# main.py
from models import build_model
model, criterion, postprocessors = build_model(args)

# models/detr.py
def build(args):
    backbone = build_backbone(args)
    transformer = build_transformer(args)
    model = DETR(backbone, transformer, num_classes=num_classes, num_queries=args.num_queries, aux_loss=args.aux_loss)
    matcher = build_matcher(args)
    criterion = SetCriterion(num_classes, matcher=matcher, weight_dict=weight_dict, eos_coef=args.eos_coef, losses=losses)
    postprocessors = {'bbox': PostProcess()}
    return model, criterion, postprocessors

따라서 아래의 과정을 따라서 차근차근 공부해나갈 예정이다.

  1. build_dataset()
  2. build_backbone()
  3. build_transformer()
  4. model = DETR()
  5. build_matcher()
  6. SetCriterion()
  7. PostProcess()

2.2.1 models/position_encoding.py /build_position_encoding()

  • 2가지 방법 embeding 방법이 존재한다.]
  1. Leaned - Positional Embeding
    • nn.Embedding, nn.init.uniform_ 를 사용해서 row_embed, col_embed 변수 정의하기
    • pos = torch.cat([x_emb.unsqueeze(0).repeat(h, 1, 1),y_emb.unsqueeze(1).repeat(1, w, 1)], dim=-1).permute(2, 0, 1).unsqueeze(0).repeat(x.shape[0], 1, 1, 1)
    • (demo) 최종 pos의 shape : (N장, 256, 25, 25) 여기서 25는 feature map의 width, hight
  2. sinusoidal - Positional Embeding
    • 어려울 것 없이, 아래의 4. Additions. Positional Embeding Code 와 똑같은 코드이다. 이 코드에 의해서도 shape( #Vector, Vector_dim )이 만들어진다. 아래에서 최종적으로 만들어진 pos_x , pos_y 또한 똑같다.
    • pos_x : shape[2, 28, 38, 128] -> 여기서 28개 모두 같은 값이고, 결국 만들어진 것은 38*128이다. (아래의 4. Additions 참조)
    • pos_y : shape[2, 28, 38, 128] -> 여기서 38개 모두 같은 값이고, 결국 만들어진 것은 28*128이다. (아래의 4. Additions 참조)
    • (main.py debug) 최종 pos의 shape : (N장, 256, 28, 38) 여기서 25는 feature map의 width, hight
      SmartSelect_20210418-161517_Samsung Notes.jpg

2.2.2 models/backbone.py /build_backbone()

def build_backbone(args):
    # 핵심 module 정의 1. position_embedding
    position_embedding = build_position_encoding(args)
    
    # 핵심 module 정의 2. backbone
    train_backbone = args.lr_backbone > 0
    return_interm_layers = args.masks
    backbone = Backbone(args.backbone, train_backbone, return_interm_layers, args.dilation) # torchvision.models

    # nn.Sequential 로 2개의 모듈 묶어주기
    model = Joiner(backbone, position_embedding)

    # model의 type은 nn.Sequential 이기 때문에, 아무런 맴버변수가 없다. 아래에 의해서 클래서 맴버변수 하나 만들어 놓음
    model.num_channels = backbone.num_channels
    return model


class Joiner(nn.Sequential):
    def __init__(self, backbone, position_embedding):
        super().__init__(backbone, position_embedding)

    def forward(self, tensor_list: NestedTensor):
        xs = self[0](tensor_list) # self.backbone(tensor_list)
        # xs['0'].tensors.shape = torch.Size([2, 2048, 28, 38])
        # 만약에 resnet 중간에 feature map을 뽑아 왔다면, xs['1'], xs['2'], xs['3'] 순차적으로 저장된다. 
        out: List[NestedTensor] = []
        pos = []
        for name, x in xs.items():
            out.append(x)
            # position encoding
            pos.append(self[1](x).to(x.tensors.dtype)) # self.position_embedding(x)

        # 여기서 0은 backbone에서 가장 마지막 C4 layer에서 뽑은 결과
        # out[0].tensors.shape = torch.Size([2, 2048, 28, 38]). out[0].mask 있음
        # pos[0].shape = torch.Size([2, 256, 28, 38])
        return out, pos

2.2.3 models/transformer.py /build_transformer()

def build_transformer(args):
    return Transformer()

class Transformer(nn.Module):
	def __init__(self, d_model=512, nhead=8, num_encoder_layers=6,
                 num_decoder_layers=6, dim_feedforward=2048, dropout=0.1,
                 activation="relu", normalize_before=False,
                 return_intermediate_dec=False):

        encoder_layer = TransformerEncoderLayer(d_model, nhead, dim_feedforward,
                                                dropout, activation, normalize_before)
        encoder_norm = nn.LayerNorm(d_model) if normalize_before else None
        self.encoder = TransformerEncoder(encoder_layer, num_encoder_layers, encoder_norm)

        decoder_layer = TransformerDecoderLayer(d_model, nhead, dim_feedforward,
                                                dropout, activation, normalize_before)
        decoder_norm = nn.LayerNorm(d_model)
        self.decoder = TransformerDecoder(decoder_layer, num_decoder_layers, decoder_norm,
                                          return_intermediate=return_intermediate_dec)
        
        # 모델의 전체 파라미터 초기화를 아래와 같이 한다. 잘 알아두기.
        self._reset_parameters()
	def _reset_parameters(self):
        for p in self.parameters():
            if p.dim() > 1:
                nn.init.xavier_uniform_(p)

image-20210418173318543

  1. 모델 전체 파라미터 초기화 nn.init.xavier_uniform_(p)
  2. 위의 코드처럼, models/transformer.py에 정의된 아래의 함수들을 순차적으로 사용한다. 차근차근 알아가보자.
    1. TransformerEncoderLayer
    2. TransformerEncoder
    3. TransformerDecoderLayer
    4. TransformerDecoder
  3. 코드 전체에서 attn_mask tgt_mask 변수는 사용되지 않는다.(=None 이다) 이 Mask 기법은 All you need attention 논문에서, decoder의 sequencial한 input과 output을 구현해 학습시키기 위해서 사용되는 변수이다. 자세한 내용은 아래 4. Attention 부분의 내용을 참조할 것.
  4. 대신 key_padding_mask 변수 은 사용한다. 배치에 들어간 모든 이미지를 한꺼번에 처리하기 위한 Transformer를 구현하기 위해, 배치 속에서 상대적으로 작은 이미지는 pading된다. 이 pading된 값에 대해서는 Attention을 구하면 안되므로, Mask처리를 해준다. Value도 0이다. 위의 Nested_tensor_from_tensor_list 내용에 있는 손 그림 참조.

image-20210418173519152

  1. MultiheadAttention

    • torch.nn.module.MultiheadAttention
    • 사용 예시 self.self_attn = nn.MultiheadAttention(d_model=512, nhead=8, dropout=dropout)
      image-20210418193545367
  2. TransformerEncoderLayer

    • MHA 이나 Feed Forward하기 전에 Normalization 하기 = forward_pre로 구현
    • MHA 이나 Feed Forward한 후에 Normalization 하기 = forward_post로 구현
      image-20210418200621077
    • dropout은 임의로 노드를 잠궈버리는 것. 즉 Zero로 만들어 버리는 것이다. 따라서 dropout의 forward를 위한 파라미터는 노드를 넣어줘야한다. (코드 예시. src = src + self.dropout1(src2))
  3. TransformerEncoder

    • 같은 TransformerEncoderLayer를 서로 다른 layer로 구성하기 위해서 아래와 같은 코드기법을 사용했다.

      def _get_clones(module, N):
          return nn.ModuleList([copy.deepcopy(module) for i in range(N)])
           
      class TransformerEncoder(nn.Module):
          def __init__(self, encoder_layer, num_layers, norm=None):
              super().__init__()
              self.layers = _get_clones(encoder_layer, num_layers)
                   
          def forward(self, src,
                      mask: Optional[Tensor] = None,
                      src_key_padding_mask: Optional[Tensor] = None,
                      pos: Optional[Tensor] = None):
              output = src
              for layer in self.layers:
                  output = layer(output, src_mask=mask, src_key_padding_mask=src_key_padding_mask, pos=pos)
      
  4. TransformerDecoderLayer

    • MHA 이나 Feed Forward하기 전에 Normalization 하기 = forward_pre로 구현
    • MHA 이나 Feed Forward한 후에 Normalization 하기 = forward_post로 구현
    • 그림은 나중에 추가할 예정(코드 미리 쪼개고 분석해놨으므로 금방한다.)
  5. TransformerDecoder

    • 위의 TransformerEncoder 코드와 같이 _get_clones 함수를 이용한다.
    • 나머지는 별거 없다.
    • 최종 Output= [100, 2, 256] * 6개 (decoder layer에서 나온 결과 모두) = torch.Size([6, 100, 2, 256])

2.2.4 models/detr.py /DETR()

self.class_embed = nn.Linear(hidden_dim, num_classes + 1) # 91 + 1 -> 92

self.bbox_embed = MLP(hidden_dim, hidden_dim, output_dim=4, num_layers=3)

# Backbone을 통과하고 나오는 결과의 Channel은 1024인데, Channel Reduction을 해준다. (to 256)
self.input_proj = nn.Conv2d(backbone.num_channels, hidden_dim, kernel_size=1)

2.2.5 models/matcher.py /build_matcher()

return HungarianMatcher(cost_class=args.set_cost_class, cost_bbox=args.set_cost_bbox, cost_giou=args.set_cost_giou)

class HungarianMatcher(nn.Module):
    def __init__(self, cost_class: float = 1, cost_bbox: float = 1, cost_giou: float = 1):
        super().__init__()
        self.cost_class = cost_class # 1
        self.cost_bbox = cost_bbox # 5
        self.cost_giou = cost_giou # 2
   @torch.no_grad()
   def forward(self, outputs, targets):
		C = self.cost_bbox * cost_bbox + self.cost_class * cost_class + self.cost_giou * cost_giou
        C = C.view(bs, num_queries, -1).cpu() # torch.Size([2 batch, 100 예측 object, 21 GT object])
        
        sizes = [len(v["boxes"]) for v in targets] #  [img1 num_box, img2 num_box]
        indices = [linear_sum_assignment(c[i]) for i, c in enumerate(C.split(sizes, -1))]
  • forward 앞에 @torch.no_grad() 가 정의되어 있다. 이런 식으로 어떤 특정 layer, module에만 @torch.no_grad()처리를 해줄 수 있다.
  • 아래의 그림에서 보라색 별이 indices 의 결과로 나온 값이다.
  • indices는 (batch, all_predict_obect, all_GT_obect) 으로 구성되어 있다. all_GT_obect 각각이 all_predict_obect 중 어디에 매칭되어야 하는지에 대한 정보가 들어가 있다.
  • linear_sum_assignment가 the Hungarian algorithm 을 사용해서, 최적의 매칭을 찾아준다. Hungarian algorithm을 사용하지 않고 Naive한 방법을 사용한다면, O(n!) (100P#GTobjec0=1.043196014E+41)s의 매칭을 고려하여 최적의 매칭을 찾아야 하지만, 헝가리안 알고리즘을 사용해서 O(n^3 = 10^e6) 의 시간복잡도 안에 최적의 매칭을 찾을 수 있다.
  • 아래의 그림에서 batch_Image 1에는 GT object가 20개가 있다. 100개의 예측 중, 20개는 object중 80개는 non-object로 분류된다.

image-20210419111125329

2.2.6 models/detr.py /class SetCriterion(nn.Module)

class SetCriterion(nn.Module):
    def __init__(self, num_classes, matcher, weight_dict, eos_coef, losses):
    def loss_labels(self, outputs, targets, indices, num_boxes, log=True):
        # 'loss_ce': Classification loss Using F.cross_entropy
        # 'class_error': Score만을 봤을 때, 정확한 예측을 한 예측의 겟수 비율을 점수로 계산 수 100점을 빼주어 error계산한다. 
    def loss_cardinality(self, outputs, targets, indices, num_boxes):
        # 100개의 예측 증 object라고 판단한 예측의 갯수가 target과 같은지 l1_loss를 계산한다
    def loss_boxes(self, outputs, targets, indices, num_boxes):
        # 'loss_bbox': l1_loss
        # 'loss_giou': using loss_giou
    def get_loss(self, loss, outputs, targets, indices, num_boxes, **kwargs):
        # 위의 3개의 loss 중에서 선택된 하나의 loss를 계산하여, dict에 값을 넣어주는 형태로, loss 값 return 해준다.
    def forward(self, outputs, targets):
        losses = {}
        for loss in self.losses: # type(loss) = (str)
            losses.update(self.get_loss(loss, outputs, targets, indices, num_boxes))
        return losses
    	'''
    	return 되는 예시.
    	losses :
    	{'cardinality_error': tensor(3.5000, devic...='cuda:0'), 
    	'class_error': tensor(23.8095, devi...='cuda:0'), 
    	'loss_bbox': tensor(0.0446, devic...='cuda:0'), 
    	'loss_ce': tensor(0.5508, devic...='cuda:0'), 
    	'loss_giou': tensor(0.4350, devic...='cuda:0')}
    	'''

2.2.7 models/detr.py /class PostProcess(nn.Module):

  • the model’s output format을 coco api에서 필요한 format으로 바꾸는 클래스이다.
class PostProcess(nn.Module):
    @torch.no_grad()
    def forward(self, outputs, target_sizes):
        out_logits, out_bbox = outputs['pred_logits'], outputs['pred_boxes']
        ## 중간 계산 절차를 거친다.
        results = [{'scores': s, 'labels': l, 'boxes': b} for s, l, b in zip(scores, labels, boxes)] 
        return results        
  • batch 2일 때, results의 결과 예시
    image-20210419152739090

2.3 engine.py /def evaluate()

# main.py
model, criterion, postprocessors = build_model(args)
test_stats, coco_evaluator = evaluate(model, criterion, postprocessors, data_loader_val, base_ds, device, args.output_dir)

# engine.py /def evaluate()
def evaluate(model, criterion, postprocessors, data_loader, base_ds, device, output_dir):
    for samples, targets in metric_logger.log_every(data_loader, 10, header):
        outputs = model(samples) # ['pred_logits'] ([2, 100, 92]),['pred_boxes'] ([2, 100, 4])
        metric_logger.update(loss=sum(loss_dict_reduced_scaled.values()),
                             **loss_dict_reduced_scaled,
                             **loss_dict_reduced_unscaled)
        metric_logger.update(class_error=loss_dict_reduced['class_error'])
    # 2500개의 validataion 이미지가 모든 계산된 이후에 아래의 작업이 수행된다.
    stats = {k: meter.global_avg for k, meter in metric_logger.meters.items()}
    return stats, coco_evaluator
    # 계산된 loss들이 stats에 들어가 반횐되고,
    # coco_evaluator는 나중에 필요하면 공부하자

3. Train 흐름 따라가기

똑같이 main.py 함수를 사용한다. 그리고 위에서 사용한 함수와 클래스들을 그대로 사용한다.

차이점은, Evaluation에서 사용하지 않던 값들은 여기 Train에서는 사용한다.

# main.py
for epoch in range(args.start_epoch, args.epochs):
    train_stats = train_one_epoch(model, criterion, data_loader_train, optimizer, device, epoch, args.clip_max_norm)
    lr_scheduler.step()
    # 이후에 checkpoint_paths에 .pth 파일 저장
    
# engine.py /train_one_epoch
def train_one_epoch(..)
	for samples, targets in metric_logger.log_every(data_loader, print_freq, header):
        outputs = model(samples)
        loss_dict = criterion(outputs, targets)
        losses = sum(loss_dict[k] * weight_dict[k] for k in loss_dict.keys() if k in weight_dict)
        optimizer.zero_grad()
        losses.backward()
        optimizer.step()
        # metric_logger 에 return 해주고 싶은 값들을 넣어준다.  
        metric_logger.update(loss=loss_value, **loss_dict_reduced_scaled, **loss_dict_reduced_unscaled)
        metric_logger.update(class_error=loss_dict_reduced['class_error'])
        metric_logger.update(lr=optimizer.param_groups[0]["lr"])
    # 1 epoch 동안 계산된 모든 것, return
	return {k: meter.global_avg for k, meter in metric_logger.meters.items()} 

4. detr_hands_on.ipynb 파일 분석하기

  • 아래 New modules에 있는 함수 중, register_forward_hook 사용해서 score 값을 뽑아 낸다.

  • 그리고 아름답게 함수를 구현해서 아래와 같은 plot 혹은 program을 만든다. 2번째 그림의 program은 신기하고 재미있으니 시간나면 한번 꼭 공부해보자. 코드는 맨 아래에 추가했다.

    1. 아래 이미지에서 좌표는, 실제 이미지 상의 좌표를 의미한다.
      image-20210419202039373

    2. 꼭 어떻게 만드는지 배워보자. (코랩 코드)

      image-20210419202103996


5. New modules

  1. torch.repeat(sizes), torch.expand

    • sizes (torch.Size or int…) – 각 차원에 대한 반복 수 (The number of times to repeat this tensor along each dimension)
    >>> x = torch.tensor([1, 2, 3])
    >>> x.repeat(4, 2)
    tensor([[ 1,  2,  3,  1,  2,  3],
            [ 1,  2,  3,  1,  2,  3],
            [ 1,  2,  3,  1,  2,  3],
            [ 1,  2,  3,  1,  2,  3]])
    >>> x.repeat(4, 2, 1).size()
    torch.Size([4, 2, 3])
       
    self.col_embed[:W].unsqueeze(0) >> torch.Size([1, 25, 128])
    self.col_embed[:W].unsqueeze(0).repeat(H, 1, 1) >> torch.Size[25,25,125] 
    
  2. torch.flatten(input, start_dim=0 : int, end_dim=-1 : int) → Tensor

    • h : (1, 256,25,25)
    • h.flatten(2) = h.flatten(start_dim = 2) : (1, 256, 625)
  3. torch.permute(2,0,1)

    • 차원 치환/교환

    • (1, 256, 625) -> (625, 1, 256)

  4. torch.unsqueeze(d)

    • d: unsqueeze하고 싶은 차원
    • d =1 -> (625, 256) -> (625, 1,256)
  5. torch.transpose(input, dim0, dim1)

    • 딱 2개의 차원을 교환한다
  6. torch.nn.Linear(in_features_int, out_features_int)

    • forward에 들어갈 수 있는 shape는, 무조건 in_feature_int 차원의 1차원 백터가 아니다!! 아래의 차원이 될 수 있다.
    • Input : (N,*,H_in), 이렇게만 forward로 들어가면 된다. Output: (N, *, H_out)
    • 여기서 H_in = in_features :int, H_out = out_features :int 이다.
  7. argparse

    • config 파일을 사용해서 환경변수를 저장하기도 하지만, 여기서는 간단하게 argparse만으로 환경변수를 정의했다.
    • main.py의 argparse 기법을 사용하면, args.junha = 1 만 코드에 추가해줘도 자동으로 새로운 key, valuse가 생성된다.
  8. torch.tensor.numel()

    • tensor 안에 들어 있는 파라메터 갯수 자연수로 반환
  9. nn.Module.named_parameters(), nn.Module.parameters()

    • iteration 이다.
    • [p.shape for p in model.parameters()], [p for p in model.parameters()] 이런식으로 출력해서 보기
    • [n for n, p in model_without_ddp.named_parameters() if "backbone" in n and p.requires_grad]
    • [(n,p) for n, p in model.named_parameters()]
      image-20210408133142137
  10. nn.Embedding

  11. nn.init.uniform_

    • torch.nn.init 여기에 모델 weight를 init하는 방법이 다 있다. 사용은 아래와 같이 한다.
    • nn.init.uniform_(self.row_embed.weight)
  12. torch.tensor.cumsum(input, dim)

    • dim 차원으로 바라봤을 때, 나오는 1차원 백터들에 대해서 누적된 합을 계산한 텐서 반환.
    • [1, 2, 3] -> [1, 3, 6]
  13. nn.Dropout(p)

    • p = 노드를 zero로 만들어 버릴 가능성
    • dropout은 임의로 노드를 잠궈버리는 것. 즉 Zero로 만들어 버리는 것이다. 따라서 dropout의 forward를 위한 파라미터는 노드를 넣어줘야한다. (코드 예시. src = src + self.dropout1(src2))
  14. nn.diag(x)

    • x가 2D이면, 대각행렬만 뽑아서 1D를 return해준다.
    • x가 1D이면, 1D를 대각항으로 가지는 2D를 return해준다.
  15. nn.topk(x,k)

    • top k개
    • tensor x중에서 가장 큰 수 k개를 뽑아낸다.
  16. torch.nn.Module.register_forward_hook(hook_function)

    • (1) torch document, (2) 한글 설명 tutorial

    • Network의 특정 module에서의 input, ouput을 확인하고 싶거나, 바꾸고 싶을 때 사용한다.

    • 해당 nn.moulde의 forward가 실행된 직후에 설정해 놓은 함수*가 실행된다. 어떻게든 register_forward_hook를 정의해 놓는 순간부터, 해당 코드에서 Network 전체 forward가 돌아가면 무조건 그 함수*가 실행된다.

    • 아래와 같이 global을 사용하지 않더라고 전역변수처럼 변수를 모듈 내부에서 사용할 수 있다.

      # use lists to store the outputs via up-values
      conv_features, enc_attn_weights, dec_attn_weights = [], [], []
            
      hooks = [
          model.backbone[-2].register_forward_hook(
              lambda self, input, output: conv_features.append(output)
          ),
          model.transformer.encoder.layers[-1].self_attn.register_forward_hook(
              lambda self, input, output: enc_attn_weights.append(output[1])
          ),
          model.transformer.decoder.layers[-1].multihead_attn.register_forward_hook(
              lambda self, input, output: dec_attn_weights.append(output[1])
          ),
      ]
            
      # propagate through the model
      outputs = model(img)
            
      for hook in hooks:
          hook.remove()
          # forward 이후에 실행해달라고 만들어 놓은 함수가 hook이다. 
          # 이 hook을 제거한다. (network를 원상복구한다.)
            
      # don't need the list anymore
      conv_features = conv_features[0] # 최종 feature map의 width, hight를 파악하기 위해서. 
      enc_attn_weights = enc_attn_weights[0] # 마지막 encoder layer의 attention score를 파악하기 위해서.
      dec_attn_weights = dec_attn_weights[0] # 마지막 decoder layer의 attention score를 파악하기 위해서.
      
  17. torchvision dataset 함수 수정해서 path 뽑아내기

    • 아래와 같이 dataset함수를 수정하니까 dataloader에서 나오는 값이 그대로 바뀌었다.
    • 즉 dataset class의 return 값만 변경해줌으로써 dataloader에서 나오는 값의 형식이 다르게 만들수 있다.
    1. torchvision.datasets.CocoDetection 여기서 path 변수 사용하는  파악
    2. detr/datatset/coco.py class CocoDetection
       - class CocoDetection(torchvision.datasets.CocoDetection):
       - 	path = self.coco.loadImgs(self.ids[idx])\[0\][\'file_name\']
       - 	return img, target, path
       -	# 위와 같이 path 내용 추가!
    3. for samples, targets, paths in metric_logger.log_every(data_loader, 10, header):
       # paths 추가! 
    

6. Additions

4.1 논문 외 보충 내용

  1. Attention is All you need의 논문 내용 정리! 같은 색 매칭해서 보기! - 그리기 코드는 맨 아래에 참조
    image-20210416222918201

    • Sinusoidal - Positional Embedding Code

      import numpy as np
      d_vector = 256
      num_vector = 625
      PEs = np.empty((num_vector,d_vector))
           
      period = 1000
      for i in range(num_vector):
          if i%2 == 0:
              w = 1/ period**(2*i/d_vector) # 상수. for문에 의해서 num_vector개 만들어짐
              pos = np.arange(256)
              PEs[i] = np.sin(pos*w)
          if i%2 != 0:    
              w = 1/ period**(2*i/d_vector)
              pos = np.arange(256)
              PEs[i] = np.cos(pos*w)
           
      %matplotlib inline
      import matplotlib.pyplot as plt
      from matplotlib.pyplot import figure
           
      figure(figsize=(25, 23), dpi=80, facecolor='black')
      imgplot = plt.imshow(PEs)
      plt.colorbar()
      
  2. Multi head self attention에서 Mask 개념, Masked Self-attention

    • Reference
      1. Transformer를 정말 완벽하게 표현설명한 외국 블로그글
      2. Transformer 코드 잘 만들어 놓은 한국 블로그글
      3. Transformer Architecture를 도식그림으로 잘 그려놓은 한국 블로그글
      4. Teacher forcing을 잘 설명해 놓은 한국 블로그글
    • NLP에서 Mask는 Decoder에서 사용되어야 하는 Layer이다. Decoder에서 3번쨰 단어(fine)를 예측할 때, Decoder의 (Emcoder 결과 말고) Input(I, am)으로 2개의 단어가 들어간다. 학습할 때 우리는 Input(I, am, find)를 모두 가지고 있으므로, find은 가린체로 decoder의 Input으로 넣어줘야한다. 그게 Mask가 하는 역할이다.
    • Mask가 처리되어야 하는 부분은 다른 곳이 아니라 저 부분만 이다. 그래서 Masked 개념은 Decoder에서만 사용된다. image-20210417165538067
    • Mask를 이용한 연산 처리 방법
      image-20210417170332292

4.2 추가 추신

  1. COCO dataset 다운로드 하기

    $ apt-get install wget
    $ wget https://gist.githubusercontent.com/mkocabas/a6177fc00315403d31572e17700d7fd9/raw/a6ad5e9d7567187b65f222115dffcb4b8667e047/coco.sh
    $ sh coco.sh
    
  2. dockerfile

    • 그대로 사용했다가, torchvision 버전 엉망됐다. requirements.txt에서 torch, torchvision, sumitit 삭제하고 설치하자.
    • $ pip install torch==1.5.0+cu101 torchvision==0.6.0+cu101 -f https://download.pytorch.org/whl/torch_stable.html
    • docker-hub의 -runtime , -devel 태크 분석하기. 지금 내가 쓰고 있는게 pytorch/pytorch:1.5-cuda10.1-cudnn7-runtime이다. 아래의 단서로 runtime은 그냥 가볍게 만든 우분투, devel은 최대한 다 넣어놓은 우분투 라고 예측할 수 있다.
      1. 지금 runtime image에 wget도 설치가 안되어 있다.
      2. docker-hub를 보면 runtime은 3.3G 정도, devel은 5.3G 정도이다.
  3. vscode에서 #%%를 사용하면서 pdb이용하기
    image-20210406212137524

    • $ pip install pdbpp
  4. vscode python debugging debug args setting 설정

    • ,"args": ["--batch_size", "1", "--no_aux_loss", "--eval", "--resume", "https://dl.fbaipublicfiles.com/detr/detr-r50-e632da11.pth", "--coco_path", "/workspace/coco"]

    • 위의 내용을, /pythonPackage/.vscode/launch.json의 config 파일에 넣어두기

      {
           "version": "0.2.0",
           "configurations": [
               {
                   "name": "Python: Current File",
                   "type": "python",
                   "request": "launch",
                   "program": "${file}",
                   "console": "integratedTerminal",
                   "debugOptions" : ["DebugStdLib"],
                   "justMyCode": false,
                   "args" : ["--batch_size", "1", "--no_aux_loss", "--eval", "--resume", "https://dl.fbaipublicfiles.com/detr/detr-r50-e632da11.pth", "--coco_path", "/workspace/coco"]
                 }
           ]
       }
      
  5. Pytorch 분산학습

    • rank, world size 을 detr에서 설정해줘야 Multi-GPU사용이 가능한데, 이게 뭔지모르니까 막무가네로 확경변수 설정 못하겠다.
    • 필요하면 다음의 Tuturial을 공부하자. Tuto1 Tuto2
  6. 추가 저장

    # debug에 사용
    from via import via; via(samples.tensors[0].cpu(), out='real_img' , color_img= True)
       
    i == self.num_layers - 1
       
    _ ,num_v ,_ = score.shape
    from via import via; via(score.reshape(num_v ,34,25)[28::40].cpu(), out='encoder_attention')
    
  7. 신경망 파라미터 변경하기
    image-20210423102540533

  8. 신경망 파라미터! 도대체 너는 뭐냐!!

    nn.Module의 __init__ 내부에 self.variable과 같이 맴버변수로 저장된 것들을, 모델 호출에 사용할 수 있다.

    image-20210423104253416

  9. Netron 프로그램 사용방법 및 파라미터 검색 방법
    image-20210423104219302

  10. 신경망 파라미터 정보 읽기 - torch.nn.Module.load_state_dict 동작원리 파악하기
    image-20210423105658212

【Transformer】Pyramid Vision Transformer



Pyramid Vision Transformer

1. Conclusion, Abstract

  • Pure Transformer backbone으로써 사용할 수 있는 PVT 를 제안했다. 특히나 이 backbone을 사용하면 dense prediction task(CNN의 Pyramid구조 그대로 output을 만들어 낼 수 있다.)
  • 새롭게 개발한 아래의 두 모듈을 사용했다. 두 모듈을 사용해서, 다음 2가지 장점을 얻을 수 있었다. (1) Pyramid 구조 생성함으로써 초반의 high resolution output 획득 가능 (2) computation/memory resources의 효율적으로 사용 가능
    1. a progressive shrinking pyramid
    2. spatial-reduction attention layer
  • PVT를 비교할 때, ResNet18,50,108 모델과 유사항 파라미터수를 가지는 모델을 만들고 그것과 성능비교를 해보았다.
  • CNN에는 SE [16], SK [24], dilated convolution [57], NAS [48], Res2Net [12], EfficientNet [48], and ResNeSt [60] 와 같은 많은 발전 모델들이 존재한다. 하지만 Transformer는 여전히 연구 초기 단계이므로 연구 개발의 많은 potential이 존재한다.
  • PS. 전체적으로 봐서, Deformable DETR과 목적이 같은 논문이다. Deformable DETR에서는 key,value를 pyramid features의 전체를 사용하는 것이 아니라, 모델이 스스로 선택한 4개*(4 level)의 key,value만을 선택해서 MHA을 수행한다. 여기서도 원래 key,value 전체를 shrink하여 갯수를 줄여서 MHA을 수행하겠다는 취지를 가지고 있다. 거기다가 Pyramid 구조를 만들었다는 것에도 큰 의의를 둘 수 있다.

2. Instruction, Relative work

  • 아래 그림이 약간 모호하니, 완벽하게 이해하려고 하지 말기

image-20210405121235389

  • 성능 비교

image-20210405121321299


3. Method

  • 아래 그림을 통해서 거의 전부 이해가 가능하다.

image-20210405122325135

image-20210405123207725

  • a progressive shrinking pyramid : 원래 ViT는 Patchsize = 1 로 하기 때문에, query의 갯수(이미지 HxW resolution)이 항상 일정하다. 하지만 PVT에서는 Patchsize는 1 이상 자연수값을 사용하기 때문에 이미지 resolution이 차츰차츰 감소한다. 따라서 Transformer만을 사용해서 Pyramid구조를 만들 수 있는 것이다.
  • 표 이미지의 청자색 필기 1~4번 필기 잘 참조하기 (핵심 및 근거 모음)
  • spatial-reduction attention layer : reduce the spatial scale(백터 갯수) of K and V 하는 것이다. 이 작업으로 largely reduces the computation/memory 가능하다.
  • Deformable DETR은 전체 HxW/P^2개의 key,value의 갯수를 특정 방식으로 몇개만 추출해서 사용했다면, 여기서는 전체 HxW/P^2개의 key,value의 갯수를 전체 HxW/R^2 개로 줄여서 사용한다.

image-20210405124016751


4. Experiments

  • Downstream Tasks
    • Classification : we append a learnable classification token (positional embeding) to the input of the last stage, and then use a fully connected layer.
    • Object Detection, Mask-RCNN : RetinaNet, Mask R-CNN
    • Semantic Segmentation : Semantic FPN (backbone의 성능을 보고 싶은 것이므로 simple한 segmentaion network)
  • Experiment Settings
    • The training image is resized to the shorter side of 800 pixels, while the longer side does not exceed 1333 pixels
    • we also randomly resize the shorter side of the input image within the range of [640, 800]
    • In the testing phase, the shorter side of the input image is fixed to 800 pixels
    • PVT+DETR : Transformer-based detection head, random flip and random scale as the data augmentation.

5. Results

  • Basic

image-20210405124523265

  • intermediate

image-20210405124408005



Pagination


© All rights reserved By Junha Song.