Python/PyTorch 공부

[PyTorch] 파이토치로 신경망 학습하기 - CNN, GPU, CIFAR10, 신경망 평가하기

AI 꿈나무 2020. 12. 6. 15:14
반응형

분류기 학습하기 - Training a classifier

 지금까지 신경망을 어떻게 정의하고, 손실을 계산하고, 신경망의 가중치를 갱신하는 것을 살펴보았습니다.

 아마 이렇게 생각할 수 있습니다.

 

데이터는 어떻게 해야하지? - What about Data?

 일반적으로 이미지, 문자, 음성, 비티오 데이터를 다룰 때, numpy 배열로 data를 불러오는 표준 python 패키지를 사용할 수 있습니다.

 그리고나서 배열을 torch.*Tensor 로 전환할 수 있습니다.

 

  • 이미지는 Pillow, OpenCV와 같은 패키지가 유용합니다.
  • 오디오는 scipy, librosa와 같은 패키지가 유용합니다.
  • 문자는 그냥 Python 이나 Cython을 사용해도 좋고 NLTK와 SpaCy가 유용합니다.

 특히 영상 분야에서 torchvision 패키지가 존재합니다. Imagenet, CIFAR10, MNISR 등등 흔한 데이터집합을 위한 데이터로더 torchvision.datasets 이미지를 위한 데이터 변환기 torch.utils.data.DataLoder 를 갖고 있습니다.

 

 이것은 엄청난 변리함을 제공하고 유사한 코드(boilerplate code)를 작성하는 것을 피합니다.

 

 이번 tutorial에서 CIFAR10 데이터셋을 사용할 것입니다.

 이것은 'airplane', ‘automobile’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’ 클래스를 갖고 있습니다.

 CIFAR-10에 존재하는 이미지는 3x32x32 크기입니다.

 이는 32x32 픽셀 크기의 3 채널 컬러 이미지를 의미합니다.

 

이미지 분류기 학습하기 - Training an image classifier

 다음의 단계를 순서대로 하겠습니다.

 

1. torchvision 을 사용해서 CIFAR10 학습용, 시험용 데이터셋을 불러오고 정규화하기

2. 합성곱 신경망 정의하기

3. 손실 함수 정의하기

4. 학습용 데이터를 사용해서 신경망 학습하기

5. 시험용 데이터를 사용해서 신경망 평가하기

 

1. CIFAR10 불러오고 정규화하기

 torchvision을 사용하면 CIFAR10을 불러오는 것은 매우 쉽습니다.

import torch
import torchvision
import torchvision.transforms as transforms

 

 torchvision 데이터셋의 출력은 [0,1] 범위의 PILimage 이미지입니다.

 이것을 [-1, 1] 범위로 정규화된 Tensor로 변환하겠습니다.

 

 만약 WIndows 환경에서 BrikenPipeError가 발생한다면, torch.utils.data.DataLoader()의 num_worker를 0으로 설정합니다.

 

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, trasform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
					shuffle=True, numworkers=2)
                                            
testset = torchvision.datasets.CIFAR10(root='./data', trian=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 :

Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./data/cifar-10-python.tar.gz
Extracting ./data/cifar-10-python.tar.gz to ./data
Files already downloaded and verified

 

 재미로 학습용 이미지를 확인하겠습니다.

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)))
    plt.show()
    
# 임의의 학습용 이미지 얻기
dataiter = iter(trainloader)
images, labels = dataiter.next()

# 이미지 출력
imshow(torchvision.utils.make_grid(images))
# 레이블 출력
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))

 

Out :

plane plane   dog  ship

 

2. 합성곱 신경망 정의하기 - Define a Convolutional Neural Network

 이전의 신경망 섹션에서 신경망을 복사한 후 (1-채널 이미지를 처리하도록 정의된 것 대신에) 3-채널 이미지를 처리하도록 수정합니다.

 

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 정의하기 - Define a Loss function and optimizer

 교차 엔트로피 손실(Croos-Entropy loss)와 모멘텀을 갖은 SGD를 사용하겠습니다.

import torch.optim as optim

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

 

4. 신경망 학습하기 - Train the network

 이제 흥미로운 것이 시작됩니다.

 단순히 데이타를 반복하고 입력값을 신경망에 입력하고 최적화합니다.

 

for epoch in range(2): # 여러번 데이터셋 반복하기

    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # 입력값을 얻습니다. 데이타는 [입력값, 레이블]의 목록입니다.
        inputs, labels = data
        
        # 매개변수 변화도를 0으로 만듭니다.
        optimizer.zeor_grad()
        
        # 순전파 + 역전파 + 최적화
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        
        # 통계 추력
        running_loss += loss.item()
        if i % 2000 == 1999: # 매 2000번 미니배치마다 출력하기
            print('[%d, %5d] loss: %.3f' %
                  (epoch +1, i+1, running_loss / 2000))
            running_loss =0.0
            
print('Finished Training')

 

Out :

[1,  2000] loss: 2.237
[1,  4000] loss: 1.922
[1,  6000] loss: 1.729
[1,  8000] loss: 1.601
[1, 10000] loss: 1.530
[1, 12000] loss: 1.461
[2,  2000] loss: 1.409
[2,  4000] loss: 1.383
[2,  6000] loss: 1.326
[2,  8000] loss: 1.319
[2, 10000] loss: 1.294
[2, 12000] loss: 1.277
Finished Training

 

 빠르게 학습된 모델을 저장합니다.

PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)

 

5. 시험용 데이터로 신경망 평가하기

 학습용 데이터셋에 대해 2회 반복하여 신경망을 학습했습니다.

 하지만 신경망이 아무것도 배우지 않았을 수 있으니 확인하겠습니다.

 

 신경망의 출력값인 클래스 레이블을 예측함으로써 확인할 수 있습니다.

 그리고 이것을 진짜 정답(ground-truth)와 비교하여 확인합니다.

 만약 예측이 옳다면 올바른 예측 목록에 샘플을 추가합니다.

 

 첫 번째 순서로 시험용 데이터를 확인하겠습니다.

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

 

Out :

GroundTruth:    cat  ship  ship plane

 

 저장된 모델을 불러오겠습니다.

 (note : 저장과 모델을 다시 불러오기는 필요하지 않지만, 어떻게 하는지 설명하기 위해 해보겠습니다.)

net = Net()
net.load_state_dict(torch.load(PATH))

 

 이제 위 예제들을 신경망이 어떻게 생각하는지 확인하겠습니다.

outputs = net(images)

 

 결과값은 10개 분류에 대한 값입니다.

 분류에 대해 높은 값이 나타나면 신경망이 그 이미지가 해당 분류로 생각한다는 의미입니다.

 가장 높은 값의 인덱스를 얻어보겠습니다.

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

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

 

Out :

Predicted:    dog   car   car   car

 

 결과가 좋습니다.

 신경망이 전체 데이타셋에 대해 어떻게 수행하는지 확인하겠습니다.

 

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

 

 (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)
        _, prediceted = 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('Accuract of %5s : %2d %%' %(
        classes[i], 100 * class_correct[i] / class_total[i]))

 

Out :

Accuracy of plane : 40 %
Accuracy of   car : 87 %
Accuracy of  bird : 44 %
Accuracy of   cat : 27 %
Accuracy of  deer : 39 %
Accuracy of   dog : 47 %
Accuracy of  frog : 59 %
Accuracy of horse : 71 %
Accuracy of  ship : 48 %
Accuracy of truck : 57 %

 

 자, 다음은 무엇을 해볼까요?

 GPU에서 이 신경망을 어떻게 실행할 수 있을까요?

 

GPU에서 학습하기 - Training on GPU

 Tenosr를 GPU로 이동했던 것과 같이, 신경망을 GPU로 이동합니다.

 CUDA가 이용가능하면 device를 첫번째 cuda 장치로 설정합니다.

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

# CUDA 기기가 존재한다고 가정하면 CUDA 장치를 출력할 것 입니다.
print(devide)

 

Out :

cuda : 0

 

 이 섹션의 남은 부분은 device를 CUDA 장치로 가정하겠습니다.

 

 이 방법은 모든 모듈을 재귀적으로 반복하고 그것의 매개변수와 버퍼를 CUDA로 전환합니다.

net.to(device)

 

 매 단계마다 input과 target를 GPU로 보내야 한다는 것을 기억해야 합니다.

inputs, labels = data[0].to(device), data[1].to(device)

 

 왜 CPU와 비교해서 엄청난 속도 향상이 생기지 않았을 까요?

 신경망이 매우 작기 때문입니다.

 

연습 : 신경망의 넓이를 증가해서 시도하기(첫 번째 nn.Conv2d의 인자2와 두 번째 nn.Conv2d의 인자 1는 동일한 숫자이어야 합니다), 그리고 얼마나 빨라지는지 확인하기

 

달성된 목표

  • 높은 수준에서 PyTorch의 Tensor 라이브러리와 신경망을 이해하기
  • 이미지를 분류하기 위해 작은 신경망을 훈련하기

 

여러개의 GPU에서 학습하기

 모든 GPU를 사용해서 엄청난 속도 향상을 확인하고 싶으면 여기(데이터 병렬화)를 확인해주세요.

 


공부 목적으로 PyTorch 튜토리얼 홈페이지를 변역해보았습니다.

 

Training a Classifier — PyTorch Tutorials 1.7.0 documentation

Note Click here to download the full example code Training a Classifier This is it. You have seen how to define neural networks, compute loss and make updates to the weights of the network. Now you might be thinking, What about data? Generally, when you ha

pytorch.org

반응형