2018년 7월 23일 월요일

PyTorch Tutorial Training a classifier



https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html

Training a classifier (분류기 훈련)

신경망을 어떻게 정의하고, 손실 계산과, 네트워크의 가중치값들을 업데이트하는 방법에 대해 보아왔습니다.

What about data?

이미지, 오디오, 텍스트, 비디오 데이터를 다루어야 할때, 당신은 numpy 배열로 데이터를 로드하는 표준 파이썬 패키지를 사용 할 수 있습니다. 그리고 당신은 이 배열을 torch.*Tensor로 변경할 수 있습니다.
- 이미지는 Pillow, OpenCV 패키지가 유용합니다.
- 오디오는 scipy 와 librosa
- 텍스트의 경우 Python 또는 Cython 기반 로딩, 또는 NLTK 및 SpaCy가 유용합니다.

비전의 경우 특별히, torchvision이라고 불리는 패키지가 만들어 졌습니다. 이것은 Imagenet, CIFAR10, MNIST 들과 같은 공통 데이터 셋과 로더들을 가지고 있습니다.
이것은 편리함을 제공해주고 중복 코드를 피하게 해줍니다.
이 예제에서는 CIFAR10 데이터셋을 사용할것 입니다. ‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’ 데이터 셋을 가지고 있고, CIFAR10 이미지들의 크기는 32*32*3인데 3은 3채널로 이미지의 색을 의미합니다. 아마도 공부전이긴 하지만 RGB각각 8bit씩이지 않을까요?



Training an image classifier (이미지 분류기의 훈련)


아래 순서로 할것입니다.
1. torchvision을 사용하여 CIFAR10 훈련 및 테스트 데이터 세트 로드 및 정규화
CIFAR10 훈련 로드하고 정규화 합니다.
2. 컨볼루션 NN 을 정의합니다.
3. 손실 함수를 정의합니다
4. 훈련 데이터로 네트워크를 훈련합니다.
5. 테스트 데이터로 네트워크를 테스트합니다.

1. CIFAR10 정규화 및 로딩

torchvision을 사용하면, CIFAR10을 로드하는 것이 매우 쉽습니다.

import torch
import torchvision
import torchvision.transforms as transforms

torchvision 데이터셋의 출력은 [0, 1] 범위의 PILImage 이미지입니다. 정규화 된 범위의 [-1, 1]의 텐서로 변환합니다.
import시 transforms를 합니다. 위 설명처럼 tensor로 변경할때 사용할 예정입니다.


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:
Files already downloaded and verified
Files already downloaded and verified
Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./data\cifar-10-python.tar.gz
Files already downloaded and verified
처음 시험시 위와 같이 나옵니다. torchvision.datasets.CIFAR10 호출될때 다운로드 하게 되며, root='./data' 여기 위치로 다운로드 됩니다. 한번 다운로드한 뒤에는 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)))

위와 같은 코드를 실행해서 에러가 발생한다면 아래 링크를 참조하여 수정하면됩니다.
https://discuss.pytorch.org/t/brokenpipeerror-errno-32-broken-pipe-when-i-run-cifar10-tutorial-py/6224/3

Original (not working):
# ... code ...
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
                                          shuffle=True, num_workers=2)
# ... code ...
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
                                         shuffle=False, num_workers=2)
# ... code ...
Modified (working):
# ... code ...
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
                                          shuffle=True, num_workers=0)
# ... code ...
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
                                         shuffle=False, num_workers=0)
# ... code ...

Out:
ship truck horse horse
실제 해보면 랜덤으로 할때마다 바뀝니다.
frog  ship  frog   cat
실제 이미지도 볼 수 있는데 해당 시점에 출력하려면, plt.show()코드를 추가해야합니다.

마지막에 plt.show() # add code 코드를 추가하면 아래와 같은 이미지를 볼 수 있습니다.


2. Define a Convolution Neural Network


Neural Networks (신경 네트워크) 섹션 http://swlock.blogspot.com/2018/07/pytorch-tutorial-neuralnetworks.html 에서 class Net(nn.Module) 복사 한 다음 3 채널 이미지 (정의 된대로 1 채널 이미지 대신)를 사용하도록 수정합니다. nn.Conv2d(1, 6, 5) -> nn.Conv2d(3, 6, 5)

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를 사용합니다.

import torch.optim as optim

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


4. Train the network

데이터 반복자를 이용하여 루프를 돌고 네트워크에 입력을 feed하고 최적화해야합니다.
range(2)에 의해서 epoch 은 0, 1 루프를 돕니다.

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')


Out:
[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. Test the network on the test data


훈련 데이터 세트에 대해 2회 네트워크를 훈련 시켰습니다.(epoch) 네트워크가 전혀 배운 게 없는지 확인해야합니다.

신경망이 출력하는 class 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)))

Out:
GroundTruth:    cat  ship  ship plane
출력으로는 NN의 Test 이미지를 넣어서 결과를 받습니다. net은 앞에서 만든 네트워크입니다.

outputs = net(images)

출력은 10 개의 클래스에 대한 에너지입니다. 클래스에 대한 에너지가 높을수록 네트워크는 이미지가 특정 클래스에 속한다고 생각합니다. 그래서 가장 높은 에너지 지수를 얻기위해서 max를 호출합니다.

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

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

Out:
Predicted:    cat   car   car plane
앞에서 테스트 이미지가 cat  ship  ship plane 였는데 결과로 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))


Out:
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]))


Out:
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 %

Training on GPU

GPU가 있는 상황은 아니라서 여기에서는 skip합니다.

전체 소스 실행 화면 입니다.
전체소스
# -*- coding: utf-8 -*-

import torch
import torchvision
import torchvision.transforms as transforms

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=0)

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=0)

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')


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)))

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()

import torch.optim as optim

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

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')

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)))


outputs = net(images)

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

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

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))

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]))

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)

실행 결과
Files already downloaded and verified
Files already downloaded and verified
 deer truck  ship  ship
[1,  2000] loss: 2.178
[1,  4000] loss: 1.845
[1,  6000] loss: 1.664
[1,  8000] loss: 1.567
[1, 10000] loss: 1.492
[1, 12000] loss: 1.459
[2,  2000] loss: 1.386
[2,  4000] loss: 1.342
[2,  6000] loss: 1.321
[2,  8000] loss: 1.321
[2, 10000] loss: 1.322
[2, 12000] loss: 1.254
Finished Training
GroundTruth:    cat  ship  ship plane
Predicted:   bird  ship  ship  ship
Accuracy of the network on the 10000 test images: 54 %
Accuracy of plane : 64 %
Accuracy of   car : 79 %
Accuracy of  bird : 48 %
Accuracy of   cat : 27 %
Accuracy of  deer : 20 %
Accuracy of   dog : 61 %
Accuracy of  frog : 60 %
Accuracy of horse : 67 %
Accuracy of  ship : 72 %
Accuracy of truck : 41 %
cpu































댓글 없음:

댓글 쓰기