Training a classifier


What about data


이미지,텍스트,av(audio and video) 데이터에 대해 다룰 때에는, 표준 파이썬 패키지를 사용하여 해당 데이터를 numpy 배열로 불러올수있습니다


그리고 이 배열을 torch.Tensor로 변환할 수 있습니다


이미지들은 Pillow, OpenCV 같은 패키지가 유용합니다

오디오는 scipy, librosa 같은 패키지가 유용합니다

텍스트는 raw레벨의 파이썬이나 Cython으로 불러오거나 NLTK, SpaCy 등이 유용합니다


Imagenet, CIFAR10, MNIST 와 같은 일반적인 데이터셋을 불러오기 위해서 torchvision이라는 패키지가 있습니다

이미지나 viz 등을 위한 데이터 변환기(transformer)로 torchvision.datasets와 torch.utils.data.DataLoader가 있습니다



이 튜토리얼에서는 CIFAR10데이터셋을 이용할것입니다. 이 데이터셋의 클래스에는 '비행기', '자동차', '새', '고양이', '사슴', 

개', '개구리', '말', '배', '트럭'가 있고 사이즈는 3x32x32입니다 (32x32 픽셀의 3채널 이미지)


cifar10


Training an image classifier


순서:


1. CIFAR10 학습 및 테스트 데이터셋을 torchvision을 이용하여 불러오고 정규화(normalizing) 합니다

2. CNN을 정의합니다

3. 로스 함수를 정의합니다

4. 학습 셋으로 네트워크를 학습합니다

5. 테스트 셋으로 네트워크를 테스흡니다


1. Loading and normalizing CIFAR10


torchvision을 이용하여 매우 쉽게 CIFAR10을 불러올 수 있습니다


import torch
import torchvision
import torchvision.transforms as transforms


토치비전 데이터셋의 아웃풋은 PILImage이며 범위는 [0,1] 입니다. 이걸 [-1, 1] 범위의 텐서로 정규화합니다


우선 아래의 코드로 CIFAR10을 다운받아 로드합니다



#데이터 가져오기

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



실행(ctrl + shift + F10)하고 조금 기다리면 데이터셋을 받고 압축을 풉니다 경로는 프로젝트 설치경로(보통 users/이름/PycharmProjects/프로젝트이름/data




아래 코드로 학습 셋 중 일부를 로드하려고 했는데 이 과정에서 matplotlib와 numpy 가 없어 설치하였음 아래 글 참조


http://busterworld.tistory.com/61?category=663469


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



*이 코드 입력시 쓰레드 관련 에러가 뜹니다. 그 이유는, 위에 데이터 가져오는 코드에서 num_workers=2 로 하여 2개 쓰레드를 이용해서

데이터를 로드했는데 로드가 끝나기 전에 trainloader를 이용해서 작업을 시도했기때문인것으로 생각되고


num_workers=0으로 고치거나, 아래 이미지 불러오는 코드에 조건문 if __name__ == '__main__': 으로 제어를 하면

해결됩니다(단, 조건문을 넣는거보다 0으로 고치는게 속도가 더 빠르네요)


*또한, 불러온 4개 이미지를 보여주기위해서 맨아래에 plt.show()를 추가했습니다 원래 코드만 작성하면 이미지를 보여주진않네요




2. Define a Convolution Neural Network


이전의 NN에서 1채널 대신 3채널 이미지를 사용하는것으로 변경



3. Define a Loss function and optimizer


Classification Cross-Entropy loss와 momentum을 포함한 SGD를 사용




4. Train the network


학습시키기


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개 패스로 학습을 시켰으니 확인을 해봐야겠쥬?


클래스를 에측해보고, 맞으면 그 샘플을 리스트에 추가할겁니다



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)


outputs는 10개 클래스에 대한 에너지들인데 에너지가 높을수록 네트워크는 해당 클래스라고 생각한다는것 따라서 가장 높은것의 인덱스를 가져옴


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


53%의 정확도를 보이는데, 랜덤하게 찍는 확률인 10%보단 훨씬 좋음. 네트워크가 뭔가를 배우고있긴함


클래스별 정확도를 araboza:


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


전체 정확도는 54퍼, 클래스별은 위와 같이 나옴 


이제 GPU에서 돌려보자:


Training on GPU


텐서를 GPU로 전달하는 거처럼 신경망도 GPU로 전달 가능함


우선 CUDA 가능한지 보고 기기를 부여해야함


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)

Out:

cuda:0


그리고 메소드들은 모든 모듈에 재귀적으로 동작하여야하며 파라미터와 버퍼를 CUDA 텐서로 바꾸어야함


이라고하면서

net.to(device)


라던지, 인풋과 타겟을 모든 단계에서 GPU로 보내야한다며


inputs, labels = inputs.to(device), labels.to(device)


이렇게 해놓고 설명 끝인데, 이 두 구문을 어디다 넣을것인가??


그 힌트는 https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/502_GPU.py


에 있는줄..알았는데 조금 다르다 직접 고민해보고 업데이트하자



우선 지금까지 cpu용으로 해온것 정리:

데이터셋 가져오기 - CIFAR10으로 트레이닝셋, 테스트셋만들기

신경망 정의

로스, 옵티마이저 정의 - CrossEntropyLoss, SGD 모멘텀 0.9

에포크 2 학습 꼬우

테스트

정확도 평가


이 과정을 정리한 소스코드는 다음과 같다:

import torch
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
import torch.nn.functional as F
import matplotlib.pyplot as plt
import numpy as np
import torch.optim as optim

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

def imshow(img):
img = img / 2 + 0.5
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))

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

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

#Training
for epoch in range(2):

running_loss = 0.0
for i, data in enumerate(trainloader, 0):
#인풋 가져오기
inputs, labels = data

#gradient 0으로
optimizer.zero_grad()

#forward + backward + optimize
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()

#통계 출력
running_loss += loss.item()
if i % 2000 == 1000: #2000 미니배치마다 출력
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
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.data, 1)
c = (predicted == labels).squeeze()
for i in range(4):
label = labels[i]
class_correct[label] += c[i].item()
class_total[label] += 1
total += labels.size(0)
correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' % (100 * correct / total))

for i in range(10):
print('Accuracy of %5s : %2d %%' % (classes[i], 100 * class_correct[i] / class_total[i]))


plt.show()



GPU로 돌리는것을 일단 짜긴 짰는데


아직 잘 다루지 않아서인지, 네트워크가 작아서인지 CPU보다 (당연히) 오래걸리는것 같다


신경망 net을 GPU로 보내고


학습이나 테스트 돌릴때 즉, 신경망을 사용할때 파라미터들을 GPU로 보내어 돌렸다


import torch
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
import torch.nn.functional as F
import matplotlib.pyplot as plt
import numpy as np
import torch.optim as optim

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

def imshow(img):
img = img / 2 + 0.5
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))

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

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)

net.to(device)

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

#Training
for epoch in range(2):

running_loss = 0.0
for i, data in enumerate(trainloader, 0):
#인풋 가져오기
inputs, labels = data
inputs, labels = inputs.to(device), labels.to(device)

#gradient 0으로
optimizer.zero_grad()

#forward + backward + optimize
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()

#통계 출력
running_loss += loss.item()
if i % 2000 == 1000: #2000 미니배치마다 출력
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 ground truth images
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

#테스트 돌림
images, labels = images.to(device), labels.to(device)
outputs = net(images)

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

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

#정확도 평가
correct = 0
total = 0
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
images, labels = images.to(device), labels.to(device)

outputs = net(images)
_, predicted = torch.max(outputs.data, 1)
c = (predicted == labels).squeeze()
for i in range(4):
label = labels[i]
class_correct[label] += c[i].item()
class_total[label] += 1
total += labels.size(0)
correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' % (100 * correct / total))

for i in range(10):
print('Accuracy of %5s : %2d %%' % (classes[i], 100 * class_correct[i] / class_total[i]))


plt.show()




신경망 정의 직후 즉, net = Net() 코드 전부터


plt.show() 전까지의 시간 측정결과 아래와 같이 나왔다



 







+ Recent posts