3 minute read

딥러닝 공부 10일차

MNIST



MNIST란? 숫자들에대한 손글씨 데이터셋이라고 보면 됩니다.

MNIST 데이터는 아래의 링크에 공개되어져 있습니다. 데이터보러가기

MNIST는 숫자 0부터 9까지의 이미지로 구성된 손글씨 데이터셋입니다.

이 데이터는 과거에 우체국에서 편지의 우편 번호를 인식하기 위해서 만들어진 훈련 데이터입니다.

총 60,000개의 훈련 데이터와 레이블, 총 10,000개의 테스트 데이터와 레이블로 구성되어져 있습니다.

레이블은 0부터 9까지 총 10개입니다.

우선 MNIST 문제를 더 자세히 보겠습니다. 각각의 이미지는 아래와 같이 28 픽셀 × 28 픽셀의 이미지입니다.

28 x 28 = 784픽셀이기 때문에, 각 이미지를 총 784개의 원소를 가진 벡터로 만들어줄겁니다.

784차원의 벡터로만드는 코드는 아래와 같습니다.

for X, Y in data_loader:
  # 입력 이미지를 [batch_size × 784]의 크기로 reshape
  # 레이블은 원-핫 인코딩
  X = X.view(-1, 28*28)

View 함수가 기억이 안난다면? 👈 보고 오시죠!

view함수를 이용하여, 784열짜리 벡터를 만들어줘 그런데 이제 행의 크기는 너가 알아서 조절해줘~ 라는 느낌이라고 했죠?

여기서 행의 크기가 배치 사이즈가 됩니다.

torchvision은 유명한 데이터셋들, 이미 구현되어져 있는 유명한 모델들, 일반적인 이미지 전처리 도구들을 포함하고 있는 패키지입니다.

이제 코드를 직접 실행해볼까요??

분류기 구현을 위한 사전 설정

우선 필요한 도구들을 임포트합니다.

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

각자의 환경에서 GPU 연산이 가능하다면 연산이 빠른 GPU를 사용하도록 합시다.

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

맥북은 cpu를 사용한다고 출력이되는 것 같습니다.

그 외 환경이신분들은 구글의 Colab 👉 런타임 👉 런타임 유형 변경 👉 하드웨어 가속기 👉 GPU 로 설정하시면 GPU로 연산하실 수 있습니다.

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

하이퍼파라미터를 변수로 둡니다.

# hyperparameters
training_epochs = 15
batch_size = 100

MNIST 분류기 구현하기

torchvision.datasets.dsets.MNIST를 사용하여 MNIST 데이터셋을 불러올 수 있습니다.

# MNIST dataset
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)

첫번째 인자 root는 MNIST 데이터를 다운로드 받을 경로입니다.

두번째 인자 train은 인자로 True를 주면, MNIST의 훈련 데이터를 리턴받으며 False를 주면 테스트 데이터를 리턴받습니다.

세번째 인자 transform은 현재 데이터를 파이토치 텐서로 변환해줍니다.

네번째 인자 download는 해당 경로에 MNIST 데이터가 없다면 다운로드 받겠다는 의미입니다.

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

**drop-last 를 하는 이유는 배치사이즈로 나눈 데이터의 마지막에 일부 데이터가 나눠떨어지지 않고 남았다면 이들을 버릴 수 있습니다.

버리려면 True를 해주면 되는데, 이는 다른 미니 배치보다 개수가 적은 마지막 배치를 경사 하강법에 사용하여 마지막 배치가 상대적으로 과대 평가되는 현상을 막아줍니다.

이제 모델을 설계합니다.

# MNIST data image of shape 28 * 28 = 784
linear = nn.Linear(784, 10, bias=True).to(device)

to()함수는 연산을 어디서 할지 정합니다. GPU를 사용하려면 to(‘cuda’)를 해주어야합니다.

이제 비용함수와 옵티마이저를 정의합니다.

# 비용 함수와 옵티마이저 정의
criterion = nn.CrossEntropyLoss().to(device) # 내부적으로 소프트맥스 함수를 포함하고 있음.
optimizer = torch.optim.SGD(linear.parameters(), lr=0.1)

앞서 소프트맥스 회귀에서는 torch.nn.functional.cross_entropy()를 사용하였었습니다. 이는 여기서 torch.nn.CrossEntropyLoss()와 동일합니다.

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

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

        optimizer.zero_grad()
        hypothesis = linear(X)
        cost = criterion(hypothesis, Y)
        cost.backward()
        optimizer.step()

        avg_cost += cost / total_batch

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

print('Learning finished')

영상처럼 파일의 크기가 커서 시간이 조금 걸리는 것을 보실 수 있습니다.

이제 실제로 테스트 데이터셋을 대입하여 예측을 잘하는지 확인해봅시다!

# 테스트 데이터를 사용하여 모델을 테스트한다.
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 = linear(X_test)
    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 = linear(X_single_data)
    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()

다음 영상처럼 처음에는 학습률이 낮아서 제대로된 결과를 도출해내지 못하는 모습을 보여줍니다.

그래서 학습률을 0.01로 수정해보았습니다. 완벽한 학습률은 아니겠지만 결과는 제대로 도출된 모습을 볼 수 있습니다.

끝!

참고문헌

Leave a comment