Loss Function

 

손실함수(Loss Funtion)은 예측 값이 실제 값과 얼마나 유사한지 판단하는 기준이 되는 함수입니다.

 

예측 값과 실제 값의 차이를 loss라고 하며, 이 loss를 줄이는 방향으로 학습이 진행됩니다.

 

https://brunch.co.kr/@mnc/9

예를 들어, 왼쪽의 그림에서 파란점이 실제 값이라고 가정하고 파란 점선을 예측 값이라고 가정하겠습니다.

 

그렇다면 실제 값과 예측 값의 차이를 계산하고 이 차이를 줄이기 위해 경사하강법과 같은 optimizer를 사용하는 것입니다.

 

※ Optimizer 정리글 : https://ggongsowon.tistory.com/99

 

 

 


Loss Function 종류

보통 Loss Function은 무슨 약자인지 알면 금방 파악할 수 있습니다.

 

 

1. 평균 절대 오차 (MAE, Mean Absolute Error)

 

Mean Absolute Error

 

MAE는 이름과 같이 예측 값과 정답 값의 차이에 절댓값을 취하여, 그 값들의 평균을 내줍니다.

 

  1.  다른 Loss Function과 비교했을때 outlier의 영향을 상대적으로 크게 받지 않습니다.
  2.  미분 불가능한 지점이 있습니다. (첨점 - 0 부분)
  3.  모든 오차에 동일한 가중치를 부여합니다.

 

 

 

2. 평균 제곱 오차 (MSE, Mean Square Error)

 

Mean Square Error

 

MSE는 MAE와는 다르게 절댓값이 아닌 제곱을 해줍니다.

 

  1.  이상치에 대해 민감하다. 즉, 오차가 크면 클수록 크게 반영됩니다.
  2.  오차값이 0~1 사이는 더 작게, 1 이상은 더 크게 반영됩니다.
  3.  모든 함수값이 미분 가능합니다.

 

 

 

3. 평균 제곱근 오차 (RMSE, Root Mean Square Error)

 

Root Mean Sqaure Error

 

RMSE는 MSE에 root를 사용했습니다.

 

  1.  MSE보다 이상치에 대해 덜 민감하다.
  2.  미분 불가능한 지점이 있습니다. (첨점 - 0 부분)

 

 

 


3가지 Loss Function 비교

 

  • MSE는 이상치에 대해 너무 민감하여, 모델의 학습을 불안정하게 만들 가능성이 크다. 현실 세계에서 마주하는 많은 데이터에는 이상치가 있고, 그 중 어떤 경우에 이상치가 처리되지 않을 때가 있는데, 그러한 경우 MSE를 이용한다면, 이상치에 민감하게 학습되기 때문에 학습 과정이 불안정할 것이다.

 

  • MSE에 루트를 취하여 이상치에 대한 민감도를 줄인 RMSE와 절대값을 취하는 MAE의 차이
    • MAE는 오차들의 절댓값의 평균을 계산한다는 점에서, 모든 오차에 동일한 가중치를 부여한다. 
    • RMSE는 각 example에 제곱을 취한 뒤 평균을 구하고, 그것에 루트를 씌우는 것이기 때문에, 각 오차가 다른 가중치를 갖는다.

 

왼쪽의 MAE는 값의 범위 상관 없이 동일하게 가중되어 있습니다.

 

하지만 오른쪽의 RMSE는 0~1 사이의 오차는 더욱 작게, 1 초과의 값들은 더욱 크게 가중되어집니다.

 

이렇게 해서 얻는 이득은 아래와 같습니다.

 

 

검은점은 예측값, 빨간선이 정답값, 파란선은 오차 입니다.

 

MAE 식을 적용해보면 1.375라는 값이 나오고

 

RMSE 식을 적용하면 1.70이라는 값이 나오게 됩니다.

 

즉, RMSE는 오차값에 따라 적용되는 가중이 달라서 큰 오차값이 있게 되면 Loss 값은 올라가게 됩니다.

 

 

 

 

 

 

 

출처:

https://jysden.medium.com/%EC%96%B8%EC%A0%9C-mse-mae-rmse%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80-c473bd831c62

 

 

 

MMDetection이란?

 

MMDetection은 Pytorch 기반의 Object Detection 오픈소스 라이브러리 입니다.

  • Modular Design
    • Customized object detection framework를 쉽게 만들 수 있습니다.
  • Support of multiple frameworks out of box
    • 인기있고 최신 detection frameworks를 지원합니다.
  • High efficiency
    • Detectron2, maskrcnn-bechmark, SimpleDet 보다 training 속도가 빠릅니다.

 

일단 공부를 하는 중이어서 그런지 framework를 쉽게 만들 수 있다는건 공감이 안됩니다.

 

 

 


TUTORIAL 1: Learn about configs

 

MMDetection은 모듈과 상속형식의 디자인을 config 파일에 통합시킨 시스템입니다.

config를 이용해서 다양한 실험을 편리하게 실행할 수 있습니다.

 

 

Modify config through script arguments (config 수정법)

 

"tools/train.py" or "tools/test.py"를 사용할때 --cfg-options를 지정하여 구성을 수정할 수 있습니다.

  • Update config keys of dict chains.
    • dict 키 순서에 따라 지정할 수 있습니다.
    • ex) --cfg-options model.backbone.norm_eval=False
  • Update keys inside a list of configs.
    • 몇몇의 config dict들은 list로 되어있습니다.
    • ex) data.train.pipline은 보통 [dict(type='LoadImageFromFile'), ...] 이런 형식입니다.
    • LoadImageFromFileLoadImageFromWebcam으로 바꾸고 싶다면
    • --cfg-options data.train.pipeline.0.type=LoadImageFromWebcam
  • Update values of list/tuples.
    • workflow=[('train', 1)] 이런 형식은 --cfg-options workflow="[(train,1),(val,1)]" 이렇게 바꾸면 됩니다.

 

 

 

Config File Structure

 

config/_base_에는 4가지의 구성으로 이루어져 있습니다.

  • dataset
  • model
  • schedue
  • default_runtime

_base_내부의 구성으로 이루어져 있는 configs를 primitive라고 부릅니다.

_base_ 안에는 기본적으로 만들어져 있는 파일들이 있습니다.

ex) Faster R-CNN, Mask R-CNN, Cascade R-CNN, RPN, SSD.

 

저자들은 primitive를 상속하여 사용할 것을 추천합니다.

예를 들어, Faster R-CNN를 base로 수정본이 있다고 하면 _base_ 안에 있는 Faster R-CNN을 상속받아서 사용하면 됩니다.

 

_base_ = ../faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py

sparse_rcnn_r50_fpn_1x_coco.py

 

 

 

Config Name Style

 

config 파일들의 이름은 아래의 규칙으로 이루어져 있습니다.

 

{model}_[model setting]_{backbone}_{neck}_[norm setting]_[misc]_[gpu x batch_per_gpu]_{schedule}_{dataset}

 

{xxx} is required field and [yyy] is optional.

  • {model}: model type like faster_rcnn, mask_rcnn, etc.
  • [model setting]: specific setting for some model, like without_semantic for htc, moment for reppoints, etc.
  • {backbone}: backbone type like r50 (ResNet-50), x101 (ResNeXt-101).
  • {neck}: neck type like fpn, pafpn, nasfpn, c4.
  • [norm_setting]: bn (Batch Normalization) is used unless specified, other norm layer type could be gn (Group Normalization), syncbn (Synchronized Batch Normalization). gn-head/gn-neck indicates GN is applied in head/neck only, while gn-all means GN is applied in the entire model, e.g. backbone, neck, head.
  • [misc]: miscellaneous setting/plugins of model, e.g. dconv, gcb, attention, albu, mstrain.
  • [gpu x batch_per_gpu]: GPUs and samples per GPU, 8x2 is used by default.
  • {schedule}: training schedule, options are 1x, 2x, 20e, etc. 1x and 2x means 12 epochs and 24 epochs respectively. 20e is adopted in cascade models, which denotes 20 epochs. For 1x/2x, initial learning rate decays by a factor of 10 at the 8/16th and 11/22th epochs. For 20e, initial learning rate decays by a factor of 10 at the 16th and 19th epochs.
  • {dataset}: dataset like coco, cityscapes, voc_0712, wider_face.

 

ex)

faster_rcnn_r50_caffe_c4.py

model : faster_rcnn

backbone : r50 (ResNet-50)

neck : c4

 

 

 

 

원본: 

https://mmdetection.readthedocs.io/en/latest/tutorials/config.html

 

https://nepersica.tistory.com/22

'Language > Python' 카테고리의 다른 글

VSCode launch.json 설정  (0) 2024.09.09
ERROR) ImportError: DLL load failed while importing _rust  (0) 2024.06.25
rec to image  (0) 2022.04.07
Selenium으로 Chorme 크롤링 & 저장  (0) 2022.03.31
Python에서 Void Pointer 사용하기  (0) 2022.03.22

이번에는 Xception 모델에 대해 알아보겠습니다.

 

Xception의 구조는 Inception V3와 비교했을때, parameters의 수는 같지만, 더 나은 성능을 보였습니다.

 

Inception

먼저 위의 사진은 Inception module의 구조입니다.

 

Xception의 concept

Xception 논문에서는 Inception과 같이 애매하게 3, 4개로 나누는게 아니라 channel 별로 완벽하게 분리하는 것을 제안합니다.

 

그래서 Depthwise separable convolution을 제안합니다.

 

 

 


Depthwise Separable Convolution

 

Depthwise Separable Convolution Construction

Depthwise Separable Convolution은 Inception과는 다르게

먼저 channel 별 Convolution을 먼저 진행하고, 1 x 1 Convolution을 진행해줍니다.

 

또, Inception module에서는 ReLU를 사용했지만, Xception에서는 사용하지 않습니다.

 

Depthwise Separable Convolution은

  1.  Depthwise Convolution
  2.  Pointwise Convolution

이렇게 두가지로 나누어 볼 수 있습니다.

 

 

 


1. Depthwise Convolution

 

일반적으로 CNN은 M채널을 가진 하나의 필터가 모든 채널에서 연산이 이루어 졌습니다.

여기서 핵심은 필터의 채널값입니다.

Depthwise Convolution은 필터의 채널값을 1로 바꾼것입니다.

 

\(D_{k}\) x \(D_{k}\) x 1 필터의 수를 Input Data의 채널과 같게 만들어서

각 채널마다 \(D_{k}\) x \(D_{k}\) x 1 필터를 적용한 것입니다.

쉽게 얘기하면 Input Data의 각 채널마다의 전용 필터가 생긴것입니다.

그렇다면 연산량을 계산해보면

\(D_{g}^{2}\) x \(D_{k}^{2}\) x M   이 되겠습니다.

비교를 해보겠습니다.

  • 기존 Convolution:    \(D_{g}^{2}\) x \(D_{k}^{2}\) x M x N
  • Depthwise Convolution:    \(D_{g}^{2}\) x \(D_{k}^{2}\)D^2g x D^2k x M 

한눈에 봐도 N이 사라지고 연산량이 줄어든걸 확인할 수 있습니다.

다음은 Pointwise Convolution입니다.

 

 

 


Pointwise Convolution

 

Pointwise Convolution은 커널의 모양이 1 x 1 x M 입니다.

연산량 계산을 해보겠습니다.

\(D_{g}^{2}\) x M x N

기존의 Convolution의 연산량과 비교해보겠습니다.

  • 기존 Convolution:   \(D_{g}^{2}\) x \(D_{k}^{2}\) x M x N
  • Pointwise Convolution:    \(D_{g}^{2}\) x M x N

여기서는 무려 D^2k가 사라졌습니다.

 

이제 Depthwise Separable Convolution의 연산량을 구해보겠습니다.

그냥 위의 Depthwise Convolution과 Pointwise Convolution의 연산량을 더해주면 됩니다!

Depthwise Separable Convolution:    \(D_{g}^{2}\) x \(D_{k}^{2}\) x M + \(D_{g}^{2}\) x M x N

\(D_{g}^{2}\) x M x ( \(D_{k}^{2}\) +N)

 

연산량을 비교해보면

연산량이 1/9까지 줄어든것을 알 수 있습니다. N과 Dk는 임의의 수를 대입해준것입니다.

Depthwise Convolution과 Pointwise Convolution은 여러곳에서 사용되니 알아두는 것이 좋습니다.

 

 

 


Xception 구조

 

Xception의 구조

구조안의 SeparableConv에는 Depthwise Separable Convolution을 적용해주면 됩니다.

 

 

코드

더보기

1. Depthwise Separable Convolution

def depthwise_separable_conv(input_dim, output_dim):
   
    depthwise_convolution = nn.Conv2d(input_dim, input_dim, kernel_size=3, padding=1, groups=input_dim, bias=False)
    pointwise_convolution = nn.Conv2d(input_dim, output_dim, kernel_size=1, bias=False)
   
    model = nn.Sequential(
        depthwise_convolution,
        pointwise_convolution
       
    )
       
    return model

 

2. Entry flow

class entry_flow(nn.Module):
    def __init__(self):
        super(entry_flow, self).__init__()
       
        self.conv2d_init_1 = nn.Conv2d(in_channels = 3,
                                       out_channels = 32,
                                       kernel_size = 3,
                                       stride = 2,
                                      )
       
        self.conv2d_init_2 = nn.Conv2d(in_channels = 32,
                                       out_channels = 64,
                                       kernel_size = 3,
                                       stride = 1,
                                      )
       
       
        self.layer_1 = nn.Sequential(
            depthwise_separable_conv(input_dim = 64, output_dim = 128),
            nn.ReLU(),
            depthwise_separable_conv(input_dim = 128, output_dim = 128),
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
           
        )
                                                             
        self.conv2d_1 = nn.Conv2d(in_channels = 64,
                                  out_channels = 128,
                                  kernel_size = 1,
                                  stride = 2
                                  )
       
        self.layer_2 = nn.Sequential(
            depthwise_separable_conv(input_dim = 128, output_dim = 256),
            nn.ReLU(),
            depthwise_separable_conv(input_dim = 256, output_dim = 256),
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
           
        )
                                                             
        self.conv2d_2 = nn.Conv2d(in_channels = 128,
                                  out_channels = 256,
                                  kernel_size = 1,
                                  stride = 2
                                  )
       
        self.layer_3 = nn.Sequential(
            depthwise_separable_conv(input_dim = 256, output_dim = 728),
            nn.ReLU(),
            depthwise_separable_conv(input_dim = 728, output_dim = 728),
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
           
        )
                                                             
        self.conv2d_3 = nn.Conv2d(in_channels = 256,
                                  out_channels = 728,
                                  kernel_size = 1,
                                  stride = 2
                                  )
       
        self.relu = nn.ReLU()
       
    def forward(self, x):
        x = self.conv2d_init_1(x)
        x = self.relu(x)
        x = self.conv2d_init_2(x)
        x = self.relu(x)
       
        output1_1 = self.layer_1(x)
        output1_2 = self.conv2d_1(x)
        output1_3 = output1_1 + output1_2
       
       
        output2_1 = self.layer_2(output1_3)
        output2_2 = self.conv2d_2(output1_3)
        output2_3 = output2_1 + output2_2
       
       
        output3_1 = self.layer_3(output2_3)
        output3_2 = self.conv2d_3(output2_3)
        output3_3 = output3_1 + output3_2
        y = output3_3
       
        return y

 

 3. Middle flow

class middle_flow(nn.Module):
    def __init__(self):
        super(middle_flow, self).__init__()
       
        self.module_list = nn.ModuleList()
       
        layers = nn.Sequential(
                nn.ReLU(),
                depthwise_separable_conv(input_dim = 728, output_dim = 728),
                nn.ReLU(),
                depthwise_separable_conv(input_dim = 728, output_dim = 728),
                nn.ReLU(),
                depthwise_separable_conv(input_dim = 728, output_dim = 728)
            )
       
        for i in range(7):
            self.module_list.append(layers)
           
    def forward(self, x):
        for layer in self.module_list:
            x_temp = layer(x)
            x = x + x_temp
       
        return x

 

 4. Exit flow

class exit_flow(nn.Module):
    def __init__(self, growth_rate=32):
        super(exit_flow, self).__init__()
       
        self.separable_network = nn.Sequential(
            nn.ReLU(),
            depthwise_separable_conv(input_dim = 728, output_dim = 728),
            nn.ReLU(),
            depthwise_separable_conv(input_dim = 728, output_dim = 1024),
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        )
       
        self.conv2d_1 = nn.Conv2d(in_channels = 728,
                                  out_channels = 1024,
                                  kernel_size = 1,
                                  stride = 2
                                  )
       
        self.separable_conv_1 = depthwise_separable_conv(input_dim = 1024, output_dim = 1536)
        self.separable_conv_2 = depthwise_separable_conv(input_dim = 1536, output_dim = 2048)
       
        self.relu = nn.ReLU()
        self.avgpooling = nn.AdaptiveAvgPool2d((1))
       
        self.fc_layer = nn.Linear(2048, 10)
       
    def forward(self, x):
        output1_1 = self.separable_network(x)
        output1_2 = self.conv2d_1(x)
        output1_3 = output1_1 + output1_2
 
        y = self.separable_conv_1(output1_3)
        y = self.relu(y)
        y = self.separable_conv_2(y)
        y = self.relu(y)
        y = self.avgpooling(y)
       
        y = y.view(-1, 2048)
        y= self.fc_layer(y)
       
       
        return y

 

5. Xception

class Xception(nn.Module):
    def __init__(self):
        super(Xception, self).__init__()
        self.entry_flow = entry_flow()
        self.middle_flow = middle_flow()
        self.exit_flow = exit_flow()
       
       
       
    def forward(self, x):
        x = self.entry_flow(x)
        x = self.middle_flow(x)
        x = self.exit_flow(x)
       
        return x

 

 

출처:

https://coding-yoon.tistory.com/81

'Deep Learning > Pytorch' 카테고리의 다른 글

C++, Pytorch 사용 예시 - Visual Studio 2019  (0) 2022.11.15
C++, Pytorch 적용하기 02 (.pt 만들기) - Visual Studio 2019  (0) 2022.11.02
32_DenseNet  (0) 2022.04.11
31_ResNext  (0) 2022.04.08
25_Pytorch_GoogLeNet 1x1 Convolution  (0) 2022.03.15

Overfitting

Overfitting은 학습 데이터에 너무 편향되게 학습이 되어,

다른 데이터가 들어왔을때 성능이 떨어지는 현상을 일컫습니다.

 

오른쪽 표를 보면 Training Set에서는 높은 Accuracy를 보이지만,

Test Set에서는 낮은 Accuracy를 보이게 됩니다.

 

그래서 이러한 Overfitting을 완화시켜 주기 위해서

Network에 규제를 가하여 중요한 정보만 담고 부수적인 정보를 제한하는

Regularization을 사용하게 됩니다.

 

 

 


우선 Regularization에서 2가지 방법을 소개하겠습니다.

 

Weight Decay

\(L_2\)공식을 보면 기존 손실함수의 output에 가중치의 제곱의 합을 더해주게 됩니다.

 

즉, 역전파를 진행하면서 기존 loss function과 penality 두가지 모두를 낮추는 겁니다.

다른 말로는 가중치가 커지는것을 규제하는 것입니다.

 

 

\(L_2\) : 큰 가중치는 많이 낮추고 작은 가중치는 적게 낮춘다.

\(L_1\) : 가중치를 동일하게 낮추어 작은 가중치는 사라지게 된다.

 

 

\(L_2\) 손으로 풀기

 

 

 


DropOut

 

뉴런들을 일정 비율로 랜덤하게 비활성화 시킵니다.

여기서 비율을 사람이 정해주는 parameter 입니다.

 

Test 시에는 뉴런들은 비활성화 시키지 않습니다.

 

DropOut의 효과

 

1. 노드 간 동조현상(co-adaptation) 을 막을 수 있다.
 - 노드를 무작위로 생략함으로써 신경망 구조의 고질적 이슈인 동조현상을 막을 수 있다.

 

예를 들어, 힌튼 교수의 해석입니다.

은행 창구에 갔는데 창구 직원이 정기적으로 바뀝니다.

그 이유는 직원들이 담합을 하여 부정 행위를 일으킬 수 있는 행위를 사전에 방지하는 것입니다.

 

즉, 뉴런간의 담합을 막는거라고 할 수 있습니다.

 

2. Overfitting을 막을 수 있다.
 - 마찬가지로 노드를 생략함으로써 모델을 간소화시키는 효과가 있습니다.

 

3. 앙상블 효과를 얻을 수 있다.
 - 매번 다른 형태의 노드 조합으로 학습하기 때문에 앙상블 효과를 얻어 성능을 향상시킬 수 있습니다.

 

 

 

 

 

 

 

출처:

https://www.youtube.com/watch?v=7wv1sjqS5IE&list=PLBiQZMT3oSxXNGcmAwI7vzh2LzwcwJpxU&index=8

Max Pooling

 

Max Pooling

 

Max Pooling은 Convolution과 비슷하게 보이지만 몇가지 차이점이 있습니다.

 

 

 

  • channel이 변하지 않는다.
    • Convolution Layer는 filter의 개수에 의해 output channel의 수가 정해졌지만, Max Pooling은 없습니다.
  • 학습 가능한 parameters가 없다.
    • Convolution Layer는 filter라는 weight를 가지지만 Max Pooling은 input data 자체로 진행합니다.

 

그렇다면 Max Pooling은 왜 사용하는 건지 한번 알아보겠습니다.

 

 

 


Max Pooling의 효과

 

Max Pooling의 적용 예

위의 사진은 각각

filter : 3, 5, 10

stride : 3, 5, 10

으로 진행한 결과입니다.

 

사이즈가 커질수록 해상도는 줄어드는것을 볼 수 있습니다.

하지만 그냥 해상도만 줄어드는게 아닌

input data에서 중요한 정보 (큰 값)만을 추출하는 과정이라고 볼 수 있습니다.

 

 

즉, Convolution Layer에서는 아래의 사진과 같이

Convolution Layer

낮은 층의 필터는 저수준의 로컬한 특징을 찾아내고,

높은 층의 필터는 고수준의 더 글로벌한 특징을 찾아냅니다.

 

하지만 Max Pooling을 사용하게 되면

Down Sampling을 통하여 더 빨리 글로벌한 특징을 찾아낼 수 있게 하는 겁니다.

또한 파라미터 숫자를 줄여서 계산 비용을 줄이고, Overfitting을 억제합니다.

 

 

 


Pooling 종류

Pooling에도 종류가 있습니다.

  • Max Pooling : filter 내에서 가장 큰 값.
  • Average Pooling : filter 내의 값들의 평균.

등...

 

 

 

 

출처:

https://www.youtube.com/watch?v=rTElRLj5chU&list=PLBiQZMT3oSxXNGcmAwI7vzh2LzwcwJpxU&index=14&t=2428s

DenseNet

 

이번에는 DenseNet이라는 모델을 알아보겠습니다.

DenseNet

 

DenseNet은 ResNet과 비교를 하여 설명합니다.

ResNet은 부분적으로 shortcut connection (잔차 학습)을 진행해주지만,

DenseNet은 전체를 다 이어주는 방법을 사용합니다.

 

 

연결이 되는 횟수는 \(\frac{L(L+1)}{2}\)입니다.

위의 사진을 예로, \(\frac{5(5+1)}{2}\) = 15번의 connection이 이루어집니다.

 

 

 

위: ResNet, 아래: DenseNet

하지만 여기서 또 다른점이 있는데,

ResNet은 각 데이터의 원소값들을 Sum 해주는 방법을 사용 했지만,

DenseNet은 Concatenation을 이용하여 연산량을 줄여줌과 동시에

이전 layer들에서 나온 feature map 정보를 보존할 수 있게 해줍니다.

 

 

 

각각의 layer들은 loss function과 input signal로부터의 gradient에 direct로 접근할 수 있어서

더 쉽게 학습할 수 있습니다.

 

 

 

또한, 논문에서는 regularizing effect가 있어서 overfitting을 방지해준다고도 나와있습니다.

 

 

Growth rate

DenseNet은 구조상 계속해서 Concatenation이 이루어져야 합니다.

그래서 layer에서 알맞은 channel 값을 사용해야 하는데

여기서 Growth rate (k)를 제시합니다.

 

Conv1

처음 Conv2d는 2k를 사용하고,

 

Bottleneck layers

Bottleneck layers에서 1x1 Conv2d layer에서는 4k의 channel 값을 사용합니다.

 

 

Down-sampling

DenseBlock에서는 본 논문에서 제시한 Densely Connected Convolution이 이루어지고,

그 뒤에 Convolution, Pooling을 통해서 Down-sampling이 이루어집니다.

그리고 너무 많은 channel이 생기는것을 방지하기위해 0.5로 줄여줍니다.

 

 

 


DenseNet 구조

 

DenseNet
DenseNet

 

 

 

 


Code

더보기
import torch
import torch.nn as nn
from torchsummary import summary


#################################################################################
class BottleNeck(nn.Module):
    def __init__(self, in_channel, growth_rate):
        super(BottleNeck, self).__init__()
        self.residual = nn.Sequential(
            nn.BatchNorm2d(in_channel),
            nn.ReLU(),
            nn.Conv2d(in_channel, growth_rate * 4, kernel_size=1, stride=1, padding=0),
            nn.BatchNorm2d(growth_rate * 4),
            nn.ReLU(),
            nn.Conv2d(growth_rate * 4, growth_rate, kernel_size=3, stride=1, padding=1)
        )

    def forward(self, x):
        return torch.cat([x, self.residual(x)], 1)


class Transition(nn.Module):
    def __init__(self, in_channel, out_channel):
        super(Transition, self).__init__()
        self.down_sample = nn.Sequential(
            nn.BatchNorm2d(in_channel),
            nn.ReLU(),
            nn.Conv2d(in_channel, out_channel, 1, stride=1, padding=1),
            nn.AvgPool2d(2, stride=2)
        )

    def forward(self, x):
        return self.down_sample(x)


class CustomDenseNet(nn.Module):
    def __init__(self, nblocks, growth_rate=32, reduction=0.5, num_classes=10, init_weights=True):
        super(CustomDenseNet, self).__init__()

        self.growth_rate = growth_rate
        inner_channel = 2 * growth_rate

        self.conv1 = nn.Sequential(
            nn.Conv2d(3, inner_channel, kernel_size=7, stride=2, padding=3),
            nn.MaxPool2d(3, 2, padding=1)
        )

        self.feature = nn.Sequential()
        for i in range(len(nblocks) - 1):
            self.feature.add_module('dense_block_{}'.format(i), self._make_dense_block(nblocks[i], inner_channel))
            inner_channel += growth_rate * nblocks[i]
            out_channel = int(reduction * inner_channel) # 너무 많은 channel이 생기는걸 방지
            self.feature.add_module('transition_layer_{}'.format(i), Transition(inner_channel, out_channel))
            inner_channel = out_channel

        self.feature.add_module('dense_block_{}'.format(len(nblocks)-1),
                                self._make_dense_block(nblocks[len(nblocks) - 1], inner_channel))
        inner_channel += growth_rate * nblocks[len(nblocks) - 1]
        self.feature.add_module('bn', nn.BatchNorm2d(inner_channel))
        self.feature.add_module('relu', nn.ReLU())

        self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))
        self.linear(nn.Linear(inner_channel, num_classes))

    def forward(self, x):
        x = self.conv1(x)
        x = self.feature(x)
        x = self.avg_pool(x)
        x = x.view(x.size(0), -1)
        x = self.linear(x)
        return x

    def _make_dense_block(self, nblock, inner_channel):
        dense_block = nn.Sequential()
        for i in range(nblock):
            dense_block.add_module('bottle_neck_layer_{}'.format(i), BottleNeck(inner_channel, self.growth_rate))
            inner_channel += self.growth_rate
        return dense_block


if __name__ == '__main__':
    device = 'cuda'
    model = CustomDenseNet([6, 12, 24, 6])
    # check model
    # model = DenseNet_121().to(device)
    summary(model, (3, 224, 224))

 

 

출처:

https://deep-learning-study.tistory.com/545

'Deep Learning > Pytorch' 카테고리의 다른 글

C++, Pytorch 적용하기 02 (.pt 만들기) - Visual Studio 2019  (0) 2022.11.02
33_Xception  (0) 2022.04.20
31_ResNext  (0) 2022.04.08
25_Pytorch_GoogLeNet 1x1 Convolution  (0) 2022.03.15
30_AlexNet  (0) 2022.03.01

ResNext는 ResNet의 발전된 형태입니다.

어떤 형태로 발전됬는지 한번 보겠습니다.

 

 

Left: ResNet, Right: ResNext

왼쪽은 ResNet, 오른쪽은 ResNext 입니다.

여기서 달라진 점은 group = 32 가 생겼습니다.

이제 group에 대해 알아보겠습니다.

 

 

 


Group Convolution

 

일단 ResNext 논문에서는 이를 Cardinality라고 부릅니다.

ResNext - Bottleneck

① 에서 1 by 1 Conv를 이용하여 in : 256 → out : 128 channel로 변경하여 줍니다.

 

② 에서 넘어온 128개의 channel을 32로 나눠 줍니다. 128 / 32 = 4

그후, 4묶음으로 나뉜 채널을 각각 3x3 filter로 Conv 연산을 진행해줍니다.

연산이 완료된 그룹을 concatenation을 해주어 out : 128 channel로 만들어 줍니다.

 

③ 에서는 ①와 같이 1 by 1 Conv 연산을 진행해줍니다.

 

 

 


Group Convolution 사용 이유

초창기 논문인 VGG 에서는 GPU의 성능으로 인해서 2개의 GPU를 사용하여 학습을 진행했습니다.

VGG

그랬더니 서로 다른 색을 가진 특징값에 집중되어 학습이 되었습니다. - 흑백, 컬러

이를 ResNet에 적용하여 아래와 같이 성능이 증가하게 되었습니다.

d 앞에는 width (channel)

그 앞의 숫자는 group의 수입니다.

 

다음은 DenseNet을 다뤄보겠습니다.

 

 

 


Code

https://github.com/ajw1587/Pytorch_Study/blob/main/31_ResNext.ipynb

 

'Deep Learning > Pytorch' 카테고리의 다른 글

33_Xception  (0) 2022.04.20
32_DenseNet  (0) 2022.04.11
25_Pytorch_GoogLeNet 1x1 Convolution  (0) 2022.03.15
30_AlexNet  (0) 2022.03.01
27_Pytorch_Custom_Dataset  (0) 2021.12.02

+ Recent posts