[Pytorch] jupyter notebook으로 MNIST 데이터 셋 학습(+정확도, loss 측정)

이번 글에서는 Pytorch를 사용하여 jupyter notebook에서 MNIST 데이터 셋을 학습하는 것에 대해 알아보려고 합니다. 모델을 구성하여 학습을 시키고, 최종적으로 epoch에 따른 loss와 정확도를 matplotlib을 이용해서 그래프를 그려보려고 합니다. 전체 코드는 제일 하단 부분에 github 사이트로 들어가시면 됩니다.

 

1. 관련 모듈 및 라이브러리 import 

import torch.nn as nn
import torch
from torchvision import datasets
from torchvision import transforms
from torch.utils.data import DataLoader
import numpy as np
import torch.optim as optim
import matplotlib.pyplot as plt

 

2. 모델 생성 (4개의 layer로 구성된 fully-connected 모델)

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(784,100)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(100,100)
        self.fc3 = nn.Linear(100,10)
    def forward(self, x):
        x1 = self.fc1(x)
        x2 = self.relu(x1)
        x3 = self.fc2(x2)
        x4 = self.relu(x3)
        x5 = self.fc3(x4)

        return x5

 

구성된 모델의 형태를 보여주는 이미지입니다.

- 모델 구조

모델을 구성하기 위해서는 nn.Module을 꼭 상속받아야 합니다. __init__ 함수를 통해서 클래스를 초기화 하였습니다. self.fc1을 예로들어서 nn.Linear 부분에 대해서 조금 설명해보겠습니다. fc1은 784개의 input 차원, 100개의 output 차원을 가집니다. input이 784가 되는 이유는 MNIST 데이터 셋의 이미지 크기가 28x28이기 때문입니다. 따라서 input layer의 출력 크기인 784를 입력으로 output을 100으로 설정해준 것입이다. fc2도 마찬가지로 fc1의 nn.Linear를 거쳐서 output이 100으로 나왔기에 그것을 받는 input을 100으로 설정해주었습니다. fc3는 fc2에서 출력으로 나온 100을 input으로 받고 최종적으로 10개의 output을 만들게됩니다. 따라서 input layer 1개와 hidden layer 2개 output layer 한 개로 구성된 신경망이라고 볼 수 있습니다. 

 

- forward

forward 함수는 feedforward를 하기위한 함수입니다. 784라는 값이 들어오면 fc1을 거쳐서 100이 x1에 들어가게 되며 첫 번째 hidden layer로 가기전에 ReLU 함수를 사용하게 됩니다. 마찬가지로 ReLU 함수를 거친 결과가 fc2의 input으로 들어가며 출력된 100이 x3로 들어갑니다. input layer와 output layer에는 ReLU를 적용하지 않은 상황입니다.

 

3. MNIST 데이터 셋 불러오기

download_root = 'MNIST_data/'

dataset1 = datasets.MNIST(root=download_root,
                         train=True,
                         transform = transforms.ToTensor(),
                         download=True)
                         
dataset2 = datasets.MNIST(root=download_root,
                         train=False,
                         transform=transforms.ToTensor(),
                         download=True)                

MNIST 데이터 셋을 불러오는 과정입니다. dataset1은 train을 위한 dataset이고 dataset2는 학습이 제대로 되었는지 확인하기 위한 dataset입니다. 인자로 전달되는 train의 값을 true로 지정하면 학습을 위한 데이터 셋이라는 의미입니다. 또한 ToTensor를 적용하게 되면 0~255까지의 값을 0~1 사이의 값으로 변환시켜줍니다.

 

4. Batch size 지정하기

# DataLoader를 이용해서 batch size 정하기
batch_s = 100
dataset1_loader = DataLoader(dataset1, batch_size=batch_s, shuffle=True)
dataset2_loader = DataLoader(dataset2, batch_size=batch_s, shuffle=True)

MNIST 학습 데이터 셋은 총 60000장으로 구성되어 있습니다. 6만장이라는 데이터 학습을 한 번에 하려면 시간이 굉장히 비효율적으로 리소스를 사용하기 때문에 Batch size를 나누어주어 쪼개서 학습을 하겠다는 의미입니다. 위 코드는 batch size를 100으로 한 것으로 총 600번의 step을 통해 1epoch을 도는 것입니다. 

 

5. loss function 및 optimizer 정의

model = Net()
model.zero_grad()
loss_function = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

위에서 만들었던 model을 생성하고 loss function과 optimizer를 정의합니다. CrossEntropy를 사용하였으며 optimizer로는 SGD를 사용하였습니다. 이때 learning late 또는 모멘텀을 줄 수 있는데 learning rate만 0.01으로 주고 학습을 진행해보려고 합니다.

 

6. epoch 및 batch 사이즈

total_batch = len(dataset1_loader)
epochs = np.arange(1,11)
print(epochs)
print(len(dataset1_loader)) #60000개의 data를 batch_size를 100으로 했기 때문에 600이 나오는 것. 

딥러닝에서 epoch란 전체 트레이닝 셋이 신경망을 통과한 횟수를 의미한다고 합니다. 위 코드에서는 epochs를 1~10까지 총 10번으로 지정해주었습니다. epochs = 10으로 바로 선언해주어도 되는데 뒤에서 epoch에 따른 loss와 accuracy를 그려주기 위해서 리스트 형태로 선언해주었습니다.

 

7. MNIST 데이터 셋 학습 및 정확도, loss 구하기

loss_list = []
accuracy_list = []
for epoch in epochs:
    cost=0
    
    for images, labels in dataset1_loader:
        images = images.reshape(100,784)
        
        optimizer.zero_grad() # 변화도 매개변수 0
        
        #forward
        pred = model.forward(images)
        loss = loss_function(pred, labels)
        
        #backward
        loss.backward()
        
        #Update
        optimizer.step()
        
        cost += loss
    
    with torch.no_grad(): #미분하지 않겠다는 것
        total = 0
        correct=0
        for images, labels in dataset2_loader:
            images = images.reshape(100,784)

            outputs = model(images)
            _,predict = torch.max(outputs.data, 1)

            total += labels.size(0)
            correct += (predict==labels).sum() # 예측한 값과 일치한 값의 합

    avg_cost = cost / total_batch
    accuracy = 100*correct/total
    
    loss_list.append(avg_cost.detach().numpy())
    accuracy_list.append(accuracy)
    
    print("epoch : {} | loss : {:.6f}" .format(epoch, avg_cost))
    print("Accuracy : {:.2f}".format(100*correct/total))
    print("------")
    

 

MNIST dataset 학습을 진행하는 과정입니다. with torch.no_grad() 전까지가 학습을 진행하는 과정입니다. 10회를 반복하며 dataset1_loader에서 100개씩 가지고 옵니다. dataset1_loader에서 가져온 데이터는 image와 label의 형태로 되어 있으며 모델에 데이터(image)가 전달될 수 있도록 image의 차원을 1차원으로 변경시킵니다.(28x28 크기의 이미지를 784크기로 1차원 백터를 만듬) forward 과정에서 loss를 얻을 수 있으며 backword 과정에서 backpropagation을 진행한 뒤 optimizer.step()을 통해서 weight들의 업데이트를 진행합니다. 

 

with.torch.no_grad()는 미분을 하지 않겠다는 의미입니다. 학습된 모델이 제대로 작동하는지 확인하기 위함이므로 gradien를 사용하지 않고, back propagation을 하지 않겠다는 의미입니다. 마찬가지로 image의 차원을 조절해준 다음 model에 전달합니다.

 

test dataset을 model에 전달했을 때 리턴되는 값을 받는 변수인 output의 output.data를 일부 출력한 결과

위 이미지는 outputs.data를 해서 출력한 결과입니다. 일부만 캡처한 내용으로 outputs은 100행 10열로 이루어져있습니다. 각 행 중에서 숫자가 가장 큰 값이 모델이 예측한 label이 되는 것입니다.  torch.max에서 두 번째 인자를 1로 지정했기 때문에 하나의 행에서 최대값을 찾게 됩니다. 이 때 dim을 설정하게 되면 가장 큰 값과 그 값의 인덱스가 같이 출력이 됩니다. 첫 번째 이미지를 예로 들면 [6]번 인덱스의 값이 가장 크기 때문에 6번 인덱스의 값과 6이 반환되는 것입니다.  모델은 test dataset의 이미지 한 개를 6이라고 예측한게 됩니다. 예측된 숫자는 predict안에 담기게 되며 예측된 숫자와 label이 맞는 횟수를 구하여 정확도를 출력합니다. 

 

8. loss와 accuracy 결과

학습 시킨 모델에 test image를 전달했을 때 loss와 accuracy 결과 출력

 

9. matplotlib을 사용하여 epoch 변화에 따른 loss와 accuracy 출력

plt.figure(figsize=(10,5))
plt.subplot(1,2,1)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.plot(epochs,loss_list)
plt.subplot(1,2,2)
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.plot(epochs, accuracy_list)
plt.show()

matplotlib을 사용하여 학습이 될 때마다 loss와 accuracy가 어떻게 변화하는지 그래프로 나타내 본 것입니다. 그래프로 보면 학습이 될 때마다 loss는 낮아지는 것을 확인할 수 있고, accuracy는 증가하는 것을 볼 수 있습니다. 

 

epoch가 증가할 때 loss와 accuracy 변화를 그래프로 나타냄

여기까지 MNIST 데이터 셋 학습에 대해 알아보았습니다.

 

 

이 글을 공유하기

댓글

Designed by JB FACTORY