PyTorch

PyTorch MNIST CPU 코딩

coding art 2019. 12. 7. 16:48
728x90

TensorFlow를 사용하는 MNIST 예제 즉 수기숫자 판독 문제는 머신러닝 학습의 핵심이라고 해도 지나치지 않다. PyTorch 사용법을 배워나가는데 있어서도 마찬가지라 할 수 있을 것이다. CPU 코딩이라 함은 GPU 가 없는 윈도우즈 10에 설치된 아나콘다에서 CPU 로 실행함을 뜻한다. GPU 로 실행하려면 cuda 코딩이 추가되어야 할 것이다.


한편 아나콘다에서 PyTorch 파이선 코드를 실행하기 위해서는 TensorFlow 와는 충돌이 있으므로 TensorFlow 가상환경과 별도로 Torch 가상환경을 만들어서 설치하여 코드를 실행시켜야 한다. 그렇지만 구글 Colabo를 사용하는 경우에는 그다지 문제가 되지 않는다. 구글 Colabo에서 GPU를 사용하여 실행하는 문제는 별도로 다루어 볼 계획이다 

필요한 라이브러리 목록을 살펴보자. torch는 뉴럴 네트워크 wmr nn.∙∙∙명령들과 옵티마이저를 지원한다. PyTorch에서는 주로 SGD(Stochastic Gradient Descent) 옵티마이저가 흔히 사용된다. SGD는 성격상 입력 데이터의 업데이트가 있을 수도 있는 예를 들면 온라인 시스템이라든지 또는 입력 데이터의 수가 어마어마하게 많아서 한 번에 다 cost 함수를 계산하기 곤란한 경우에 사용하기 편리하며 최소화 과정에서도 cost 함수의 거동이 상당히 들쭉날쭉한 경향을 보여준다.

torchvision 라이브러리가 지원하는 datasets transformsPyToch에서 입력 데이터를 처리하는 필수적인 과정으로서 이미지형 입력 데이터를 처리함에 있어서 반드시 거쳐야 한다.




다음의 view_classify 함수는 테스트 과정에서 테스트 샘플의 이지지를 출력하고 학습 결과를 사용하여 Softmax 확률 계산 결과를 작도한다. 코드 후반에서 한번



사용된다. ncols=2 는 그림이 2개의 컬럼으로 구성된다는 뜻이며 ax1은 첫 번째 컬럼으로 샘플 이미지이며 ax2는 확률계산 결과 작도를 뜻한다.

 

입력 데이터 로딩 과정에서 Normalization을 실행한다. MNIST 데이터 입력은 TensorFlow 코딩 과정에서 이미 불러다 놓은 MNIST 폴더를 경로로 사용한다. 즉 현재의 PyTorch 파일과 MNIST 폴더가 디렉토리 내에 함께 있으면 된다. TensorFlow MNIST 코드 실행 결과와 비해 보기 위해서 batch_size=100으로 설정하도록 한다. trainloader는 학습용 데이터이며 valloader 는 테스트용이다.

  

 

아래의 출력 결과는 batch_size 샘플들 중에서 첫 번째 숫자 하나르 포함 나머지 60개를 출력해 본 결과이다.



수기숫자 1개의 픽셀 수는 28X28=784 이다. hiden_size wide deep을 줄 경우에는 필요하지만 784에서 직접 10개 출력을 계산해 버릴 경우에는 굳이 필요가 없다. 하지만 아무래도 hidden layer를 사용하게 되면 인식률이 많이 올라간다.

nn.Sequential에서 nn.Linear(784,10) 계산 후 막바로 nn.LogSoftmax 계산하자. LogSoftmaxSoftmax에 비해서 exponential 중간과정 계산 단계에서의 overflow underflow를 방지하는데 유익하다. nn.NLLLossnegative log likelyhood loss를 계산하는데 편리하다. LogSoftmax 사용과 NLLLoss 사용이 밀접한 관계를 맺고 있다.

next(iter(trainloader)) TensorFlow에서 Placeholder 선언과 함께 Session에서 사용하는 mnist.train.next_batch 명령에 해당한다.

images.shape[0]784를 뜻하며 1은 다른 dimension을 추론해 내는데 여기서는 batch_size=100을 의미한다. 따라서 images.size()를 출력해 보면 (100, 784)가 된다. 이는 TensorFlow에서 shape (None, 784)batch_size=100 에 해당하는 내용이다.

이와 같이 모든 준비가 되었으면 최종적으로 loss 함수를 계산하고 SGD 옵티마이저를 설정한다 learning rate lr 의 값은 경험적으로 선정한다.

  

 

TensorFlow 예제에서처럼 epoch=15 로 계산을 실행하자.

 



테스트 데이터를 사용한 결과를 관찰해 보자. 랜덤하게 선정된 하나의 셈플 테스트 데이타 images[0].view(1,784)의 비트별 확률 계산 결과를 보면 0,1,2,3,4,5,6,∙∙∙중에서 6에 해당하는 확률이 0.998임을 알 수 있다.

   

구글 Colaboratory에서 GPU로 실행할 경우에는 PC에 저장되어 있는 MNIST_data 폴더를 구글 드라이브에 불러 와야 한다. 폴더 아네 들어 있는 4개의 파일들을 각각 업로딩 후 mkdir MNIST_data 로 폴더를 생상하고 mv *.gz 명령에 의해 폴더로 옮겨 놓은 후 pytorch_mnist_plain.py를 한 번에 모조리 Ctrl+C 하여 Ctrl+V 로 옮겨 실행하면 된다.


실행 결과는 아래와 같다.


#pytorch_mnist_plain
import numpy as np
import torch
import torchvision
import matplotlib.pyplot as plt
from time import time
from torchvision import datasets, transforms
from torch import nn, optim

def view_classify(img, ps):
    ''' Function for viewing an image and it's predicted classes.
    '''
    ps = ps.data.numpy().squeeze()

    fig, (ax1, ax2) = plt.subplots(figsize=(6,9), ncols=2)
    ax1.imshow(img.resize_(1, 28, 28).numpy().squeeze())
    ax1.axis('off')
    ax2.barh(np.arange(10), ps)
    ax2.set_aspect(0.1)
    ax2.set_yticks(np.arange(10))
    ax2.set_yticklabels(np.arange(10))
    ax2.set_title('Class Probability')
    ax2.set_xlim(0, 1.1)
    plt.tight_layout()
   
transform = transforms.Compose([transforms.ToTensor(),
                              transforms.Normalize((0.5,), (0.5,)),
                              ])

trainset = datasets.MNIST('MNIST_data/', download=True, train=True, transform=transform)
valset = datasets.MNIST('MNIST_data/', download=True, train=False, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=100, shuffle=True)
valloader = torch.utils.data.DataLoader(valset, batch_size=100, shuffle=True)

dataiter = iter(trainloader)
images, labels = dataiter.next()

print(images.shape)
print(labels.shape)

plt.imshow(images[0].numpy().squeeze(), cmap='gray_r')

figure = plt.figure()
num_of_images = 60
for index in range(1, num_of_images + 1):
    plt.subplot(6, 10, index)
    plt.axis('off')
    plt.imshow(images[index].numpy().squeeze(), cmap='gray_r')
   
input_size = 784
hidden_sizes = [128, 64]
output_size = 10

model = nn.Sequential(nn.Linear(input_size, output_size),
                      nn.LogSoftmax(dim=1))
print(model)

criterion = nn.NLLLoss()
images, labels = next(iter(trainloader))
images = images.view(images.shape[0], -1)
print(images.size())
logps = model(images) #log probabilities
loss = criterion(logps, labels) #calculate the NLL loss

print('Before backward pass: \n', model[0].weight.grad)
loss.backward()
print('After backward pass: \n', model[0].weight.grad)

optimizer = optim.SGD(model.parameters(), lr=0.003, momentum=0.9)
time0 = time()
epochs = 15
for e in range(epochs):
    running_loss = 0
    for images, labels in trainloader:
        # Flatten MNIST images into a 784 long vector
        images = images.view(images.shape[0], -1)
   
        # Training pass
        optimizer.zero_grad()
       
        output = model(images)
        loss = criterion(output, labels)
       
        #This is where the model learns by backpropagating
        loss.backward()
       
        #And optimizes its weights here
        optimizer.step()
       
        running_loss += loss.item()
    else:
        print("Epoch {} - Training loss: {}".format(e, running_loss/len(trainloader)))
        print("\nTraining Time (in minutes) =",(time()-time0)/60)

images, labels = next(iter(valloader))

img = images[0].view(1, 784)
with torch.no_grad():
    logps = model(img)

ps = torch.exp(logps)
probab = list(ps.numpy()[0])
print(probab)
print("Predicted Digit =", probab.index(max(probab)))
view_classify(img.view(1, 28, 28), ps)

correct_count, all_count = 0, 0
for images,labels in valloader:
  for i in range(len(labels)):
    img = images[i].view(1, 784)
    with torch.no_grad():
        logps = model(img)

   
    ps = torch.exp(logps)
    probab = list(ps.numpy()[0])
    pred_label = probab.index(max(probab))
    true_label = labels.numpy()[i]
    if(true_label == pred_label):
      correct_count += 1
    all_count += 1

print("Number Of Images Tested =", all_count)
print("\nModel Accuracy =", (correct_count/all_count))