관리 메뉴

TEAM EDA

[파이토치로 시작하는 딥러닝 기초] 3.1 Convolution Neural Network 본문

EDA Study/PyTorch

[파이토치로 시작하는 딥러닝 기초] 3.1 Convolution Neural Network

김현우 2020. 3. 21. 12:09

이번 글에서는 PyTorch로 Convolution Neural Network 하는 것에 대해서 배워보도록 하겠습니다. 이번 글은 EDWITH에서 진행하는 파이토치로 시작하는 딥러닝 기초를 토대로 하였고 같이 스터디하는 팀원분들의 자료를 바탕으로 작성하였습니다. 

목차

  • Convolution? 
  • Neuron과 Convolution 
  • Pooling 
  • Mnist 구현

1. Convolution 

Convolution Neural Network(CNN)의 구조는 위의 사진과 같습니다. 이미지가 들어오면 Convolutions 작업으로 feature maps를 만들어 내고 Subsampling을 통해서 그 사이즈를 줄입니다. 마찬가지로 Convolutions - Subsampling 작업을 반복하다가 마지막에 Full Connection이라는 작업을 통해서 Output(Fully Connected layer)을 산출합니다. 

Convolution이란, 이미지 위에서 stride 값 만큼 filter(kernel)을 이동시키면서 겹쳐지는 부분의 각 원소의 값을 곱해서 모두 더한 값을 출력으로 하는 연산입니다. Convolved Feature의 첫 번째 칸은 (1x1) + (1x0) + (1x1) + (0x0) + (1x1) + (1x0) + (0x1) + (0x0) + (1x1) = 4의 과정을 통해서 나온 값입니다. 이런 Convolution은 두 가지 장점이 있습니다. 

  • Local Invariance : 국소적으로 비슷하다. convolution filter가 전체이미지를 돌아다니기 때문에, 우리가 찾고자 하는 물체가 어디에 있는지는 알지 못하지만 물체의 정보를 잘 포함하고 있다. 
  • Compositionality : 앞에서 봤던 구조를 의미. 반복적으로 계층 구조를 쌓음 

2. Neuron과 Convolution  

Convolution의 정의에 stride라는 표현이 나오는데, stride는 filter를 한번에 얼마나 이동할 것인가입니다. 아래의 왼쪽 예시에서는 1이고 오른쪽 예시에서는 2인 것을 볼 수 있습니다. stride를 2보다 크게 하는 것은 뒤에서 나오게 되는 Pooling이라는 개념과 같이 데이터 사이즈를 줄이는 것에 있습니다. 가장자리 정보의 손실이 발생하지도 않으면서 특징을 추출하고 같은 정보를 여러 번 선택하지 않으므로 overfitting의 위험도 떨어집니다. 

padding은 input이미지의 가장자리에 값을 채워넣는 것을 의미합니다. 

위의 Image처럼 가장자리에 0으로 채워넣는 것을 zero-padding이라 합니다. 이렇게 되면 장점이 3가지가 있습니다. 

  • 가장자리에 있던 정보의 손실이 사라진다. zero padding을 하지 않으면 사이즈가 계속 줄어들면서 가장자리의 정보가 줄어든다. 
  • Convolved Feature의 크기가 줄어들지 않는다. (이미지의 경우 구조 자체가 중요할 수 있기에 구조가 바뀌지 않는다는 것이 큰 장점) 
  • 0이라는 값을 Feature에 반영시켜서 Overfitting을 방지 
  • 참고 : zero-padding을 하지 않으면 이미지가 줄어들면서 가장자리의 정보가 손실되지만, stride의 경우는 가장자리의 정보가 손실되는 것은 아님 

  • input type : torch.Tensor 
  • input shape : (N x C x H x W) = (batch_size, channel, height, width) 

위에서 배운 내용을 한번 정리해보면, 주황색 (3x3x3) filter(3x4x4)의 batch를 돌면서 빨간색의 특징을 추출하게 됩니다. Output size는 위의 식처럼 ((4x4) - (3x3) + (2))/1 + 1 = (4x4)가 나오고 마지막 7은 filter의 갯수를 의미합니다. (주황색 filter가 7개가 있음) 참고로 parameter의 수는 3x3x3x7(filter x filter 수)으로 189입니다. 

3. Pooling (subsampling) 

처음 CNN의 구조를 다시 살펴보면 Convolution의 과정 이후에 Subsampling이라는 과정이 있습니다. 이러한 subsampling은 아래의 사진처럼 이미지의 특징을 보존하면서 크기를 줄여주는 역할을 합니다. 

출처 : https://www.slideshare.net/agdatalab/deep-learning-convolutional-neural-network

Pooling을 해주는 방법으로는 크게 2가지가 있습니다. 평균적인 정보를 담는 Average pooling과 가장 큰 정보를 담는 max pooling입니다. 이러한 Pooling은 차원을 축소하는 것뿐만 아니라 Overfitting을 방지하는 장점도 있습니다. Stride도 비슷한 역할을 하지만 차이점은 Pooling은 모든 정보를 가진 채 정보를 희석한다는 점이 다릅니다. 

4. Mnist CNN

# CNN Model
class CNN(torch.nn.Module):

    def __init__(self):
        super(CNN, self).__init__()
        self.keep_prob = 0.5
        # L1 ImgIn shape=(?, 28, 28, 1)
        #    Conv     -> (?, 28, 28, 32)
        #    Pool     -> (?, 14, 14, 32)
        self.layer1 = torch.nn.Sequential(
            torch.nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(kernel_size=2, stride=2))
        # L2 ImgIn shape=(?, 14, 14, 32)
        #    Conv      ->(?, 14, 14, 64)
        #    Pool      ->(?, 7, 7, 64)
        self.layer2 = torch.nn.Sequential(
            torch.nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(kernel_size=2, stride=2))
        # L3 ImgIn shape=(?, 7, 7, 64)
        #    Conv      ->(?, 7, 7, 128)
        #    Pool      ->(?, 4, 4, 128)
        self.layer3 = torch.nn.Sequential(
            torch.nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(kernel_size=2, stride=2, padding=1))

        # L4 FC 4x4x128 inputs -> 625 outputs
        self.fc1 = torch.nn.Linear(4 * 4 * 128, 625, bias=True)
        torch.nn.init.xavier_uniform_(self.fc1.weight)
        self.layer4 = torch.nn.Sequential(
            self.fc1,
            torch.nn.ReLU(),
            torch.nn.Dropout(p=1 - self.keep_prob))
        # L5 Final FC 625 inputs -> 10 outputs
        self.fc2 = torch.nn.Linear(625, 10, bias=True)
        torch.nn.init.xavier_uniform_(self.fc2.weight)

    def forward(self, x):
        out = self.layer1(x)
        out = self.layer2(out)
        out = self.layer3(out)
        out = out.view(out.size(0), -1)   # Flatten them for FC
        out = self.layer4(out)
        out = self.fc2(out)
        return out