본문 바로가기
수업 복습/머신러닝과 딥러닝

간단한 CNN 모델 만들기 실습

by 김복칠 2024. 1. 23.
import torch
import torch.nn as nn
import torch.optim as optim
inputs = torch.Tensor(1,1,28,28)
print(inputs.shape)
# 결과 : torch.Size([1, 1, 28, 28])
  • 먼저 필요한 모듈들을 import 해주고 임의로 설정들을 준 변수를 tensor 형태로 만들어줍니다

첫번째 Conv2D

# padding을 통해 이미지 크기(?)를 유지 할 수 있음, 현재 코드에서 padding을 주지 않았을 시에는 이미지 크기(?)가 26이 됨
conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3, padding='same')
out = conv1(inputs)
print(out.shape) # 채널만 늘어난 것을 볼 수 있음
# 결과 : torch.Size([1, 32, 28, 28])
  • 먼저 CNN 레이어 과정을 진행하게 되는데 컨볼루션 작업을 진행합니다
  • 이때 kernel_size는 filtter의 크기를 나타내는것 입니다
  • padding = same 은 이미지 데이터의 크기를 동일하게 유지하기 위한 설정입니다
  • 따라서 기존의 설정에서 out_channels 가 32로 늘어난걸 확인할 수 있습니다

첫번째 MaxPool2D

pool = nn.MaxPool2d(kernel_size=2)
out = pool(out)
print(out.shape) # kernel_size 설정에 따라 이미지의 크기가 비율에 맞춰서 줄어듦(비율에 맞춰서 줄여줘야 함)
# 결과 : torch.Size([1, 32, 14, 14])
  • 이후 이미지의 불필요한 부분을 제외하기 위해 풀링 작업을 진행합니다
  • 기존의 28로 설정한 w, h를 줄여주기 위해 kernel_size로 조절해 줍니다
  • 따라서 높이와 너비가 절반으로 줄어든 것을 확인할 수 있습니다

두번째 Conv2D, MaxPool2D

conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding='same')
out = conv2(out)
pool = nn.MaxPool2d(kernel_size=2)
out = pool(out)
print(out.shape) # kernel_size 설정에 따라 이미지의 크기가 줄어든 것을 볼 수 있음
# 결과 : torch.Size([1, 64, 7, 7])

flatten = nn.Flatten()
out = flatten(out)
print(out.shape) # 64 * 7 * 7
# 결과 : torch.Size([1, 3136])
fc = nn.Linear(3136, 10)
out = fc(out)
print(out.shape)
# 결과 : torch.Size([1, 10])
    • 첫번째 과정과 동일하게 채널은 늘려주고 불필요한 부분은 줄여주면서 이미지를 조절할 과정을 구축합니다
    • 이후 학습시키기 위해 조절(?)된 이미지를 학습시키기 위한 레이어를 만들어서 모델을 완성해줍니다

CNN으로 MNIST 분류하기

import torchvision.datasets as datasets
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
from torch.utils.data import DataLoader
device = 'cuda' if torch.cuda.is_available() else 'cpu'

train_data = datasets.MNIST(
    root = 'data',
    train = True,
    transform = transforms.ToTensor(),
    download = True
)

test_data = datasets.MNIST(
    root = 'data',
    train = False,
    transform = transforms.ToTensor(),
    download = True
)

loader = DataLoader(
    dataset = train_data, # list 화 할 필요 없음
    batch_size = 64,
    shuffle = True
)​
  • 이제 이렇게 만들어진 CNN 모델로 MNIST 데이터셋을 학습시켜보겠습니다
  • 필요한 모듈(?)을 불러온 뒤 MNIST 데이터셋을 train과 test로 나누어 줍니다
  • 이후 학습할 train 데이터셋의 DataLoader를 만들어줍니다
  • 여기서 batch_size는 한번에 학습할 데이터의 개수입니다
model = nn.Sequential(
    nn.Conv2d(1,32,kernel_size=3,padding='same'),
    nn.ReLU(),
    nn.MaxPool2d(kernel_size=2),

    nn.Conv2d(32,64,kernel_size=3,padding='same'),
    nn.ReLU(),
    nn.MaxPool2d(kernel_size=2),

    nn.Flatten(),
    nn.Linear(7*7*64,10)
).to(device)
  • 그리고 위에서 만들었던 모델을 torch 형태로 만들어서 model 변수안에 저장해 줍니다
  • 구조를 확인해보면 하나의 input 으로 받아서 채널을 32개로 늘리고 fillter를 3으로 설정해줍니다
  • 이후 높이와 너비를 반으로 줄이는데 이 작업을 두번 반복해서 최종 레이어에 넣어줍니다.
  • 그러면 이미지 구조는 (1, 64, 7, 7)로 하나가 들어가 64개의 채널로 나누어지고 높, 너비는 7이 되게 됩니다
# 학습
# optimizer : Adam
# Epoch     1/10    Loss : 0.180667     Accurcay : 94.62%
# ...
# Epoch     10/10   Loss : 0.010452     Accurcay : 99.64%

optimizer = optim.Adam(model.parameters(), lr=0.001)
epochs = 10


for epoch in range(epochs + 1):
    sum_losses = 0
    sum_accs = 0

    for x,y in loader:     # 전체 데이터를 64개씩 뽑아서 돌림
        x,y = x.to(device), y.to(device)    # 위에서 모델을 GPU로 설정해 놓았기 때문에 받아온 데이터도 GPU로 설정해 주어야 함
        y_pred = model(x)
        loss = nn.CrossEntropyLoss()(y_pred, y)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        sum_losses = sum_losses+loss.item() # GPU로 설정했을 경우에는 value 값만 뽑아와야 하기 때문에 item() 함수를 적용

        y_prob = nn.Softmax(1)(y_pred)
        y_pred_index = torch.argmax(y_prob, axis=1)
        acc = (y == y_pred_index).float().sum() / len(y) * 100
        sum_accs = sum_accs + acc

    avg_loss = sum_losses / len(loader)
    avg_acc = sum_accs / len(loader)
    print(f'Epoch {epoch:4d}/{epochs} Loss : {avg_loss:.6f} Accuracy : {avg_acc:.2f}')
    
# 결과 
Epoch    0/10 Loss : 0.185871 Accuracy : 94.55
Epoch    1/10 Loss : 0.056025 Accuracy : 98.28
Epoch    2/10 Loss : 0.041502 Accuracy : 98.74
Epoch    3/10 Loss : 0.033060 Accuracy : 98.93
Epoch    4/10 Loss : 0.026642 Accuracy : 99.19
Epoch    5/10 Loss : 0.021057 Accuracy : 99.33
Epoch    6/10 Loss : 0.018160 Accuracy : 99.41
Epoch    7/10 Loss : 0.014223 Accuracy : 99.53
Epoch    8/10 Loss : 0.012186 Accuracy : 99.61
Epoch    9/10 Loss : 0.009820 Accuracy : 99.71
Epoch   10/10 Loss : 0.009074 Accuracy : 99.71
  • 이렇게 준비가 끝났으면 데이터를 학습시켜주는데 학습 횟수는 epochs으로 10회 진행하며 위에서 지정한 train 데이터셋인 loader를 불러옵니다
  • 그리고 64개씩 학습을 하고 그 과정에서 발생한 loss 수치와 Accuracy 수치를 저장하고 다시 초기화하는 과정을 반복해서
  • epochs이 한번 돌때마다 그 수치를 확인해줍니다
  • 그렇게 10번을 반복하는 반복문을 작동시킵니다
loader = DataLoader(
    dataset = test_data, # list 화 할 필요 없음
    batch_size = 64,
    shuffle = True
)

sum_accs = 0
model.eval() # 모델을 테스트 모드로 전환(초기화(?) 해서 메모리 사용량을 줄임)

for x,y in loader:
        x,y = x.to(device), y.to(device)
        y_pred = model(x)
        y_prob = nn.Softmax(1)(y_pred)

        y_pred_index = torch.argmax(y_prob, axis=1)
        accuracy = (y == y_pred_index).float().sum() / len(y) * 100
        sum_accs = sum_accs + accuracy

avg_acc = sum_accs / len(loader)
print(f'테스트 정확도는 {avg_acc:.2f}% 입니다')
# 결과 : 테스트 정확도는 99.18% 입니다
  • 학습이 된 모델을 가지고 test 데이터를 통해 정확도를 확인해 줍니다
  • 데이터로더를 생성하고 데이터를 학습된 모델의 성능을 확인하기 위해 반복문을 작동시켜 줍니다
  • 그리고 학습과정과 동일하게 정확도 수치를 출력합니다

'수업 복습 > 머신러닝과 딥러닝' 카테고리의 다른 글

포켓몬 분류  (0) 2024.03.27
전이 학습  (0) 2024.03.26
CNN 기초  (0) 2024.01.23
비선형 활성화 함수  (0) 2024.01.17
딥러닝  (0) 2024.01.16