본문 바로가기

프로그래머/Pytorch

[Pytorch] VGG CIFAR-10에 적용 및 정리

VGG CIFAR-10에 적용 및 정리

모두의 딥러닝 시즌2 - Pytorch를 참고 했습니다.
모두의 딥러닝 시즌2 깃헙


import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
device = 'cuda' if torch.cuda.is_available() else 'cpu'

torch.manual_seed(123)
if device =='cuda':
    torch.cuda.manual_seed_all(123)
transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

trainset = torchvision.datasets.CIFAR10(root='./cifar10', train=True,
                                        download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=512,
                                          shuffle=True, num_workers=0)

testset = torchvision.datasets.CIFAR10(root='./cifar10', 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')
  • torchvision.transforms.Compose(transforms) -> 여러개의 데이터 transformer을 묶어놓을 수 있다.
  • torchvision.transforms.functional.normalize(tensor, mean, std, inplace=False) -> data를 normalize한다. 현재는 적당한 값으로 집어 넣었지만, 더 좋은 성능을 내기 위해서는 trainset에 대해서 mean과 std를 알아낼 필요가 있다.
  • CIFAR-10에는 10개의 class가 있다.
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline

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

dataiter = iter(testloader)
images, labels = dataiter.next()
imshow(torchvision.utils.make_grid(images))

print(' '.join('%5s' % classes[labels[j]] for j in range(4)))

testset 중 4개를 표시해본다.

import vgg_01
  • 이전에 저장해둔 코드가 있으면 불러온다.
  • 같은 디렉토리(폴더) 내의 vgg_01.py 이름을 가진 파일을 불러온다.
  • ipynb 파일은 불러올 수 없으므로, jupyer notebook을 이용해 코드를 짯다면, ipython nbconvert --to script vgg_01.ipynb를 이용해 .py로 변환한다.
cfg = [32,32,'M', 64,64,128,128,128,'M',256,256,256,512,512,512,'M'] #13 + 3 =vgg16
class VGG(nn.Module):
    def __init__(self, features, num_classes=1000, init_weights=True):
        super(VGG, self).__init__()
        self.features = features
        #self.avgpool = nn.AdaptiveAvgPool2d((7, 7))
        self.classifier = nn.Sequential(
            nn.Linear(512 * 4 * 4, 4096),
            nn.ReLU(True),
            nn.Dropout(),
            nn.Linear(4096, 4096),
            nn.ReLU(True),
            nn.Dropout(),
            nn.Linear(4096, num_classes),
        )
        if init_weights:
            self._initialize_weights()

    def forward(self, x):
        x = self.features(x)
        #x = self.avgpool(x)
        x = x.view(x.size(0), -1)
        x = self.classifier(x)
        return x

    def _initialize_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.constant_(m.weight, 1)
                nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                nn.init.normal_(m.weight, 0, 0.01)
                nn.init.constant_(m.bias, 0)
  • CIFAR-10은 image size가 32323으로, 작은 편이다.
  • 기존 VGG의 형태를 변경한다.
  • avgpool 제거, feature을 거친 후 최종단 output (512, 4, 4)로 줄어듬
  • num_class = 10
vgg16= VGG(vgg_01.make_layers(cfg),10,True).to(device)
a = torch.Tensor(1,3,32,32).to(device)
out = vgg16(a)
print(out)

tensor([[-5.5271e+30, 2.0224e+30, -1.0700e+30, 2.5091e+30, 1.5906e+30,
-5.5477e+29, -2.7936e+30, 1.4075e+29, -5.7797e+29, 1.5343e+30]],
device='cuda:0', grad_fn=)

dataset을 다운 받지 못했다면, 이렇게 랜덤 input을 만들어 테스트 해 볼 수도 있다.

criterion = nn.CrossEntropyLoss().to(device)
optimizer = torch.optim.SGD(vgg16.parameters(), lr=0.005, momentum=0.9)

lr_sche = optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.9)

torch.optim.lr_scheduler.StepLR(optimizer, step_size, gamma=0.1, last_epoch=-1) -> 5 step을 거칠 때마다, lr을 x0.9만큼 줄여주면서 학습시킨다.

print(len(trainloader))
epochs = 30

for epoch in range(epochs):
    running_loss = 0.0
    lr_sche.step()
    for i, data in enumerate(trainloader, 0): # 0부터 시작
        inputs, labels = data
        inputs = inputs.to(device)
        labels = labels.to(device)

        optimizer.zero_grad()

        outputs = vgg16(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

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

print('finished')

98
[1, 30] loss: 2.298
[1, 60] loss: 2.288
[1, 90] loss: 2.227
[2, 30] loss: 2.083
...

dataiter = iter(testloader)
images, labels = dataiter.next()

imshow(torchvision.utils.make_grid(images))
print('GT: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))
outputs = vgg16(images.to(device))
_, 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
        images = images.to(device)
        labels = labesl.to(device)
        outputs = vgg16(images)

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

        total += labels.size(0)

        correct += (predicted == labels).sum().item()

print('Acc : %d %%' % (
    100*correct / total))