Overfitting(과적합) 이란 데이터의 학습결과가 학습데이터에만 최적화되어 모집단에 대한 일반화(Generalization)에 실패한 경우를 의미한다. 일반적으로 딥러닝 알고리즘에서는 Underfitting 보다 Overfitting이 자주 발생하기 때문에 딥러닝 모델을 구성할 때 이를 피하는 방법을 항상 고려해야 한다.

Overfitting을 피하기 위해 주로 사용하는 방법은 다음과 같다.

  1. Validation data set
  2. Regularization
  3. Dropout

1. Validation data set

Validation data set을 이용하는 방법은 주어진 데이터를 학습데이터와 테스트데이터 Validation data라고 하는 확인데이터(또는 검증데이터)로 분할하는 것이다(학습데이터와 테스트데이터 두 개로만 구분하는 것이 아닌). 여기서 Validation data란 데이터 학습에는 쓰이지는 않지만 cost를 계산해서 학습을 중단할 시점을 결정하는데 쓰이는 데이터이다. 딥러닝 알고리즘에서 반복적으로 학습데이터로 학습을 진행하면 학습데이터의 cost함수는 계속 감소하게된다. 이 과정에서 모델이 학습데이터에만 최적화되는 Overfitting 현상이 발생하는데 학습에 사용되지 않는 Validation data의 cost 값이 증가하는 시점에서 학습을 중단시켜 Overfitting되는 현상을 막는다(그림 참조). 즉, validation data를 활용하여 학습 횟수를 결정한다.

  • Training data : 모집단의 분포를 예측하기 위한 학습에 쓰이는 데이터이다.
  • Validation data : 학습에는 쓰이지 않고 학습데이터에 모델이 Overfitting되는 현상을 막는데 쓰인다.
  • Test data : 학습이 제대로 이루어 졌는지 즉, 일반화가 잘 됐는지 확인하는데 쓰인다.


2. Regularization

두 번째 방법으로, Regularization term (또는 weight decay term라고도 함)을 활용하는 방법이 있다. 이후 진행되는 코드에서는 Overfitting이 발생하는 경우를 확인하기 위한 것으로 테스트데이터와 Validation 데이터를 따로 설정하지 않았다. Regularization term은 예측값과 실제값의 오차를 계산하는 cost function에 Weight에 대한 강도를 감소시키기 위한 cost function에 추가하는 항으로 보통 weight의 \(L^2\)-norm 이나 \(L^1\)-norm을 사용한다. 우리는 목적함수 cost를 최소화하는 쪽으로 학습을 진행하기 때문에 weight의 값들이 과도하게 커지는 것을 막을 수 있다.

즉, cost function을 다음과 같이 정의한다.

\[cost \, += \lambda \frac{1}{2}\sum_{weight \, W} W^2 \qquad (L^2-loss)\]

또는

\[cost \, += \lambda \sum_{weight \, W} |W| \qquad (L^1-loss).\]

\(\lambda\)는 regularization constant로 Weight의 강도를 감소시키는 것의 중요도를 의미한다(\(\lambda\)=0인 경우 기존의 cost function과 같다). 아래 그림은 동일한 데이터를 동일 횟수로 학습한 결과를 보여준다.

파란색 점은 학습데이터를 나타내고 민트색선은 예측값을 나타낸다. 위의 그림은 cost로 MSE를 사용하였고 아래 그림은 MSE에 Regularization term을 추가 한 것이다. 그림에서 보는 것처럼 Regularization term 추가로 overfitting을 막는 효과를 확인할 수 있다.


3. Dropout

마지막으로 Dropout이란 딥러닝 네트워크를 구성할 때 모든 노드를 complete graph로 연결하는 것이 아니라 확률적으로 노드를 막아서 데이터 학습을 진행하는 방법을 말한다. 즉, weight와 bias가 학습할 때 확률적으로 선택된 노드에서만 진행한다고 생각하면 된다. 즉 노드를 남길 확률인 keep_prob=1 인 경우는 Dropout을 사용하지 않은 것이랑 같은 알고리즘다. 최종적으로 예측값을 구할 때는 노드를 막지 않고(keep_prob=1) complete graph 상태에서 예측값을 구한다.

아래 그림은 위와 같이 동일한 데이터를 동일 횟수로 학습했고 두 경우 모두 cost function은 MSE를 사용하였다.

위의 그림은 Dropout을 사용하지 않고 학습을 진행해서 예측한 결과를 보여주는 그래프이고 아래 그림은 Dropout (keep_prob=0.5)을 사용하여 학습한 결과다. 위 그림에서 보는 것 처럼 Dropout을 사용한 예측이 Overfitting 없이 적절하게 모집단의 분포를 파악하고 있는 것을 확인할 수 있다.


TensorFlow Code

1. Validation data set

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

%matplotlib inline

# 데이터 구성 (100개 데이터)
np.random.seed(20)
tf.set_random_seed(20)
x_data = np.random.uniform(-10, 10, size=[100,1])

y_data = np.sin(x_data + 0.2 * np.random.normal(size=[100,1]))- 0.3 * np.random.normal(size=[100,1])

# 딥러닝 네트워크 구성
X = tf.placeholder(tf.float32, shape=[None,1])
Y = tf.placeholder(tf.float32, shape=[None,1])

W0 = tf.Variable(tf.random_normal([1,50]), dtype=tf.float32)
b0 = tf.Variable(tf.random_normal([50]), dtype=tf.float32)

H0 = tf.sigmoid(tf.matmul(X,W0)+b0)

W1 = tf.Variable(tf.random_normal([50,50]), dtype=tf.float32)
b1 = tf.Variable(tf.random_normal([50]), dtype=tf.float32)

H1 = tf.sigmoid(tf.matmul(H0,W1)+b1)

W2 = tf.Variable(tf.random_normal([50,1]), dtype=tf.float32)
b2 = tf.Variable(tf.random_normal([1]), dtype=tf.float32)

H = tf.matmul(H1,W2)+b2

loss = tf.reduce_mean(tf.square(H-Y))

train = tf.train.GradientDescentOptimizer(learning_rate = 0.01).minimize(loss)

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

# 학습 및 결과 시각화
list_tr_cost = []
list_va_cost = []
list_te_cost = []

# training 60개, validation 20개, test 20개

iteration = 150000
for step in range(iteration):
    _, cost0 = sess.run([train, loss], feed_dict={X: x_data[:60], Y: y_data[:60]})
    cost1 = sess.run(loss, feed_dict={X: x_data[60:80], Y: y_data[60:80]})
    list_tr_cost.append(cost0)
    list_va_cost.append(cost1)
    if (step+1) % (iteration//10) ==0 :
        print("Step : %i, Cost : %s" %((step+1), cost0))

prediction = sess.run(H, feed_dict={X: x_data[80:]})

plt.figure(figsize=(16,12))
plt.subplot(211)
plt.xlabel("Steps")
_ = plt.plot(list_tr_cost[50000:], "c--", label='Cost of Training data')
_ = plt.plot(list_va_cost[50000:], "k--", label='Cost of Validation data')

plt.legend(loc='lower left')


plt.subplot(212)
_ = plt.plot(x_data[80:], y_data[80:], "bo")
_ = plt.plot(x_data[80:], prediction, "co")

실행화면

Step : 15000, Cost : 0.139464
Step : 30000, Cost : 0.118637
Step : 45000, Cost : 0.103913
Step : 60000, Cost : 0.0931755
Step : 75000, Cost : 0.08788
Step : 90000, Cost : 0.0847073
Step : 105000, Cost : 0.0824003
Step : 120000, Cost : 0.0806131
Step : 135000, Cost : 0.0803184
Step : 150000, Cost : 0.0791041

2. Regularization

loss0은 MSE cost function이고 loss1은 \(\lambda=1\)인 regularization term이 추가된 cost function이다.

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

%matplotlib inline

# 데이터 구성
np.random.seed(20)
tf.set_random_seed(20)
x_data = np.reshape(np.linspace(-3, 10, 100), [-1,1])

y_data = (x_data + np.random.normal(size=[100,1]))**2- 0.3 * np.random.normal(size=[100,1])

# 딥러닝 네트워크 구성
X = tf.placeholder(tf.float32, shape=[None,1])
Y = tf.placeholder(tf.float32, shape=[None,1])

W0 = tf.Variable(tf.random_normal([1,50]), dtype=tf.float32)
b0 = tf.Variable(tf.random_normal([50]), dtype=tf.float32)

H0 = tf.sigmoid(tf.matmul(X,W0)+b0)

W1 = tf.Variable(tf.random_normal([50,50]), dtype=tf.float32)
b1 = tf.Variable(tf.random_normal([50]), dtype=tf.float32)

H1 = tf.sigmoid(tf.matmul(H0,W1)+b1)

W2 = tf.Variable(tf.random_normal([50,1]), dtype=tf.float32)
b2 = tf.Variable(tf.random_normal([1]), dtype=tf.float32)

H = tf.matmul(H1,W2)+b2

# 두 가지 loss(cost) function
# loss0 : MSE loss, loss1 : regularization 추가

loss0 = tf.reduce_mean(tf.square(H-Y))

Ws = [W0, W1, W2]

loss1 = loss0
for weight in Ws:
    loss1 += tf.nn.l2_loss(weight)

loss = loss1

train = tf.train.GradientDescentOptimizer(learning_rate = 0.01).minimize(loss)

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

list_tr_cost = []

iteration = 100000
for step in range(iteration):
    _, cost0 = sess.run([train, loss], feed_dict={X: x_data, Y: y_data})
    list_tr_cost.append(cost0)
    if (step+1) % (iteration//10) ==0 :
        print("Step : %i, Cost : %s" %((step+1), cost0))

prediction = sess.run(H, feed_dict={X: x_data})

plt.figure(figsize=(16,12))
plt.subplot(211)
plt.xlabel("Steps")
_ = plt.plot(list_tr_cost, "c--", label='Cost of Training data')

plt.legend(loc='lower left')


plt.subplot(212)
_ = plt.plot(x_data, y_data, "bo")
_ = plt.plot(x_data, prediction, "c")


3. Dropout

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

%matplotlib inline

# 데이터 구성
np.random.seed(20)
tf.set_random_seed(20)
x_data = np.reshape(np.linspace(-3, 10, 100), [-1,1])

y_data = (x_data + np.random.normal(size=[100,1]))**2- 0.3 * np.random.normal(size=[100,1])

# 딥러닝 네트워크 구성
# 노드를 확률적으로 막음 (keep_prob)
X = tf.placeholder(tf.float32, shape=[None,1])
Y = tf.placeholder(tf.float32, shape=[None,1])

prob = tf.placeholder(tf.float32)

W0 = tf.Variable(tf.random_normal([1,50]), dtype=tf.float32)
b0 = tf.Variable(tf.random_normal([50]), dtype=tf.float32)

H0 = tf.nn.dropout(tf.sigmoid(tf.matmul(X,W0)+b0), keep_prob=prob)

W1 = tf.Variable(tf.random_normal([50,50]), dtype=tf.float32)
b1 = tf.Variable(tf.random_normal([50]), dtype=tf.float32)

H1 = tf.nn.dropout(tf.sigmoid(tf.matmul(H0,W1)+b1), keep_prob=prob)

W2 = tf.Variable(tf.random_normal([50,1]), dtype=tf.float32)
b2 = tf.Variable(tf.random_normal([1]), dtype=tf.float32)

H = tf.matmul(H1,W2)+b2

loss = tf.reduce_mean(tf.square(H-Y))


train = tf.train.GradientDescentOptimizer(learning_rate = 0.01).minimize(loss)

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

list_tr_cost = []


# 학습을 진행할 때는 keep_prob = 0.5, 예측할 때는 keep_prob = 1
iteration = 100000
for step in range(iteration):
    _, cost0 = sess.run([train, loss], feed_dict={X: x_data, Y: y_data, prob : 0.5})
    list_tr_cost.append(cost0)
    if (step+1) % (iteration//10) ==0 :
        print("Step : %i, Cost : %s" %((step+1), cost0))

prediction = sess.run(H, feed_dict={X: x_data, prob : 1})

plt.figure(figsize=(16,12))
plt.subplot(211)
plt.xlabel("Steps")
_ = plt.plot(list_tr_cost, "c--", label='Cost of Training data')

plt.legend(loc='lower left')


plt.subplot(212)
_ = plt.plot(x_data, y_data, "bo")
_ = plt.plot(x_data, prediction, "c")