6. Pytorch를 이용한 MNIST 이미지 ANN Classification
이번에는 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으로는 어떻게 구현하는지 다음에 알아보도록 하겠다.