본문 바로가기
AI | 딥러닝/Coding

[Pytorch 프로젝트] MLP(Multi-Layer Perceptron)으로 MNIST 데이터 분류하기

by 고뭉나무 2021. 10. 24.

🔊 해당 포스팅에서 사용된 컨텐츠는 위키독스 PyTorch로 시작하는 딥러닝 입문 내용을 기반으로 했음을 알립니다. 설명에서 사용된 자료는 최대한 제가 직접 재구성한 자료임을 알립니다.

 

 

 

아래 포스팅은 Softmax regression 기법을 이용하여 MNIST 데이터를 분류하였다.

아래 방식과 차이점을 비교하여 이번 포스팅을 보면 훨씬 이해하는 데 도움이 될 것이다.

2021.10.24 - [AI | 딥러닝/Project] - [Pytorch] Softmax regression으로 MNIST 데이터 분류하기

 

 

  • 사용 Framework: Pytorch
  • 사용 기법: MLP(Multi-Layer Perceptron)
  • 사용 함수: nn.Sequential()
  • 사용 데이터: MNIST (손글씨 숫자)

 

모델링을 할 때 크게 4가지 틀을 기억하고 지켜주면 된다.

1. Dataset 설정
2. 모델 설계
3. Cost 함수와 Optimizer 설정
4. Training 과 Back-propagation 수행

 

 

 

 

모델링 (Modeling)

위의 4가지 틀은 softmax regression 방식과 동일하게 적용되며, 아래 코드 주석에서 '*' 표시 부분만 차이가 있다.

 

import torch
import torchvision.datasets as dsets
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
import torch.nn as nn
import matplotlib.pyplot as plt
import random

USE_CUDA = torch.cuda.is_available() # GPU를 사용가능하면 True, 아니라면 False를 리턴
device = torch.device("cuda" if USE_CUDA else "cpu") # GPU 사용 가능하면 사용하고 아니면 CPU 사용
print("다음 기기로 학습합니다:", device)

#for reproducibility
random.seed(777)
torch.manual_seed(777)
if device == 'cuda':
    torch.cuda.manual_seed_all(777)

#hyperparameters
learning_rate = 0.001 #learning rate를 parameter에 먼저 선언함*
training_epochs = 15  
batch_size = 100
drop_prob = 0.3 #dropout 확률 추가*


----------------------------------## Dataset 설정 ##----------------------------------
#MNIST dataset
#60,000개의 train data, 10,000개의 test data
mnist_train = dsets.MNIST(root='MNIST_data/',
                          train=True,
                          transform=transforms.ToTensor(),
                          download=True)

mnist_test = dsets.MNIST(root='MNIST_data/',
                         train=False,
                         transform=transforms.ToTensor(),
                         download=True)

#dataset loader
data_loader = DataLoader(dataset=mnist_train,
                                          batch_size=batch_size, #배치 크기는 100
                                          shuffle=True,
                                          drop_last=True)



------------------------------------## 모델 설계 ##------------------------------------
#MLP(Multi-Layer Perceptron)이므로 여러 Layer 설정*
linear1 = nn.Linear(784, 512, bias=True)
linear2 = nn.Linear(512, 512, bias=True)
linear3 = nn.Linear(512, 512, bias=True)
linear4 = nn.Linear(512, 10, bias = True)

relu = nn.ReLU() #activation function으로 ReLU 설정
dropout = nn.Dropout(p=drop_prob) #dropout 설정*

#Weight를 어떤 상수로 초기화*
#Xavier uniform initialization 적용
nn.init.xavier_uniform_(linear1.weight)
nn.init.xavier_uniform_(linear2.weight)
nn.init.xavier_uniform_(linear3.weight)
nn.init.xavier_uniform_(linear4.weight)

#model 생성*
model = nn.Sequential(
        linear1, relu, dropout,
        linear2, relu, dropout,
        linear3, relu, dropout,
        linear4
)

-----------------------------## Cost함수 & Optimizer ##--------------------------------
#비용 함수와 optimizer 정의
criterion = nn.CrossEntropyLoss().to(device)    #내부적으로 소프트맥스 함수를 포함하고 있음
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) #gradient descent할 때 adam 활용*



-------------------------## Training & Back-propagation ##---------------------------
total_batch = len(data_loader)
model.train()    #set the model to train mode (dropout=True)*

#앞서 training_epochs의 값은 15로 지정함
for epoch in range(training_epochs):
    avg_cost = 0

    for X, Y in data_loader:
        #배치 크기가 100이므로 아래의 연산에서 X는 (100, 784)의 텐서가 된다.
        X = X.view(-1, 28 * 28).to(device)
        #레이블은 원-핫 인코딩이 된 상태가 아니라 0 ~ 9의 정수.
        Y = Y.to(device)

		#back-propagation 계산을 할 때마다 gradient 값을 누적시키기 때문에 gradient를 0으로 초기화 해주기 위한 것.
        optimizer.zero_grad()
        hypothesis = model(X)
        cost = criterion(hypothesis, Y)
        #gradient 계산
        cost.backward()
        #새로 계산된 w로 업데이트되고 다음 epoch로 넘어가기
        optimizer.step()

        avg_cost += cost / total_batch

    print('Epoch:', '%04d' % (epoch + 1), 'cost =', '{:.9f}'.format(avg_cost))


------------------------------------#############------------------------------------

print('Learning finished')

 

 

Cost 에러율 결과

아래는 Softmax regression 기법으로 했을 때 최종 에러율이다.

epoch 총 15회 진행, 결과는 27%였다. 

 

그러나 MLP 기법으로 진행한 최종 에러율은 아래와 같다.

다음 기기로 학습합니다: cpu
Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz to MNIST_data/MNIST/raw/train-images-idx3-ubyte.gz
9913344/? [00:00<00:00, 35809102.49it/s]
Extracting MNIST_data/MNIST/raw/train-images-idx3-ubyte.gz to MNIST_data/MNIST/raw

Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz to MNIST_data/MNIST/raw/train-labels-idx1-ubyte.gz
29696/? [00:00<00:00, 621300.88it/s]
Extracting MNIST_data/MNIST/raw/train-labels-idx1-ubyte.gz to MNIST_data/MNIST/raw

Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz to MNIST_data/MNIST/raw/t10k-images-idx3-ubyte.gz
1649664/? [00:00<00:00, 7259962.55it/s]
Extracting MNIST_data/MNIST/raw/t10k-images-idx3-ubyte.gz to MNIST_data/MNIST/raw

Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz to MNIST_data/MNIST/raw/t10k-labels-idx1-ubyte.gz
5120/? [00:00<00:00, 78475.27it/s]
Extracting MNIST_data/MNIST/raw/t10k-labels-idx1-ubyte.gz to MNIST_data/MNIST/raw

/usr/local/lib/python3.7/dist-packages/torchvision/datasets/mnist.py:498: UserWarning: The given NumPy array is not writeable, and PyTorch does not support non-writeable tensors. This means you can write to the underlying (supposedly non-writeable) NumPy array using the tensor. You may want to copy the array to protect its data or make it writeable before converting it to a tensor. This type of warning will be suppressed for the rest of this program. (Triggered internally at  /pytorch/torch/csrc/utils/tensor_numpy.cpp:180.)
  return torch.from_numpy(parsed.astype(m[2], copy=False)).view(*s)
Epoch: 0001 cost = 0.279104322
Epoch: 0002 cost = 0.127674669
Epoch: 0003 cost = 0.096985653
Epoch: 0004 cost = 0.083047971
Epoch: 0005 cost = 0.073426105
Epoch: 0006 cost = 0.064969040
Epoch: 0007 cost = 0.057077248
Epoch: 0008 cost = 0.056553707
Epoch: 0009 cost = 0.047229204
Epoch: 0010 cost = 0.044573892
Epoch: 0011 cost = 0.043869291
Epoch: 0012 cost = 0.041133054
Epoch: 0013 cost = 0.037419930
Epoch: 0014 cost = 0.036002152
Epoch: 0015 cost = 0.036294751
Learning finished

에러율은 고작 3.6%...!

이것이 딥러닝이 각광받고 있는 이유를 확실히 증명할 수 있는 결과이다.

 

 

검증

실제 데이터 넣어보며 직접 정확도 확인

 

# 테스트 데이터를 사용하여 모델을 테스트한다.
with torch.no_grad(): # torch.no_grad()를 하면 gradient 계산을 수행하지 않는다.
    X_test = mnist_test.test_data.view(-1, 28 * 28).float().to(device)
    Y_test = mnist_test.test_labels.to(device)

    prediction = model(X_test)  #model() 사용*
    correct_prediction = torch.argmax(prediction, 1) == Y_test
    accuracy = correct_prediction.float().mean()
    print('Accuracy:', accuracy.item())

    # MNIST 테스트 데이터에서 무작위로 하나를 뽑아서 예측을 해본다
    r = random.randint(0, len(mnist_test) - 1)
    X_single_data = mnist_test.test_data[r:r + 1].view(-1, 28 * 28).float().to(device)
    Y_single_data = mnist_test.test_labels[r:r + 1].to(device)

    print('Label: ', Y_single_data.item())
    single_prediction = model(X_single_data)    #model() 사용*
    print('Prediction: ', torch.argmax(single_prediction, 1).item())

    plt.imshow(mnist_test.test_data[r:r + 1].view(28, 28), cmap='Greys', interpolation='nearest')
    plt.show()

 

결과

정확도 또한 softmax regression과 비교했을 때 증가했다.

softmax regression의 정확도: 88.8%

MLP의 정확도: 97.6%

 

/usr/local/lib/python3.7/dist-packages/torchvision/datasets/mnist.py:67: UserWarning: test_data has been renamed data
  warnings.warn("test_data has been renamed data")
/usr/local/lib/python3.7/dist-packages/torchvision/datasets/mnist.py:57: UserWarning: test_labels has been renamed targets
  warnings.warn("test_labels has been renamed targets")
Accuracy: 0.975600004196167
Label:  5
Prediction:  5

 

위 글이 도움이 되셨다면, 아래 하트를 눌러주세요↓

감사합니다 \( ˆoˆ )/​

반응형

댓글