Training a classifie
어떻게 신경망을 정의하고 loss를 계산하고, 신경망의 가중치를 갱신하는 지를 보았다.
그럼 data는 어떻게 다룰 것인가?
일반적으로 이미지, 텍스트, 음성, 비디오 데이터를 다룰 때, 기본 python package로 data를 numpy array에 불러온다. 그 후 이 array를 torch.*Tensor로 변환한다.
- 이미지는 Pillow나 OpenCV가 유용하다.
- 음성 데이터는 scipy나 librosa같은 패키지가 유용하다.
- 텍스트의 경우 Python이나 Cython 기반 loading을 쓰거나 NLTK나 SpaCy가 유용하다.
특히 Vision에 있어서, torchvision package를 만들어 두었다. 그리고 이 package는 잘 알려진 dataset인 Imagenet이나 CIFAR10, MNIST 등의 데이터를 불러오는 장치 (data loaders)와 이미지, viz. , torchvision.datasets, 그리고 torch.utils.data.DataLoader.를 위한 data 변환기도 존재한다.
이 패키지는 엄청난 편리함을 제공하고 boilerplate code를 안 써도 되게 해 준다.
- boilerplate code: https://en.wikipedia.org/wiki/Boilerplate_code
이 tutorial에선 CIFAR10 dataset을 쓸 것이다. ‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck'의 카테고리가 있다. CIFAR-10에서 쓰이는 이미지의 크기는 3x32x32이며 이는 3-channel의 색(3색)과 크기가 32x32 픽셀임을 말한다.
이미지 분류기에서 훈련시키기
다음과 같은 순서에 따라 진행할 것이다.
- torchvision을 이용해 CIFAR10 훈련, 테스트 데이터셋을 불러오고 정규화한다.
- Convolutional Neural Network를 정의한다.
- loss function을 정의한다.
- 훈련 데이터에서 신경망을 훈련시킨다,
- 테스트 데이터에서 신경망을 테스트한다.
1. CIFAR10을 불러오고 정규화하기
import torch
import torchvision
import torchvision.transforms as transforms
torchvision 데이터셋의 출력값은 [0,1]의 범위를 갖는 PILImage이다. 이를 [-1,1]의 정규화된 범위를 갖는 Tensor로 변환할 것이다.
주의: Windows 환경에서 실행할 경우 BrokenPipeError를 받게될 것이다. torch.utils.data.DataLpader()의 num_worker를 0으로 set해야 한다.
#변환 객체를 설정 및 받아옴
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 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')
정규화: 표준 정규분포로 만들기, (x-평균)/표준편차
몇 개의 이미지를 한 번 살펴보자.
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)))
plt.show()
# 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)))
2. Convolutional Neural Network 정의하기
이전 튜토리얼에서 만든 신경망을 복사한 후 입력값을 3-channel을 받고록 수정한다.
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()
((32-4)/2-4)/2 = 5
3. loss function과 optimizer 정의하기
classification Cross-Entropy loss와 momentum을 이용한 SGD 사용
import torch.optim as optim
criterion = nn.CrossEntropyLoss() #loss 측정 기준
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
4. 신경망 훈련시키기
단순히 데이터의 iterator를 이용해 데이터를 돌며 input을 신경망에 feed하고 optimize한다.
for epoch in range(2): # loop over the dataset multiple times, 여기선 2번 돎
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# get the inputs; data is a list of [inputs, labels]
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')
- momentum은 가중 평균을 이용해 경사 하강 정도를 정함. https://towardsdatascience.com/stochastic-gradient-descent-with-momentum-a84097641a5d
훈련시킨 모델 저장
PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)
5. test data를 이용해 신경망 test
신경망을 2번 돌았다. 그러나 진전이 없었을 수도 있으니 확인해봐야 한다.
이를 확인하는 방법으로 신경망 output에 붙은 calss label을 예측해보고 실제 class에 붙은 label과 비교하는 방법을 쓸 것이다. 만일 예측 값이 정답이라면, 이 sample을 정답 list에 넣을 것이다.
우선 첫번째로 test set에서 가져온 image를 display해보자.
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)))
그 다음으로 저장했던 모델을 불러온다. (주의: 모델을 저장하고 다시 불러오는 것은 여기서 중요한 게 아니기에 어떻게 하는 지만 보여주었었다. )
net = Net()
net.load_state_dict(torch.load(PATH))
우리가 훈련시킨 신경망이 위의 예시들에 대해 어떤 결과를 예측하는 지 보자.
outputs = net(images)
output은 10개의 카테고리(class)들에 대한 정도(크기)를 나타낸다. 한 카테고리에 대해 확률 값이 클수록 더 많은 신경망들이 그 이미지가 특정 카테고리임을 나타내게 될 것이다. 따라서, 가장 큰 값을 갖는 인덱스를 찾아보자.
_, predicted = torch.max(outputs, 1) #max로 가장 큰 값을 찾아냄
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))
결과를 보면, 찍어서 맞출 확률(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]))
GPU로 훈련시키기
Tensor를 GPU에 올릴 수 있었던 것과 같이, 신경망을 GPU에 올릴 수 있다. 우선 CUDA가 있다면 우리가 가진 장치를 첫번째 보이는 cuda device로 정의하자.
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# Assuming that we are on a CUDA machine, this should print a CUDA device:
print(device)
필자는 GPU가 없기에 'cpu'라고 뜬다
나머지 부분은 device를 CUDA device로 가정하고 설명한다.
그럼, 이 methods들은 재귀적으로 모든 module을 돌고, 모든 매개 변수들과 buffer들을 CUDA tensor로 변환할 것이다.
net.to(device)
입력값과 target을 매 단계에서 GPU에 보내야 함을 명심해야 한다.
inputs, labels = data[0].to(device), data[1].to(device)
왜 cpu에 비해 엄청난 속도 향상을 언급하지 않았는가?(GPU를 썼을 때,) -> 이 신경망의 크기가 상당히 작기에 그 정도 차이가 나지 않는다.
excercise: 신경망의 width를 증가시켜 보기(첫번째 nn.Conv2d의 2번째 argument와 nn.Conv2d의 1번째 argument는 같아야 한다.), 속도 향상이 있었는지를 확인해보자.
Goals achieved
- pytorch의 Tensor library와 신경망을 high level 측면에서 이해
- 매우 작은 신경망을 이용해 이미지 분류해 보기
다중 GPU로 훈련 시켜 보기
만일 GPU를 활용한 엄청난 속도 향상을 얻고 싶다면 다음을 확인하라. https://pytorch.org/tutorials/beginner/blitz/data_parallel_tutorial.html
'컴퓨터' 카테고리의 다른 글
[pytorch] 언어별 이름(성씨) 분류 (0) | 2020.02.13 |
---|---|
[pytorch] 파이토치 튜토리얼 5 (0) | 2020.02.11 |
[pytorch] 파이토치 튜토리얼3 (0) | 2020.02.10 |
[pytorch] 파이토치 튜토리얼2 (0) | 2020.02.07 |
[pytorch] 파이토치 튜토리얼 1 (0) | 2020.02.03 |
댓글