CNN MNIST Classifier & CUDA

meta fring·2023년 8월 30일
0

torch를 이용하여 손글씨를 분류하는 모델을 만들어 보자.

from torch.utils.data import DataLoader
from torchvision.datasets import MNIST
from torchvision.transforms import ToTensor

batch_size = 32
train_dataset = MNIST(root="data", train=True, download=True, transform=ToTensor())
val_dataset = MNIST(root="data", train=False, download=True, transform=ToTensor())
train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
val_dataloader = DataLoader(val_dataset, batch_size=batch_size, shuffle=True)

from torch.utils.data import DataLoader : PyTorch의 DataLoader 클래스를 가져온다. DataLoader는 데이터셋을 쉽게 배치 형식으로 사용할 수 있게 도와준다.

from torchvision.datasets import MNIST : torchvision 라이브러리의 MNIST 클래스를 가져온다. 이 클래스는 MNIST 데이터셋을 다운로드하고 로드하는 기능을 제공한다.

from torchvision.transforms import ToTensor: torchvision의 ToTensor 변환 함수를 가져온다. 이 함수는 이미지 데이터를 PyTorch 텐서로 변환해준다.

batch_size = 32 : 배치 크기를 32로 설정한다.

train_dataset = MNIST(root="data", train=True, download=True, transform=ToTensor()) : MNIST 클래스를 이용하여 훈련 데이터를 다운로드 및 로드한다.

root="data": 데이터가 저장될 경로를 "data" 디렉터리로 지정한다.
train=True: 훈련 데이터셋을 사용하겠다는 의미다.
download=True: 데이터셋이 지정된 경로에 없으면 다운로드 받겠다는 의미다.
transform=ToTensor(): 로드된 이미지 데이터를 텐서로 변환한다.

val_dataset = MNIST(root="data", train=False, download=True, transform=ToTensor()) : 검증 데이터셋을 위해 MNIST 클래스를 사용하여 로드한다.

train=False : 검증 데이터셋을 사용하겠다는 것을 의미한다.

train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True) : DataLoader를 사용하여 훈련 데이터셋을 배치 형식으로 사용할 수 있게 설정한다.

shuffle=True: 에포크마다 데이터셋을 무작위로 섞는다. 이는 모델 학습 시 과적합(overfitting)을 방지하는 데 도움이 된다.

val_dataloader = DataLoader(val_dataset, batch_size=batch_size, shuffle=True) : 검증 데이터셋도 동일하게 DataLoader를 사용하여 설정한다. shuffle=True로 설정하여 섞는다.

이 코드를 통해 MNIST 데이터셋을 로드하고 배치 형식으로 사용할 준비가 된다.

import torch
from torch import nn


class SimpleCNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=8, kernel_size=3, padding="same")
        self.conv2 = nn.Conv2d(in_channels=8, out_channels=16, kernel_size=3, padding="same")
        self.activation = nn.ReLU()
        self.pool = nn.MaxPool2d(2, 2)
        self.fc = nn.Linear(in_features=784, out_features=10)

    def forward(self, x):
        x = self.pool(self.activation(self.conv1(x)))
        x = self.pool(self.activation(self.conv2(x)))
        x = torch.flatten(x, 1)
        x = self.fc(x)
        return x

class SimpleCNN(nn.Module): nn.Module을 상속받아 커스텀 신경망 모듈을 정의한다.

def init(self):
super().init() : 부모 클래스의 초기화 함수를 호출한다.

self.conv1 = nn.Conv2d(in_channels=1, out_channels=8, kernel_size=3, padding="same")
self.conv2 = nn.Conv2d(in_channels=8, out_channels=16, kernel_size=3, padding="same") :
두 개의 2D 컨볼루션 레이어를 정의한다. MNIST는 흑백 이미지이므로 in_channels=1이다.

self.activation = nn.ReLU() : 활성화 함수로 ReLU(Rectified Linear Unit)를 사용한다.

self.pool = nn.MaxPool2d(2, 2) : 2x2 크기의 MaxPooling 레이어를 정의한다.

self.fc = nn.Linear(in_features=784, out_features=10) : 완전 연결 레이어 (Fully Connected Layer)를 정의한다. MNIST의 출력은 10개의 클래스이므로 out_features=10이다.

def forward(self, x): 순전파 함수로서 모델이 입력을 받아 출력까지 어떻게 처리할지를 정의하는 함수다.

x = self.pool(self.activation(self.conv1(x)))
x = self.pool(self.activation(self.conv2(x))) : 입력 x에 대해 순차적으로 컨볼루션, 활성화 함수, 풀링 레이어를 적용한다.

x = torch.flatten(x, 1) : 2D의 텐서를 1D로 평탄화(flatten)한다. 이는 완전 연결 레이어에 입력하기 위한 준비다.

x = self.fc(x) : 완전 연결 레이어를 통해 최종 출력을 얻는다.

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
learning_rate = 0.001
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
criterion = torch.nn.CrossEntropyLoss()
epochs = 10
model = SimpleCNN().to(device)

하이퍼 파라미터를 셋팅하는 코드다.

device = torch.device("cuda" if torch.cuda.is_available() else "cpu") : GPU에서 CUDA가 사용 가능한 경우 device를 "cuda"로 설정하고, 그렇지 않은 경우 "cpu"로 설정한다. 모델과 데이터를 GPU로 이동시키기 위한 준비 단계다.

learning_rate = 0.001 : 학습률을 설정한다.

optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) : 옵티마이저를 Adam으로 설정한다.

criterion = torch.nn.CrossEntropyLoss() : criterion으로 교차 엔트로피 손실(CrossEntropyLoss)을 설정한다.

epochs = 10 : 반복 횟수를 10으로 설정한다.

model = SimpleCNN().to(device) : SimpleCNN 모델을 인스턴스화하고, .to(device)를 사용하여 모델을 GPU나 CPU로 이동시킨다.

def get_mean(metrics):
    return round(sum(metrics) / len(metrics), 4)

주어진 숫자의 리스트나 배열에 대해 평균을 계산하고, 결과를 소수점 네 번째 자리까지 반올림하여 반환하는 함수다.

from collections import defaultdict
import numpy as np
from tqdm import tqdm
from sklearn.metrics import accuracy_score


def train_model(model):
    model.train()
    loss_list = []
    acc_list = []
    for x_train, y_train in tqdm(train_dataloader):
        x_train = x_train.to(device)
        y_train = y_train.to(device)

        outputs = model(x_train)
        loss = criterion(outputs, y_train)
        loss_list.append(loss.item())

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

        pred = torch.argmax(outputs, dim=1)
        acc = ((y_train == pred).sum() / len(y_train)).item()
        acc_list.append(acc)
    return get_mean(loss_list), get_mean(acc_list)

model.train() : 모델이 학습 모드로 전환된다.

for x_train, y_train in tqdm(train_dataloader): train_dataloader를 통해 학습 데이터의 배치를 가져온다.

x_train = x_train.to(device)
y_train = y_train.to(device) : 데이터를 장치로 이동시킨다.

outputs = model(x_train)
loss = criterion(outputs, y_train)
loss_list.append(loss.item()) : 예측 및 손실을 계산한다.

optimizer.zero_grad()
loss.backward()
optimizer.step() : 모델 파라미터를 업데이트 한다.

pred = torch.argmax(outputs, dim=1)
acc = ((y_train == pred).sum() / len(y_train)).item()
acc_list.append(acc) : 정확도를 계산한다.

return get_mean(loss_list), get_mean(acc_list) : 평균 손실과 정확도 반환한다.

def validate_model(model):
    model.eval()
    loss_list = []
    acc_list = []
    for x_val, y_val in tqdm(val_dataloader):
        x_val = x_val.to(device)
        y_val = y_val.to(device)
        with torch.no_grad():
            outputs = model(x_val)
            loss = criterion(outputs, y_val)
            loss_list.append(loss.item())

            pred = torch.argmax(outputs, dim=1)
            acc = ((y_val == pred).sum() / len(y_val)).item()
            acc_list.append(acc)
    return get_mean(loss_list), get_mean(acc_list)

model.eval() : 모델을 평가 모드로 설정한다.

from collections import defaultdict


def train_validate_model(model):
    logs = defaultdict(list)
    for epoch in range(epochs):
        train_loss, train_acc = train_model(model)
        val_loss, val_acc = validate_model(model)
        logs["train_loss"].append(train_loss)
        logs["train_acc"].append(train_acc)
        logs["val_loss"].append(val_loss)
        logs["val_acc"].append(val_acc)
        print(f"epoch {epoch + 1} train - loss: {train_loss} acc: {train_acc} val - loss: {val_loss} acc: {val_acc}")
    return logs

전체 학습 및 검증 프로세스를 수행하고, 각 epoch의 학습 및 검증 손실과 정확도를 포함하는 로그 딕셔너리를 반환하는 함수다.

logs = defaultdict(list) : 학습과 검증의 결과를 저장하기 위한 logs 딕셔너리를 초기화한다.

train_loss, train_acc = train_model(model) : train_model 함수를 사용하여 모델을 학습시키고, 학습 도중의 평균 손실 및 정확도를 얻는다.

val_loss, val_acc = validate_model(model) : validate_model 함수를 사용하여 모델을 검증하고, 검증 도중의 평균 손실 및 정확도를 얻는다.

logs["train_loss"].append(train_loss)
logs["train_acc"].append(train_acc)
logs["val_loss"].append(val_loss)
logs["val_acc"].append(val_acc) : 학습 및 검증의 결과를 logs 딕셔너리에 저장한다.

logs = train_validate_model(model)

train_validate_model 함수가 호출되어 모델을 학습하고 검증한다.

profile
긍정적인 개발자

0개의 댓글