뉴비에욤

PyTorch 튜토리얼 4 - 분류기(classifier) 학습 본문

Machine Learning/PyTorch

PyTorch 튜토리얼 4 - 분류기(classifier) 학습

초보에욤 2018. 5. 1. 18:00

2017/07/13 - [Machine Learning/PyTorch] - 윈도우 10 PyTorch 환경 구성 - 설치

2018/04/30 - [Machine Learning/PyTorch] - PyTorch 튜토리얼 1 - PyTorch란?

2018/04/30 - [Machine Learning/PyTorch] - PyTorch 튜토리얼 2 - Autograd : 자동 미분

2018/05/01 - [Machine Learning/PyTorch] - PyTorch 튜토리얼 3 - 뉴럴 네트워크(신경망)



원문 : http://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html


* 17년도 07월에 초번 번역 후, 파이토치 최신 버전인 0.4.0에 맞추어 갱신 된 번역본

* 최근 갱신일 : 2018-05-01


목 차 ( 초보자 튜토리얼 )

A. 파이토치와 함께하는 딥러닝 : 60분만에 끝장내기!

4. 분류기 학습

- 데이터는 어떻게 하지?

- 이미지 분류기 학습

1. CIFAR-10 로딩 및 정규화

2. 컨볼루션 뉴럴 네트워크 정의

3. 손실 함수 및 optimizer 정의

4. 네트워크 학습

5. 평가 데이터를 이용한 네트워크 평가

- GPU를 이용한 학습

- 여러 개의 GPU를 이용한 학습

추가적으로 참고할 링크




분류기 학습


지금까지 뉴럴 네트워크 정의, loss(손실) 계산, 네트워크 weight(가중치) 갱신에 대하여 살펴보았다. 그럼 다음과 같은 의문점이 생각날 수도 있다.




데이터는 어떻게 하지?


일반적으로 이미지, 텍스트, 오디오, 비디오 데이터를 처리하고자 할 때 해당 데이터들을 numpy 배열에 로드하는 표준 파이썬 패키지를 이용한 뒤, 그 다음 numpy 배열의 값들을 torch.*Tensor로 변환할 수 있다.

  • 이미지 데이터를 대상으로 Pillow, OpenCV 패키지를 사용할 수 있다.
  • 오디오 데이터를 대상으로 scipy, librosa 패키지를 사용할 수 있다.
  • 텍스트 데이터를 대상으로 raw 파이썬이나 Cython 기반의 로딩 코드, 아니면 NLTK나 SpaCy 패키지를 사용할 수 있다.

특히 vision은 파이토치에서 torchvision 패키지라는 이름으로 제공되는데 해당 패키지는 일반적으로 사용되는 Imagenet, CIFAR10, MNIST 등과 같은 데이터셋들에 대한 데이터 로더(torchvision.datasets)와 이미지용 데이터 변환기(torch.utils.data.DataLoader)를 제공한다.


이는 엄청난 편의성을 제공함과 동시에 보일러 플레이트 코드[각주:1] 사용을 최소화 할 수 있다.

이번 튜토리얼에서는 CIFAR-10 데이터셋을 이용할 것이다. 해당 데이터셋은 비행기, 자동차, 새, 고양이, 사슴, 개, 개구리, 말, 배, 트럭 순서의 클래스들을 가지고 있다. CIFAR-10의 이미지들은 3*32*32인데 다시 말해 32*32 픽셀 크기의 3채널 컬러 이미지이다.

cifar10
그림. CIFAR-10 데이터셋의 클래스와 이미지



이미지 분류기 학습

다음과 같은 순서로 진행 할 것이다.
  1. torchvision을 이용하여 CIFAR-10의 학습(training), 평가(test) 데이터셋을 로드하여 정규화 한다.
  2. 컨볼루션 뉴럴 네트워크를 정의한다.
  3. 손실 함수를 정의 한다.
  4. 학습 데이터를 이용하여 네트워크를 학습 시킨다.
  5. 평가 데이터셋을 이용하여 네트워크를 평가 한다.


1. CIFAR-10 로딩 및 정규화

torchvision을 이용하면 CIFAR-10 로딩은 엄청 쉬운 작업이다.
- 소스
import torch
import torchvision
import torchvision.transforms as transforms


torchvision 패키지의 출력은 [0, 1] 범위의 PILImage 이미지이다. 우린 출력 데이터를 범위 [-1, 1]의 텐서로 정규화 시킬 것이다.
- 소스
transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
                                          shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
                                         shuffle=False, num_workers=2)

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck') 
- 결과
Files already downloaded and verified
Files already downloaded and verified


재미있는 학습 이미지 몇 가지를 보여주자면 다음과 같다.

- 소스

import matplotlib.pyplot as plt
import numpy as np

# functions to show an image


def imshow(img):
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))


# get some random training images
dataiter = iter(trainloader)
images, labels = dataiter.next()

# show images
imshow(torchvision.utils.make_grid(images))
# print labels
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))

- 결과

ship truck horse horse



2. 컨볼루션 뉴럴 네트워크(합성곱 신경망) 정의


이전의 뉴럴 네트워크 포스팅에서 관련 코드를 복사하고 3-채널 이미지를 입력으로 사용할 수 있도록 수정한다.

( 기존 코드는 1 채널 이미지만을 입력으로 사용할 수 있었다. )

- 소스

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


class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x


net = Net()




3. 손실 함수 및 optimizer 정의


손실 함수는 Classification Cross-Entropy를 사용하고, optimizer는 momentum을 세팅한 SGD를 이용할 것이다.

- 소스

import torch.optim as optim

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)




4. 네트워크 학습


슬슬 흥미로워 지는 단계이다.  우린 단순히 데이터를 반복시켜 네트워크와 optimizer의 입력으로 넘겨주기만 하면 된다.

- 소스

for epoch in range(2):  # loop over the dataset multiple times

    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # get the inputs
        inputs, labels = data

        # zero the parameter gradients
        optimizer.zero_grad()

        # forward + backward + optimize
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # print statistics
        running_loss += loss.item()
        if i % 2000 == 1999:    # print every 2000 mini-batches
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0

print('Finished Training') 

- 결과

[1,  2000] loss: 2.199
[1,  4000] loss: 1.856
[1,  6000] loss: 1.688
[1,  8000] loss: 1.606
[1, 10000] loss: 1.534
[1, 12000] loss: 1.488
[2,  2000] loss: 1.420
[2,  4000] loss: 1.384
[2,  6000] loss: 1.336
[2,  8000] loss: 1.351
[2, 10000] loss: 1.309
[2, 12000] loss: 1.277
Finished Training




5. 평가 데이터를 이용한 네트워크 평가


학습 데이터셋을 이용하여 총 2번 반복하면서 학습을 시켰다. 그러나 실제로 네트워크가 무엇인가를 배웠는지에 대하여 테스트를 해야 한다.


뉴럴 네트워크의 출력인 클래스 label을 예측하고 실제 데이터와 비교함으로써 테스트를 수행할 수 있는데 만약 예측 결과가 올바르다면 올바른 예측 리스트에 샘플을 추가할 수 있다.


우선, 첫번째로 이해를 돕기 위해 테스트셋의 이미지를 띄운다.

- 소스

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

# print images
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

- 결과

GroundTruth:    cat  ship  ship plane



자 이제 뉴럴 네트워크가 위 사진의 예제를 어떻게 생각하는지 다음의 코드를 통해 살펴본다.

- 소스

outputs = net(images)



출력은 총 10개의 클래스 에너지가 된다.  특정 클래스의 에니저가 높을수록 네트워크는 대상 이미지를 특정 클래스화 할 수 있다. 즉, 에너지가 높을 수록 정확도가 높다는 뜻이다. 가장 높은 에너지들의 인덱스를 뽑으면 다음과 같다.

- 소스

_, predicted = torch.max(outputs, 1)

print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
                              for j in range(4))) 

- 결과

Predicted:    cat   car   car plane



결과는 꽤 괜찮아 보인다. 

- 전체 데이터셋에 대한 네트워크의 학습 정확도는 다음과 같다.

correct = 0
total = 0
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' % (
    100 * correct / total))

결과

Accuracy of the network on the 10000 test images: 53 %



총 10개의 클래스 중 한개를 선택하는 10% 보다는 확률이 높다 보인다. 일단 네트워크가 무언가는 배웠다고 생각할 수 있다.

그렇다면 학습이 잘 된 클래스와 잘 되지 않은 클래스는 어떻게 될까? 이에 대한 대답은 다음의 코드로 알아낼 수 있다.

- 소스

class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs, 1)
        c = (predicted == labels).squeeze()
        for i in range(4):
            label = labels[i]
            class_correct[label] += c[i].item()
            class_total[label] += 1


for i in range(10):
    print('Accuracy of %5s : %2d %%' % (
        classes[i], 100 * class_correct[i] / class_total[i])) 

- 결과

Accuracy of plane : 60 %
Accuracy of   car : 75 %
Accuracy of  bird : 33 %
Accuracy of   cat : 50 %
Accuracy of  deer : 26 %
Accuracy of   dog : 47 %
Accuracy of  frog : 54 %
Accuracy of horse : 66 %
Accuracy of  ship : 48 %
Accuracy of truck : 70 %




GPU를 이용한 학습


텐서를 GPU에 전송하는 것처럼 뉴럴 네트워크를 GPU에 전송할 수 있다. 

CUDA를 사용할 수 있는 상태라면, 첫 번째 CUDA 디바이스를 사용한다고 명시해주어야 한다.

- 소스

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

# Assume that we are on a CUDA machine, then this should print a CUDA device:

print(device) 

- 결과

cuda:0



아래의 내용은 device가 CUDA 디바이스라고 가정한 후 진행한 것이다.


CUDA 디바이스를 명시했다면, 모든 모듈을 재귀적으로 처리하고 파라미터와 버퍼를 CUDA 텐서로 변환한다.

- 소스

net.to(device)


GPU를 사용할 때도 마찬가지로 매 단계마다 입력과 정답을 전송해야 한다.

- 소스

inputs, labels = inputs.to(device), labels.to(device)


실제 돌려보면 CPU와 GPU를 사용했을 때의 학습 속도는 그렇게 크지 않다. 이유는 지금 작성한 네트워크는 소규모이기 때문이다.




연습 


네트워크의 범위를 늘려서(첫 번째 nn.Conv2d의 두 번째 인자와 두 번째 nn.Conv2d의 첫 번째 인자는 같은 숫자여야 함)  속도 향상을 얼마만큼 얻을 수 있는지 알아보기 바란다.




달성 목표

  • PyTorch의 텐서 라이브러리와 뉴럴 네트워크에 대한 고수준의 이해
  • 이미지 판별을 위한 소규모 뉴럴 네트워크 학습


여러 개의 GPU를 이용한 학습

본인이 가지고 있는 모든 GPU를 이용하여 얼마나 큰 속도 향상을 이룰 수 있는지 궁금하다면 다음 링크를 참고하기 바란다.
http://pytorch.org/tutorials/beginner/blitz/data_parallel_tutorial.html



추가적으로 참고할 링크
  • 비디오 게임 플레이를 위한 뉴럴 네트워크 학습
    • http://pytorch.org/tutorials/intermediate/reinforcement_q_learning.html
  • ImageNet 대상으로 최첨단 ResNet 네트워크 학습
    • https://github.com/pytorch/examples/tree/master/imagenet
  • Generative Adversarial 네트워크를 이용한 얼굴 생성기 학습
    • https://github.com/pytorch/examples/tree/master/dcgan
  • 반복적인 LSTM 네트워크를 사용하여 단어 수준 언어 모델 훈련
    • https://github.com/pytorch/examples/tree/master/word_language_model
  • 추가 예제
    • https://github.com/pytorch/examples/tree/master/word_language_model
  • 추가 튜토리얼
    • https://github.com/pytorch/examples
  • PyTorch 토론장
    • https://discuss.pytorch.org/
  • Slack에서 다른 사용자들과 채팅
    • https://pytorch.slack.com/?redir=%2Fmessages%2Fbeginner









  1. 꼭 필요하면서 간단한 기능이지만, 코드 라인이 적지 않게 필요함. http://aroundck.tistory.com/2082 [본문으로]
Comments