본문 바로가기
Computer Science/Deep Learning

[비전공자용] 오버피팅 Overfitting 억제법 - 1.가중치 감소 2.드롭아웃 Dropout

by 롱일스 2020. 7. 10.
반응형

# 오버피팅 Overfitting 현상

오버피팅은 주로 다음 2 경우에 발생합니다.

  • 매개변수가 많고 표현력이 높은 모델
  • 훈련 데이터가 적음

오버피팅이 어떻게 발생하는 지 알아보기 위해 MNIST 데이터셋을 이용해서 학습을 시켜보겠습니다.

원래 60,000개인 MNIST 데이터셋의 훈련 데이터 중 300개만 사용하고,
7층 네트워크를 사용해서 네트워크의 복잡성을 높여서 오버피팅이 일어나게끔 환경을 조성합니다.
각 층의 뉴런은 100개이고 활성화 함수로 ReLU를 사용합니다.

파이썬 코드는 아래와 같습니다.

# coding: utf-8
import os
import sys

sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from common.multi_layer_net import MultiLayerNet
from common.optimizer import SGD

(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)

# 오버피팅을 재현하기 위해 학습 데이터 수를 줄임
x_train = x_train[:300]
t_train = t_train[:300]

# weight decay(가중치 감쇠) 설정 =======================
weight_decay_lambda = 0 # weight decay를 사용하지 않을 경우 (오버피팅 그래프)
#weight_decay_lambda = 0.1 # weight decay 사용한 경우
# ====================================================

network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10,
                        weight_decay_lambda=weight_decay_lambda)
optimizer = SGD(lr=0.01) # 학습률이 0.01인 SGD로 매개변수 갱신

max_epochs = 201
train_size = x_train.shape[0]
batch_size = 100

train_loss_list = []
train_acc_list = []
test_acc_list = []

iter_per_epoch = max(train_size / batch_size, 1)
epoch_cnt = 0

for i in range(1000000000):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]

    grads = network.gradient(x_batch, t_batch)
    optimizer.update(network.params, grads)

    if i % iter_per_epoch == 0:
        train_acc = network.accuracy(x_train, t_train)
        test_acc = network.accuracy(x_test, t_test)
        train_acc_list.append(train_acc)
        test_acc_list.append(test_acc)

        print("epoch:" + str(epoch_cnt) + ", train acc:" + str(train_acc) + ", test acc:" + str(test_acc))

        epoch_cnt += 1
        if epoch_cnt >= max_epochs:
            break


# 그래프 그리기==========
markers = {'train': 'o', 'test': 's'}
x = np.arange(max_epochs)
plt.plot(x, train_acc_list, marker='o', label='train', markevery=10)
plt.plot(x, test_acc_list, marker='s', label='test', markevery=10)
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()

 훈련 결과 정확도 변화는 다음과 같습니다.

훈련 데이터(train)와 시험 데이터(test)의 에폭별 정확도 추이

정확도 accuracy 변화를 보면 훈련 데이터를 사용하여 측정한 정확도는 100 epochs 지나면서 거의 100%가 되었지만, 시험 데이터는 거의 증가하지 않습니다. 정확도 차이가 크게 벌어지는 것은 훈련 데이터에만 적응 fitting 되어버려서 그렇습니다. 이런 현상을 오버피팅되었다고 합니다.

 

# 오버피팅 억제법 - 1. 가중치 감소 Wieght decay

가중치 감소 방법은 큰 가중치에 대해 그에 상응하는 큰 페널티를 부과하여 오버피팅을 억제하는 방법입니다.
오버 피팅은 가중치 매개변수의 값이 커서 발생하는 경우가 많이 때문에 이 방법이 효과를 보입니다.

가중치 감소에서는 모든 가중치 각각의 손실 함수(loss function)에

가중치의 제곱 놈(L2-norm)을 더해주어 가중치의 기울기를 구할 때 오차역전파법에 따른 결과에 정규화 항을 미분한 λW 를 더합니다. 
여기서 p-norm의 정의는 벡터 x에 대해 다음과 같이 나타낼 수 있습니다.

위의 코드에서 λ = 0.1로 두어서 가중치 감소를 적용하면, 아래와 같은 정확도 변화를 나타냅니다.

가중치 감소를 이용한 훈련 데이터(train)와 시험 데이터(test)에 대한 정확도 추이

훈련 데이터와 시험 데이터의 정확도 차이가 감소한 걸 확인할 수 있습니다. 즉, 오버피팅이 조금은 억제되었다고 볼 수 있습니다. 하지만 이전에는 훈련 데이터의 정확도가 100%에 거의 도달했지만 가중치 감소법을 적용했을 땐 도달하지 못한 걸 확인할 수 있습니다. 

 

# 오버피팅 억제법 - 1. 드롭아웃 Dropout

신경망 모델이 복잡해지면 앞서 살펴본 가중치 감소만으로는 오버피팅을 억제하기가 어려워집니다. 이럴 때는 흔히 드롭아웃 기법을 사용합니다.

드롭아웃은 뉴런을 임의로 삭제하며 학습하는 방법으로 훈련과정에서 은닉층의 뉴런을 무작위로 골라 삭제합니다. 단, 시험 때는 모든 뉴런에 신호를 전달하고 각 뉴런의 출력에 훈련 때 삭제한 비율을 곱하여 출력합니다.

드롭아웃의 개념

드롭아웃을 파이썬 코드로 다음과 같이 구현합니다.

class Dropout:
    """
    http://arxiv.org/abs/1207.0580
    """
    def __init__(self, dropout_ratio=0.5):
        self.dropout_ratio = dropout_ratio
        self.mask = None

    def forward(self, x, train_flg=True):
        # 순전파 때마다 self.mask에 삭제할 뉴런을 False로 표시
        if train_flg:
	  # x와 형상이 같은 배열을 무작위 생성 후 그 값이 dropout_ratio보다 큰 원소만 True로 설정
            self.mask = np.random.rand(*x.shape) > self.dropout_ratio
            return x * self.mask
        
        else:
            return x * (1.0 - self.dropout_ratio)

    def backward(self, dout):
        return dout * self.mask  # ReLU에서와 동일한 역전파 동작
	# 순전파 때 신호를 통과시킨 뉴런은 역전파 때도 신호 그대로 통과, 
       #                 통과못한                           통과못함

드롭아웃 클래스를 이용해서 위의 신경망과 같이 7층 네트워크로 각 층의 뉴런 수는 100개, 활성화함수는 ReLU를 사용한 학습을 하기 위한 파이썬 코드를 다음과 같이 설정할 수 있습니다. 이 때, 드롭아웃 비율은 0.2입니다.

# coding: utf-8
import os
import sys
sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from common.multi_layer_net_extend import MultiLayerNetExtend
from common.trainer import Trainer

(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)

# 오버피팅을 재현하기 위해 학습 데이터 수를 줄임
x_train = x_train[:300]
t_train = t_train[:300]

# 드롭아웃 사용 유무와 비울 설정 ========================
use_dropout = True  # 드롭아웃을 쓰지 않을 때는 False
dropout_ratio = 0.2
# ====================================================

network = MultiLayerNetExtend(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100],
                              output_size=10, use_dropout=use_dropout, dropout_ration=dropout_ratio)
trainer = Trainer(network, x_train, t_train, x_test, t_test,
                  epochs=301, mini_batch_size=100,
                  optimizer='sgd', optimizer_param={'lr': 0.01}, verbose=True)
trainer.train()

train_acc_list, test_acc_list = trainer.train_acc_list, trainer.test_acc_list

# 그래프 그리기==========
markers = {'train': 'o', 'test': 's'}
x = np.arange(len(train_acc_list))
plt.plot(x, train_acc_list, marker='o', label='train', markevery=10)
plt.plot(x, test_acc_list, marker='s', label='test', markevery=10)
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()

 

드롭아웃을 적용하니 훈련 데이터와 시험 데이터에 사이의 정확도 차이가 줄어든 걸 확인할 수 있지만, 역시나 훈련 데이터에 대한 정확도가 100%에 도달하지는 못하는 걸 볼 수 있습니다.  

[참고]
기계학습에서는 앙상블 학습 Ensemble Learning을 애용합니다. 이 학습 방법은 개별적으로 학습시킨 여러 모델의 출력을 평균 내어 추론하는 방식입니다.
예를 들어, 같은 혹은 비슷한 구조의 네트워크를 5개 준비하여 따로 학습시키고, 시험 때는 그 5개의 출력을 평균 내어 답하는 것입니다. 앙상블 학습을 수행하면 신경망의 정확도가 다소 개선된다고 실험적으로 알려져 있습니다.
앙상블 학습은 드롭아웃과 밀접한 관련이 있습니다. 드롭아웃에서 무작위로 뉴런을 삭제하는 것을 앙상블 학습에서 매번 다른 모델을 학습시키는 것과 연결지을 수 있기 때문입니다. 그리고 추론 inference 시에 뉴런의 출력에 삭제한 비율을 곱함으로써 앙상블 학습에서 여러 모델의 평균을 내는 것과 같은 맥락으로 이해할 수 있습니다.
즉, 드롭아웃을 앙상블 학습과 같은 효과를 하나의 네트워크로 구현했다고 볼 수 있습니다. 

지금까지 오버피팅과 오버피팅을 억제하는 여러 방법들에 대해 알아봤습니다.

 

[출처] Deep Learning from Scratch, ゼロ から作る

728x90
반응형