본문 바로가기
IT 도서/Pytorch로 시작하는 딥 러닝 입문

06. 합성곱 신경망 (Convolution Neural Network)

by 이민우 2021. 3. 2.
728x90
반응형

wikidocs.net/62304

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net

*해당 글은 학습을 목적으로 위의 포스팅 내용 중 일부 내용만을 요약하여 작성한 포스팅입니다.

 상세한 내용 및 전체 내용 확인은 위의 링크에서 확인하실 수 있습니다.

 

 

 

합성곱 신경망

  • 이미지 처리에 탁월한 성능을 보이는 신경망
  • 합성곱층과 풀링층으로 구성된다.
  • 기존의 DNN은 반드시 데이터를 1차원 데이터로 Flatten 시켜야 했다.
  • 하지만 이는 이미지의 공간적인 구조 정보의 유실을 만들었고, 학습을 불안정하게 만들었다.
  • 그래서 이미지의 공간적인 구조 정보를 보존하면서 학습을 할 수 있는 방법이 필요해졌고, 이를 위해 CNN이 등장했다.

 

 

채널 (Channel)

  • 이미지는 (높이, 너비, 채널)로 이루어진 3차원 텐서이다.
  • 높이는 세로, 너비는 가로 방향의 픽셀 수이고, 채널은 색 성분을 의미한다.
  • 흑백 이미지의 채널 수는 1, 컬러 이미지의 채널 수는 3이다.
  • 각 픽셀은 0~255 사이의 값을 가진다.
  • 채널은 때로는 깊이라고도 한다.

 

 

합성곱 연산 (Convolution operation)

  • 이미지의 특징을 추출하는 과정
  • 커널 또는 필터를 활용해 이미지를 훑으면서 각 이미지와 커널의 원소의 값을 보두 더한 값을 출력으로 한다.
  • 해당 과청을 거쳐 나온 결과를 특성 맵이라고 한다.
  • 특성 맵은 커널의 크기와 스트라이드에 따라 달라질 수 있는데, 패딩을 하면 입력의 크기와 동일하게 유지할 수도 있다.
  • 커널을 활용해 가중치를 줄 수 있고, 별도로 편향을 추가해 특성맵의 모든 원소에 특정 값을 더할 수도 있다.

https://wikidocs.net/62306

*다수의 채널을 가질 경우 (3차원 텐서의 경우)에는 각 채널을 채널간 합성곱 연산을 실시하여 모두 더한다.

https://wikidocs.net/62306

 

 

풀링층

  • 합성곱층 다음에 추가하여 특성 맵을 다운샘플링하여 특성 맵의 크기를 줄인다.
  • 일반적으로 최대 풀링과 평균 풀링이 사용된다.
  • 풀링 연산에서도 커널과 스트라이드의 개념을 가진다.
  • 특성 맵의 크기를 줄임으로써 특성 맵의 가중치의 개수를 줄이는 역할을 수행한다.

 

 

 

 

CNN_MNIST

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torchvision.datasets as dsets
import torchvision.transforms as transforms
import torch.nn.init

device = 'cuda' if torch.cuda.is_available() else 'cpu'

# 랜덤 시드 고정
torch.manual_seed(777)

# GPU 사용 가능일 경우 랜덤 시드 고정
if device == 'cuda':
    torch.cuda.manual_seed_all(777)


mnist_train = dsets.MNIST(root='MNIST_data/', # 다운로드 경로 지정
                          train=True, # True를 지정하면 훈련 데이터로 다운로드
                          transform=transforms.ToTensor(), # 텐서로 변환
                          download=True)

mnist_test = dsets.MNIST(root='MNIST_data/', # 다운로드 경로 지정
                         train=False, # False를 지정하면 테스트 데이터로 다운로드
                         transform=transforms.ToTensor(), # 텐서로 변환
                         download=True)

data_loader = torch.utils.data.DataLoader(dataset=mnist_train,
                                          batch_size=128,
                                          shuffle=True,
                                          drop_last=True)


#모델
class CNNModel(nn.Module) :
    def __init__(self, input_size, output_size) :
        super().__init__()
        self.conv1 = nn.Conv2d(1, 32, kernel_size = 3, stride = 1, padding = 1) #순서대로 in_channel, out_channel 28*28 => 28 (패딩으로 양쪽에 2 추가해서.)
        self.pool1 = nn.MaxPool2d(kernel_size = 2, stride = 2) #28 -> 14 오버랩 풀링은 귀찮으니 패스
        self.conv2 = nn.Conv2d(32, 64, kernel_size = 3, stride = 1, padding = 1) #14 -> 14
        self.pool2 = nn.MaxPool2d(kernel_size = 2, stride = 2) #14 -> 7
        self.conv3 = nn.Conv2d(64, 128, kernel_size = 3, stride = 1, padding = 1) #7 -> 7
        self.pool3 = nn.MaxPool2d(kernel_size = 2, stride = 2) #7 -> 3

        self.linear1 = nn.Linear(3*3*128, 256, bias = True)
        self.linear2 = nn.Linear(256, 128, bias = True)
        self.linear3 = nn.Linear(128, output_size, bias=True)
    
    def forward(self, x) :
      output = self.conv1(x)
      output = self.pool1(output)
      output = self.conv2(output)
      output = F.relu(output)
      output = self.pool2(output)
      output = self.conv3(output)
      output = F.relu(output)
      output = self.pool3(output)
      output = output.view(output.size(0), -1) #flatten (128,128,3,3) => (128, 1152)

      output = self.linear1(output)
      output = F.relu(output)
      ourpur = nn.Dropout(0.5)
      output = self.linear2(output)
      output = F.relu(output)
      ourpur = nn.Dropout(0.5)
      output = self.linear3(output)
      output = F.softmax(output)

      return output


model = CNNModel(28*28, 10).to(device)
criterion = nn.CrossEntropyLoss().to(device)    # 비용 함수에 소프트맥스 함수 포함되어져 있음.
optimizer = optim.Adam(model.parameters(), lr=0.001)

n_epoch = 20
total_batch = len(data_loader)

for epoch in range(n_epoch):
    avg_cost = 0

    for X, Y in data_loader: # 미니 배치 단위로 꺼내온다. X는 미니 배치, Y느 ㄴ레이블.
        # image is already size of (28x28), no reshape
        # label is not one-hot encoded
        X = X.to(device)
        Y = Y.to(device)

        hypothesis = model(X)
        cost = criterion(hypothesis, Y)

        optimizer.zero_grad()
        cost.backward()
        optimizer.step()

        avg_cost += cost / total_batch

    print('[Epoch: {:>4}] cost = {:>.9}'.format(epoch + 1, avg_cost))

  
# 학습을 진행하지 않을 것이므로 torch.no_grad()
with torch.no_grad():
    X_test = mnist_test.test_data.view(len(mnist_test), 1, 28, 28).float().to(device)
    Y_test = mnist_test.test_labels.to(device)

    prediction = model(X_test)
    correct_prediction = torch.argmax(prediction, 1) == Y_test
    accuracy = correct_prediction.float().mean()
    print('Accuracy:', accuracy.item())
728x90
반응형