반응형

안녕하세요. 

 

Plain Network(단순히 Layer을 깊게 쌓음)에서 발생하는 Vanishing Gradient(기울기 소실), Overfitting(과적합) 등의 문제를 해결하기 위해 ReLU, Batch Nomalization 등 많은 기법이 있습니다. 

 

ILSVRC Challenge
2021년 3월 24일 기준 인용

ILSVRC 대회에서 2015년, 처음으로 Human Recognition보다 높은 성능을 보인 것이 ResNet입니다.

그 위용은 무지막지한 논문 인용 수로 확인할 수 있습니다. 

 

그렇기 때문에 ResNet은 딥러닝 이미지 분야에서 바이블로 통하고 있습니다. 

 

Plain Netwrok Vs ResNet

Plain Network는 단순히 Convolution 연산을 단순히 쌓는다면, ResNet은 Block단위로 Parameter을 전달하기 전에 이전의 값을 더하는 방식입니다. 

 

Residual Block

F(x) : weight layer => relu => weight layer 

x : identity

 

weight layer들을 통과한 F(x)와 weight layer들을 통과하지 않은 x의 합을 논문에서는 Residual Mapping 이라 하고, 그림의 구조를 Residual Block이라 하고, Residual Block이 쌓이면 Residual  Network(ResNet)이라고 합니다.

Residual Mapping은 간단하지만, Overfitting, Vanishing Gradient 문제가 해결되어 성능이 향상됐습니다.

그리고 다양한 네트워크 구조에서 사용되며, 2017년 ILSVRC을 우승한 SeNet에서 사용됩니다. ( 이 글을 쓴 이유이기도 합니다. ) 

 

Plain Network VS ResNet (Error)

 

Residual Block

class Residual_Block(nn.Module):
    def __init__(self, in_dim, mid_dim, out_dim):
        super(Residual_Block,self).__init__()
        # Residual Block
        self.residual_block = nn.Sequential(
                nn.Conv2d(in_dim, mid_dim, kernel_size=3, padding=1),
                nn.ReLU,
                nn.Conv2d(mid_dim, out_dim, kernel_size=3, padding=1),
            )            
        self.relu = nn.ReLU()
                  
    def forward(self, x):
       out = self. residual_block(x)  # F(x)
        out = out + x  # F(x) + x
        out = self.relu(out)
        return out
    

 

그리고 Residual Block 소개 후 BottleNeck이 나옵니다. 아래 글을 참고하시면 좋을 것 같습니다. 

coding-yoon.tistory.com/116?category=825914

 

[딥러닝] DeepLearning CNN BottleNeck 원리(Pytorch 구현)

안녕하세요. 오늘은 Deep Learning 분야에서 CNN의 BottleNeck구조에 대해 알아보겠습니다. 대표적으로 ResNet에서 BottleNeck을 사용했습니다. ResNet에서 왼쪽은 BottleNeck 구조를 사용하지 않았고, 오른쪽은..

coding-yoon.tistory.com

ResNet 원문

arxiv.org/abs/1512.03385

 

Deep Residual Learning for Image Recognition

Deeper neural networks are more difficult to train. We present a residual learning framework to ease the training of networks that are substantially deeper than those used previously. We explicitly reformulate the layers as learning residual functions with

arxiv.org

 

728x90
반응형
반응형
글의 가독성을 위해 아래 링크에 다시 정리함.

https://blog.naver.com/younjung1996/223413266165

 

[딥러닝] Depth-wise Separable Convolution

Depth-wise Separable Convolution은 합성곱 신경망(CNN:Convolution Neural Network)의 효율성과...

blog.naver.com

안녕하세요. 오늘은 CNN에서 Depth-wise Separable Convolution에 대해 이야기해보겠습니다. 

 

Depth-wise Separable Convolution

Depth-wise separable Convolution을 가장 잘 표현한 그림이라고 생각합니다.

 

하지만 CNN에 대해 자세한 이해가 없으면 이 그림을 보더라도 이해가 잘 가지 않습니다. 

 

위 그림을 이해하기 위해서는 Standard(일반) Convolution을 정확히 파악해야 합니다. 

 

Standard Convolution 연산 과정

위 gif는 제 PPT 내용을 짜집어서 Standard Convolution 연산을 설명한 것입니다. 

 

기본적으로 하나의 Convolution을 통과하면 하나의 Feature Map(Activation Map)이 생성됩니다. 

( pytorch에서 out_channels을 통해 Feature Map의 개수를 정할 수 있습니다. )

 

이때 Feature Map 한 개를 생성하는데 Kernel Size x Kernel Size x Input Channel(입력 이미지 개수)의 Parameter가 필요합니다.

 

 

 

 

 

1. Standard Convolution vs Depth-wise Convolution

 

(색으로 비교하시면서 보십시오. )

하나의 Feature Map = Kernel Size x Kernel Size x 3

 

 

 

 

반면, Depth-wise은 아래와 같습니다.

 

차이를 아시겠나요? 

 

Depth-wise Convolution은 한 번 통과하면, 하나로 병합되지 않고, (R, G, B)가 각각 Feature Map이 됩니다. 

 

3 x Feature Map = Kernel Size x Kernel Size x 3

 

정리하게 되면,

 

하나의 Feature Map = Kernel Size x Kernel Size

 

두 개를 논문 Figure로 비교하면, 

 

 Depth-wise Convolution에서 연산량이 Input Channel 만큼 연산량이 줄어듭니다. 

 

 

2. Separable Convolution

 

Separable Convolution은 Point-wise Convolution 입니다.

 

Point-wise Convolution는 1x1 Convolution입니다. 

 

coding-yoon.tistory.com/116

 

[딥러닝] DeepLearning CNN BottleNeck 원리(Pytorch 구현)

안녕하세요. 오늘은 Deep Learning 분야에서 CNN의 BottleNeck구조에 대해 알아보겠습니다. 대표적으로 ResNet에서 BottleNeck을 사용했습니다. ResNet에서 왼쪽은 BottleNeck 구조를 사용하지 않았고, 오른쪽은..

coding-yoon.tistory.com

1x1 Convolution의 역할을 자세히 알고 싶으시다면 위 글을 참고하시기 바랍니다. 

 

1x1 Convolution은 두 가지 특성을 가지고 있습니다 .

 

(1) 공간적인 특성을 가지고 있지 않다.

(2) 연산량이 가장 적게 필요로 하기 때문에, Feature Map 개수를 조절할 때 사용된다. 

 

 

 

 

 

 

3. Depth-wise Separable Convolution

 

 

(2) Pointwise Convolution에서 BottleNeck구조를 이해하셨으면, 바로 감이 오실 겁니다. 

 

 

BottleNeck의 구조 핵심은 연산량이 높은 곳은 최대한 Feature Map을 적게 생성하고, 연산량이 낮은 곳에서 Feature Map을 가지고 노는 것입니다. 

 

Depth-wise Convolution이 연산량이 적다 하더라도, Point-wise Convolution에 비하면 연산량이 굉장히 많은 편입니다. 

 

Depth-wise Convolution에서 Feature Map을 최대한 적게 뽑아내고, Point-wise Convolution에서 Feature Map을 필요한 만큼 뽑아냅니다.

 

 

 

 

 

3. vs Depth-wise Separable Convolution

 

두 식을 정리하면, 최종적으로 곱셈이 덧셈으로 바뀌게 됩니다. 

 

보통 Kernel Size보다 Output Channel이 훨씬 크기 때문에 계산해보면 약 Kernel size 제곱만큼의 차이가 발생합니다. 

 

이를 Pytorch로 구현하는 방법은 간단합니다. 

class depthwise_separable_conv(nn.Module):
    def __init__(self, input_dim, output_dim):
        super(depthwise_seperable_conv, self).__init__()
        self.depthwise = nn.Conv2d(input_dim, input_dim, kernel_size=3, padding=1,
                                                              groups=input_dim)
        self.pointwise = nn.Conv2d(input_dim, output_dim, kernel_size=1)

    def forward(self, x):
        x = self.depthwise(x)
        x = self.pointwise(x)
        
       return x

 

Convolution에서 groups라는 인자가 있습니다. 

 

말 그대로 Input Channel을 얼마만큼 그룹 지어서 출력할지 정하는 인자입니다. 

 

 Group을 최대로 지으면, 결국 서로 각각의 독립적인 계산을 하게 됩니다. 

 

아래 Pytorch Document를 참고하시면 좋습니다. 

 

pytorch.org/docs/stable/generated/torch.nn.Conv2d.html?highlight=conv2d#torch.nn.Conv2d

 

Conv2d — PyTorch 1.7.0 documentation

Shortcuts

pytorch.org

 

728x90
반응형
반응형

안녕하세요. 오늘은 Deep Learning 분야에서 CNN의 BottleNeck구조에 대해 알아보겠습니다. 

 

대표적으로 ResNet에서 BottleNeck을 사용했습니다. 

 

ResNet에서 왼쪽은 BottleNeck 구조를 사용하지 않았고, 오른쪽은 BottleNeck 구조를 사용했습니다.

 

BottleNeck을 설명하기 전, Convolution의 Parameters을 계산할 줄 알아야 합니다. 이 부분은 다른 글에서 자세히 설명하겠습니다.

 

Convolution Parameters = Kernel Size x Kernel Size x Input Channel x Output Channel

 

BottleNeck의 핵심은 1x1 Convolution입니다. ( Pointwise Convolution 이라고도 합니다. 이는 Depthwise Separable Convolution에서도 똑같은 원리로 이용되기 때문에 잘 알아두면 좋습니다.)

1x1 Convolution의 Parameters는 1 x 1 x Input Channel x Output Channel입니다.

대게 1x1 Convolution은 연산량이 작기 때문에 Feature Map(Output Channel)을 줄이거나 키울 때 사용됩니다.

 

 

BottleNeck 구조

 

 

1. Input Channel = 256인 320x320  Input Image가 있다고 가정합니다. 

B=Batch SIze 

 

 

 

2. Channel Compression ( 채널 압축 )

Input Channel 256 -> Output Channel 64

256을 64로 채널을 강제로 축소한 이유는 오로지 연산량을 줄이기 위함입니다. 

 

1x1 Convolution에는 Spatial(공간적인) 특징을 가지고 있지 않습니다. Convolution 연산이 공간적인 특징을 추출하기 위해선 Kernel이 최소 2 이상 되어야 합니다. 

 

 

 

 

 

3. 특징 추출

 

Input Channel 64 -> Output Channel 64 

 

3x3 Convolution은 특성을 추출하는 역할을 합니다.

3x3 Convolution 연산은 = 3 x 3 x Input Channel x Output Channel 입니다. ( 3 x 3 x 64 x 64 )

 

3x3 Convolution은 1x1 Convolution 보다 9배 연산량이 많기 때문에, 1x1 Convolution에서 채널을 줄인 후에 

3x3 Convolution에서 특성을 추출합니다. 

 

 

 

 

4. Channel Increase( 채널 증가 )

Input Channel 64 -> Output Channel 256

 

CNN은 Feature Map의 특성이 많으면 많을수록 학습이 잘 되기 때문에, 1x1 Convolution으로 강제적으로 채널을 증가시켜줍니다. 

 

BottleNeck의 구조는 1x1 Convolution으로 장난을 치면서 연산량을 최소화하는 것입니다. 

 

하지만 강제로 채널을 줄이고 늘리는 것은 정보 손실을 일으킵니다. 

정보 손실은 모델의 정확성을 떨어뜨립니다. 

 

연산량과 정보손실은 서로 tradeoff 관계이기 때문에 서로의 합의점을 찾는 것이 중요합니다. 

ResNet이 제시한 두 구조를 Pytorch로 구현해 Parameter를 확인하겠습니다. 

 

Standard는 Channel 수가 적을지라도, 3x3 Convolution을 두 번 통과했고, 

BottleNeck은 1x1, 3x3, 1x1 순으로 Convolution을 통과하고, Channel 수는 4배 정도 많지만, Parameter가 세 배 정도 적습니다. 

 

그리고 형성된 것이 도로의 병목 현상과 비슷하다 하여 BottleNeck 구조라고 불립니다.

병목 현상

 

 

cf) Pytorch Code

# standard
class Standard(nn.Module):
    def __init__(self, in_dim=256, mid_dim=64, out_dim=64):
        super(BuildingBlock, self).__init__()
        self.building_block = nn.Sequential(
            nn.Conv2d(in_channels=in_dim, out_channels=mid_dim, kernel_size=3, padding=1, bias=False),
            nn.ReLU(),
            nn.Conv2d(in_channels=mid_dim, out_channels=out_dim, kernel_size=3, padding=1, bias=False),
        )
        self.relu = nn.ReLU()

    def forward(self, x):
        fx = self.building_block(x)  # F(x)
        out = fx + x  # F(x) + x
        out = self.relu(out)
        return out
# BottleNeck
class BottleNeck(nn.Module):
    def __init__(self, in_dim=256, mid_dim=64, out_dim=256):
        super(BottleNeck, self).__init__()
        self.bottleneck = nn.Sequential(
            nn.Conv2d(in_channels=in_dim, out_channels=mid_dim, kernel_size=1, bias=False),
            nn.ReLU(),
            nn.Conv2d(in_channels=mid_dim, out_channels=mid_dim, kernel_size=3, padding=1, bias=False),
            nn.ReLU(),
            nn.Conv2d(in_channels=mid_dim, out_channels=in_dim, kernel_size=1, bias=False),
        )

        self.relu = nn.ReLU()

    def forward(self, x):
        fx = self.bottleneck(x)  # F(x)
        out = fx + x  # F(x) + x
        out = self.relu(out)
        return out
728x90
반응형

+ Recent posts