Study/Deep Learning

PyTorch 기초. MNIST 데이터를 이용한 Neural Networks

MJ_DL 2019. 2. 26. 17:15

Autograd : 자동 미분(Automatic Differentiation)

  • autograd 패키지는 텐서의 모든 연산에 대하여 자동 미분을 제공.

텐서 (Tensors)

  • torch.Tensor는 패키지에서 가장 중심이 되는 클래스. 텐서의 속성 중 .requires_grad를 True로 세팅하면, 텐서의 모든 연산에 대하여 추적을 시작한다. 계산 작업이 모두 수행 되었다면 .backward()를 호출하여 모든 그라디언트들을 자동으로 계산할 수 있다. 이 텐서를 위한 그라디언트는 .grad 속성에 누적되어 저장 된다.

뉴럴 네트워크 ( 신경망, Neural Networks )

  • 뉴럴 네트워크는 torch.nn 패키지를 이용하여 생성.

일반적인 뉴럴 네트워크의 학습 절차.

  • 학습 가능한 파라미터나 weight(가중치)가 있는 뉴럴 네트워크를 정의.
  • 입력 데이터셋을 네트워크에 넣음.
  • 네트워크를 통해 입력 값을 처리.
  • loss를 계산. 출력(output)과 정답(target)의 차이
  • 그라디언트(기울기)를 네트워크의 파라미터로 역전파 시킨다.
  • 네트워크의 파라미터들을 갱신.


Dataset 클래스

  • pytorch의 Dataset 클래스를 상속받아 custom dateset 클래스 생성 가능.
  • 기본적으로 3개의 함수를 상속 받아 생성
from torch.utils.data improt Dataset

class custom_dataset(Dataset):
  def __init__(self):
    pass
  def __len__(self):
    pass
  def __getitem__(self,idx):
    pass


Example

# Custom_Dataset Class

class Custom_Dataset(Dataset):

  def __init__(self,x ,y ,data_set,transform=None):

    self.x = x

    self.y = y

    self.data = data_set

    self.transform = transform


  def __len__(self):

    return len(self.x)


  def __getitem__(self,idx):

    x = self.x[idx]

    y = self.y[idx]


    if self.data == 'cifar':

        img = Image.fromarray(x)

    elif self.data == 'svhn':

        img = Image.fromarray(np.transpose(x, (1, 2, 0)))


    x = self.transform(img)

 

    return x, y, idx

MNIST DATA SET을 이용한 실습.

transforms.Compose TORCHVISION.TRANSFORMS

  • 쉽게 말해 우리의 데이터를 전처리하는 패키지.
  • 타입이나 데이터 argument를 하기위한 함수 내장.

실습

  • transforms.ToTensor()
    • 데이터 타입을 Tensor 형태로 변경
  • transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
    • 이미지의 경우 픽셀 값 하나는 0 ~ 255 값을 갖는다. 하지만 ToTensor()로 타입 변경시 0 ~ 1 사이의 값으로 바뀜.
    • transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))를 이용하여 -1 ~ 1사이의 값으로 normalized 시킴
  • datasets.MNIST()
    • root : 경로 지정
    • train : train or test 데이터를 받아옴.
    • transorm 우리가 사전에 설정해 놓은 데이터 처리 형태
    • download 데이터 셋이 없을때.
  • torch.utils.data.DataLoader torch.utils.data.DataLoader

    • dataset : 우리가 불러올 데이터 셋
    • batch_size = batch 단위 만큼 데이터를 뽑아옴.
    • shuffle : 데이터를 shuffle할 것인지.

    Normalize

    • 크게 두가지 연산으로 나눠짐.
      • scaling : 데이터의 scale을 줄여줌.
      • centering : 데이터의 중심을 원점으로 맞춰주는 것.
    • ToTensor()를 해주면 scaling을 해준거고, Normalize를 해주면 centering + rescaling을 해준것.
      • 정확하게 하기 위해선 학습 데이터로부터 각 픽셀별로 평균을 구하던가 채널별로 평균을 구해서 centering 해야함 .

import torch
from torchvision import datasets, transforms

#The output of torchvision datasets are PILImage images of range [0, 1]. We transform them to Tensors of normalized range [-1, 1].
transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

# MNIST Dataset
train_dataset = datasets.MNIST(root='./mnist_data/', train=True, transform=transform, download=True)
test_dataset = datasets.MNIST(root='./mnist_data/', train=False, transform=transform)

# Data Loader 
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=32, shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=32, shuffle=False)


mnist image

  • 28 x 28 픽셀로 구성.
  • input으로 들어갈 경우 28 x 28 = 784로 변경.
  • view()를 이용해 28x28 -> 784

기본적인 네트워크 구조 만들기

  • class를 이용해 생성 *nn.Module 상속
    • init() 에서 super를 통해 상속.
  • 기본적으로 init, forword 함수 생성.
  • init 함수에서 super(classname, self).init() 정의.

PyTorch: 사용자 정의 nn 모듈

  • forward에 사용할 layer를 생성.
  • 입력 Variable을 받아 다른 모듈 또는 Variable의 autograd 연산을 사용하여 출력 Variable을 생성하는 forward 를 정의

nn vs F

  • nn의 경우 레이어 안에서 weight 공유가 가능.
  • F의 경우 단순한 연산 기능만.

Pytorch : nn

  • 연산 그래프와 autograd는 복잡한 연산자를 정의하고 도함수(derivative)를 자동으로 계산
  • 모듈은 입력 Variable을 받고 출력 Variable을 계산

import torch.nn as nn
import torch.nn.functional as F

class Network(nn.Module):
    def __init__(self):
        super(Network, self).__init__()
        self.dense1 = nn.Linear(784, 512)
        self.dense2 = nn.Linear(512, 10)

    def forward(self, input):
        output = input.view(-1,784)
        output = self.dense1(output)
        output = self.dense2(output)
        return output


Network 객체 생성 및 손실 함수, 최적화 함수 생성.

  • model = Net().cuda() : .cuda() 를 이용해 GPU로 할당 가능. -> 기본 구조는 .cuda()를 쓰지 않음.
    • 기본적으로 .cuda()는 model 부분과 아래 설명될 Variable().cuda()에 쓰임.
  • criterion : 손실함수 지정.
    • nn.CrossEntropyLoss()의 경우 기본적으로 LogSoftmax()가 내장.
  • optimizer : 최적화 함수 지정. :
    • model.parameters()를 통해 model의 파라미터들을 할당.
    • lr : learning_rate 지정


import torch.optim as optim


# use gpu -> .cuda()  -> model and Variable 

model = Network().cuda()

# This criterion combines nn.LogSoftmax() and nn.NLLLoss() in one single class. so not use softmax()

criterion = nn.CrossEntropyLoss()

optimizer = optim.RMSprop(model.parameters(), lr=0.01)


train의 기본 구조

  • output = model(data) : 모델에서 정의한 forward 단계실행. 모델에 data를 전달하여 예상하는 label 값 계산
  • loss = criterion(output, label) : 모델에서 나온 output과 label을 이용해 loss 계산
  • optimizer.zero_grad() : 갱신할 Variable들에 대한 모든 변화도를 0으로 만듬
  • loss.backward() : 역전파 단계 실행. 모델의 Variable들에 대한 손실의 변화도를 계산합니다.
  • optimizer.step() : 가중치 갱신.

train_loader : MNIST의 train 데이터를 받아오는 함수. *data, label 값을 return 해줌. Variable : Variable 클래스는 Tensor를 감싸고 있으며, Tensor에 정의된 거의 모든 연산을 지원. 계산을 마친 후에 .backward()를 호출하면, 자동으로 모든 그레디언트 계산.


예측값과 결과값을 비교해 ACC 구하기

  • torch.max() : 텐서 배열의 최대 값이 들어있는 index를 리턴하는 함수.
  • pred.eq(data) : pred 값과 data값을 비교하는 함수.
  • torch.max를 통해서 모델을 통해 나온 예측 결과를 구하고, pred.eq(data)를 이용해 예측값과 실제값을 구함.
    • batch 단위 계산이기 떄문에 sum()을 통해 일치하는 갯수들의 합을 구함.

from torch.autograd import Variable


def train(epoch):

    model.train()

    train_loss = 0

    train_acc = 0

    for data, label in train_loader:

        data, label = Variable(data).cuda(), Variable(label).cuda()

        output = model(data)

        loss = criterion(output, label)

        

        optimizer.zero_grad()

        loss.backward()

        optimizer.step()

        

        train_loss += loss.item()

        pred = output.data.max(1, keepdim=True)[1]

        train_acc += pred.eq(label.data.view_as(pred)).sum()

    train_loss /= len(train_loader.dataset)

    print('Train Epoch: {} Average loss: {:.4f} Accuracy : {:.4f}%)'.format(epoch, train_loss, 100. * train_acc / len(train_loader.dataset)))



Test 구조

  • Test 시에는 train 에서 학습된 파라미터를 가지고 모델에만 통과를 시켜주기 때문에 학습 시 파라미터 업데이트를 진행하지 않음.
  • model.eval() 를 통해 Test 과정이라고 내부적으로 알려줌.
    • 나중에 배울 train과 test시 다르게 적용되는 여러 기법들(batch_norm, dropout)을 model.train(), model.eval() 통해 간단히 따로 구현 가능.
  • 기본적으로 test_loader를 통해 데이터들을 불러오고 데이터들을 Variable에 할당시키고 loss와 acc를 계산.
    • model.eval()을 써서 상관없는 부분이지만 Variable을 단순히 backprob을 시키지 않게 하기 위한 volatile=True 파라미터가 존재.
      • backprob이 필요없는 변수들에 대해서 메모리 절약과 학습 속도를 높힐 수 있음.

def test():

    model.eval()

    test_loss = 0

    test_acc = 0

    for data, target in test_loader:

        # volatile=True no use backprob

        data, target = Variable(data, volatile=True).cuda(), Variable(target).cuda()

        output = model(data)

        test_loss += criterion(output, target).item()

        pred = output.data.max(1, keepdim=True)[1]

        test_acc += pred.eq(target.data.view_as(pred)).sum()


    test_loss /= len(test_loader.dataset)

    print('Test set: Average loss: {:.4f}, Accuracy: {:.0f}%)'.format(test_loss, 100. * test_acc / len(test_loader.dataset)))


학습 및 테스트 실험

  • epoch 수를 정하고 학습 및 테스트 실험.
for epoch in range(1, 6):
    train(epoch)
    test()