.. _sphx_glr_beginner_blitz_cifar10_tutorial.py: 분류기(Classifier) 학습하기 =========================== 지금까지 어떻게 신경망을 정의하고, 손실을 계산하며 또 가중치를 갱신하는지에 대해서 배웠습니다. 이제 아마도 이런 생각을 하고 계실텐데요, 데이터는 어떻게 하나요? ------------------------ 일반적으로 이미지나 텍스트, 오디오나 비디오 데이터를 다룰텐데요, 이러한 데이터는 표준 Python 패키지를 사용하여 불러온 후 NumPy 배열로 변환하면 됩니다. 그리고 그 배열을 ``torch.*Tensor`` 로 변환하면 됩니다. - 이미지는 Pillow나 OpenCV 같은 패키지가 유용합니다. - 오디오를 처리할 때는 SciPy와 LibROSA가 유용하고요. - 텍스트의 경우에는 그냥 Python이나 Cython의 것들을 사용하거나, NLTK나 SpaCy도 좋습니다. 특별히 영상 분야를 위해서는 ``torchvision`` 이라는 패키지를 만들어두었는데요, 여기에는 Imagenet이나 CIFAR10, MNIST 등과 같은 일반적으로 사용하는 데이터셋을 불러오는 함수들(data loaders)이나, image, viz., ``torchvision.datasets`` 와 ``torch.utils.data.DataLoader`` 데이터 변환기가 포함되어 있습니다. 이러한 기능은 엄청나게 편리하며, 매번 유사한 코드(boilerplate code)를 반복해서 작성하는 것을 피할 수 있습니다. 이 튜토리얼에서는 CIFAR10 데이터셋을 사용할 텐데요, 여기에는 다음과 같은 분류들이 있습니다: '비행기(airplane)', '자동차(automobile)', '새(bird)', '고양이(cat)', '사슴(deer)', '개(dog)', '개구리(frog)', '말(horse)', '배(ship)', '트럭(truck)'. 그리고 CIFAR10에 포함된 이미지의 크기는 3x32x32인데요, 이는 32x32 픽셀 크기의 이미지가 3개 채널(channel)로 이뤄져 있다는 뜻입니다. .. figure:: /_static/img/cifar10.png :alt: cifar10 cifar10 이미지 분류기 학습하기 ---------------------------- 다음의 단계로 진행해보겠습니다: 1. CIFAR10의 학습용 / 시험(test)용 데이터셋을 ``torchvision`` 을 사용하여 불러오고, 정규화(nomarlizing)합니다. 2. 합성곱 신경망(Convolution Neural Network)을 정의합니다. 3. 손실 함수를 정의합니다. 4. 학습용 데이터를 사용하여 신경망을 학습합니다. 5. 시험용 데이터를 사용하여 신경망을 검사합니다. 1. CIFAR10를 불러오고 정규화하기 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``torchvision`` 을 사용하면 매우 쉽게 CIFAR10 데이터를 불러올 수 있습니다. .. code-block:: python import torch import torchvision import torchvision.transforms as transforms torchvision 데이터셋의 출력(output)은 [0, 1] 범위를 갖는 PILImage 이미지입니다. 이를 [-1, 1]의 범위로 정규화된 Tensor로 변환하겠습니다. .. code-block:: python 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') .. rst-class:: sphx-glr-script-out Out:: Files already downloaded and verified Files already downloaded and verified 재미삼아 학습용 이미지 몇 개를 보겠습니다. .. code-block:: python import matplotlib.pyplot as plt import numpy as np # 이미지를 보여주기 위한 함수 def imshow(img): img = img / 2 + 0.5 # unnormalize npimg = img.numpy() plt.imshow(np.transpose(npimg, (1, 2, 0))) # 학습용 이미지를 무작위로 가져오기 dataiter = iter(trainloader) images, labels = dataiter.next() # 이미지 보여주기 imshow(torchvision.utils.make_grid(images)) # 정답(label) 출력 print(' '.join('%5s' % classes[labels[j]] for j in range(4))) .. image:: /beginner/blitz/images/sphx_glr_cifar10_tutorial_001.png :align: center .. rst-class:: sphx-glr-script-out Out:: horse car frog truck 2. 합성곱 신경망(Convolution Neural Network) 정의하기 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 이전에 배웠던 신경망 섹션에서 신경망을 복사하고, (기존에 1채널 이미지만 처리하던 것 대신) 3채널 이미지를 처리할 수 있도록 수정합니다. .. code-block:: python from torch.autograd import Variable 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 정의하기 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 이제, 분류에 대한 교차 엔트로피 손실(Cross-Entropy loss)과 momentum을 갖는 SGD를 사용합니다. .. code-block:: python import torch.optim as optim criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) 4. 신경망 학습하기 ^^^^^^^^^^^^^^^^^^^^ 이제부터 흥미로우실 겁니다. 데이터를 반복해서 신경망에 입력으로 제공하고, 최적화(Optimize)만 하면 됩니다. .. code-block:: python for epoch in range(2): # 데이터셋을 수차례 반복합니다. running_loss = 0.0 for i, data in enumerate(trainloader, 0): # 입력을 받은 후, inputs, labels = data # Variable로 감싸고 inputs, labels = Variable(inputs), Variable(labels) # 변화도 매개변수를 0으로 만든 후 optimizer.zero_grad() # 학습 + 역전파 + 최적화 outputs = net(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() # 통계 출력 running_loss += loss.data[0] 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') .. rst-class:: sphx-glr-script-out Out:: [1, 2000] loss: 2.176 [1, 4000] loss: 1.838 [1, 6000] loss: 1.663 [1, 8000] loss: 1.575 [1, 10000] loss: 1.509 [1, 12000] loss: 1.447 [2, 2000] loss: 1.384 [2, 4000] loss: 1.331 [2, 6000] loss: 1.340 [2, 8000] loss: 1.314 [2, 10000] loss: 1.255 [2, 12000] loss: 1.262 Finished Training 5. 시험용 데이터로 신경망 검사하기 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 학습용 데이터셋을 2회 반복하여 신경망을 학습시켰는데요, 신경망이 전혀 배운게 없을지도 모르니 확인해보겠습니다. 신경망이 예측한 정답과 진짜 정답(Ground-truth)을 비교하는 방식으로 확인할텐데요, 예측이 맞다면 샘플을 '맞은 예측값(Correct predictions)'에 넣겠습니다. 먼저 시험용 데이터를 좀 보겠습니다. .. code-block:: python dataiter = iter(testloader) images, labels = dataiter.next() # 이미지 출력 imshow(torchvision.utils.make_grid(images)) print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4))) .. image:: /beginner/blitz/images/sphx_glr_cifar10_tutorial_002.png :align: center .. rst-class:: sphx-glr-script-out Out:: GroundTruth: cat ship ship plane 좋습니다, 이제 신경망이 어떻게 예측했는지를 보죠: .. code-block:: python outputs = net(Variable(images)) 출력은 10개 분류 각각에 대한 값으로 나타납니다. 어떤 분류에 대해서 더 높은 값이 나타난다는 것은, 신경망이 그 이미지가 더 해당 분류에 가깝다고 생각한다는 것입니다. 따라서, 가장 높은 값을 갖는 인덱스(index)를 뽑아보겠습니다: .. code-block:: python _, predicted = torch.max(outputs.data, 1) print('Predicted: ', ' '.join('%5s' % classes[predicted[j]] for j in range(4))) .. rst-class:: sphx-glr-script-out Out:: Predicted: cat ship ship ship 결과가 괜찮아보이네요. 그럼 전체 데이터셋에 대해서는 어떻게 동작하는지 보겠습니다. .. code-block:: python correct = 0 total = 0 for data in testloader: images, labels = data outputs = net(Variable(images)) _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum() print('Accuracy of the network on the 10000 test images: %d %%' % ( 100 * correct / total)) .. rst-class:: sphx-glr-script-out Out:: Accuracy of the network on the 10000 test images: 55 % (10가지 분류에서 무작위로) 찍었을 때의 정확도인 10% 보다는 나아보입니다. 신경망이 뭔가 배우긴 한 것 같네요. 그럼 어떤 것들을 더 잘 분류하고, 어떤 것들을 더 못했는지 알아보겠습니다: .. code-block:: python class_correct = list(0. for i in range(10)) class_total = list(0. for i in range(10)) for data in testloader: images, labels = data outputs = net(Variable(images)) _, predicted = torch.max(outputs.data, 1) c = (predicted == labels).squeeze() for i in range(4): label = labels[i] class_correct[label] += c[i] class_total[label] += 1 for i in range(10): print('Accuracy of %5s : %2d %%' % ( classes[i], 100 * class_correct[i] / class_total[i])) .. rst-class:: sphx-glr-script-out Out:: Accuracy of plane : 52 % Accuracy of car : 63 % Accuracy of bird : 35 % Accuracy of cat : 57 % Accuracy of deer : 39 % Accuracy of dog : 30 % Accuracy of frog : 61 % Accuracy of horse : 64 % Accuracy of ship : 77 % Accuracy of truck : 76 % 자, 이제 다음은 뭘까요? 이러한 신경망들을 GPU에서 실행한다면 어떨까요? GPU에서 학습하기 ---------------- Tensor를 GPU로 옮겼던 것처럼, 신경망을 GPU로 옮길 수 있습니다. 이렇게 하면 모든 모듈에 대해서 재귀적으로 처리하며 매개변수와 버퍼들을 CUDA tensor로 변환하게 됩니다: .. code:: python net.cuda() 모든 단계에서 입력(input)과 정답(target)도 GPU로 보내야 한다는 것도 기억하셔야 합니다: :: inputs, labels = Variable(inputs.cuda()), Variable(labels.cuda()) CPU와 비교했을 때 어마어마한 속도 차이가 나지 않는 것은 왜 그럴까요? 그 이유는 바로 신경망이 너무 작기 때문입니다. **Exercise:** 신경망의 크기를 키웠을 때 얼마나 빨라지는지 확인해보세요. (첫번째 ``nn.Conv2d`` 의 2번째 매개변수와 두번째 ``nn.Conv2d`` 의 1번째 매개변수는 같아야 합니다.) **목표를 달성했습니다**: - 높은 수준에서 PyTorch의 Tensor library와 신경망를 이해합니다. - 이미지를 분류하는 작은 신경망을 학습시킵니다. 여러개의 GPU에서 학습하기 ------------------------- 모든 GPU를 활용해서 더욱 더 속도를 올리고 싶다면, :doc:`data_parallel_tutorial` 을 참고하세요. 이제 뭘 해볼까요? ------------------- - :doc:`Train neural nets to play video games ` - `Train a state-of-the-art ResNet network on imagenet`_ - `Train a face generator using Generative Adversarial Networks`_ - `Train a word-level language model using Recurrent LSTM networks`_ - `다른 예제들 참고하기`_ - `더 많은 튜토리얼 보기`_ - `포럼에서 PyTorch에 대해 얘기하기`_ - `Slack에서 다른 사용자와 대화하기`_ .. _Train a state-of-the-art ResNet network on imagenet: https://github.com/pytorch/examples/tree/master/imagenet .. _Train a face generator using Generative Adversarial Networks: https://github.com/pytorch/examples/tree/master/dcgan .. _Train a word-level language model using Recurrent LSTM networks: https://github.com/pytorch/examples/tree/master/word_language_model .. _다른 예제들 참고하기: https://github.com/pytorch/examples .. _더 많은 튜토리얼 보기: https://github.com/pytorch/tutorials .. _포럼에서 PyTorch에 대해 얘기하기: https://discuss.pytorch.org/ .. _Slack에서 다른 사용자와 대화하기: http://pytorch.slack.com/messages/beginner/ **Total running time of the script:** ( 1 minutes 1.551 seconds) .. only :: html .. container:: sphx-glr-footer .. container:: sphx-glr-download :download:`Download Python source code: cifar10_tutorial.py ` .. container:: sphx-glr-download :download:`Download Jupyter notebook: cifar10_tutorial.ipynb ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_