pytorch

6. Pytorch를 이용한 MNIST 이미지 ANN Classification

륵기 2020. 3. 21. 18:32
728x90
반응형

이번에는 ANN을 이용해서 MNIST 이미지를 분류해보는 모델을 만들어보자.

파이토치에는 비젼분야를 위한 torchvision이 있어서 매우 용이하다.

 

import torch
import torch.nn as nn
import torch.nn.functional as F

from torch.utils.data import DataLoader
from torchvision import datasets, transforms

import numpy as np
import pandas as np
from sklearn.metrics import confusion_matrix
import matplotlib.pyplot as plt
%matplotlib inline

앞서 작성한 글에서도 언급했듯이, 어떤 데이터가 되었든 간에 모델 학습을 위해서 데이터 형태는 텐서로 구성이 되어 있어야하며, 이미지 역시 마찬가지이다.

여기서 이미지를 텐서로 바꾸어주는 transforms를 사용할 것이다.

transform = transforms.ToTensor()

이 코드로 인해서 image to Tensor 가 가능하다.

이제 MNIST 데이터를 불러올 것인데 이는 파이토치의 내부 데이터 셋에 있다.

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

MNIST 데이터를 train set과 Test set으로 구분해서 불러왔으며, train_data 에는 60000장, test_data에는 10000장이 있다.

 

train_data[0]

'''
(tensor([[[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
           0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
           0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
           0.0000, 0.0000, 0.0000, 0.0000],
          [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
           0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
        	.
            .
            .
	          
          [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
           0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
           0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
           0.0000, 0.0000, 0.0000, 0.0000],
          [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
           0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
           0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
           0.0000, 0.0000, 0.0000, 0.0000]]]), 5)
'''

train_data를 봤을 때 , 이미지 텐서 값과 마지막에 레이블 값으로 구성된 것을 확인할 수 있다.

image, label = train_data[0]
image.shape
# torch.Size([1, 28, 28])

label # 5

이미지는 (1,28,28) 의 형태를 갖고 있는데, 이는 28 x 28 이미지에 Grey color channel을 갖고 있다는 뜻이다.

plt.imshow(image.reshape(28,28),cmap='gist_yarg')

시각화를 통해서 5의 이미지를 확인해보았다.

 

데이터가 적은 경우는 상관 없지만, 데이터가 많은 경우에는 연산 속도가 오래걸리기에 데이터를 나누어서 계산을 하는 미니 배치를 적용한다. 파이토치에서는 DataLoader를 통해 batch_size를 정할 수 있다.

torch.manual_seed(101)

# 0 images, 1 images....
train_loader = DataLoader(train_data, batch_size=100, shuffle=True)
test_loader = DataLoader(test_data,batch_size=500, shuffle=False)
from torchvision.utils import make_grid
np.set_printoptions(formatter=dict(int=lambda x : f'{x:4}')) #formatting

#first batch
for images,labels in train_loader:
    break
    
images.shape
# torch.Size([100, 1, 28, 28])
# 100개 이미지, 28 x 28 그레이 스케일

labels.shape
# torch.Size([100])

print('Labels:', labels[:12].numpy())
im = make_grid(images[:12],nrow=12)
plt.figure(figsize=(10,4))
plt.imshow(np.transpose(im.numpy(),(1,2,0))) # color, width, height -> whc

Labels: [ 0 5 7 8 6 7 9 7 1 3 8 4]

Out[33]:

<matplotlib.image.AxesImage at 0x23b3ee37cc8>

100개의 이미지 중에서 12개의 이미지와 레이블 값을 확인해보았다.

 

이제 모델을 만들어보자.

class MultilayerPerceptron(nn.Module):
	def __init__(self, in_sz , out_sz, layers=[120, 84]):
    	super().__init__()
        
        self.fc1 = nn.Linear(in_sz,layers[0])
        self.fc2 = nn.Linear(layers[0],layers[1])
        self.fc3 = nn.Linear(layers[1],out_sz)
        
    def forward(self, X):
    	X = F.relu(self.fc1(X))
        X = F.relu(self.fc2(X))
        X = self.fc3(X)
        
        return F.log_softmax(X,dim = 1)

위와 같이 fully connected layer를 3개를 만들었다. input ->hidden -> hidden -> output

입력으로는 28  x 28 = 784개를 입력해서 첫번째 은닉층으로 120, 두번째는 84, 출력으로는 0 ~ 9 의 10개로 신경망 모델을 구성하였으며, 순전파에는 전부 relu 함수를 사용하였다.

return 값으로는 이번에 다루는 문제가 multiclass classification 이므로 softmax를 사용하였다.

 

torch.manual_seed(101)
model = MultilayerPerceptron()
model

'''
:
MultilayerPerceptron(
  (fc1): Linear(in_features=784, out_features=120, bias=True)
  (fc2): Linear(in_features=120, out_features=84, bias=True)
  (fc3): Linear(in_features=84, out_features=10, bias=True)
)
'''

모델을 생성했으니 이제 손실함수와 최적화 함수를 구현하자.

criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(),lr=0.001)

모델을 훈련해보자.

import time
start_time = time.time()

#training
epochs = 10
train_losses = []
test_losses = []
train_correct = []
test_correct = []

for i in range(epochs):
	trn_corr = 0
    tst_corr = 0
    
    for b,(X_train, y_train) in enumerate(train_loader): #image / label
    	b += 1 #index 값
        y_pred = model(X_train.view(100,-1))
        loss = criterion(y_pred, y_train)
        
        predicted = torch.max(y_pred.data, 1)[1]
        batch_corr = (predicted == y_train).sum()
        trn_corr += batch_corr
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        if b%200==0:
            acc = trn_corr.item() * 100 / (100*b)
            print(f'epoch {i} batch {b} loss : {loss.item()} acc : {acc}')
        
    train_losses.append(loss)
    train_correct.append(trn_corr)

모델을 훈련하고 이제 테스트 데이터에 대해서 성능 평가를 해야한다.

with torch.no_grad():
	for b, (X_test, y_test) in enumerate(test_loader):
            y_val = model(X_test.view(500,-1))
            predicted = torch.max(y_val.data,1)[1]
            tst_corr += (predicted== y_test).sum()
            
    loss = criterion(y_val,y_test)
    test_losses.append(loss)
    test_correct.append(tst_corr)
        
total_time = time.time() - start_time
print(f'Duration : {total_time/60}mins')

여기서 with 문은 위의 첫 번째 for문 안에 같이 선언해줘야한다.

plt.plot(train_losses,label='Training Loss')
plt.plot(test_losses, label='test/validation loss')
plt.legend()

train data와 test data의 loss를 비교해보니 꽤 차이가 나는 것을 볼 수가 있다.

train_acc = [t/600 for t in train_correct]
test_acc = [t/100 for t in test_correct]

plt.plot(train_acc,label='train acc')
plt.plot(test_acc , label='test acc')
plt.legend()

이번 시간에는 ANN을 구현해서 MNIST 이미지 파일을 Multi-Classification을 해보았다.

CNN으로는 어떻게 구현하는지 다음에 알아보도록 하겠다.

728x90
반응형