본문 바로가기
AI Research/Deep Learning

[Pytorch-기초강의] 이미지 처리 능력이 탁월한 CNN(Deep CNN)

by ga.0_0.ga 2023. 3. 1.
728x90
반응형

이번 게시물에서는 다수의 CNN 계층으로 이루어진 유명한 모델들에 대해 설명하겠습니다.

살펴볼 CNN 모델들은,

- AlexNet

- VGG

- GoogleNet

- ResNet

이렇게 총 4가지 입니다.

▶ Deep CNN

●Deep CNN 이란?

대규모 벤치마크 데이터셋에 적용하기 위한 깊은 CNN 구조를 말합니다.

● Image Classfication의 경우를 보면 신경망을 깊이 쌓을 수록 분류 정확도가 높아지는 것을 알 수 있습니다.

신경망을 깊게 할수록 좋은 이유는 문제를 더 작은 단위로 분해하여 학습 효율이 좋아지기 때문입니다.

● 그러나, 무작정 인공 신경망을 여러 개 쌓는다고 학습 성능이 무한히 좋아지는 것은 아닙니다.

그 이유는 여러 단계의 신경망을 거치며 최초 입력 이미지에 대한 정보가 소실되기 때문입니다. 이러한 문제점과 함께 deep cnn이 어떤 과정으로 발전해왔는지 설명하도록 하겠습니다.

● AlexNet

AlexNet 구조도

- 8 layer로 구성된 크게 복잡하지 않은 단순한 구조

- 복잡한 인식 작업을 위해 설계된 최초의 CNN

- 네트워크를 크게 두 부분으로 나눈 후 GPU 병렬 연산 수행(단순 큰 네트워크를 처리하기 위함, GPU메모리 부족)

- ReLU를 사용하여 경사 소실 문제 해결 (최초로 ReLu함수 사용)

- 드롭아웃의 적용

- 합성곱 + 풀링 + 완전 연결 계층을 결합

- 그러나! 규모가 큰 필터(11 * 11)를 사용하였고 여전히 층을 깊게 쌓을 수 없다는 문제가 존재함.

● VGG

VGG 구조도

- AlexNet보다 작은 필터(3*3)를 규칙적으로 적용하여 더 깊은 네트워크 설계(간단하고 직관적인 설계 방식으로 좋은 성적을 낼 수 있었음)

- 16 개의 계층들로 구성된 심층 합성곱 신경망

- 합성곱 + 풀링 + 완전 연결 계층의 구조는 유지(stride 1, 3x3 convolution, maxpooling, average pooling)

- 주로 VGG-16과 VGG-19를 사용

● GoogleNet

GoogleNet 구조도

- 22 layer로 구성

- CNN 계산 용량 최적화(매개변수와 연산 수 감소)

- Inception 블록 이용(1x1 convolution, 3x3 convolution, 5x5 convolution -> 채널 방향으로 filter concat)

- Auxiliary classifier :

CNN의 깊이가 깊어질수록 기울기가 소실되는 문제 해결(gradient vanishing)

중간에 분류기를 삽입하여 얻어진 결과를 바탕으로 오차를 역전파

● Inception 블록이란?

Inception 블록과 1*1 convolution의 파라미터 수

1. 1*1, 3*3, 5*5의 convolution 연산 수행

2. 다양한 세부 특징을 잡아내는 것이 가능

:하나의 inception 모듈에서의 다양한 크기의 특징을 결합하여 더 광범위한 정보를 얻음 => 연산량이 너무 많은 문제

3. 1 * 1 convolution 의 추가(노란 박스) => 성능을 크게 저하시키지 않으면서 채널수를 조정할 수 있고 연산량이 감소함(자세한 내용은 이곳을 참고해 주세요)

● ResNet

ResNet 구조도

- Residual Block을 이용 => 층이 깊어질수록 최초 이미지에 대한 정보가 손실되는 문제 해결

Residual Block

=> 입력 값을 더해줌으로써 네트워크를 더 깊이 설계

=> 입력과 출력이 같아지도록 학습 :

H(x) = F(x) + x에서 F(x)를 0에 가깝게 만드는 것이 목표(바로 H(x)를 학습하는게 어렵기때문에)

=> Residual 블록의 출력에 입력이었던 x를 더함으로써 모델을 훨씬 깊게 설계할 수 있도록 함. 입력과 출력의 관계를 바로 학습하기보다 입력과 출력의 차이를 따로 학습하는 게 성능이 좋다는 철학

- 이를 이용하여 100 layer이상 쌓을수 있도록 함

▶ ResNet으로 CIFAR-10 데이터셋 분류하기

- CIFAR-10 데이터셋

:32 × 32 크기의 이미지 6만 개로 이루어져 있고,

자동차, 새, 고양이, 사슴 등 10가지의 분류 클래스를 가지고 있습니다.

전체 코드 주소입니다! 

 

GitHub - jgyy4775/3-min-pytorch: <펭귄브로의 3분 딥러닝, 파이토치맛> 예제 코드

<펭귄브로의 3분 딥러닝, 파이토치맛> 예제 코드. Contribute to jgyy4775/3-min-pytorch development by creating an account on GitHub.

github.com

 

● 간단 코드 설명

이전 게시물들에서 설명한 부분은 제외하고 설명하겠습니다.

- 모듈을 정의해줍니다.

=> nn.BatchNorm2d() : 배치 정규화(Batch Normalization) 함수입니다.

=> 학습률을 너무 높게 잡으면 기울기가 소실되거나 발산하는 증상을 예방하여 학습 과정을 안정화하는 방법중 하나입니다. 즉, 학습 중 각 계층에 들어가는 입력을 평균과 분산으로 정규화함으로써 학습을 효율적으로 만들어줍니다.

class BasicBlock(nn.Module):
    def __init__(self, in_planes, planes, stride=1):
        super(BasicBlock, self).__init__()
        self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3,
                               stride=stride, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(planes) # 배치 정규화 사용
        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3,
                               stride=1, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(planes)

        self.shortcut = nn.Sequential()
        if stride != 1 or in_planes != planes:
        # 두 번째 블록부터 in _planes를 받아 self.bn2 계층의 출력 크기와 같은 planes와
        # 더해주는 self.shortcut 모듈을 정의
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_planes, planes,
                          kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(planes)
            )

 

- 데이터의 모델입력 과정입니다.

1. 입력 x가 들어와 컨볼루션 -> 배치정규화 -> 활성화 함수를 거칩니다.

2. 다시 입력 x를 self.shortcut을 거치게하여 크기를 같게 해주고 -> 활성화 함수를 거친 값에 더해줍니다.

3. ReLU를 통과하여 최종 출력을 도출합니다.

def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += self.shortcut(x)
        out = F.relu(out)
        return out

 

- 모델을 정의합니다.

class ResNet(nn.Module):
    def __init__(self, num_classes=10):
        super(ResNet, self).__init__()
        self.in_planes = 16

        # 3 × 3의 커널 크기를 가지며 3개의 채널을 16개로 변경
        self.conv1 = nn.Conv2d(3, 16, kernel_size=3,
                               stride=1, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(16)
        self.layer1 = self._make_layer(16, 2, stride=1) # layer1 거치면 size: [16 × 32 × 32]
        self.layer2 = self._make_layer(32, 2, stride=2) # layer2 거치면 size: [32 × 16 × 16]
        self.layer3 = self._make_layer(64, 2, stride=2) # layer3 거치면 size: [64 × 8 × 8]
        self.linear = nn.Linear(64, num_classes)

    def _make_layer(self, planes, num_blocks, stride):
        # BasicBlock 클래스가 이 함수를 통해 하나의 모듈로 객체화
        # 여러 모듈을 하나로 묶어주는 역할
        strides = [stride] + [1]*(num_blocks-1)
        layers = []
        for stride in strides:
            layers.append(BasicBlock(self.in_planes, planes, stride))
            self.in_planes = planes
        return nn.Sequential(*layers)

 

- 데이터의 모델입력 과정입니다.

1. 입력 x가 들어와 컨볼루션 -> 배치정규화 -> 활성화 함수를 거칩니다.

2. layer1 -> 2 -> 3 순서대로 통과합니다.

3. 평균 풀링을 한 후, 마지막 계층을 거쳐 분류 결과를 도출합니다.

 def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.layer1(out)
        out = self.layer2(out)
        out = self.layer3(out)
        out = F.avg_pool2d(out, 8)
        out = out.view(out.size(0), -1)
        out = self.linear(out)
        return out

● 위 코드로 정의된 ResNet 모델의 구조도

728x90
반응형

댓글