함께하는 데이터 분석

[Find-A][Pytorch] 정규화 본문

학회 세션/파인드 알파

[Find-A][Pytorch] 정규화

JEONGHEON 2022. 9. 25. 19:45

정규화

학습 데이터에서는 잘 동작하는데 테스트 데이터에서는 학습이 제대로 안 된다면 단순히 오버피팅 문제가 아니라 두 데이터의 분포가 달라서인 경우도 존재

 

왼쪽이 학습 데이터 오른쪽이 테스트 데이터라 하면 학습 시 결과가 잘 나오던 모델도 테스트 시에는 결과가 좋지 않게 나올 수밖에 없을 것임

 

또한 학습 시에도 데이터 간의 분포가 다르다면 각 분포에 맞춰 변수가 업데이트될 테니 그 데이터를 그대로 쓰면 학습조차 제대로 안될 것임

 

이럴 때 필요한 것이 정규화(normalization)

 

데이터를 정규화 하는 방법은 여러가지가 있는데 대표적인 방법으로 표준화(standardization)가 있음

 

표준화는 데이터에서 평균을 빼고 표준편차로 나눠주는 과정을 거치는데 이렇게 되면 평균은 0, 분산은 1이 되어 데이터 분포가 표준정규분포화 됨

 

이렇게 되면 네트워크에 들어오는 입력값이 일정한 분포로 들어오기 때문에 학습에 유리

 


Pytorch

라이브러리 불러오기

import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.init as init
import torchvision
import torchvision.datasets as dset
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
batch_size = 256
learning_rate = 0.0002
num_epoch = 10

 

Data

# 정규화는 transform을 통해 가능합니다.
# 여기서 mean, std는 미리 계산된 값입니다.
# 각각이 1개인 이유는 MNIST 데이터의 채널이 하나이기 때문입니다. 

mnist_train = dset.MNIST("./", train=True, 
                         transform=transforms.Compose([
                             transforms.ToTensor(),
                             transforms.Normalize(mean=(0.1307,), std=(0.3081,))
                         ]),
                         target_transform=None, 
                         download=True)
mnist_test = dset.MNIST("./", train=False, 
                        transform=transforms.Compose([
                             transforms.ToTensor(),
                             transforms.Normalize(mean=(0.1307,), std=(0.3081,))
                        ]),
                        target_transform=None, 
                        download=True)
print(mnist_train.__getitem__(0)[0].size(), mnist_train.__len__())
mnist_test.__getitem__(0)[0].size(), mnist_test.__len__()

>>> (torch.Size([1, 28, 28]), 10000)
train_loader = torch.utils.data.DataLoader(mnist_train,batch_size=batch_size, shuffle=True,num_workers=2,drop_last=True)
test_loader = torch.utils.data.DataLoader(mnist_test,batch_size=batch_size, shuffle=False,num_workers=2,drop_last=True)

 

CNN Model

class CNN(nn.Module):
    def __init__(self):
        super(CNN,self).__init__()
        self.layer = nn.Sequential(
            nn.Conv2d(1,16,3,padding=1),  # 28 x 28
            nn.ReLU(),
            nn.Conv2d(16,32,3,padding=1), # 28 x 28
            nn.ReLU(),
            nn.MaxPool2d(2,2),            # 14 x 14
            nn.Conv2d(32,64,3,padding=1), # 14 x 14
            nn.ReLU(),
            nn.MaxPool2d(2,2)             #  7 x 7
        )
        self.fc_layer = nn.Sequential(
            nn.Linear(64*7*7,100),
            nn.ReLU(),
            nn.Linear(100,10)
        )        
        
    def forward(self,x):
        out = self.layer(x)
        out = out.view(batch_size,-1)
        out = self.fc_layer(out)
        return out

 

Loss func & Optimizer

USE_CUDA = torch.cuda.is_available() # gpu 사용 가능하면 true or false
device = torch.device("cuda" if USE_CUDA else "cpu")
print(f'다음 기기로 학습 : {device}')

>>> 다음 기기로 학습 : cpu
model = CNN().to(device)
loss_func = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)

 

Train

for i in range(num_epoch):
    for j,[image,label] in enumerate(train_loader):
        x = image.to(device)
        y_= label.to(device)
        
        optimizer.zero_grad()
        output = model.forward(x)
        loss = loss_func(output,y_)
        loss.backward()
        optimizer.step()
        
    if i % 10 == 0:
        print(loss) 
        
>>> tensor(2.3086, grad_fn=<NllLossBackward0>)

 

Test

correct = 0
total = 0

with torch.no_grad():
    for image,label in test_loader:
        x = image.to(device)
        y_= label.to(device)

        output = model.forward(x)
        _,output_index = torch.max(output,1)

        total += label.size(0)
        correct += (output_index == y_).sum().float()

    print("Accuracy of Test Data: {}".format(100*correct/total))
    
>>> Accuracy of Test Data: 10.09615421295166

 


 

표준화 이외에도 많이 사용되는 정규화 방법 중 최소극대화(minmax) 정규화가 있음

 

최소극대화 정규화는 데이터를 0에서 1 사이로 압축하거나 늘리는 방법

 

x = ( x - x.min() ) / ( x.max() - x.min() )

이렇게 되면 0에서 1 사이 밖에 있는 값들은 0과 1사이로 압축되고, 전체 범위가 1이 안 되던 작은 값들은 0과 1사이로 늘어나게 됨

 

최소극대화 정규화도 표준화처럼 일정 범위 내로 값들을 이동 시키긴 하지만 평균적 범위를 넘어서는 너무 작거나 너무 큰 이상치가 있는 경우에는 오히려 학습에 방해가 되기도 함

 

정규화를 하면 일반작으로 학습이 더 잘되는데, 그 이유는 위의 사진과 같음

 

가중치와 편차에 따른 손실 그래프를 그린 것인데 데이터가 정규화 되지 않았을 때는 업데이트 과정에서 지그재그 모양으로 불필요한 업데이트가 많고 업데이트 횟수도 많이 필요

 

하지만 정규화된 손실 그래프는 원형에 가까운 형태를 가지기 때문에 불필요한 업데이트가 적고 더 큰 학습률을 적용할 수 있음

 

다시 말해, 데이터가 정규화되지 않았다면 데이터의 각 요소별로 범위가 다를 것

 

그렇게 되면 모델을 학습시킬 때 이상적으로 어떤 값은 크게 업데이트하고 어떤 값은 비교적 작은 수치로 업데이트해야 빠른 시간 안에 손실이 최소가 되는 지점에 도달

 

하지만 각 변수마다 범위가 다르기 때문에 어떤 변수를 기준으로 학습률을 정하는지에 따라 어떤 변수는 손실 최소 지점을 중심에 두고 왔다 갔다 할 것

 

이에 비해 정규화된 데이터는 변수들의 범위가 일정하기 때문에 비교적 높은 학습률을 적용시킬 수 있고 따라서 최소 지점에 더 빠르게 도달할 수 있게 됨


https://www.hanbit.co.kr/store/books/look.php?p_code=B7818450418 

 

파이토치 첫걸음

딥러닝 구현 복잡도가 증가함에 따라 ‘파이써닉’하고 사용이 편리한 파이토치가 주목받고 있다. 파이토치 코리아 운영진인 저자는 다년간 딥러닝을 공부하고 강의한 경험을 살려 딥러닝의

www.hanbit.co.kr