앎을 경계하기

Machine Learning

DEEP LEARNING WITH PYTORCH: A 60 MINUTE BLITZ - TRAINING A CLASSIFIER

양갱맨 2021. 3. 8. 17:49

What aoubt data?

일반적으로 이미지, 텍스트, 비디오, 오디오 데이터를 다룰 때 numpy array로 데이터를 읽고 torch의 tensor로 변환한다. 각 데이터마다 유용하게 사용하는 라이브러리가 존재한다.

  • Images : Pillow, OpenCV
  • Audio : scipy, librosa
  • Text : NLTK, SpaCy

특히 vision 분야에서는 torchvision을 사용해서 유명한 데이터셋을 불러서 사용할 수 있다.

torchvision.datasets, torch.utils.data.DataLoader

유명 이미지 데이터셋 중 하나인 CIFAR10을 사용해보자.

CIFAR10은 RGB 컬러 32*32 사이즈 이미지의 총 10개의 카테고리로 이루어져있는 이미지 데이터셋이다.

Training an image classifier

데이터셋을 읽고 학습 및 테스트 하는 과정은 다음과 같다.

  1. torchvision을 사용해서 CIFAR10 데이터셋의 train/test dataset을 만든다.
  1. CNN 모델을 만든다.
  1. loss function을 정의한다.
  1. training dataset을 사용해서 CNN을 학습시킨다.
  1. test dataset을 사용해서 학습된 CNN을 테스트한다.

1. CIFAR10 dataset 읽고 정규화하기

import torch
import torchvision
import torchvision.transforms as transforms

torchvision에서 제공하는 데이터셋들은 PILImage 형태로 [0,1] 범위의 값으로 표현된다. 이것을 [-1, 1] 범위로 normalization 해준다.

transform = transforms.Compose(
					[transforms.ToTensor(),                               # Tensor화
					 transforms.Normalize((0.5,0.5,0.5), (0.5,0.5,0.5))]) # Normalization

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

학습 이미지가 어떻게 생겼는지 matplotlib을 사용해서 확인해보자.

import matplotlib.pyplot as plt
import numpy as np

def imshow(img):
	img = img / 2 + 0.5
	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)))

2. CNN 만들기

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. Loss function과 optimizer 생성

classification 문제에서 loss function은 cross-entropy loss가 많이 사용된다.

optimizer는 SGD를 사용한다.

import torch.optim as optim

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

4. 네트워크 학습 시작

데이터셋을 몇 번 반복할 것인지 횟수를 뜻하는 epoch만큼 트레이닝을 한다.

for epoch in range(2):
	running_loss = 0.0                        # epoch마다 loss 저장

	for i, data in enumerate(trainloader, 0):
		inputs, labels = data

		optimizer.zero_grad()                   # optimizer 초기화

		outputs = net(inputs)
		loss = criterion(outputs, labels)
		loss.backward()                         # backprop part
		optimizer.step()                        # backprop part

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

print('Finished Training')
[1,  2000] loss: 2.198
[1,  4000] loss: 1.829
[1,  6000] loss: 1.644
[1,  8000] loss: 1.564
[1, 10000] loss: 1.489
[1, 12000] loss: 1.474
[2,  2000] loss: 1.379
[2,  4000] loss: 1.347
[2,  6000] loss: 1.340
[2,  8000] loss: 1.338
[2, 10000] loss: 1.313
[2, 12000] loss: 1.285
Finished Training

학습한 모델을 저장하면 나중에 저장한 파일을 불러서 사용할 수 있다.

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

Test data 적용해보기

학습시킨 CNN 모델이 처음 보는 데이터에도 좋은 성능을 보이는지 test data로 확인한다.

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

먼저 테스트 데이터셋 몇 개를 확인해본다.

net = Net()
net.load_state_dict(torch.load(PATH)) # 저장한 모델 불러오기

outputs = net(images)
_, predicted = torch.max(outputs, 1)  # 제일 높은 값의 인덱스를 반환함

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

예측한 값 중 가장 높은 값의 인덱스를 반환해서 어떤 카테고리에 해당하는지 출력해본다.

Predicted:    cat  ship  ship plane

꽤 좋은 결과가 나오는 것을 확인할 수 있다.

이것을 test dataset 전체에 대해 적용해보자.

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

결과적으로 10000개 테스트 이미지에 대한 정확도는 56%였다.

이번에는 카테고리 별 정확도를 확인한다.

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 : 69 %
Accuracy of   car : 78 %
Accuracy of  bird : 38 %
Accuracy of   cat : 37 %
Accuracy of  deer : 43 %
Accuracy of   dog : 44 %
Accuracy of  frog : 59 %
Accuracy of horse : 67 %
Accuracy of  ship : 61 %
Accuracy of truck : 61 %

카테고리 별 정확도를 분석해서 네트워크가 어떤 카테고리에 대해 정확도가 낮은지 알 수 있기 때문에 이러한 분석은 모델의 성능을 개선하는데 도움이 되는 정보가 된다.

Training on GPU

PyTorch가 좋은 이유는 CPU보다 빠른 연산 수행이 가능한 GPU를 활용하여 네트워크 학습이 가능하다는 것이었다. GPU를 사용해서 트레이닝하려면 먼저 CUDA가 설치되어 있어야 한다.

CUDA가 설치되어 사용 가능한 GPU가 있으면 cuda:0를 출력한다.

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

이제 GPU에서 연산 가능한 변수들을 모두 GPU로 옮긴다.

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

GPU 활용의 효과는 네트워크가 클수록 더 잘 나타난다.

CPU보다 더, single GPU보다 더 가속 효과를 내려면 Multi-GPU를 사용하면 된다.

PyTorch에서는 Data Parallelism에 대해서도 지원한다.