관리 메뉴

TEAM EDA

[파이토치로 시작하는 딥러닝 기초] 2.6 Batch Normalization 본문

EDA Study/PyTorch

[파이토치로 시작하는 딥러닝 기초] 2.6 Batch Normalization

김현우 2020. 3. 20. 23:41

이번 글에서는 PyTorch로 Batch Normalization 하는 것에 대해서 배워보도록 하겠습니다. 이번 글은 EDWITH에서 진행하는 파이토치로 시작하는 딥러닝 기초를 토대로 하였고 같이 스터디하는 팀원분들의 자료를 바탕으로 작성하였습니다. 

목차 

  • Gradient Vanishing / Exploding 
  • Internal Covariate Shift 
  • Batch Normalization 
  • Code: mnist_batchnorm 

1. Gradient Vanishing / Exploding 

  • Gradient Vanishing : 역전파시에 그래디언트가 사라지는 현상 
    • 앞쪽의 레이어가 영향을 주지 못하고 뒤쪽의 레이어에 의해 모델이 결정됨 
  • Gradient Exploding : 역전파시에 그래디언트가 매우 커지는 현상 
    • 가중치가 크게 바뀌게 되어 모델이 불안정해지거나 NaN으로 사라지는 현상 
  • 해결책 
    • activation function(활성화 함수)를 변경 
    • initialization을 주의깊게 사용 
    • learning rate를 작게 설정 
    • Batch Normalization 

2. Internal Covariate Shift 

  • 정의 : 특정 숨겨진 계층의 출력이 활성 분포의 변화 때문에, 신경망의 훈련에 방해. (입력값이 바뀌게 된 효과) 
  • 아래의 그림처럼 출력층을 나온 분포가 달라서 학습의 진행이 잘 안되는 현상 
  • 해결 
    1. 초기값을 잘 설정 
    2. learning rate를 작게 설정 
    3. Batch Normalization 

출처 : https://www.slideshare.net/ssuser950871/why-batch-normalization-works-so-well

3. Batch Normalization 

출처 : https://data-newbie.tistory.com/356

  • 출력값의 분포를 안정화 
  • Vanishing gradient 문제가 감소 
  • 가중치 초기화에 덜 민감 
  • 하이퍼 파라미터 탐색을 쉽게 (Normalization을 통해서 학습의 속도가 빨라짐 - learning rate를 높게 할 수 있음) 
  • 정규화 효과가 있어서 dropout이 필요 없음(단, 실제로는 그 효과가 미비해서 같이 사용을 많이 함) 
  • 신경망과 하이퍼 파라미터의 상관관계를 낮추어 많은 파라미터가 잘 작동하도록 함 
  • 오버피팅을 억제 

3.1 Activation Function 

  • Training의 속도를 증가 
  • gradient Vanishing 문제를 없애줌 

4. Code: mnist_batchnorm 

torch.nn.BatchNorm1d(32)
# Lab 10 MNIST and softmax
import torch
import torchvision.datasets as dsets
import torchvision.transforms as transforms
import matplotlib.pylab as plt

device = 'cuda' if torch.cuda.is_available() else 'cpu'

# for reproducibility
torch.manual_seed(1)
if device == 'cuda':
    torch.cuda.manual_seed_all(1)
    
# parameters
learning_rate = 0.01
training_epochs = 10
batch_size = 32

# MNIST dataset
mnist_train = dsets.MNIST(root='MNIST_data/',
                          train=True,
                          transform=transforms.ToTensor(),
                          download=True)

mnist_test = dsets.MNIST(root='MNIST_data/',
                         train=False,
                         transform=transforms.ToTensor(),
                         download=True)
                         
                         
 # dataset loader
train_loader = torch.utils.data.DataLoader(dataset=mnist_train,
                                          batch_size=batch_size,
                                          shuffle=True,
                                          drop_last=True)

test_loader = torch.utils.data.DataLoader(dataset=mnist_test,
                                          batch_size=batch_size,
                                          shuffle=False,
                                          drop_last=True)
                                          
# nn layers
linear1 = torch.nn.Linear(784, 32, bias=True)
linear2 = torch.nn.Linear(32, 32, bias=True)
linear3 = torch.nn.Linear(32, 10, bias=True)
relu = torch.nn.ReLU()
bn1 = torch.nn.BatchNorm1d(32)
bn2 = torch.nn.BatchNorm1d(32)

nn_linear1 = torch.nn.Linear(784, 32, bias=True)
nn_linear2 = torch.nn.Linear(32, 32, bias=True)
nn_linear3 = torch.nn.Linear(32, 10, bias=True)

# model
bn_model = torch.nn.Sequential(linear1, bn1, relu,
                            linear2, bn2, relu,
                            linear3).to(device)

                               
# define cost/loss & optimizer
criterion = torch.nn.CrossEntropyLoss().to(device)    # Softmax is internally computed.
bn_optimizer = torch.optim.Adam(bn_model.parameters(), lr=learning_rate)

# Save Losses and Accuracies every epoch
# We are going to plot them later
train_losses = []
train_accs = []

valid_losses = []
valid_accs = []

train_total_batch = len(train_loader)
test_total_batch = len(test_loader)
for epoch in range(training_epochs):
    bn_model.train()  # set the model to train mode

    for X, Y in train_loader:
        # reshape input image into [batch_size by 784]
        # label is not one-hot encoded
        X = X.view(-1, 28 * 28).to(device)
        Y = Y.to(device)

        bn_optimizer.zero_grad()
        bn_prediction = bn_model(X)
        bn_loss = criterion(bn_prediction, Y)
        bn_loss.backward()
        bn_optimizer.step()


    with torch.no_grad():
        bn_model.eval()     # set the model to evaluation mode

        # Test the model using train sets
        bn_loss, nn_loss, bn_acc, nn_acc = 0, 0, 0, 0
        for i, (X, Y) in enumerate(train_loader):
            X = X.view(-1, 28 * 28).to(device)
            Y = Y.to(device)

            bn_prediction = bn_model(X)
            bn_correct_prediction = torch.argmax(bn_prediction, 1) == Y
            bn_loss += criterion(bn_prediction, Y)
            bn_acc += bn_correct_prediction.float().mean()

        bn_loss,  bn_acc, = bn_loss / train_total_batch, bn_acc / train_total_batch
        bn_loss, bn_acc  = 0, 0
        for i, (X, Y) in enumerate(test_loader):
            X = X.view(-1, 28 * 28).to(device)
            Y = Y.to(device)

            bn_prediction = bn_model(X)
            bn_correct_prediction = torch.argmax(bn_prediction, 1) == Y
            bn_loss += criterion(bn_prediction, Y)
            bn_acc += bn_correct_prediction.float().mean()

        bn_loss,  bn_acc = bn_loss / test_total_batch, bn_acc / test_total_batch


print('Learning finished')