오버피팅 문제 해결 방안

  • 학습 데이터에만 최적화되지 않고 테스트 데이터에 대해서도 정확도가 높아지도록 패턴 분류를 실행하는 것을 일반화(generalization)이라고 하는데, 오버피팅을 방지하기 위해서는 신경망 모델의 일반화 성능을 향상시켜야만 한다.
  • 드롭아웃(dropout) : 학습을 시킬 때 무작위로 뉴런을 '드롭아웃(=제외)'시키는 방법으로 드롭아웃이 적용된 신경망 모델의 예가 그림 9.1이다. 은영처리된 뉴런이 드롭아웃된 뉴런으로 이 뉴런은 마치 '신경망에 존재하지 않는 것'으로 취급된다.
    • 학습을 시킬 때마다 드롭아웃시킬 뉴런을 무작위로 선택해서 학습 전체에서 매개변수 값이 조정되도록 한다.
    • 뉴런이 드롭아웃될 확률 $p$는 일반적으로 $p=0.5$의 값을 사용한다.
    • 학습이 끝난 후에 테스트와 예측을 실행할 때에는 드롭아웃을 시행하지 않지만 신경망 모델의 전체 웨이트가 $W$일 때 학습 전체의 평균을 나타내는 $(1-p)W$의 출력을 사용하게 된다.

그림 9.1 드롭아웃이 적용된 신경망의 예

 

드롭아웃과 일반화 성능의 관계

  • 하나의 모델로 학습을 시키면 어쩔 수 없이 오버피팅이 발생할 수 밖에 없다.
  • 드롭아웃을 적용하면 학습할 때마다 새로운 신경망 모델이 만들어지기 때문에 다수의 신경망 모델로 학습을 하고 각 모델로 예측을 한다면 '집단지성'을 통해 예측을 하기 때문에 오버피팅을 방지할 수 있다.
  • 하나의 신경망 모델에서 여러 개의 신경망 모델을 생성해 학습을 시키는 것을 앙상블 학습(ensemble learning)이라고 한다.
  • 드롭아웃 학습법은 유사적으로 앙상블 학습을 하는 것이라고 볼 수 있다.

 

드롭아웃 학습법 해석

  • 드롭아웃 학습법을 수식을 통해 해석한다고 하면 뉴런을 무작위로 선택해 드롭아웃을 시켜야 하는 상황을 고려해야 한다.
  • 신경망 모델의 뉴런을 무작위로 선택해서 $0$(드롭아웃)이나 $1$이라는 값을 취하는 마스크를 씌우면 된다.
  • $\mathbb=(m_1, \ldots, m_j, \ldots, m_J)$라는 벡터에서 $m_j$는 확률 $p$로 $0$을, 확률 $(1-p)$로 $1$을 취하는 값이라고 가정해보자.
  • 드롭아웃이 없는 경우 1개의 은닉층을 가진 신경망 모델의 순전파 식은 식 $(9.1)$과 같다.

$$\begin{align} \mathbb{h}_1 = f(W\mathbb{x}+\mathbb{b}) \tag{9.1}\end{align}$$

  • 드롭아웃이 있는 경우에는 식 $(9.1)$에 마스크를 추가하면 되기 때문에 식 $(9.2)$와 같다.

$$\begin{align} \mathbb{h}_1 = f(W\mathbb{x}+\mathbb{b})\odot\mathbb{m} \tag{9.2}\end{align}$$

  • 순전파에서 마스크를 씌운다는 것은 순전파를 진행할 때도 마스크 항이 추가되기 때문에 $\mathbb{h}_1$ 다음의 2번째 은닉층 식 $(9.3)$라고 하자.

$$\begin{align} \mathbb{h}_2 = g(V\mathbb{h}_1+\mathbb{c}) \tag{9.3}\end{align}$$

  • 또 활성화되기 전의 상태를 다음과 같이 정의하자.

$$\begin{align} \mathbb{p} &= W\mathbb{x}+\mathbb{b} \tag{9.4}\\\mathbb{q} &= V\mathbb{h}_1+\mathbb{c}\\ &= Vf(\mathbb{p})\odot\mathbb{m}+\mathbb{c}\tag{9.5}\end{align}$$

  • 이 신경망 모델에서 식 $(9.4)$와 식 $(9.5)$에 대한 오차항 $\mathbb{e}_{h_1}, \mathbb{e}_{h_2}$은 식 $(9.6)$과 식 $(9.7)$로 표현할 수 있다.

$$\begin{align} \mathbb{e}_{h_1} &= \frac{\partial E_n}{\partial \mathbb{p}} \tag{9.6}\\ \mathbb{e}_{h_2} &= \frac{\partial E_n}{\partial \mathbb{q}}\tag{9.7}\end{align}$$

  • $\mathbb{e}_{h_1}$은 식 $(9.8)$과 같이 계산되기 때문에 오차역전파에서 마스크 $\mathbb{m}$이 필요하다는 것을 알 수 있다.

$$\begin{align} \mathbb{e}_{h_1} &= \frac{\partial E_n}{\partial \mathbb{q}}\frac{\partial \mathbb{q}}{\partial \mathbb{p}}\\ &= \mathbb{e}_{h_1}\frac{\partial }{\partial \mathbb{p}} \Big(Vf(\mathbb{p})\odot\mathbb{m}+\mathbb{c}\Big) \\&= f'(\mathbb{p})\odot\mathbb{m}\odot V\mathbb{e}_{h_2}\tag{9.8}\end{align}$$

 

MNIST 드롭아웃 모델 구현하기

  • TensorFlow
    • 수식과 마찬가지로 순전파의 기본식을 정의하고 마스크를 씌우는 순서대로 모델을 정의한다.
    • 전체 코드
      • 드롭아웃 확률은 학습 시에는 $p=0.5$이지만 예측을 할 때에는 $p=1.0$인 것에 유의
import numpy as np 
import tensorflow as tf 
from sklearn import datasets 
from sklearn.model_selection import train_test_split 
from sklearn.utils import shuffle 

MNIST = datasets.fetch_mldata('MNIST original', data_home='.') 

n = len(MNIST.data) 
N = 10000 
train_size = 0.8 
indices = np.random.permutation(range(n))[:N] 

X = MNIST.data[indices] 
Y = MNIST.target[indices] 
Y = np.eye(10)[Y.astype(int)] 

X_train, X_test, Y_train, Y_test = train_test_split(X, Y, train_size=train_size) 

# 모델 설정 
n_in = len(X[0]) 
n_hidden = 200 
n_out = len(Y[0]) 

x = tf.placeholder(tf.float32, shape=[None, n_in]) 
y = tf.placeholder(tf.float32, shape=[None, n_out]) 
keep_prob = tf.placeholder(tf.float32) # 드롭아웃하지 않을 확률 정의 

# 입력층 - 은닉층 
W0 = tf.Variable(tf.truncated_normal([n_in, n_hidden], stddev=0.01)) 
b0 = tf.Variable(tf.zeros([n_hidden])) 
h0 = tf.nn.relu(tf.matmul(x, W0) + b0) 
h0_drop = tf.nn.dropout(h0, keep_prob) 

# 은닉층 - 은닉층 
W1 = tf.Variable(tf.truncated_normal([n_hidden, n_hidden], stddev=0.01)) 
b1 = tf.Variable(tf.zeros([n_hidden])) 
h1 = tf.nn.relu(tf.matmul(h0_drop, W1) + b1) 
h1_drop = tf.nn.dropout(h1, keep_prob) 

W2 = tf.Variable(tf.truncated_normal([n_hidden, n_hidden], stddev=0.01)) 
b2 = tf.Variable(tf.zeros([n_hidden])) h2 = tf.nn.relu(tf.matmul(h1_drop, W2) + b2) 
h2_drop = tf.nn.dropout(h2, keep_prob) 

# 은닉층 - 출력층 
W3 = tf.Variable(tf.truncated_normal([n_hidden, n_out], stddev=0.01)) 
b3 = tf.Variable(tf.zeros([n_out])) 
hypothesis = tf.nn.softmax(tf.matmul(h2_drop, W3) + b3) 

cross_entropy = tf.reduce_mean(-tf.reduce_sum(y * tf.log(hypothesis), axis=1)) 
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy) 
correct_prediction = tf.equal(tf.argmax(hypothesis, 1), tf.argmax(y, 1)) 

accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) 

# 모델 학습 과정 
epochs = 30 
batch_size = 200 

init = tf.global_variables_initializer() 
sess = tf.Session() 
sess.run(init) 

n_batches = (int)(N * train_size) // batch_size 

for epoch in range(epochs): 
    X_, Y_ = shuffle(X_train, Y_train) 
    
    for i in range(n_batches): 
        start = i * batch_size 
        end = start + batch_size 
        
        sess.run(train_step, feed_dict={x: X_[start:end], y: Y_[start:end], keep_prob: 0.5}) 
        
    # 학습 데이터에 대응하는 학습 진행 상황을 출력 
    loss = cross_entropy.eval(session=sess, feed_dict=) 
    acc = accuracy.eval(session=sess, feed_dict=)
    print(f'epoch = , loss: , accuracy: %') 
    
# 예측 결과 평가 
accuracy_rate = accuracy.eval(session=sess, feed_dict=) 
print(f'accuracy: %')
    • 실행 결과
epoch = 027, loss: 0.079016678, accuracy: 97.6875 % 
epoch = 028, loss: 0.077762701, accuracy: 97.8875 % 
epoch = 029, loss: 0.06995143, accuracy: 97.95 % 

accuracy: 93.95 %
  • Keras
    • 전체 코드
    • TensorFlow에서는 dropout이 드롭아웃하지 않을 뉴런의 비율을 의미했지만, Kera에서는 드롭아웃시킬 뉴런의 비율을 의미함
import numpy as np 
from keras.models import Sequential 
from keras.layers.core import Dense, Activation, Dropout 
from keras.optimizers import SGD 
from sklearn import datasets 
from sklearn.model_selection import train_test_split 
from time import time 

# 데이터 생성 
MNIST = datasets.fetch_mldata('MNIST original', data_home='.') 
n = len(MNIST.data) 
N = 10000 
indices = np.random.permutation(range(n))[:N] 

X = MNIST.data[indices] 
Y = MNIST.target[indices] 
Y = np.eye(10)[Y.astype(int)] 

X_train, X_test, Y_train, Y_test = train_test_split(X, Y, train_size=0.8) 

# 모델 설정 
n_in = len(X[0])
n_hidden = 200 
n_out = len(Y[0]) 

model = Sequential() 
model.add(Dense(n_hidden, input_dim=n_in)) 
model.add(Activation('tanh')) 
model.add(Dropout(0.5)) 
model.add(Dense(n_hidden)) 
model.add(Activation('tanh')) 
model.add(Dropout(0.5)) 
model.add(Dense(n_hidden)) 
model.add(Activation('tanh')) 
model.add(Dropout(0.5)) 
model.add(Dense(n_out)) 
model.add(Activation('softmax')) 
model.compile(loss='categorical_crossentropy', optimizer=SGD(lr=0.01), metrics=['accuracy']) 

start = time() 

# 모델 학습시키기 
epochs = 150 
batch_size = 200 

model.fit(X_train, Y_train, epochs=epochs, batch_size=batch_size) 
print(f'\nElapse training time : {(time() - start)//60}분 {(time() - start) % 60:.6}초\n') 

# 정확도 측정 
start = time() 
loss_and_metrics = model.evaluate(X_test, Y_test) 
print(f'\nLoss : ') 
print(f'Accuracy : %') 
print(f'Elapse test time : 초') 
    • 실행 결과
Elapse training time : 0.0분 45.3053초 

  32/2000 [..............................] - ETA: 1s 
2000/2000 [==============================] - 0s 33us/step 

Loss : 0.329999 
Accuracy : 90.85% 
Elapse test time : 0.06604743003845215초

 

출처 : 정석으로 배우는 딥러닝

 

'신경망(Neural Network) 스터디' 카테고리의 다른 글

11. 학습 과정 시각화  (3) 2018.01.19
10. 신경망 모델 구현 방법  (0) 2018.01.19
8. 경사 소실 문제 해결하기  (0) 2018.01.18
7. 심층 신경망  (0) 2018.01.10
6. 다층 퍼셉트론 모델링  (0) 2017.12.28

+ Recent posts