Deep Learning with PyTorch: A 60 Minutes Blitz (4) – Training a classifier
http://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html
Deep Learning with PyTorch: A 60 Minute Blitz
Training a classifier
이제야 왔습니다. 지금까지 신경망을 어떻게 정의하는지 알아보았고, 로스를 계산하여 가중치를 업데이트하는 것까지 살펴보았습니다.
뭐가 나올지 알고 있을 것입니다.
What about data?
일반적으로 이미지, 텍스트, 오디오, 비디오 데이터를 처리하려고 할 때, 이들 데이터를 표준 python 패키지를 이용해서 numpy array로 변환한 다음, torch.*Tensor
로 변환하면 됩니다.
- 이미지의 경우, Pillow, OpenCV 패키지들이 유용합니다.
- 오디오의 경우 scipy나 librosa가 적당합니다.
- 텍스트의 경우 Python이나 Cython으로 직접 처리하거나, NLTK와 SpaCy가 유용합니다.
특별히 vision
의 경우에는 torchvision
이라는 패키지를 만들어두었습니다. 이것을 통해 Imagenet, CIFAR10, MNIST 등의 일반적으로 쓰이는 데이터셋을 읽어올 수 있습니다. 또한 이미지들의 data transformer와 viz., torchvision.datasets
과 torch.utils.data.DataLoader
또한 제공합니다.
이러한 기능들은 큰 편의성을 제공하고, 매번 똑같이 타이핑해야 하는 코드들을 줄일 수 있습니다.
이 튜토리얼에서는 CIFAR10 데이터셋을 사용할 것입니다. 이 데이터셋은 'airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck'의 클래스를 사용합니다. CIFAR-10의 이미지들은 3x32x32의 크기를 가집니다. 즉 32x32 픽셀 크기를 가지는 3채널의 컬러이미지입니다.
Training an image classifier
이제 다음의 단계들을 순서대로 살펴볼 것입니다.
torchvision
을 이용하여 CIFAR10 트레이닝, 테스트 데이터셋을 불러오고 normalzing하기- Convolutional Neural Network를 정의하기
- 로스 함수를 정의하기
- 트레이닝 데이터를 이용하여 망을 학습하기
- 테스트 데이터에서 망을 테스트하기
1. Loading and normalizing CIFAR10
torchvision
을 이용하면 CIFAR10을 엄청 쉽게 불러올 수 있습니다.
1 2 3 4 |
import torch import torchvision import torchvision.transforms as transforms |
torchvision
데이터셋의 출력은 [0, 1]의 범위를 갖는 PILImage image입니다. 이제 이것을 [-1, 1]로 normalize한 Tensor로 변환할 것입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
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') |
Out:
123 Files already downloaded and verifiedFiles already downloaded and verified
재미 삼아 트레이닝 이미지들에 어떤 것들이 있는지 한번 살펴봅시다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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))) |
Out:
12 frog ship bird truck
2. Define a Convolution Neural Network
이전의 살펴본 Neural Network 섹션의 신경망을 복사한 다음, 3채널 이미지를 입력 받도록 수정해봅시다. 이전에는 1채널 이미지를 받도록 되어있었습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
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. Define a Loss function and optimizer
이제 Classification Cross-Entropy 로스와 Momentum을 적용한 SGD를 사용해봅시다.
1 2 3 4 5 |
import torch.optim as optim criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) |
4. Train the network
이제 재미있어지기 시작할 것입니다. 우리가 가진 데이터의 iterator에 대해 반복하여 네트워크에 입력을 주면서, 최적화해야합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
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 # wrap them in Variable inputs, labels = Variable(inputs), Variable(labels) # 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.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') |
Out:
1234567891011121314 [1, 2000] loss: 2.204[1, 4000] loss: 1.855[1, 6000] loss: 1.677[1, 8000] loss: 1.577[1, 10000] loss: 1.508[1, 12000] loss: 1.485[2, 2000] loss: 1.403[2, 4000] loss: 1.392[2, 6000] loss: 1.355[2, 8000] loss: 1.332[2, 10000] loss: 1.300[2, 12000] loss: 1.282Finished Training
5. Test the network on the test data
여기까지 트레이닝 데이터에 대해 2번의 패스를 돌려 네트워크를 학습시켰습니다. 하지만 모든 데이터에 대해 잘 배웠는지 체크를 해봐야 합니다.
신경망의 출력인 클래스 레이블을 예측하여 ground-truth와 동일한지 체크할 것입니다. 예측 값이 일치한다면 샘플을 correct prediction 리스트에 추가합니다.
먼저 이해를 돕기 위해 테스트셋에서 데이터를 하나 뽑아 출력해봅시다.
1 2 3 4 5 6 7 |
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))) |
Out:
12 GroundTruth: cat ship ship plane
이제 위의 이미지에 대해 신경망이 어떻게 생각하는지 살펴봅시다.
1 2 |
outputs = net(Variable(images)) |
출력은 10개의 클래스에 대한 에너지를 나타냅니다. 높은 에너지를 갖는 클래스는 신경망이 이미지가 좀 더 그 클래스일 것이라고 생각하는 것입니다. 따라서 가장 높은 에너지를 갖는 인덱스를 뽑아냅시다.
1 2 3 4 5 |
_, predicted = torch.max(outputs.data, 1) print('Predicted: ', ' '.join('%5s' % classes[predicted[j]] for j in range(4))) |
Out:
12 Predicted: cat car car ship
결과가 괜찮아 보입니다.
이제 모든 데이터셋에 대해서 신경망이 어떻게 생각하는지 봅시다.
1 2 3 4 5 6 7 8 9 10 11 12 |
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)) |
Out:
12 Accuracy of the network on the 10000 test images: 53 %
대충 찍는 것보다는 조금 좋아보입니다. 대충 찍으면 10개의 클래스이기 때문에 10%정도의 정확도를 가질 것입니다. 신경망이 뭔가 배운 것 같습니다.
어떤 클래스가 잘 되고 어떤 클래스는 잘 안되는지 알아봅시다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
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])) |
Out:
1234567891011 Accuracy of plane : 43 %Accuracy of car : 67 %Accuracy of bird : 27 %Accuracy of cat : 60 %Accuracy of deer : 44 %Accuracy of dog : 36 %Accuracy of frog : 64 %Accuracy of horse : 56 %Accuracy of ship : 55 %Accuracy of truck : 73 %
다 됐습니다. 다음은 뭘까요?
신경망을 GPU에서 돌려보는 건 어떨까요?
Training on GPU
Tensor를 GPU로 보내는 것과 똑같이 신경망도 GPU로 보낼 수 있습니다. 신경망이 가지는 파라미터와 버퍼들을 순차적으로 방문하면서 CUDA tensor로 변환하게 됩니다.
1 2 |
net.cuda() |
모든 학습 step에서 입력과 목표 값도 함께 GPU로 올려야 한다는 것을 잊지 맙시다.
1 2 |
inputs, labels = Variable(inputs.cuda()), Variable(labels.cuda()) |
처음부터 CPU보다 엄청나게 빨리 돌리는 방법이 있는지 이야기하지 않았을까요? 그것은 신경망이 그럴 필요도 없이 작았기 때문입니다.
연습: 네트워크의 폭을 늘려보세요. 첫번째 nn.Conv2d
의 두번째 파라미터와 두번째 nn.Conv2d
의 첫번째 파라미터를 바꾸면 됩니다. 단, 두 수는 동일해야 합니다. 얼마나 빨라지는지 살펴봅시다.
이제 다 배웠습니다.
- PyTorch의 Tensor 라이브러리와 신경망을 살펴보았습니다.
- 이미지를 분류하는 작은 신경망을 학습하였습니다.
Training on mumltiple GPUs
가지고 있는 모든 GPU를 이용해서 속도를 올리는 법을 보려면 Optional: Data Parallelism을 보시면 됩니다.
Where do I go next?
- 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
- More examples
- More tutorials
- Discuss PyTorch on the Forums
- Chat with other users on Slack