Minist 예제

Minist 예제를 살펴봅시다. 사실 minist 예제는 3장 다룬 기초적인 Neural Networw와 거의 동일 합니다.

단지, 입력 DataLoader를 사용하여 Minist dataset를 이용하는 부분만 차이가 나고, 데이터량이 많아서 시간이 좀 많이 걸리는 부분입니다.
입력 DataLoader를 이용하는 것은 4장에서 잠시 다루었기 때문에, 시간을 줄이기 위해서 cuda gpu를 사용하는 부분을 추가했습니다. 입력변수와 network상의 변수의 torch.Tensor를 cuda() 함수를 통해서 선언하면 됩니다.

is_cuda =  torch.cuda.is_available()
if is_cuda :  model.cuda()
if is_cuda :  data, target = data.cuda(), target.cuda()

In [1]:
%matplotlib inline

1. 입력DataLoader 설정

  • train 데이터로 loader를 지정 (dataset은 Minist, batch 사이즈 50, shuffle를 실행)
  • test 데이터로 loader를 지정 (dataset은 Minist, batch 사이즈 1000)

In [2]:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision
from torchvision import datasets, transforms
from torch.autograd import Variable
import matplotlib.pyplot as plt
import numpy as np

is_cuda = torch.cuda.is_available() # cuda사 사용가능시, True

batch_size = 50
train_loader = torch.utils.data.DataLoader(
    datasets.MNIST('data', train=True, download=True, transform=transforms.ToTensor()),
    batch_size=batch_size, shuffle=True)

test_loader = torch.utils.data.DataLoader(
    datasets.MNIST('data', train=True, transform=transforms.ToTensor()),
    batch_size=1000)


Files already downloaded

2. 사전 설정

* model
* loss
* opimizer

In [3]:
class MnistModel(nn.Module):
    def __init__(self):
        super(MnistModel, self).__init__()
        # input is 28x28
        # padding=2 for same padding
        self.conv1 = nn.Conv2d(1, 32, 5, padding=2)
        # feature map size is 14*14 by pooling
        # padding=2 for same padding
        self.conv2 = nn.Conv2d(32, 64, 5, padding=2)
        # feature map size is 7*7 by pooling
        self.fc1 = nn.Linear(64*7*7, 1024)
        self.fc2 = nn.Linear(1024, 10)
        
    def forward(self, x):
        x = F.max_pool2d(F.relu(self.conv1(x)), 2)
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = x.view(-1, 64*7*7)   # reshape Variable
        x = F.relu(self.fc1(x))
        x = F.dropout(x, training=self.training)
        x = self.fc2(x)
        return F.log_softmax(x)
    
model = MnistModel()
if is_cuda :  model.cuda()
loss_fn = nn.NLLLoss() 
optimizer = torch.optim.Adam(model.parameters(), lr=0.0001)

3. Trainning loop

* (입력 생성)
* model 생성
* loss 생성
* zeroGrad
* backpropagation
* optimizer step (update model parameter)

Summary 추가 및 출력하기

간단하게 loss와 accuracy를 list에 추가하도록 나중에 matplotlib를 이용하여 출력하겠습니다. 이 부분은 train_loss, train_accu라는 list에 loss와 accuracy를 append하면 됩니다. 물론 Tensorboard같은 별도의 전용 tool이 있으면 편리하겠지만, 간단히 확인할 경우 큰 불편사항은 없습니다.

일단 빈 list를 선언한 다음,

train_loss = []
train_accu = []

train과정이나 test과정에 loss와 accuracy를 추가하면 됩니다.

pred = output.data.max(1)[1]
accuracy = pred.eq(target.data).sum()/batch_size

train_loss.append(loss.data[0])
train_accu.append(accuracy)

In [4]:
# trainning
model.train()
train_loss = []
train_accu = []

for epoch in range(3):
    for i, (image, target) in enumerate(train_loader):
        if is_cuda :  image, target = image.cuda(), target.cuda() 
        image, target = Variable(image), Variable(target) # 입력image Target 설정 
        output = model(image) # model 생성
        loss = loss_fn(output, target) #loss 생성
        optimizer.zero_grad() # zero_grad
        loss.backward() # calc backward grad
        optimizer.step() # update parameter
        
        pred = output.data.max(1)[1]
        accuracy = pred.eq(target.data).sum()/batch_size
        
        train_loss.append(loss.data[0])
        train_accu.append(accuracy)

        if i % 300 == 0:
            print(i, loss.data[0])


0 2.3031723499298096
300 0.22894291579723358
600 0.2739720940589905
900 0.31153997778892517
0 0.14070188999176025
300 0.06042580306529999
600 0.048374027013778687
900 0.03470887243747711
0 0.02888297848403454
300 0.15377072989940643
600 0.06820391863584518
900 0.027004022151231766

In [5]:
plt.plot(train_accu)


Out[5]:
[<matplotlib.lines.Line2D at 0x7fe330338d30>]

In [6]:
plt.plot(train_loss)


Out[6]:
[<matplotlib.lines.Line2D at 0x7fe3302e6128>]

4. Predict & Evaluate


In [7]:
model.eval()
correct = 0
for image, target in test_loader:
    if is_cuda :  image, target = image.cuda(), target.cuda() 
    image, target = Variable(image, volatile=True), Variable(target)
    output = model(image)
    prediction = output.data.max(1)[1]
    correct += prediction.eq(target.data).sum()

print('\nTest set: Accuracy: {:.2f}%'.format(100. * correct / len(test_loader.dataset)))


Test set: Accuracy: 98.65%

5. save model parameter

훈련시킨 model의 parameter를 파일에 저장한다. 다음장에서 저장한 parameter를 restore할 것입니다.


In [8]:
checkpoint_filename = 'minist.ckpt'
torch.save(model.state_dict(), checkpoint_filename)

6. plot images which failed to predict

여러장을 표시할 경우 별로 함수를 이용하여도 되나, pytorch에서 제공하는 함수를 이용하여 출력하여 보았습니다.

torchvision.utils.make_grid(tensor, nrow=8, padding=2)

plt.imshow할때도 약간 주의해야 하는데, array는 colordepth x Height x Width 되어 있지만, Height x Width x colordepth 형태로 바꾸어야 해서 transpose(1, 2, 0)를 수행하였습니다.


In [9]:
model.eval()
image, target = iter(test_loader).next() #test_loader로 부터 한번만 dataset을 호출

if is_cuda :  image, target = image.cuda(), target.cuda() 
image, target = Variable(image, volatile=True), Variable(target)
output = model(image)

## 이미지, 참값, 예측값을 numpy array로 변환 
images = image.data.cpu().numpy()
cls_true = target.data.cpu().numpy().squeeze()
prediction = output.data.max(1)[1].cpu().numpy().squeeze()

# 예측값이 참값과 틀린것을 확인
incorrect = (prediction != cls_true)

# 예측이 틀린 것만을 추출
images = images[incorrect]
cls_true = cls_true[incorrect]
prediction = prediction[incorrect]

# 에러율을 표지
print('error : {:.1%}, number ={:}'.format(incorrect.sum()/len(incorrect), incorrect.sum()))

# 틀린 것들의 이미지를 표시
tensorImg = torch.Tensor(images)
plt.imshow(torchvision.utils.make_grid(tensorImg).numpy().transpose((1,2,0)))
plt.show()

# 틀린 것들의 예측치를 표시
print('prediction :')
pred_resized = np.pad(prediction, (0, 8 - len(prediction)%8), 'constant', constant_values=(0, 0))
print(pred_resized.reshape(-1,8))
print('\n')

# 틀린 것들의 참값을 표시
print('True :')
true_resized = np.pad(cls_true, (0, 8 - len(cls_true)%8), 'constant', constant_values=(0, 0))
print(true_resized.reshape(-1,8))


error : 1.3%, number =13
prediction :
[[8 1 9 7 1 5 0 2]
 [4 2 4 0 4 0 0 0]]


True :
[[9 5 7 9 7 8 6 3]
 [6 5 9 9 7 0 0 0]]

In [ ]: