본문 바로가기
DeepLearning Framework & Coding/Pytorch

[pytorch 따라하기-5] 합성곱신경망(CNN) 구현

by 노마드공학자 2021. 7. 25.

[pytorch 따라하기-1] 구글 Colab에 pytorch 세팅하기 https://limitsinx.tistory.com/136

[pytorch 따라하기-2] Tensor생성 및 Backward https://limitsinx.tistory.com/137 

[pytorch 따라하기-3] 경사하강법을 통한 선형회귀 구현 https://limitsinx.tistory.com/138

[pytorch 따라하기-4] 인공신경망(ANN) 구현 https://limitsinx.tistory.com/139

 

※이 전글에서 정리한 코드/문법은 재설명하지 않으므로, 참고부탁드립니다

※해당 글은 PC에서 보기에 최적화 되어있습니다.

 


CNN은 이미지 딥러닝에 사용되는 아주 기본적인 기술입니다!

이미지를 학습시키려면, 이미지를 다루는 라이브러리와 데이터들이 있어야겠죠?

 

console창에 하기와 같이 입력해주시고, 완료되시면 해당 코드를 실행해주세요!

!pip3 install torchvision

 

CNN이란?

CNN

이번에는 드디어, 그 유명한 CNN에 관해 정리를 해보도록 하겠습니다.

CNN은 제가 딥러닝을 적용하는 방식은 아닙니다. 왜냐하면, 저는 실수형 데이터를 주로 다룰 뿐 아니라

 

제가 하는것은 Classification이 아닌, Following이기 때문이죠!

즉, 일반적으로 "딥러닝!" 이라고 하면 떠오르는 장르(classification)를 하고 있는것은 아니기에..

 

이런 연유로 저는 주로 RNN쪽을 많이 사용합니다.:)

하지만, CNN에 대해서 이해를 하고 넘어가는것과, 안쓴다고 그냥 넘어가는것은 큰 차이가있죠!

 

정리를 시작해보겠습니다.

 

CNN은 근본적으로는, 이미지를 학습시키는데 사용되는 방법이기에 전자공학과의 "영상처리"에서 배우는 내용들이 상당수 들어 있습니다.

 

일단 Convolution이 무엇인지부터 알아야하는데요,

사실 학부생에게는 컨벌루션이 뭔지만 2주정도 강의를 하는 내용입니다.

 

코드를 돌리는데는 아무지장 없으니, 문과출신이시거나 전자과,공학수학 수강자분이 아니시면 그냥 넘어가셔도 됩니다.

 

★ 기본적으로 이미지는 RGB3차원 값 혹은, 조/명/채도 3차원값으로, 숫자로 변환 하여 학습을 진행합니다.

 

"컨벌루션??"

컨벌루션, 출처 : 위키백과

컨벌루션 수식, 출처 :  https://pinkwink.kr/156

컨벌루션이란, 이미지 2개를 합친다고 하면 하나를 Y축(세로)기준으로 대칭을 시킨후 차례차례 곱해나가는 것입니다.

즉, 첫번째 이미지에서  파란색과 빨간색 함수를 이미지라고 가정해보면

 

빨간색 함수를 Y축 기준 대칭시키고, 파란색 이미지를 향해 오른쪽으로 1씩 움직이면서 차츰차츰 곱한 결과값들을 누적하는것입니다.

 

이것을 수학적으로 정의하면, 두번째 이미지와 같은 수식이 얻어지게 됩니다.

이런 원리를 토대로 Neural Network의 이미지 학습에도 적용한것이 바로 "CNN"인데요

 

요것을 이미지로 옮기면 이렇게 해석 됩니다.

 

출처 :  https://github.com/hunkim/DeepLearningZeroToAll/blob/master/lab-11-0-cnn_basics.ipynb

 

 

맨왼쪽 그림이 이미지라고 가정하고, [1 1, 1 1]이라는 필터가 있다고 생각해보면,

필터와 각 위치에 해당하는 값들을 모두 곱하고 더해서 맨오른쪽 이미지처럼 만들어 주게 됩니다.

 

이렇게 되면 기존의 3X3 이미지가 2X2로 줄어들게 되겠죠?

 

즉, 정보의 손실이 일어남과 동시에 특징들을 뭉뚱그려서 더 작은값으로 저장하게 되는것입니다.

이 "정보의 손실" 이라는것을 최소화 하고자 "Zero Padding"이라는 기술이 존재하는데요

 

Zero padding이란, 영상처리에서 아주 흔히 쓰이는 기술로

 

 

출처 :  https://github.com/hunkim/DeepLearningZeroToAll/blob/master/lab-11-0-cnn_basics.ipynb

 

 

이렇게, 학습하는 이미지의 끝자락 부분을 0으로 채워(padding)줘서, 필터를 통과시킨 후에도 3X3의 사이즈를

유지하도록 하는것입니다.

 

그다음, 필터를 거쳐 나온 값들 중에 숫자가 가장 큰것이 있겠죠?

 

숫자가 가장 크다는것은, 특징이 가장 뚜렷하게 나타나는 것이라고 할 수 있습니다

 

필터를 거쳐서 나온 것들 중, 가장 큰 값 1개만 남기고 다 없애버리는 것이 바로 'Max pooling' 이라는 기술입니다.

 

출처 : Andrej karpathy CNN 강의

 

이렇게 Max pooling을하게되면, 정보의 손실이 그야말로 엄청납니다!

 

위의 이미지만 봐도 16개의 포인트를 4개의 포인트만 남기고 12개를 죽여버리는것을 확인하실 수 있죠

하지만, 가장 강한 특징들만 남겼기에 이정도는 감수를 해야 앞으로 진행할 이미지 연산이 가능하게 됩니다.

 

 

위에서 정리한, Convolution을 통한 Filtering과 Max pooling을 몇차레 반복하다보면

 

진짜 진짜 진짜 중요한 특징 몇가지만 딱 남게되고, 나머지는 모두 사라지게 되겠죠!

 

이렇게 정제된 특징들을 기반으로 최종적으로, 이 이미지가 무엇이냐?를 분류하는것이 바로 CNN입니다.

 

이것을 크게 보면 다음과 같습니다.

 

출처 :  https://www.researchgate.net/figure/The-overall-architecture-of-the-Convolutional-Neural-Network-CNN-includes-an-input_fig4_331540139
출처 : https://towardsdatascience.com/a-comprehensive-guide-to-convolutional-neural-networks-the-eli5-way-3bd2b1164a53

 

 

이미지 자체의 특징 하나하나를 보기에는 1024 * 860 *3 (이미지 사이즈 * RGB 3차원) = 2,580,480개의 데이터를 가집니다.

즉, 이미지 한개에 250만개의 특징을 가지는데, 이런 사진 수만장~수백만장까지 학습을 진행하기란 사실상 불가능하죠

 

 

설령 가능하다고해도 1개 사진에서 250만개의 특징을 모두 살리면서, 제대로 인식을 할지조차 미지수입니다.

따라서, CNN을 딱 한줄로 요약하자면

 

 

"Convolution을 통한 Filtering과 Max pooling을 반복하여, 정말 중요한 특징들을 정제한 후, Classification을 하는것"

 

 

이라고 할 수 있습니다.

 

개념설명은 잘 안하려고하는데요, CNN이 무엇인지는 알아야 코드를 정리하는게 의미가 있을것 같아 간략히 설명드렸고, 이제는 코드를 정리해보겠습니다.

 

 

 

 

 코드

import torch

import torch.nn as nn

import torch.optim as optim

import torch.nn.init as init

import matplotlib.pyplot as plt

import torchvision.datasets as dset

import torchvision.transforms as transforms

from torch.utils.data import DataLoader

#MNIST라는 숫자 데이터셋을 다운받기위해 torchvision이라는 라이브러리를 미리 다운로드 해줍니다.

#가지고 있는 데이터의 순서를 섞거나, 원하는 비율로 나누거나...하는 데이터 전처리를 위해 DataLoader를 선언

 

batch_size = 256

learning_rate = 0.0002

num_epoch = 10

#CNN에서 batch_size 는 한번에 학습하는 이미지의 수 입니다. 즉, MNIST는 6만장의 데이터가 있는데요

#이걸 한장한장씩 학습하는게 아니라, 256개씩 묶어서 진행하겠다는 뜻입니다.(256이 아니어도 됩니다.)

#6만장의 사진을 학습하므로, learning rate는 조금 낮게 잡는게 발산할 수 있는 가능성을 낮추어줍니다.

#데이터 사이즈가 큰 관계로 epoch은 10번만 해줍니다.

 

mnist_train = dset.MNIST("./",train=True, transform = transforms.ToTensor(),target_transform=None, download = True)

mnist_test = dset.MNIST("./", train=False, transform = transforms.ToTensor(), target_transform=None, download = True)

#torchvision.datasets라이브러리에서 MNIST데이터를 받아오는 코드

 

train_loader = torch.utils.data.DataLoader(mnist_train,batch_size=batch_size,shuffle=True,num_workers=2,drop_last=True)

test_loader = torch.utils.data.DataLoader(mnist_test,batch_size=batch_size,shuffle=False,num_workers=2,drop_last=True)

#받아온 데이터를 학습하기 위해 나누어줍니다.

#batch_size선언, shuffle : 데이터를 무작위로 섞을때

#num_workers : 데이터를 묶을때 사용하는 프로세스 갯수

#drop_last : 묶고 남은 자투리 데이터들은 버릴지 말지 

 

class CNN(nn.Module):

    #C++에서 사용되는 Class 선언(파이썬 : 객체지향 언어)

    def __init__(self: 

        super(CNN,self).__init__() #Super class로 지금 작성하고있는 클래스 자체를 초기화하기 위함

        self.layer = nn.Sequential(

            nn.Conv2d(1,16,5),

            nn.ReLU(),

            nn.Conv2d(16,32,5),

            nn.ReLU(),

            nn.MaxPool2d(2,2),

            nn.Conv2d(32,64,5),

            nn.ReLU(),

            nn.MaxPool2d(2,2)

        )

        #Conv2d : Convolution Filtering이라는 Signal Processing적인 방법으로 이미지를 처리 하는것으로,

        #nn.Conv2d(1,16,5)는 1개필터짜리 입력(28x28 해상도의 이미지, default filter 갯수 = 1)을 받아 16개의 필터로 size 5의 Kernel(Filtering)을 하는것입니다.

        #기본적으로 CNN은 신호/영상처리에 대한 기본적인 이해가 있어야합니다.

        #Kernel size가 5인경우, Convoltuion을 하게 되면 4개의 pixel이 사라지게 되어(28x28)의 input 이미지가 (24x24)가 됩니다.

        #이런식으로 이미지의 사이즈를 줄여가며 강한 특징만을 추려나가는게 CNN입니다.

 

        #MaxPooling을 중간중간 섞어줌으로써, Convolution보다 더욱 강하게 Feature들을 뽑아내줍니다.

        self.fc_layer = nn.Sequential(

            nn.Linear(64*3*3,100),

            nn.ReLU(),

            nn.Linear(100,10)

        )

        #self.layer : CNN이 끝난 이후, 최종적으로 나오는 결과물은 [batch_size,64,3,3]입니다.

        #즉, 256개의 이미지 묶음씩 64개의 필터, (3x3)의 이미지가 남게 되는것으로, pixel갯수로 따지면 64*3*3이 나오게 되는것입니다.

        #따라서, 64*3*3의 결과값을 nn.Linear(100,10)을 통해 최종적으로 10개의 값이 나오게하는데

        #이 10개의 값이 내가 넣은 이미지가 0~9(10개)중 어떤것일지에 대한 각각의 확률입니다.

    def forward(self,x):

        out = self.layer(x)

        out = out.view(batch_size, -1)

        out = self.fc_layer(out)

        return out

        #CNN함수의 전체적인 그림으로, Conv2d -> Linear Regression -> 추정 입니다.

 

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

  #이부분은 굳이 안해주셔도 됩니다. GPU를 사용할 수 없는경우 CPU를 쓰겠다는 것으로, 이부분을 주석처리하고

  # model = CNN()로만 해주셔도 됩니다.

model = CNN().to(device)

loss_func = nn.CrossEntropyLoss()

optimizer = torch.optim.Adam(model.parameters(),lr=learning_rate)

#Cross Entropy Loss function, Adam optimizer

 

loss_arr = []

for i in range(num_epoch):

    for j,[image,label] in enumerate(train_loader):

        x = image.to(device)

        #mnist 학습용 data를 불러옵니다.(28x28)

        y_ = label.to(device)

        #각각의 data들이 0~9중 어떤숫자인지도 불러옵니다.

        optimizer.zero_grad()

        #optimizer 초기화

        output = model.forward(x)

        #학습용 데이터로 CNN 실시

        loss = loss_func(output,y_)

        #학습해서 추정해낸 값과, 실제 라벨된 값 비교

        loss.backward()

        #오차만큼 다시 Back Propagation 시행

        optimizer.step()

        #Back Propagation시 ADAM optimizer 매 Step마다 시행

        if j % 1000 == 0 :

            print(loss)

            loss_arr.append(loss.cpu().detach().numpy())

 

correct = 0

total = 0

with torch.no_grad():

    for image,label in test_loader : 

        x = image.to(device)

        y_ = label.to(device)

 

        output = model.forward(x)

        _,output_index = torch.max(output,1)

 

        total += label.size(0)

        correct += (output_index == y_).sum().float()

 

    print("Accuracy of Test Data : {}".format(100*correct/total))

 

 

결과값

Train Data로 학습시키고, Test Dataset으로 검증하면 

약 98.66%의 정확도로 사진의 숫자를 추정하는것을 확인할 수 있습니다.

댓글