Learning rate와 평가 방법 연습

Training dataset과 test dataset 연습

  • 우리가 가지고 있는 데이터를 그림 1과 같이 training dataset과 test dataset으로 나누어야 한다

그림 1  데이터를 학습 데이터와 테스트 데이터로 구분


  • 프로그램 전체 코드
    • Prediction과 Accuracy를 계산할 때 테스트 데이터셋을 사용하는 것에 주의
import tensorflow as tf

x_data = [[1, 2, 1],
          [1, 3, 2],
          [1, 3, 4],
          [1, 5, 5],
          [1, 7, 5],
          [1, 2, 5],
          [1, 6, 6],
          [1, 7, 7]]

y_data = [[0, 0, 1],
          [0, 0, 1],
          [0, 0, 1],
          [0, 1, 0],
          [0, 1, 0],
          [0, 1, 0],
          [1, 0, 0],
          [1, 0, 0]]

x_test = [[2, 1, 1],
          [3, 1, 2],
          [3, 3, 4]]

y_test = [[0, 0, 1],
          [0, 0, 1],
          [0, 0, 1]]

X = tf.placeholder(dtype='float', shape=[None, 3])
Y = tf.placeholder(dtype='float', shape=[None, 3])
W = tf.Variable(tf.random_normal([3, 3]))
b = tf.Variable(tf.random_normal([3]))

hypothesis = tf.nn.softmax(tf.matmul(X, W) + b)
cost = tf.reduce_mean(-tf.reduce_sum(Y * tf.log(hypothesis), axis=1))
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.1).minimize(cost)

# 모델의 성능 평가
prediction = tf.arg_max(hypothesis, 1)
is_correct = tf.equal(prediction, tf.arg_max(Y, 1))
accuracy = tf.reduce_mean(tf.cast(is_correct, tf.float32))

# 텐서 그래프 실행
with tf.Session() as sess:
    # 텐서플로우 변수 초기화
    sess.run(tf.global_variables_initializer())

    for step in range(20):
        cost_val, W_val, _ = sess.run([cost, W, optimizer], feed_dict={X: x_data, Y: y_data})
        print(f'STEP = {step:>03}, cost_val = {cost_val}, \n\tW_val = {W_val}')

    # 테스트 데이터로 결과값 예측하기
    print(f'Prediction: {sess.run(prediction, feed_dict={X: x_test})}')

    # 정확도 계산하기
    print(f'Accuracy: {sess.run(accuracy, feed_dict={X: x_test, Y: y_test})}')


  • 테스트 데이터를 정확하게 예측하여 정확도 100%
STEP = 000, cost_val = 7.7362213134765625, W_val = [[-1.78199244 -1.11868954  0.31097311]
 [-0.96270818  0.571316    0.59686804]
 [ 0.54613751  0.99451631  0.52300102]]
STEP = 001, cost_val = 4.277797222137451, W_val = [[-1.75701272 -1.09903288  0.26633674]
 [-0.80025268  0.65380424  0.35192436]
 [ 0.70857799  1.08600318  0.26907364]]
STEP = 002, cost_val = 3.3367347717285156, W_val = [[-1.73212302 -1.13073337  0.27314749]
 [-0.63800389  0.47786415  0.36561567]
 [ 0.87070632  0.91418201  0.27876648]]
STEP = 003, cost_val = 2.3596982955932617, W_val = [[-1.70765483 -1.13776052  0.25570655]
 [-0.4767729   0.41531134  0.26693752]
 [ 1.0309391   0.86074114  0.17197455]]
STEP = 004, cost_val = 1.578892469406128, W_val = [[-1.68549311 -1.15126801  0.24705234]
 [-0.32190347  0.31800923  0.20937021]
 [ 1.18043995  0.77415037  0.10906445]]
STEP = 005, cost_val = 0.9504669904708862, W_val = [[-1.67428207 -1.15372241  0.2382957 ]
 [-0.2109751   0.27382281  0.14262828]
 [ 1.27377284  0.74603862  0.04384331]]
STEP = 006, cost_val = 0.7754672765731812, W_val = [[-1.68434417 -1.14607823  0.24071361]
 [-0.20814292  0.2871232   0.1264957 ]
 [ 1.25186896  0.7789281   0.03285767]]
STEP = 007, cost_val = 0.7643951773643494, W_val = [[-1.68975425 -1.14531982  0.24536531]
 [-0.17962518  0.26310939  0.12199181]
 [ 1.25664175  0.77485752  0.03215552]]
STEP = 008, cost_val = 0.7564760446548462, W_val = [[-1.69974136 -1.13999033  0.25002289]
 [-0.17699562  0.26532796  0.11714368]
 [ 1.23577583  0.79623437  0.03164455]]
STEP = 009, cost_val = 0.7493610382080078, W_val = [[-1.7059654  -1.13871193  0.2549684 ]
 [-0.15456522  0.2457495   0.11429172]
 [ 1.23580301  0.79542232  0.03242938]]
STEP = 010, cost_val = 0.742727518081665, W_val = [[-1.71523523 -1.1342057   0.25973195]
 [-0.14976113  0.24479081  0.11044633]
 [ 1.21865392  0.81247455  0.03252625]]
STEP = 011, cost_val = 0.7365438938140869, W_val = [[-1.72175562 -1.13258219  0.26462874]
 [-0.13076863  0.22854277  0.10770187]
 [ 1.21668315  0.81373602  0.03323561]]
STEP = 012, cost_val = 0.730728030204773, W_val = [[-1.73047745 -1.12861145  0.26937979]
 [-0.12475161  0.22597784  0.10424978]
 [ 1.20225775  0.82799172  0.03340538]]
STEP = 013, cost_val = 0.7252621054649353, W_val = [[-1.73711765 -1.12680638  0.27421495]
 [-0.10819503  0.21210794  0.10156309]
 [ 1.19927382  0.83040279  0.03397828]]
STEP = 014, cost_val = 0.7201066017150879, W_val = [[-1.74541831 -1.12322795  0.27893719]
 [-0.10161517  0.20870271  0.09838846]
 [ 1.1868645   0.842641    0.03414936]]
STEP = 015, cost_val = 0.7152454257011414, W_val = [[-1.75208461 -1.12134075  0.28371629]
 [-0.08691914  0.19661304  0.09578211]
 [ 1.18339944  0.84564602  0.03460943]]
STEP = 016, cost_val = 0.7106539011001587, W_val = [[-1.76004636 -1.11806726  0.28840458]
 [-0.08016367  0.19280966  0.09283005]
 [ 1.17255294  0.85634184  0.03476014]]
STEP = 017, cost_val = 0.7063170671463013, W_val = [[-1.76668465 -1.11615694  0.29313251]
 [-0.06695879  0.18211773  0.09031708]
 [ 1.16891909  0.8596077   0.03512816]]
STEP = 018, cost_val = 0.7022162079811096, W_val = [[-1.77436292 -1.11313081  0.29778466]
 [-0.06025715  0.17818163  0.08755152]
 [ 1.15933442  0.86907136  0.03524918]]
STEP = 019, cost_val = 0.6983374357223511, W_val = [[-1.78094149 -1.11123252  0.3024649 ]
 [-0.04829093  0.16863091  0.08513602]
 [ 1.1557188   0.87239617  0.03553999]]
Prediction: [2 2 2]
Accuracy: 1.0


Learning rate 연습

  • learning rate가 큰 경우에는 그림 2의 왼쪽 그림과 같이 cost 함수 값이 발산하는 경우(overshooting)가 생길 수 있음
  • learning rate가 작은 경우에는 그림 2의 오른쪽 그림과 같이 cost 함수 값이 local minimum으로 수렴하지 못하게 됨


그림 2  learning rate 값이 크거나 작은 경우에 발생하는 문제점


 

Learning rate가 큰 경우

  • 정확도가 100%인 위의 모델에서 learning rate를 15로 크게 만들면 cost 함수값이 발산하여 NaN이 됨
optimizer = tf.train.GradientDescentOptimizer(learning_rate=15).minimize(cost)


  • 두번째 학습(STEP = 001)부터 cost 함수의 값이 발산하는 것을 볼 수 있다
STEP = 000, cost_val = 4.2111382484436035, W_val = [[  1.17594206  -4.12693262   3.55341578]
 [ 16.24326897 -26.29383278  10.33104706]
 [ 16.27523804 -21.74674416   8.40264225]]
STEP = 001, cost_val = nan, W_val = [[ nan  nan  nan]
 [ nan  nan  nan]
 [ nan  nan  nan]]
STEP = 002, cost_val = nan, W_val = [[ nan  nan  nan]
 [ nan  nan  nan]
 [ nan  nan  nan]]
STEP = 003, cost_val = nan, W_val = [[ nan  nan  nan]
 [ nan  nan  nan]
 [ nan  nan  nan]]
STEP = 004, cost_val = nan, W_val = [[ nan  nan  nan]
 [ nan  nan  nan]
 [ nan  nan  nan]]


Learning rate가 작은 경우

  • 정확도가 100%인 위의 모델에서 learning rate를 1e-15로 작게 만들면 cost 함수값이 거의 변화가 없어 학습이 진행되지 않게됨
optimizer = tf.train.GradientDescentOptimizer(learning_rate=1e-15).minimize(cost)


  • 두번째 학습(STEP = 001)부터 cost 함수의 값이 발산하는 것을 볼 수 있다
STEP = 000, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 001, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 002, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 003, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 004, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 005, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 006, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 007, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 008, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 009, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 010, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 011, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 012, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 013, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 014, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 015, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 016, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 017, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 018, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
STEP = 019, cost_val = 1.9577536582946777, W_val = [[ 1.18757093 -3.92518234  1.47391939]
 [-0.13499689 -0.66945004 -1.03208375]
 [-0.31017131  1.51171744  0.99123961]]
Prediction: [2 2 2]
Accuracy: 1.0


데이터 정규화 연습

  • 그림 3과 같은 가상의 데이터를 다루는 경우 데이터의 분포는 그림 3의 아래처럼 된다

그림 3  데이터 값들의 차이가 큰 경우


  • 데이터 값들의 차이가 큰 경우 cost 함수의 값이 발산하는 것을 볼 수 있다

그림 4  데이터 값들의 차이가 큰 경우의 예측 모델


  • 그림 3의 데이터를 정규화하면 그림 4와 같은 분포를 띄게 되며 결과도 제대로 나오는 것을 알 수 있다
    • 정규화를 위해 MinMaxScaler() 함수를 사용한다

그림 4  그림 3의 데이터를 정규화한 결과


머신러닝 알고리즘 응용


Learning rate

  • Gradient Descent Algorithm에서 cost 값의 최소값을 찾아갈 때 learning rate \(\alpha\) 값을 정의하였음
  • 그림 1처럼 프로그램으로 구현하였으며 learning_rate를 잘 정하는 것이 중요

그림 1  TensorFlow에서 learning_rate를 설정하는 방법

Learning rate가 큰 경우

  • learning rate는 그림 2처럼 곡선의 경사면을 따라 내려가는 간격을 의미
  • 이동하는 간격이 크다면 곡선의 최소점으로 수렴하는 것이 아니라 최소점 근처에서 왔다갔다 할 수도 있다
  • 이동하는 간격이 너무 크다면 곡선 아래가 아닌 위로 발산하는 경우도 생길 수 있다
  • 이와 같은 경우를 overshooting이라고 하며, cost 함수값을 계산하는데 cost 값이 줄어들지 않고 커지는 경우에는 overshooting을 의심해봐야 한다

그림 2  cost function을 구하기 위해 weight 값을 간격을 크게하는 경우


Learning rate가 작은 경우

  • learning rate가 작은 경우에는 경사면을 따라 이동하는 것이 더디게 된다
  • 마치 산에 있는데 해가 질 때까지 하산을 못하는 경우랑 같다
  • 따라서 일정 시간이후에 이동을 멈출 수 밖에 없는데 최저점에 이르지 못한 상태가 된다
  • 이를 회피하기 위해서는 cost 함수값을 출력해서 변화되는 값을 봐야한다. 거의 변하지 않으면 learning rate가 너무 작은 것인지 의심해봐야 한다

그림 3  cost function을 구하기 위해 weight 값을 간격을 작게하는 경우


learning rate 결정에 답은 없다!!

  • 보통 0.01을 사용하는데 데이터에 따라 달라질 수 있어 cost 함수의 값을 보고 결정해야 한다



데이터 전처리(Data Preprocessing)


weight가 1개인 경우와 2개인 경우의 weight-cost 그래프

  • 그림 4에서 오른쪽 파란색 그림은 weight가 1개인 경우의 그래프이며
  • 그림 4의 왼쪽 그림은 weight가 2개인 경우로 등고선처럼 표현 가능
  • 어떤 점에서 시작해 경사면을 따라 내려가서 최저점에 이르는 것이 목표


그림 4  데이터의 특성이 2개($w_1, w_2$)인 경우(좌)와 1개인 경우(우)


weight가 2개인 경우의 weight-cost 그래프 예

  • 예를 들어 그림 5의 왼쪽 표와 같은 데이터가 있다고 해보자
  • 표에서 \(x_1\)과 \(x_2\)의 데이터처럼 값에서 차이가 많이 난다면 \(w_1\)과 \(w_2\)에 대한 그래프는 그림 5의 오른쪽 그래프처럼 옆으로 길쭉한 모양의 등고선 형태가 된다
  • 이 상태에서 learning rate \(\alpha\)값을 잘 못 잡으면 밖으로 튀어가는 수가 있어 데이터를 표준화하는 것이 필요함



그림 4  데이터의 특성이 2개($w_1, w_2$)인 경우의 weight-cost 그래프


데이터 정규화(Data Normalization 또는 Regularization)

  • 데이터 정규화에는 여러 방법이 있다
  • 예를 들어 원시 데이터가 그림 5의 왼쪽 그래프와 같이 분포해 있는 경우에, 많이 쓰는 방법은 그림 5의 가운데 그래프처럼 데이터의 중심이 \(0\)이 위치하도록 하는 하는 방법이다(zero-centered data)
  • 가장 널리 사용하는 방법은 그림 5의 오른쪽 그래프처럼 데이터의 범위가 특정 범위 안에 항상 들어가도록 정규화시키는 방법을 가장 많이 사용한다(normalized data)



그림 5  데이터 표준화 방법의 예



  • learning rate도 잘 잡았는 것 같은데 이상하게 학습이 진행되지 않고 cost 함수의 값이 발산하거나 이상한 동작을 보일 때에는
  • 데이터의 값들 중에 차이가 크게 나는 것이 있는지 보고 전처리를 했는지 살펴보아야 한다

데이터 정규화 방법

  • 데이터를 정규화하기 위해서는 식 (1)을 사용
    • \(x_j\) : 원시 데이터
    • \(\mu_j\) : 원시 데이터의 평균
    • \(\sigma_j\) : 원시 데이터의 표준편차

\begin{eqnarray} x'_j = \frac{x_j - \mu_j}{\sigma_j} \tag{1} \end{eqnarray}

  • 코드는 다음과 같이 사용
x_std[:, 0] = (x[:, 0] - x[:, 0].mean()) / x[:,0].std() 


Overfitting

  • 머신러닝은 학습을 통해서 모델을 만들어 가는 과정이나
  • 학습 데이터에 너무 잘맞는 모델이 만들어 질 수도 있다
  • 그렇게 되면 학습 데이터를 가지고 물어보면 제대로 답을 잘 하겠지만
  • 테스트 데이터로 물어보거나 실제로 사용해보면 잘 안맞는 경우가 다소 있다.


  • 예를 들어 그림 6에서 '+'와 '-'를 예측하는 형태의 문제가 주어졌다고 해보자
  • 일반적으로 그림 6의 왼쪽 그림처럼 구분을 하는 모델(model)이 좋은 모델이라고 할 수 있다
  • 그러나 우리의 모델(model2)이 그림 6의 오른쪽 그림처럼 만들어 졌다면 왼쪽 그림의 모델(model)과 비교했을 때 어떤 모델이 더 좋은 모델이라고 할 수 있을까?
    • model이 좀더 일반적인 모델로 다른 데이터가 들어오더라도 잘 맞는 모델이 될 수 있지만
    • model2는 학습 데이터는 딱 들어맞지만 실제적으로 사용할 때에는 정확도가 떨어질 수 있다
  • 학습 데이터에 딱 들어맞지만 실제 사용시에는 정확도가 떨어지는 모델을 overfitting되었다고 한다

그림 6  overfitting의 예


Overfitting을 막는 방법

  • 학습 데이터를 많이 준비해 학습시켜야 한다
  • 특징(feature 또는 weight)의 개수를 줄인다
  • 데이터를 정규화(regularization)한다


Regularization

  • weight를 너무 크게 만들지 말자
    • 그림 7과 같은 학습 데이터에 맞게 decision boundary를 막 구부리는데 구부리지 말고 펴도록 해야 한다
    • 곡선을 편다는 이야기는 weight이 작은 값을 가지며
    • 곡선을 구부린다는 이야기는 어떤 값에서는 weight 큰 값을 가진다는 것을 의미


그림 7  overfitting의 예


  • 학습을 한다는 것은 cost 함수를 최적화시킨다는 것인데 cost 함수 계산식에 정규화를 위한 항을 추가시킨다
    • 추가되는 항은 그림 8과 같이 weight의 element들을 제곱해서 더한 다음 regularization strength \(\lambda\)를 곱한 값이다
      • \(\lambda=0\)이면 regularization을 사용하지 않게 되며
      • \(\lambda>1\)이면 regularization을 굉장히 중요하게 생각해 사용하며
      • \(\lambda\)이 적당한 값이면 regularization을 사용하지만 크게 중요하게는 생각하지 않는다는 의미
  • TensorFlow에서는 그림 8처럼 l2reg를 정의해서 사용하면 된다


그림 8  cost 함수에 regularization을 위한 항이 추가된 식과 파이썬 코드




머신러닝 모델의 평가 방법


  • 머신러닝 모델을 만든 다음에 데이터를 가지고 학습을 시킨다
  • 학습된 모델을 어떻게 평가를 할 수 있을까?


학습 데이터를 사용한 모델 평가 방법의 예

  • 그림 9와 같은 데이터를 학습 데이터로 사용하여 모델을 학습시킨 다음에
  • 다시 학습 데이터(예: 1600)를 가지고 머신러닝 모델에 물어보게 되면 모델이 답을 하거나 예측을 할 수 있게 된다
  • 이런 방식으로 물어보게 되면 정말로 공정한 평가 방법일까?
    • 학습 데이터로 모델을 평가하게 되면 거의 100% 확실한 답을 내게 될 것이다
    • 학습 데이터를 통째로 저장하고 있으면 되기 때문에 가능
  • 즉, 학교에서 시험을 보고 똑같은 문제로 시험을 다시 보는 것이랑 유사하기 때문에 좋은 평가 방법이 아니다



그림 8  학습 데이터로 머신러닝 모델을 학습시키고 평가하는 예


머신러닝 모델 평가 방법

  • 시험을 보는 방법과 똑같은 방법으로 평가해야 한다
  • 학습 데이터를 70:30으로 나누는데 70%를 학습 데이터 셋(training data set)라 하고, 30%를 테스트 데이터 셋(test data set)라고 한다
  • 그림 9와 같이 70%의 학습 데이터 셋을 가지고 모델을 학습시키고 학습이 끝난 후에 테스트 데이터 셋에 대한 모델의 예측값과 실제의 값을 비교하여 성능을 평가해야 한다

다시 학습 데

그림 9  학습 데이터 셋과 테스트 데이터 셋으로 구분하여 학습시키고 평가한다


Traing과 validation, test set의 차이

  • 일반적으로 traing set과 test set으로 나눈다
  • learning rate와 regularization strength를 사용하는 경우, 모델 튜닝을 위해 training set을 training set과 validation set으로 나눈 다음 training set으로 학습을 시킨 다음 validation set으로 모의 시험을 한 후, 제대로 동작하면 test set으로 최종 평가를 한다
    • validation은 모의 평가시험이라 생각하면 된다


그림 10  학습 데이터 셋과 검증 데이터 셋, 테스트 데이터 셋의 차이


온라인 학습(online learning)

  • 학습 데이터가 너무나 많은 경우에 한번에 다 학습을 시키기 어려운 경우가 있다
  • 예를 들어, 그림 11과 같이 100만개의 데이터가 있는 경우 10만개 씩 데이터를 나누어 순차적으로 학습을 시킨다
  • 이 때 모델이 해야할 일은 첫번째 학습 결과가 남아 있어 모델이 두번째 학습을 할 때 첫번 째 학습에 반영이 되어야 한다
  • 이런 형태의 학습을 온라인 학습이라고 한다


그림 11  온라인 학습 방법



정확도(Accuracy)

  • 테스트 데이터 셋을 통해 모델의 정확도를 측정할 수 있다
  • 보통 95~99% 수준이 되어야 한다


배치 정규화

  • 데이터 셋을 사전에 정규화하는 것과는 달리 배치 정규화(batch normalization)은 각 미니 배치별로 학습 전에 정규화 처리를 하는 기법이다.
  • 데이터의 전처리로서 데이터 셋을 정규화하고 weight의 초기값을 정리함으로써 학습이 잘 진행되기는 하지만 학습시킬 대에는 신경망 모델 내부에서 분산이 편중되어 버리기 때문에 전처리를 한 효과가 한정적일 수 밖에 없다.
  • 배치 정규화는 학습에 사용하는 각 미니 배치별로 정규화를 하기 때문에 학습 과정 전체에서 효과가 나타난다.
  • 예를 들어 $m$개의 데이터로 구성된 미니 배치 $\mathbf{B}=\{x_1, x_2, \cdots, x_m\}$이 있다고 하면 미니 배치의 평균 $\mu_\mathbf{B}$과 분산 $\sigma^2_\mathbf{B}$은 각각 식 $(14.1)$과 식 $(14.2)$로 나타낼 수 있다.

\begin{align}\mu_\mathbf{B} &= \frac{1}{m} \sum_{i=1}^mx_i\tag{15.1}\\\\ \sigma^2_\mathbf{B} &= \frac{1}{m}\sum_{i=1}^m(x_i-\mu_\mathbf{B})^2\tag{15.2}\end{align}


  • 이와 달리 배치 정규화는 미니 배치에 포함된 각 데이터 $x_i$는 식 $(15.3)$와 식 $(15.4)$를 통해 변환된 $\{y_1,y_2,\cdots,y_m\}$을 배치 정규화된 출력 값으로 사용한다.
    • $\gamma$와 $\beta$가 이 모델의 매개변수이다.

\begin{align} \hat{x}_i &= \frac{x_i - \mu_\mathbf{B}}{\sqrt{\sigma^2_\mathbf{B} + \epsilon}}\tag{15.3}\\\\ y_i &= \gamma \hat{x}_i + \beta\tag{15.4}\end{align}


  • 배치 정규화를 사용한 딥러닝에서는 오차함수 $E$에 관해 해당 모델의 매개변수인 $\gamma$와 $\beta$가 이전 층에 전달할 $x_i$에 대한 경사를 각각 계산해야 하는데, 계산식은 다음과 같다.

\begin{align} \frac{\partial E}{\partial \gamma} &= \sum_{i=1}^m \frac{\partial E}{\partial y_i} \frac{\partial y_i}{\partial \gamma}\tag{15.5}\\&= \sum_{i=1}^m \frac{\partial E}{\partial y_i} \frac{\partial}{\partial \gamma}(\gamma \hat{x}_i + \beta)\tag{15.6}\\&= \sum_{i=1}^m \frac{\partial E}{\partial y_i} \hat{x}_i\tag{15.7}\\\\\frac{\partial E}{\partial \beta} &= \sum_{i=1}^m \frac{\partial E}{\partial y_i} \frac{\partial y_i}{\partial \beta}\tag{15.8}\\&= \sum_{i=1}^m \frac{\partial E}{\partial y_i} \frac{\partial}{\partial \beta}(\gamma \hat{x}_i + \beta)\tag{15.9}\\&= \sum_{i=1}^m \frac{\partial E}{\partial y_i} \tag{15.10}\\\\\frac{\partial E}{\partial x_i} &= \frac{\partial E}{\partial  \hat{x}_i} \frac{\partial \hat{x}_i}{\partial x_i} + \frac{\partial E}{\partial \mu_\mathbf{B}} \frac{\partial \mu_\mathbf{B}}{\partial x_i} + \frac{\partial E}{\partial \sigma^2_\mathbf{B}} \frac{\partial \sigma^2_\mathbf{B}}{\partial x_i} \tag{15.11}\\&= \frac{\partial E}{\partial  \hat{x}_i} \frac{\partial }{\partial x_i}\Bigg(\frac{x_i - \mu_\mathbf{B}}{\sqrt{\sigma^2_\mathbf{B} + \epsilon}}\Bigg) + \frac{\partial E}{\partial \mu^2_\mathbf{B}} \frac{\partial}{\partial x_i}\Bigg(\frac{1}{m} \sum_{i=1}^mx_i\Bigg) +\frac{\partial E}{\partial \sigma^2_\mathbf{B}} \frac{\partial}{\partial x_i} \Bigg(\frac{1}{m}\sum_{i=1}^m(x_i-\mu_\mathbf{B})^2\Bigg)\tag{15.12}\\&= \frac{\partial E}{\partial \hat{x}_i} \frac{1}{\sqrt{\sigma^2_\mathbf{B} + \epsilon}} + \frac{\partial E}{\partial \mu^2_\mathbf{B}} \frac{1}{m} +\frac{\partial E}{\partial \sigma^2_\mathbf{B}} \frac{2(x_i-\mu_\mathbf{B})}{m}\tag{15.13}\end{align}


  • 여기서 $\frac{\partial E}{\partial y_i}$는 역전파되어 온 오차로 우리가 알고 있는 값이다.  그러나 다른 경사값은 다음과 같이 구할 수 있기 때문에 모든 경사를 오차역전파법으로 최적화할 수 있다.

\begin{align} \frac{\partial E}{\partial \hat{x}_i} &= \frac{\partial E} {\partial y_i} \frac {\partial y_i} {\partial \hat{x}_i}\tag{15.14}\\&= \frac{\partial E} {\partial y_i} \frac {\partial } {\partial \hat{x}_i}\Bigg( \gamma \hat{x}_i + \beta\Bigg)\tag{15.15}\\&= \frac{\partial E} {\partial y_i} \gamma \tag{15.16}\\\\ \frac{\partial E}{\partial \sigma^2_\mathbf{B}} &= \sum_{i=1}^m \frac{\partial E} {\partial \hat{x}_i} \frac {\partial \hat{x}_i} {\partial \sigma^2_\mathbf{B}} \tag{15.17}\\&= \sum_{i=1}^m \frac{\partial E} {\partial \hat{x}_i} \frac {\partial } {\partial \sigma^2_\mathbf{B}} \Bigg( \frac{x_i - \mu_\mathbf{B}}{\sqrt{\sigma^2_\mathbf{B} + \epsilon}} \Bigg)\tag{15.18}\\&= \sum_{i=1}^m \frac{\partial E} {\partial \hat{x}_i} \frac {\partial } {\partial \sigma^2_\mathbf{B}} \Bigg( \bigg(x_i - \mu_\mathbf{B}\bigg)\bigg(\sigma^2_\mathbf{B} + \epsilon\bigg)^{-\frac{1}{2}}\tag{15.19}\\&= \sum_{i=1}^m \frac{\partial E} {\partial \hat{x}_i} \bigg(-\frac{1} {2}\bigg) \bigg(x_i - \mu_\mathbf{B}\bigg)\bigg(\sigma^2_\mathbf{B} + \epsilon\bigg)^{-\frac{1}{2}-1}\cdot \frac{\partial}{\partial \sigma^2_\mathbf{B}}\Big(\sigma^2_\mathbf{B}+\epsilon\Big)\tag{15.20}\\&= -\sum_{i=1}^m \frac{\partial E} {\partial \hat{x}_i}  \frac{x_i - \mu_\mathbf{B}} {2\sqrt{\big(\sigma^2_\mathbf{B} + \epsilon\big)^3 }}\tag{15.21}\\\\\end{align}


\begin{align}\frac{\partial E}{\partial \mu_\mathbf{B}}  &= \sum_{i=1}^m \frac{\partial E} {\partial \hat{x}_i} \frac {\partial \hat{x}_i} {\partial \mu_\mathbf{B}} + \frac {\partial E} {\partial \sigma^2_\mathbf{B} } \frac {\partial \sigma^2_\mathbf{B}} {\partial \mu_\mathbf{B}}\tag{15.22}\\&= \sum_{i=1}^m \frac{\partial E} {\partial \hat{x}_i} \frac {\partial } {\partial \mu_\mathbf{B}} \Bigg( \frac{x_i - \mu_\mathbf{B}}{\sqrt{\sigma^2_\mathbf{B} + \epsilon}} \Bigg)+ \frac {\partial E} {\partial \sigma^2_\mathbf{B} } \frac {\partial } {\partial \mu_\mathbf{B}} \Bigg( \frac{1}{m}\sum_{i=1}^m(x_i-\mu_\mathbf{B})^2 \Bigg) \tag{15.23}\\ &= \sum_{i=1}^m \frac{\partial E} {\partial \hat{x}_i} \Bigg( \frac{- 1}{\sqrt{\sigma^2_\mathbf{B} + \epsilon}} \Bigg)+ \frac {\partial E} {\partial \sigma^2_\mathbf{B} } \Bigg( \frac{2}{m}\sum_{i=1}^m(x_i-\mu_\mathbf{B})^{2-1} \Bigg) \frac{\partial } {\partial \mu_\mathbf{B}}\Big( x_i-\mu_\mathbf{B} \Big) \tag{15.24}\\&= \sum_{i=1}^m \frac{\partial E} {\partial \hat{x}_i} \Bigg( \frac{- 1}{\sqrt{\sigma^2_\mathbf{B} + \epsilon}} \Bigg)+ \frac {\partial E} {\partial \sigma^2_\mathbf{B} } \Bigg( \frac{2}{m}\sum_{i=1}^m(x_i-\mu_\mathbf{B})^{2-1} \Bigg) (-1) \tag{15.25}\\&= \sum_{i=1}^m \frac{\partial E} {\partial \hat{x}_i} \Bigg( \frac{- 1}{\sqrt{\sigma^2_\mathbf{B} + \epsilon}} \Bigg) - \frac {\partial E} {\partial \sigma^2_\mathbf{B} } \Bigg( \frac{2}{m}\sum_{i=1}^m(x_i-\mu_\mathbf{B}) \Bigg) \tag{15.26}\end{align}


  • 배치 정규화는 미니 배치에 포함된 데이터를 정규화하기 때문에 지금까지는 층의 활성화 함수를 식 $(12.127)$과 같이 표현했다.

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


  • 미니 배치에 해당하는 식 $(15.4)$의 처리과정을 $BN_{\gamma,\beta}(\mathbb{x}_i)$라고 한다면 층의 활성화를 나타내는 식 $(15.27)$은 다음과 같이 쓸 수 있다.

\begin{align} \mathbb{h} = f(BN_{\gamma,\beta}(W\mathbb{x}_i) )\tag{15.28}\end{align}


  • 식 $(15.28)$에서 바이어스는 신경쓰지 않아도 된다는데 왜 그렇지?
  • 위 논문에서 주장하는 미니 배치의 장점으로는 다음과 같다.
    • 학습률을 크게 설정해도 학습이 잘 진행됐다.
    • 드롭아웃을 사용하지 않아도 일반화 성능이 높다.


  • TensorFlow에서는 tf.nn.batch_normalization()를 사용하면 되지만 해당 API를 사용하지 않고 코드로 구현하면 다음과 같다.
    • 식 $(15.3)$과 식 $(15.4)$를 구현한 것이 batch_normalization()이다.
    • tf.nn.moments() 함수는 평균과 분산을 계산해 반환한다.
    • '은닉층-출력층'은 지금까지 해왔던 것처럼 softmax() 함수를 사용한다.
def inference(x, keep_prob, n_in, n_hiddens, n_out):
    # 미니 배치
    def weight_variable(shape):
        initial = np.sqrt(2.0 / shape[0]) * tf.truncated_normal(shape)
        return tf.Variable(initial)

    def bias_variable(shape):
        initial = tf.zeros(shape)
        return tf.Variable(initial)

    def batch_normalization(shape, x):
        # 배치 정규화 처리
        eps = 1e-8
        beta = tf.Variable(tf.zeros(shape))
        gamma = tf.Variable(tf.ones(shape))
        mean, var = tf.nn.moments(x, [0])
        return gamma * (x - mean) / tf.sqrt(var + eps) + beta

    # 미니 배치  입력층-은닉층, 은닉층-은닉층
    for i, n_hidden in enumerate(n_hiddens):
        if i == 0:
            input = x
            input_dim = n_in

        else:
            input = output
            input_dim = n_hiddens[i-1]

        W = weight_variable([input_dim, n_hidden])
        u = tf.matmul(input, W)
        h = batch_normalization([n_hidden], u)
        output = tf.nn.relu(h)

    # 은닉층-출력층
    W_out = weight_variable([n_hiddens[-1], n_out])
    b_out = bias_variable([n_out])
    y = tf.nn.softmax(tf.matmul(output, W_out) + b_out)

    return y

그림 15.1 TensorFlow에서 미니 배치 정규화 알고리즘에서 Adam Optimizer를 사용했을 때의 정확도와 오차 변화


  • Keras에서는 from keras.layers.normalization import BatchNormalization을 선언하고 학습과정에서 BatchNormalization()을 사용하면 된다.
model = Sequential()

for i, input_dim in enumerate(([n_in] + n_hiddens)[:-1]):
    model.add(Dense(input_dim=input_dim, units=n_hiddens[i], init=weight_variable))
    model.add(BatchNormalization())
    model.add(LeakyReLU(alpha=alpha))

model.add(Dense(units=n_out, kernel_initializer=weight_variable))
model.add(Activation('softmax'))

그림 15.2 Keras에서 미니 배치 정규화 알고리즘에서 Adam Optimizer를 사용했을 때의 정확도와 오차 변화


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

학습 조기 종료(Early Stopping)

  • 학습 횟수(=epoch 수)가 많을수록 학습 데이터에 관한 오차는 작아지지만 이것이 오버피팅을 초래해서 모델의 일반화 성능이 떨어지게 된다.
  • 실제 epoch = 300으로 설정하고 학습시킨 결과 처음에는 오차가 순조롭게 줄어들다가 어느 시점부터 오차가 커져가는 것(즉, 오버피팅이 되는 것)을 알 수 있다.

그림 14.1  epoch=300을 했을 때 정확도와 오차의 변화

 

  • 이 문제를 해결하기 위해 사용하는 기법이 조기 종료(early stopping)로 '이전 epoch 때와 비교해서 오차가 증가했다면 학습을 중단한다'라는 방법이다.
  • 개념을 설명하자면 각 epoch의 마지막 부분에 조기 종료를 시킬 것인지 판단하는 부분을 추가하는 것으로 유사 코드는 다음과 같다.
for epoch in range(epochs):     loss = model.train()['loss']      if early_stopping(loss):         break

 

  • 조기 종료를 구현할 때, 주의할 사항은 바로 직전의 epoch 오차를 비교하기만 해서는 안된다. 그림 14.1의 그래프가 보여주는 것처럼 오차값은 각 epoch마다 올라가기도 하고 내려가기도 하며, 드롭아웃을 적용한 경우에는 아직 학습이 되지 않은 뉴런이 존재할 가능성이 있기 때문에 오차값이 상하로 움직이게 된다.
  • 따라서 '어떤 일정한 epoch 수를 거듭하면서 계속해서 오차가 증가하면 학습을 중단한다'는 방식으로 구현해야 한다.
  • TensorFlow에서는 tf.contrib.learntf.contrib.learn.monitors.ValidationMonitor()를 사용하면 되지만 다음과 같은 코드를 사용하면 조기 종료 기능을 구현할 수 있다.
class EarlyStopping():     def __init__(self, patience=0, verbose=0):         self._step = 0         self._loss = float('inf')         self.patience  = patience         self.verbose = verbose      def validate(self, loss):         if self._loss < loss:             self._step += 1             if self._step > self.patience:                 if self.verbose:                     print(f'Training process is stopped early....')                 return True         else:             self._step = 0             self._loss = loss          return False

 

  • 위 코드에서 patience는 '오차를 보기 위해 과거 몇 epoch까지 거슬러 올라갈 것인가'를 설정하는 값이다.
early_stopping = EarlyStopping(patience=10, verbose=1)

 

  • 학습을 시키기 전에 EarlyStopping의 인스턴스를 위와 같은 방법으로 생성하고 아래와 같이 반복 학습 과정의 마지막에 early_stopping.validate()을 추가하면 조기 학습 기능을 구현할 수 있게 된다.
if __name__ == '__main__':          early_stopping = EarlyStopping(patience=10, verbose=1)      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, feed_dict={x: X_[start: end], y: Y_[start: end], keep_prob: p_keep})          # 검증 데이터를 사용해 정확도 측정         val_loss = loss.eval(session=sess, feed_dict={x: X_val,                                                       y: Y_val,                                                       keep_prob: 1.0})          val_accuracy = accuracy.eval(session=sess, feed_dict={x: X_val,                                                               y: Y_val,                                                               keep_prob: 1.0})          # 검증 데이터에 대한 학습 진행 상황을 저장         history['val_loss'].append(val_loss)         history['val_acc'].append(val_accuracy * 100)          if early_stopping.validate(val_loss):             break

그림 14.1 TensorFlow에서 조기 종료 기능을 사용했을 때 정확도와 오차 변화

 

  • Keras에서는 from keras.callbacks import EarlyStopping을 선언하면 조기 종료 기능을 사용할 수 있다.
    • keras.callbacks는 각 epoch마다 모델을 학습시킨 후에 호출하는 콜백 함수이다
    • 모델을 학습시키기 전에 다음과 같이 선언하고
early_stopping = EarlyStopping(monitor='val_loss', patient=10, verbose=1)

 

  • 아래와 같이 학습 모델 model.fit()callbacks=early_stopping을 사용하면 된다.
hist = model.fit(X_train, Y_train,                  epochs=epochs,                  batch_size=batch_size,                  validation_data=(X_val, Y_val),                  callbacks=[early_stopping])

그림 14.2 Keras에서 조기 종료 기능을 사용했을 때 정확도와 오차 변화

 

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

 

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

15. 배치 정규화  (0) 2018.02.01
13. 학습률 설정  (0) 2018.01.29
12. 데이터 정규화 및 매개변수 초기화  (0) 2018.01.23
11. 학습 과정 시각화  (3) 2018.01.19
10. 신경망 모델 구현 방법  (0) 2018.01.19

학습률을 설정하는 방법

  • 신경망 모델을 학습시킬 때 일반적으로 경사하강법(GDA)을 사용하는데, 이 때 학습률은 \(0.1\)이나 \(0.01\)과 같은 값을 사용하였다.
  • 학습률의 값을 어떻게 설정하는지에 따라 최적해가 구해지는지 또는 구해지지 않는지가 정해진다고 하면 이 학습률도 적절히 설정하는 방법들이 개발되어 있다.


모멘텀(Momentum)

  • 국소최적해에 빠지지 않고 효율적으로 해를 구하려면 학습률을 '처음에는 크게, 그리고 점점 작게'하는 것이 바람직하다고 알려져 있다.
  • 모멘텀은 학습률의 값 자체는 같지만 매개변수를 변경해갈 때 모멘텀 항이라는 조정항을 사용해 유사적으로 '처음에는 크게 그리고 점점 작게'라는 개념을 표현한다.
  • 오차함수 \(E\)에 대한 신경망 모델의 매개변수를 \(\theta\)라 하고 \(\theta\)에 대한 \(E\)의 경사를 \(\nabla_\theta E\), 매개변수의 차 \(\Delta\theta^{(t)}\)를 식 \((13.1)\)라고 하면 스텝 \(t\)에서 모멘텀을 사용해  매개변수를 변경해 가는 식은 식 \((13.2)\)와 같다.
    • \(\gamma\Delta\theta^{(t-1)}\) : 모멘텀항
    • 계수 \(\gamma(<1)\)는 일반적으로 \(0.5\)나 \(0.9\)와 같은 값을 설정

\begin{align}\Delta\theta^{(t)} &= \theta^{(t-1)} - \theta^{(t)} \tag{13.1}\\&=-\eta\nabla_\theta E(\theta) + \gamma \Delta\theta^{(t-1)} \tag{13.2}\\\\ \Delta\theta^{(t)} - \gamma \Delta\theta^{(t-1)}&= -\eta\nabla_\theta E(\theta)\tag{13.3}\\\\\frac{1}{\eta}\Delta\theta^{(t)} - \frac{\gamma}{\eta} \Delta\theta^{(t-1)}&= -\nabla_\theta E(\theta)\tag{13.4}\end{align}

  • 식 \((13.2)\)는 물리학에 비유하면 식 \((13.5)\)와 같은 소위 '공기 저항식'과 같은 모양으로 단계가 높아질 수록 경사가 점점 낮아진다는 것을 알 수 있다.

\begin{align} m\frac{d^2\theta}{dt^2} + \mu\frac{d\theta}{dt} = -\Delta_\theta E(\theta)\tag{13.5}\end{align}


  • TensorFlow에서는 tf.train.momentumOptimizer()를 사용하면 모멘텀을 구현할 수 있다.
def train(loss):
    optimizer = tf.train.MomentumOptimizer(learning_rate=0.01, momentum=0.9)
    train = optimizer.minimize(loss)
    return train

그림 13.1 TensorFlow에서 Momentum Optimizer를 사용했을 때의 정확도와 오차 변화


  • Keras에서는 SGD()의  매개변수로 momentum=을 지정하면 된다.
model.compile(loss='categorical_crossentropy', 
              optimizer=SGD(lr=0.01, momentum=0.9), 
              metrics=['accuracy'])

그림 13.2 Keras에서 Momentum Optimizer를 사용했을 때의 정확도와 오차 변화


Nesterov Momentum

  • Yurii Nesterov, A method for unconstrained convex minimization problem with the rate of convergence \(O(1/k^2)\) . Doklady AN SSSR , 269(3):543–547, 1983
    • 해당 논문의 원문을 찾을 수가 없어 링크를 걸지 못함
  • Nesterov는 식 \((13.2)\)로 표현되는 순수한 모멘텀을 약간 변형해서 매개변수가 '어느 방향을 향해야 하는지'를 식으로 만든 것으로 식 \((13.2)\)에서 \(\Delta\theta^{(t)}=v^{(t)}\)라 놓으면 식 \((13.2)\)는 식 \((13.5)\)와 식 \((13.6)\)으로 표현할 수 있다.
\begin{align} v^{(t)} &= -\eta \nabla _\theta E(\theta) + \gamma \Delta \theta ^{(t-1)} \tag{13.5}\\\\ \theta^{(t)} &= \theta^{(t-1)} - v^{(t)}\tag{13.6}\end{align}
  • \((13.5)\)에 식 \((13.6)\)을 적용하여 모멘텀을 다시 표현하면 식 \((13.7)\)과 식 \((13.8)\)로 나타낼 수 있다.

\begin{align} v^{(t)} &= -\eta \nabla _\theta E(\theta + \gamma v^{(t-1)}) + \gamma \Delta \theta ^{(t-1)} \tag{13.7}\\ &= -\eta \nabla_\theta E(\theta + \gamma \Delta\theta^{(t-1)})+ \gamma \Delta \theta ^{(t-1)}\tag{13.8}\\\\ \theta^{(t)} &= \theta^{(t-1)} - v^{(t)}\tag{13.9}\end{align}

  • \((13.5)\)와 식 \((13.7)\)의 차이는 \(E(\theta + \gamma v^{(t-1)})\)에 있는데, 이 차이로 인해 다음 단계에서 매개변수의 근사값이 구해지므로 학습률을 효율적으로 설정해 해를 구할 수 있다.


  • TensorFlow에서는 tf.train.momentumOptimizer()의 매개변수로  nesterov=True를 지정하면 된다.
def train(loss):
    optimizer = tf.train.MomentumOptimizer(learning_rate=0.01, momentum=0.9, use_nesterov=True)
    train = optimizer.minimize(loss)
    return train

그림 13.3 TensorFlow에서 Nestrov Momentum Optimizer를 사용했을 때의 정확도와 오차 변화


  • Keras에서는 SGD()의 매개변수로 nesterov=True를 지정하면 된다.
model.compile(loss='categorical_crossentropy', 
              optimizer=SGD(lr=0.01, momentum=0.9, nesterov=True), 
              metrics=['accuracy'])

그림 13.4 Keras에서 Nestrov Momentum Optimizer를 사용했을 때의 정확도와 오차 변화


Adaptive Gradient(Adagrad) 알고리즘

  • 모멘텀은 학습률 값을 고정하고 모멘텀 항으로 매개변수의 변경값을 조정했지만 Adagrad 방법은 학습률 값 자체를 변경한다.
  • 다음과 같이 정의하고 식을 전개해보자.

\begin{align} g_i = \nabla_\theta E(\theta_i)\tag{13.10}\end{align}

  • Adagrad 방법은 식 \((13.11)\)처럼 표현된다.

\begin{align} \theta_i^{(t+1)} = \theta_i^{(t)} - \frac{\eta}{\sqrt{G_{ii}^{(t)} +\epsilon}}\odot g^{(t)}\tag{13.11}\end{align}

  • 행렬 \(G^{(t)}\)는 대각행렬이며, \((i,i)\) 성분은 \(t\)번째 단계까지 \(\theta_i\)에 관한 경사를 제곱해서 총합을 낸 것으로 식 \((13.12)\)로 표현할 수 있다.

\begin{align} G_{ii}^{(t)} = \sum_{\tau=0}^t g_i^{(\tau)} \cdot g_i^{(\tau)}\tag{13.12}\end{align}

  • \(\epsilon\)은 분모가 \(0\)이 되지 않도록 하기 위한 작은 값으로 일반적으로 \(\epsilon=1.0\times 10^{-6}\sim1.0\times 10^{-8}\)정도로 설정한다.
  • 식 \((13.11)\)은 다음과 같이 정리할 수 있다.

\begin{align} \theta^{(t+1)} = \theta^{(t)} - \frac{\eta}{\sqrt{G^{(t)} + \epsilon}}\odot G^{(t)}\tag{13.13}\end{align}

  • Adagrad 알고리즘은 모멘텀과 비교하면 하이퍼  매개변수가 적고 이제까지의 경사를 바탕으로 해서 자동으로 학습률 \(\eta\)를 수정하므로 더욱 다루기 쉬운 기법이라고 할 수 있다.
  • 식 \((13.11)\)이나 식 \((13.13)\)의 유사 코드는 다음과 같지만 TensorFlow와 Keras에서 Adagrad 알고리즘 관련 API를 제공하므로 라이브러리를 사용하면 된다.
G[i][i] += g[i] * g[i]
theta[i] -= (learning_rate / sqrt(G[i][i] + epsilon)) * g[i]


  • TensorFlow에서는 tf.train.AdagradOptimizer()를 사용하면 된다.
def train(loss):
    optimizer = tf.train.AdagradOptimizer(learning_rate=0.01)
    train = optimizer.minimize(loss)
    return train

그림 13.5 TensorFlow에서 Adagrad Optimizer를 사용했을 때의 정확도와 오차 변화


  • Keras에서는 from keras.optimizers import Adagrad를 선언하고 optimizer=Adagrad()를 사용하면 된다.
model.compile(loss='categorical_crossentropy', 
              optimizer=SGD(lr=0.01, momentum=0.9, nesterov=True), 
              metrics=['accuracy'])

그림 13.6 Keras에서 Adagrad Optimizer를 사용했을 때의 정확도와 오차 변화


Adadelta 알고리즘

  • Adagrad 알고리즘을 통해 학습률을 자동으로 조정할 수 있게 되었지만 대각행렬 \(G^{(t)}\)는 경사의 제곱의 누적합이기 때문에 단조증가한다.
  • 따라서 식 \((13.13)\)에서도 알 수 있듯이 학습의 단계가 진행될 수록 곱해지는 계수의 값이 급격하게 작아져버려 학습을 진행할 수 없게되는 문제가 발생하는데 이 문제를 해결한 것이 Adadelta 알고리즘이다.
  • Adadelta의 기본적인 개념은 \(0\)번째 단계부터 제곱합을 누적해가는 것이 아니라 누적해가는 단계 수를 정수 \(w\)로 제한하는 것이다.
  • 단순히 \(w\)만큼 제곱합을 동시에 유지하는 것은 구현의 관점에서 보면 비효율적인 방법이므로 Adadelta는 직전의 단계까지의 모든 경사의 제곱합을 감쇠평균시켜서 재귀식으로 계산한다.
  • 식을 간단하게 정리하기 위해 이제까지 \(g^{(t)}\)라고 한 것을 \(g_t\)라고 쓰면 \(t\)단계에서의 경사의 제곱(\(g_t\odot g_t\))의 이동평균 \(E[g^2]_t\)는 다음과 같이 쓸 수 있다.

\begin{align} E[g^2]_t = \rho E[g^2]_{t-1} + (1-\rho)g^2_t\tag{13.14}\end{align}

  • 식 \((13.14)\)를 보면 단계가 거듭될 수록 경사의 합이 지수함수적으로 작아져간다는 것을 알 수 있다.
  • 그런데 Adagrad를 나타내는 식은 식 \((13.15)\)인데

\begin{align} \theta_{t+1} = \theta_{t} - \frac{\eta}{\sqrt{G^{t} + \epsilon}}\odot G_t\tag{13.15}\end{align}

  • Adadelta는 \(G_t\)를 이제까지의 경사를 제곱한 것의 감쇠평균인 \(E[g^2]_t\)로 치환되므로 식 \((13.16)\)과 같이 나타낼 수 있다.

\begin{align} \theta^{(t+1)} = \theta^{(t)} - \frac{\eta}{\sqrt{E[g^2]_t} + \epsilon}g^t\tag{13.16}\end{align}

  • 이 때 \(\sqrt{E[g^2]_t + \epsilon}\)에 주목하면 (\(\epsilon\)을 무시하면) 제곱평균제곱근(root mean square) 형태이기 때문에 \(\text{RMS}[\cdot]\)로 나타내면 식 \((13.16)\)은 식 \((13.17)\)과 같이 정리된다.

\begin{align} \theta^{(t+1)} = \theta^{(t)} - \frac{\eta}{\textrm{RMS}[g]_t}g^t\tag{13.17}\end{align}

  • 해당 논문에서는 식 \((13.17)\)을 한 단계 더 변형해서 학습률 \(\eta\)를 설정하지 않아도 되게 한다. 다음과 같이 정의하면

\begin{align} \Delta\theta_t = \theta^{(t)} - \frac{\eta}{\textrm{RMS}[g]_t}g^t\tag{13.17}\end{align}

  • 식 \((13.14)\)에 대해 \(\nabla \theta^2_t\)의 감쇠평균은 식 \((13.18)\)과 같이 나타낼 수 있으며

\begin{align} E[\Delta \theta^2]_t = \rho E[\Delta \theta^2]_{t-1} + (1-\rho)\Delta \theta^2_t\tag{13.18}\end{align}

  • \(\Delta\theta_t\)는 알 수 없으므로

\begin{align} \text{RMS}[\Delta\theta]_t=\sqrt{E[\Delta\theta^2]_t+\epsilon}\tag{13.19}\end{align}

  • 식 \((13.19)\)를 \(t-1\)에서의 \(\text{RMS}\)로 근사하면 식 \((13.20)\)으로 표현되는 Adadelta 식이 구해진다.

\begin{align} \Delta\theta_t = - \frac{\text{RMS}[\Delta\theta]_{t-1}}{\text{RMS}[g]_t}g_t\tag{13.20}\end{align}

  • 결국 Adadelta에서는 \(\rho\)(또는 \(epsilon\))만 설정하면 된다는 것이며 이것은 일반적으로 \(\rho=0.95\)로 설정한다.


  • TensorFlow에서는 tf.train.AdadeltaOptimizer()를 사용하면 된다.
    • 수식에서는 식 \((13.18)\)처럼 학습률이 필요없지만 구현할 때에는 learning_rate=1.0이라고 해야 한다. 이는 \(\theta_{t+1}=\theta_t+\alpha\Delta\theta_t\)의 \(\alpha\)를 설정하기 위한 것이다.
    • TensorFlow에서는 기본적으로 이 값을 \(0.001\)로 정하는데 앞서 나온 식의 흐름을 따라가보면 이 값을 \(1.0\)으로 정해도 문제없으므로 learning_rate=1.0을 매개변수로 넘겨준 것이다.
def train(loss):
    optimizer = tf.train.AdadeltaOptimizer(learning_rate=1.0, rho=0.95)
    train = optimizer.minimize(loss)
    return train

그림 13.7 TensorFlow에서 Adadelta Optimizer를 사용했을 때의 정확도와 오차 변화


  • Keras에서는 from keras.optimizers import Adadelta를 선언하고 optimizer=Adadelta(rho=0.95)를 사용하면 된다.
model.compile(loss='categorical_crossentropy', 
              optimizer=Adadelta(rho=0.95), 
              metrics=['accuracy'])

그림 13.8 Keras에서 Adadelta Optimizer를 사용했을 때의 정확도와 오차 변화


RMSprop 알고리즘

  • RMSprop 알고리즘은 Adadelta와 마찬가지로 Adagrad에서 학습률이 급격하게 감소하는 문제를 해결하기 위한 기법이다.
  • RMSprop 알고리즘은 Adadelta와 비슷한 시기에 고안된 기법이지만 논문의 형태로 존재하지 않고 Coursera 온라인 강의에 사용되는 강의 자료로 정리되어 있는 기법이다.
  • RMSprop은 Adadelta의 간이 버전이라고 할 수 있으며 식 \((13.14)\)에서 \(\rho=0.9\)로 설정해서

\begin{align} E[g^2]_t &= \rho E[g^2]_{t-1} + (1-\rho)g^2_t\tag{13.21}\\ &=0.9 E[g^2]_{t-1} + (1-0.9)g^2_t\tag{13.22}\\ &=0.9 E[g^2]_{t-1} + 0.1g^2_t\tag{13.23}\end{align}

  • 식을 위와 같이 두고

\begin{align} \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{E[g^2]_t+\epsilon}}g^t\tag{13.24}\end{align}

  • 식 \((13.24)\)로 매개변수를 변경해가는 기법이다. 학습률 \(\eta\)는 일반적으로 \(0.001\)과 같은 작은 값으로 설정한다.


  • TensorFlow에서는 tf.train.RMSpropOptimizer()를 사용하면 된다.
def train(loss):
    optimizer = tf.train.RMSpopOptimizer(learning_rate=0.01)
    train = optimizer.minimize(loss)
    return train

그림 13.9 TensorFlow에서 RMSprop Optimizer를 사용했을 때의 정확도와 오차 변화


  • Keras에서는 from keras.optimizers import RMSprop을 선언하고 optimizer=RMSprop(lr=0.001)를 사용하면 된다.
model.compile(loss='categorical_crossentropy', 
              optimizer=RMSprop(lr=0.001), 
              metrics=['accuracy'])

그림 13.10 Keras에서 RMSprop Optimizer를 사용했을 때의 정확도와 오차 변화


Adam 알고리즘

  • Adadelta와 RMSprop이 직전의 단계인 \(t-1\)까지 경사의 제곱의 이동평균 \(v_t=E[g^2]_t\)를 지수함수적으로 감쇠평균한 항을 저장해가며 유지하고 매개변수의 변경식에 이 값을 사용했던 것과는 달리 Adam(Adaptive moment estimation)에서는 여기에 추가로 단순한 경사의 이동평균인 \(m_t=E[g]_t\)를 지수함수적으로 감쇠시킨 항도 사용한다.
  • \(m_t\)와 \(v_t\)는 각각 식 \((13.25)\)와 식 \((13.26)\)으로 나타낼 수 있다.
    • \(\beta_1,\beta_2\in [0,1]\)은 하이퍼 매개변수이며 이동평균의 (지수함수적인) 감쇠율을 조정한다.
    • \(m_t\)와 \(v_t\)는 각각 경사의 1차 모멘트 (평균), 2차 모멘트 (분산)의 추정값에 해당한다.

\begin{align} m_t &= \beta_1m_{t-1} + (1-\beta_1)g_t\tag{13.25}\\\\ v_t &= \beta_2v_{t-1} + (1-\beta_2)g^2_t\tag{13.26}\end{align}

  • 두 개의 이동평균 \(m_t\)와 \(v_t\)는 모두 편중된 모멘트이므로 이 편중은 보정한(편중을 \(0\)으로 만든) 추정값을 구하는 것을 생각해보자.
  • 이 때, \(v_0=\mathbf{0}\)으로 초기화했다고 가정하면 식 \((13.26)\)에 의해 식 \((13.27)\)을 얻을 수 있다.

\begin{align} v_t=(1-\beta_2)\sum_{i=1}^t\beta_2^{t-i}\cdot g_i^2\tag{13.27}\end{align}

  • 이제 우리가 알고싶은 것은 2차 모멘트 \(v_t\)의 이동평균 \(E[v_t]\)와 2차 모멘트 \(E[g_t^2]\)의 관련성이므로 이것을 식 \((13.27)\)을 통해 구하면 다음과 같은 식을 얻을 수 있다.

\begin{align} E[v_t] &= E\Bigg[(1-\beta_2)\sum_{i=1}^t\beta_2^{t-i}\cdot g_i^2\Bigg]\tag{13.28}\\&= E[g_i^2]\cdot(1-\beta_2)\sum_{i=1}^t\beta_2^{t-i}+\zeta\tag{13.29}\\&= E[g_t^2]\cdot(1-\beta_2^t) + \zeta\tag{13.30}\end{align}

  • 이 때 \(\zeta=0\)으로 근사할 수 있도록 하이퍼 매개변수의 값을 설정하면 식 \((13.31)\)을 얻을 수 있다.
    • 2차 모멘트 \(E[g_t^2]\)가 불변이면 \(\zeta=0\)이고, 그렇지 않으면 각 감쇠율 \(1-\beta_1\)과 \(1-\beta_2\)를 축소시켜 \(zeta=0\)으로 근사시킬 수 있다.
    • 따라서 일반적으로 \(\beta_1=0.9\), \(\beta_2=0.999\)로 설정한다.

\begin{align} \hat{v}_t = \frac{v_t}{1-\beta_2^t}\tag{13.31}\end{align}

  • 식 \((13.31)\)과 같이 (편중되지 않은) 추정값을 얻을 수 있으며, \(m_t\)도 같은 방식으로 계산하면 식 \((13.32)\)을 얻을 수 있다.

\begin{align} \hat{m}_t = \frac{m_t}{1-\beta_1^t}\tag{13.32}\end{align}

  • 따라서 매개변수 변경식은 다음과 같이 식 \((13.33)\)으로 정리된다.

\begin{align}\theta_t = \theta_{t-1} - \frac{\alpha}{\sqrt{\hat{v}_t}+\epsilon}\hat{m}_t\tag{13.33}\end{align}

  • Adam도 식은 복잡하지만 최종적인 알고리즘은 단순하게 정리되는데 다음과 같은 유사코드를 보며 생각해보자.
# 초기화
m = 0
v = 0

# 반복
m = beta1 * m + (1 - beta1) * g
v = beta2 * v + (1 - beta2) * g * g
m_hat = m / (1 - beta1 ** t)
v_hat = v / (1 - beta2 ** t)
theta -= learning_rate * m_hat / (sqrt(v_hat) + epsilon)


  • 구체적은 계산은 생략하겠지만 학습률 \(\alpha\)는 식 \((13.34)\)와 같다.

\begin{align}\alpha_t = \alpha \cdot \frac{\sqrt{1-\beta_2^t}}{1-\beta_1^t} \tag{13.34}\end{align}

  • 식 \((13.34)\)를 만족시키는 \(\alpha\)를 사용하면 더욱 효율적으로 탐색할 수 있다. 이 경우의 유사코드는 다음과 같다.
# 초기화
m = 0
v = 0

# 반복
learning_rate_t = learning_rate * sqrt(1 - beta2 ** t) / (1 - beta1 ** t)
m = beta1 * m + (1 - beta1) * g
v = beta2 * v + (1 - beta2) * g * g
theta -= learning_rate * m / (sqrt(v) + epsilon)


  • TensorFlow에서는 tf.train.AdamOptimizer()를 사용하면 된다.
def train(loss):
    optimizer = tf.train.AdamOptimizer(learning_rate=0.001,
                                       beta1=0.9,
                                       beta2=0.999)
    train = optimizer.minimize(loss)
    return train

그림 13.11 TensorFlow에서 Adam Optimizer를 사용했을 때의 정확도와 오차 변화


  • Keras에서는 from keras.optimizers import Adam을 선언하고 optimizer=Adam(lr=0.001, beta_1=0.9, beta_2=0.999)를 사용하면 된다.
model.compile(loss='categorical_crossentropy', 
              optimizer=Adam(lr=0.001, beta_1=0.9, beta_2=0.999), 
              metrics=['accuracy'])

그림 13.12 Keras에서 Adam Optimizer를 사용했을 때의 정확도와 오차 변화



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

데이터 정규화 및 웨이트 초기화

  • weight의 초깃값이 신경망 모델의 학습에 영향을 미치는데, 초깃값을 어떻게 설정해야 좋을까?
  • weight에 관해 생각해보기 위해 입력 데이터를 '깔끔하게' 정리를 해보자.
  • 다양한 입력 데이터가 일정한 범위에 들어가도록 전처리를 해보자. 가장 좋은 방법은 이 범위를 \(0~1\) 사이의 값을 갖도록 하는 것이다.
  • 예를 들어 MNIST 데이터의 경우 데이터가 \(0\)에서 \(255\)까지의 gray scale 값을 갖기 때문에 다음과 같이 코드를 작성하면 입력 데이터의 값이 모두 \(0\)부터 \(1\) 사이의 값을 갖게 된다.
X = X / 255


  • 위의 코드를 다른 모든 데이터에도 적용이 가능하도록 일반화하면 다음과 같다.
X = X / X.max()


  • 위의 과정처럼 데이터가 일정한 범위 안에 들어가게 해서 그 다음 처리를 원할하게 이루어지도록 하는 것을 정규화(normalization)이라고 한다.
  • 데이터의 분포를 생각하면 데이터의 평균이 \(0\)이 되도록 정규화하는 것이 좋다. 이 과정을 코드로 바꾸면 다음과 같다.
X = X / X.max()
X = X - X.mean(axis=1).reshape(len(X), 1)


  • 데이터를 정규화했을 때 데이터의 분포가 한쪽으로 치우지지 않는다면 weight의 성분은 양수값과 음수값을 모두 갖게 될 것이다. 이 때 weight의 값이 한쪽으로 치우지지 않는다면 양수와 음수가 거의 반반으로 비슷하게 분포할 것이다. 따라서 weight의 성분을 모두 $0$으로 초기화하는 것을 가장 먼저 생각해볼 수 있다.
    • weight의 성분을 모두 같도록 초기화하면 오차역전파식을 사용했을 때 경사값도 같아져버리므로 weight 값이 제대로 갱신되지 못하기 때문에 \(0\)에 가까운 난수로 초기화를 해야한다.
    • 지금까지 weight의 값을 정규분포를 사용해 초기화한 이유는 평균이 \(\mu=0\)에 가까운 난수를 얻기 위함이다. 또한 표준편차 \(\sigma\)가 작을수록 weight의 성분들은 \(0\)에 가까워져 평균도 \(0\)에 가깝기 때문에 좋다. 따라서 이를 위하여 np.random.normal(scale=0.01, size=shape) 함수를 사용하는 것이다.
  • 단순히 표준편차 \(\sigma\)를 작게 한다고 좋아지는 것은 아니다. 초깃값이 너무 작으면 weight가 계수로 곱해지는 경사값도 너무 작아져 학습이 제대로 진행되지 않는 문제가 발생한다.
    • ReLU는 \(\mathbb{x}=0\) 부근에서도 경사가 소실되지 않는 성질을 지난 활성화 함수이기 때문에 표준편차를 \(\sigma=0.01\)과 같이 작은 값으로 잡아야 학습이 잘 진행되는 경향이 있다.
  • 문제 해결을 위해 표준편차가 \(\sigma=1.0\)인 표준정규분포를 전제로 여기에 적절한 계수를 곱해서 좋은 초깃값을 생성하는 방법을 사용하는데, a*np.random.normal(scale=0.01, size=shape)에서 a에 어떤 값을 줘야 하는 것인가이다.
    • 주의할 점은 입력 데이터의 차원 수가 높을 수록 (표준편차의 값이 \(1.0\)이기 때문에) 생성되는 weight의 성분 값들이 서로 흩어지기 쉬워진다는 것이다. 따라서 weight를 초기화할 때 이 흩어짐을 억제할 수 있는 방법이 필요하다.


웨이트 성분값들의 흩어짐을 막기 위한 방법

  • 입력 데이터 \(\mathbb{x}\)가 \(n\)차원이고, weight가 \(W\)일 때, 활성화되기 전의 값을 \(\mathbb{p}\)라고 하면 \(\mathbb{p}\)의 각 성분 \(p_j\)는 식 \((12.1)\)과 같이 나타낼 수 있다.

\begin{align}p_j = \sum_{i=1}^nw_{j_i}x_i \tag{12.1}\end{align}

  • 이 때 \(E[\cdot]\)가 기대값(평균)이라 하고, \(Var[\cdot]\)를 분산이라고 하면 식 \((12.2)\)가 성립한다.

\begin{align} Var[X]=E[X^2]-(E[X])^2 \tag{12.2} \end{align}

  • \(p_j\)의 분산은 다음과 같다.

\begin{align}Var\left[p_j\right] &= Var\left[\sum_{i=1}^nw_{j_i}x_i\right]\tag{12.3}\\&= \sum_{i=1}^n Var\left[w_{j_i}x_i\right]\tag{12.4}\\&= \sum_{i=1}^n \left\{E \left[ \left(w_{j_i}x_i\right)^2 \right] - \left( E \left[ w_{j_i}x_i \right] \right)^2 \right\}\quad(\because\,\,(12.2))\tag{12.5}\\&= \sum_{i=1}^n \left\{E \left[ w_{j_i}^2x_i^2 \right] - \left( E \left[ w_{j_i} \right] \cdot E \left[x_i \right]\right)^2 \right\}\tag{12.6}\\&= \sum_{i=1}^n \left\{E \left[ w_{j_i}^2\right] \cdot E \left[ x_i^2 \right] - \left( E \left[ w_{j_i} \right] \right)^2 \cdot \left(E \left[x_i \right]\right)^2 \right\}\tag{12.7}\end{align}

  • 이제 \(E [ w_{j_i}^2]\)와 \(E [ x_i^2 ]\)를 따로 계산하면 다음과 같다.

\begin{align}E \Big[ w_{j_i}^2\Big] &= Var\Big[w_{j_i}\Big] + \Big(E \Big[ w_{j_i}\Big]\Big)^2 \tag{12.8}\end{align}

\begin{align}E \Big[ x_i^2 \Big] &= Var\Big[x_i\Big] + \Big(E \Big[ x_i\Big]\Big)^2\tag{12.9}\end{align}


  • 식 \((12.8)\)과 식 \((12.9)\)의 곱을 계산하면 식 \((12.10)\)과 같다.

\begin{align}E \Big[ w_{j_i}^2\Big] \cdot E \Big[ x_i^2 \Big] &= \bigg(Var\Big[w_{j_i}\Big] + \Big(E \Big[ w_{j_i}\Big]\Big)^2\bigg) \cdot \bigg(Var\Big[x_i\Big] + \Big(E \Big[ x_i\Big]\Big)^2\bigg) \tag{12.10}\\&=Var\Big[w_{j_i}\Big]Var\Big[x_i\Big] + Var\Big[w_{j_i}\Big]\Big(E \Big[ x_i\Big]\Big)^2 + \Big(E \Big[ w_{j_i}\Big]\Big)^2 Var\Big[x_i\Big] +  \Big(E \Big[ w_{j_i}\Big]\Big)^2\Big( E \Big[ x_i \Big] \Big)^2 \tag{12.11}\end{align}


  • 식 \((12.11)\)을 식 \((12.9)\)에 대입하면 식 \((12.11)\)과 식 \((12.9)\)의 마지막 항들이 소거되어 식 \((12.12)\)가 된다.

\begin{align}Var\big[p_j\big] &= \sum_{i=1}^n \Bigg\{ Var\Big[w_{j_i}\Big]Var\Big[x_i\Big] + Var\Big[w_{j_i}\Big]\Big(E \Big[ x_i\Big]\Big)^2 + \Big(E \Big[ w_{j_i}\Big]\Big)^2 Var\Big[x_i\Big] \Bigg\} \tag{12.12}\end{align}


  • 이제 입력 데이터가 정규화가 되어 있다면 \(E[x_i]=0\)이며, 또한 weight가 이상적으로 정규분포를 따른다고 가정하면 \(E[w_{j_i}]=0\)이기 때문에 식 \((12.12)\)는 식 \((12.15)\)가 된다.

\begin{align}Var\big[p_j\big] &= \sum_{i=1}^n \Bigg\{ Var\Big[w_{j_i}\Big]Var\Big[x_i\Big] + Var\Big[w_{j_i}\Big]\Big(\not{E} \Big[ x_i\Big]\Big)^2 + \Big(\not{E} \Big[ w_{j_i}\Big]\Big)^2 Var\Big[x_i\Big]\Bigg\}\tag{12.13}\\ &= \sum_{i=1}^n Var\Big[w_{j_i}\Big]Var\Big[x_i\Big] \tag{12.14}\\ &= n\cdot Var\Big[w_{j_i}\Big]Var\Big[x_i\Big]\tag{12.15}\end{align}


  • 식 \((12.15)\)에 따라 \(\mathbb{p}\)의 분산을 \(\mathbb{x}\)의 분산과 같게 하려면 weight \(W\)의 각 성분의 분산은 \(\frac{1}{n}\)이어야 한다. 즉 \(Var(w_j)=\frac{1}{n}\)이어야 \(Var(W)=\frac{1}{n}\)이기 때문에 식 \((12.15)\)에 의해 \(Var\big[\mathbb{p}\big] = Var\big[\mathbb{x}\big]\)이 된다.


  • \(a\)가 상수이고 \(X\)를 확률변수라고 하면 \(Var[aX]=a^2Var(X)\)이기 때문에 앞에서 언급한 것처럼 a*np.random.normal(scale=0.01, size=shape)에서 a에 어떤 값을 줘야 하는 것인가 하는 문제로 돌아가면 \(a\)는 식 \((12.16)\)이 되어야 한다.

\begin{align} a = \sqrt{\frac{1}{n}} \tag{12.16}\end{align}


  • 따라서 a*np.random.normal(scale=0.01, size=shape)는  다음과 같이 구현해야 한다.
np.sqrt(1.0 / n) * np.random.normal(size=shape)


  • 식 \((12.15)\)를 구하기 위해 몇 가지 가정을 한 상태에서 식을 전개하였다. 이 가정을 바꾸면 초기화하는 방법들을 만들어 낼 수 있다.


LeCun 등 1998년 논문에서 제시하는 초기화 방법

  • Yann LeCun, Leon Bottou, Genevieve B. Orr and Klaus-Robert Müller, Efficient BackProp, Neural Networks: Tricks of the Trade, pp. 9-50, Springer, 1998

  • 이 방법은 정규분포 또는 균등분포를 적용해 초기화하며 균등분포를 적용할 경우의 코드는 다음과 같다.
np.random.uniform(low=np.sqrt(1.0 / n),
                  high=np.sqrt(1.0 / n),
                  size=shape)


  • Keras에서는 kernel_initializer='lecun_uniform'이라는 별칭을 사용할 수 있다.


Glorot and Bengio 2010년 논문에서 제시하는 초기화 방법

  • 이 방법은 정규분포나 균등분포를 사용할 경우 각각의 웨이트 초깃값에 관해 연구한 것으로 코드로 나타내면 균등분포일 경우에는 다음과 같다.
np.random.uniform(low=np.sqrt(6.0 / (n_in + n_out)),
                  high=nq.sqrt(6.0 / (n_in + n_out)),
                  size=shape)


  • 정규분포일 경우에는 다음과 같이 구현하는 것으 좋다고 알려져있다.
np.sqrt(3.0 / (n_in + n_out)) * np.random.normal(size=shape)


  • TensorFlow에서 이 초기화 방법을 사용하려면 tf.contrib.layers.xavier_initializer(uniform=True)를 호출하면 된다.
  • Keras에서는 각각 init='glorot_uniform'init='glorot_normal'을 호출하면 된다.


He 등 2015년 논문에서 제시하는 초기화 방법

  • ReLU를 사용할 경우 어떻게 초기화하는지에 관해 설명하는데 다음과 같이 구현하는 좋다고 주장한다.
np.sqrt(2.0 / n) * np.random.normal(size=shape)


  • Keras에서는 init='he_normal' 사용할 수 있다.



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





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

14. 학습 조기 종료  (2) 2018.02.01
13. 학습률 설정  (0) 2018.01.29
11. 학습 과정 시각화  (3) 2018.01.19
10. 신경망 모델 구현 방법  (0) 2018.01.19
9. 오버피팅 문제 해결  (0) 2018.01.19

학습 과정을 시각화

  • 지금까지 수행한 결과는 정량적인 평가로 테스트 데이터에 대한 예측 정확도를 측정하였다.
  • 학습 데이터에 대해서도 오차 함수의 값이 예측 정확도을 출력하기는 했어도 이는 학습이 진행되는지만을 확인하기 위한 것이었고 어떤 학습이 진행되고 있는지 대략적인 파악만 가능했다.
  • 데이터의 규모가 커지면 검증 데이터를 사용해서 학습을 평가하는 작업도 적절한 선에서 실시해야 한다.
  • 테스트 데이터에 대한 예측 정확도는 (데이터가 1개일 경우) 결과가 하나의 수치로 표현되지만 학습 데이터 또는 검증 데이터에 관한 예측 정확도는 각 epoch마다 평가해야 하므로 여러 개의 수치를 한 눈에 볼 수 있어야 하며, 이는 숫자들을 보는 것보다는 그래프 형태로 볼 수 있으면 직관적으로 파악이 가능하다.
  • 지금까지 구현한 코드에 다음 처리 과정을 추가해보도록 한다.
    • 검증 데이터를 사용해서 학습시키고 예측한다.
    • 학습시킬 때의 예측 정확도를 시각화한다.


TensorFlow로 구현

  • 학습 데이터와 검증 데이터, 테스트 데이터를 준비하는 부분 구현
    • 전체 데이터를 학습 데이터 테스트 데이터로 나누고, 다시 학습 데이터를 학습 데이터와 학습한 모델을 평가하기 위한 검증 데이터로 나눈다.
if __name__ == '__main__':
    MNIST = datasets.fetch_mldata('MNIST original', data_home='.')

    n = len(MNIST.data)
    indices = np.random.permutation(range(n))[:N]
    X = MNIST.data[indices]
    Y = MNIST.target[indices]
    Y = np.eye(10)[Y.astype(int)]  # 1-of-K hot coding 표현으로 변환

    test_size = 0.2
    # 학습 데이터와 테스트 데이터를 80:20으로 나눈다
    X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=test_size)

    # 학습 데이터의 20%를 검증 데이터로 사용한다
    X_train, X_val, Y_train, Y_val = train_test_split(X_train, Y_train, test_size=test_size)


  • 모든 학습 데이터를 학습 데이터와 검증 데이터로 완전히 분리하고 같은 검증 데이터를 모델 평가에 사용하는 기법을 홀드아웃 검증(hold-out validation)이라고 한다.
  • 학습 데이터를 $K$개의 데이터 셋으로 나누고 그 중 하나를 검증 데이터로 사용하고 나머지 $K-1$개의 데이터를 학습에 사용하는 기법을 $K$-분할교차 검증($K$-cross validation)이라고 한다.
    • $K$-분할교차 검증에서는 $K$개 나눈 데이터 셋을 조합해서 총 $K$번의 학습과 검증을 수행한다.
    • 이 과정에서 얻어진 예측 정확도의 평균을 모델의 성능으로 간주한다.
    • $K$-분할교차 검증이 일반화 성능을 매우 엄밀하게 평가할 수 있지만 딥러닝에서는 모델을 학습시키는데 방대한 시간이 걸릴 경우가 많기 때문에 $K$-분할교차 검증은 잘 사용되지 않는다.


  • 검증 데이터에 대한 오차나 예측 정확도는 각 epoch마다 평가해야 하기 때문에 모델을 학습시키는 부분은 다음과 같이 구현한다.
for epoch in range(epoch):
    X_, Y_ = shuffle(x_train, y_train)
    
    for i in range(n_batches):
        start = i * batch_size
        end = start + batch_size
        
        sess.run(train, feed_dict={
            x: X_[start:end],
            y: Y_[start:end],
            keeP_prob: p_keep
        })
        
    # 검증 데이터를 사용해 평가한다
    val_loss = loss.eval(session=sess, feed_dict={
        x: x_val,
        y: v_val,
        keep_prob: 1.0
    })
    
    val_acc = accuracy.eval(session=sess, feed_dict={
        x: x_val,
        y: y_val,
        keep_prob: 1.0
    })


  • 검증 데이터에 대한 손실을 나타내는 val_loss와 예측 정확도를 나타내는 val_acc를 각 epoch마다 출력해도 학습이 진행되는 상황을 확인할 수 있지만 시각화를 위해 저장해둘 변수를 설정한다. 프로그램 선언부에 명시한다.
history = {
    'val_loss' = [],
    'val_acc' = []
}


  • 이제 각 epoch마다 나오는 val_lossval_acc를 추가해 저장한다.
    # 학습 진행 상황 데이터를 저장할 변수 설정
    history = {'val_loss': [],
               'val_acc': []}
    
    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, feed_dict={x: X_[start: end], y: Y_[start: end], keep_prob: p_keep})

        # 검증 데이터를 사용해 정확도 측정
        val_loss = loss.eval(session=sess, feed_dict={x: X_val
                                                      y: Y_val,
                                                      keep_prob: 1.0})

        val_accuracy = accuracy.eval(session=sess, feed_dict={x: X_val,
                                                              y: Y_val,
                                                              keep_prob: 1.0})

        # 검증 데이터에 대한 학습 진행 상황을 저장
        history['val_loss'].append(val_loss)
        history['val_acc'].append(val_accuracy)


matplotlib 패키지를 이용해 그래프 그리기

  • 검증 데이터의 정확도를 그래프로 그리자
import matplotlib.pyplot as plt
import matplotlib

    # 폰트 설정
    font_name = matplotlib.font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name()
    matplotlib.rc('font', family=font_name)
    # 도화지 생성
    fig = plt.figure()
    # 정확도 그래프 그리기
    plt.plot(range(epochs), history['val_acc'], label='Accuracy', color='darkred')
    # 축 이름
    plt.xlabel('epochs')
    plt.ylabel('검증 정확도(%)')
    plt.title('epoch에 따른 검증 데이터에 대한 정확도 그래프')
    plt.grid(linestyle='--', color='lavender')
    # 그래프 표시
    plt.show()
    # 그래프 저장
    plt.savefig('mnist_tensorflow_acc.png')


  • 전체 코드
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.utils import shuffle
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
import matplotlib

def inference(x, keep_prob, n_in, n_hiddens, n_out):
    def weight_variable(shape):
        initial = tf.truncated_normal(shape, stddev=0.01)
        return tf.Variable(initial)

    def bias_variable(shape):
        initial = tf.zeros(shape)
        return tf.Variable(initial)

    # 입력층-은닉층과 은닉층-은닉층 설계
    for i, n_hidden in enumerate(n_hiddens):
        if i == 0:  # 입력층-은닉층
            input = x
            input_dim = n_in
        else:   # 은닉층-은닉층
            input = output  # 출력층이 다음 입력층이 된다
            input_dim = n_hiddens[i-1]

        W = weight_variable([input_dim, n_hidden])  # weight의 shape은 입력층 차원(784) x 뉴런의 개수
        b = bias_variable([n_hidden])               # bias의 shape은 뉴런의 개수

        hidden_layer = tf.nn.relu(tf.matmul(input, W) + b)  # 은닉층의 활성화 함수로 ReLU를 사용
        output = tf.nn.dropout(hidden_layer, keep_prob=keep_prob)   # 출력은 드롭아웃을 적용

    # 은닉층-출력층 설계
    W_out = weight_variable([n_hiddens[-1], n_out]) # 출력층의 weight shape은 뉴런의 개수 x 출력층의 차원(10개)
    b_out = bias_variable([n_out])                  # bias의 shape은 출력층의 차원

    hypothesis = tf.nn.softmax(tf.matmul(output, W_out) + b_out)    # 출력 활성화 함수는 softmax를 사용

    return hypothesis

def loss(hypothesis, y):
    # cross entropy
    cross_entropy = tf.reduce_mean(-tf.reduce_sum(y * tf.log(hypothesis), reduction_indices=[1]))

    return cross_entropy

def train(loss):
    optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)
    train = optimizer.minimize(loss)

    return train

def accuracy(hypothesis, y):
    correct_prediction = tf.equal(tf.argmax(hypothesis, 1), tf.argmax(y, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

    return accuracy


if __name__ == '__main__':
    MNIST = datasets.fetch_mldata('MNIST original', data_home='.')

    n = len(MNIST.data)
    N = int(n * 1.0)
    indices = np.random.permutation(range(n))[:N]
    X = MNIST.data[indices]
    Y = MNIST.target[indices]
    Y = np.eye(10)[Y.astype(int)]  # 1-of-K hot coding 표현으로 변환

    test_size = 0.2
    # 학습 데이터와 테스트 데이터를 80:20으로 나눈다
    X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=test_size)

    # 학습 데이터의 20%를 검증 데이터로 사용한다
    X_train, X_val, Y_train, Y_val = train_test_split(X_train, Y_train, test_size=test_size)

    # 2. 모델을 설정한다
    # 입출력 데이터와 은닉층의 뉴런 개수 설정
    n_in = len(X[0])  # 입력 데이터 이미지의 크기 28x28(=784)
    n_hiddens = [200, 200, 200]  # 각 은닉층의 뉴런 개수
    n_out = len(Y[0])  # 출력 데이터의 개수 (0~9)
    p_keep = 0.5        # 드롭아웃 확률의 비율

    x = tf.placeholder(tf.float32, shape=[None, n_in])
    y = tf.placeholder(tf.float32, shape=[None, n_out])

    keep_prob = tf.placeholder(tf.float32)

    # 가설식을 그래프에 추가
    hypothesis = inference(x, keep_prob, n_in=n_in, n_hiddens=n_hiddens, n_out=n_out)

    # 오차 함수를 그래프에 추가
    loss = loss(hypothesis, y)

    # 학습 함수를 그래프에 추가
    train = train(loss)

    # 정확도 예측 함수 그래프에 추가
    accuracy = accuracy(hypothesis, y)

    # 3. 모델을 학습시키면서 그래프를 실행
    epochs = 50
    batch_size = 200

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

    n_batches = len(X_train) // batch_size

    # 학습 진행 상황 데이터를 저장할 변수 설정
    history = {'val_loss': [],
               'val_acc': []}

    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, feed_dict={x: X_[start: end], y: Y_[start: end], keep_prob: p_keep})

        # 검증 데이터를 사용해 정확도 측정
        val_loss = loss.eval(session=sess, feed_dict={x: X_val,
                                                      y: Y_val,
                                                      keep_prob: 1.0})

        val_accuracy = accuracy.eval(session=sess, feed_dict={x: X_val,
                                                              y: Y_val,
                                                              keep_prob: 1.0})

        # 검증 데이터에 대한 학습 진행 상황을 저장
        history['val_loss'].append(val_loss)
        history['val_acc'].append(val_accuracy * 100)

        print(f'epoch : {epoch:03}, Validation loss : {val_loss:.7}, Validation accuracy : {val_accuracy*100:.6} %')

    # 4. 모델을 평가한다
    accuracy_rate = accuracy.eval(session=sess, feed_dict={x: X_test, y: Y_test, keep_prob: 1.0})
    print(f'Test Accuracy : {accuracy_rate*100:.6} %')

    # 폰트 설정
    font_name = matplotlib.font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name()
    matplotlib.rc('font', family=font_name)
    # 도화지 생성
    fig = plt.figure()
    # 정확도 그래프 그리기
    plt.plot(range(epochs), history['val_acc'], label='Accuracy', color='darkred')
    # 축 이름
    plt.xlabel('epochs')
    plt.ylabel('검증 정확도(%)')
    plt.title('epoch에 따른 검증 데이터에 대한 정확도 그래프')
    plt.grid(linestyle='--', color='lavender')
    # 그래프 표시
    plt.show()
    plt.savefig('mnist_tensorflow_acc.png')


  • 코드를 실행하면 그림 $11.1$과 같은 그래프를 볼 수 있다

그림 11.1 epoch에 따른 검증 정확도 변화


  • 그림 $11.1$을 보면 검증 정확도가 점점 올라갔다가 오느 시점부터는 전혀 학습이 이루어지지 않는다는 것을 알 수 있다.
  • 이는 경사 소실 문제와 마찬가지로 softmax 함수(sigmoid 함수)를 통해 전달되는 경사값이 너무 작아져서 $0$처럼 취급되어 계산되는 것이 원인이다.
  • 이 문제 해결을을 위해서 오차 함수 식을 다음과 같이 수정해야만 한다.
    • 계산에 사용되는 하한값(또는 상한값)을 새롭게 추가된 tf.clip_by_value()가 결정한다.
    • 이 하한값을 1e-10$(=10^{-10})$과 같이 작은 값으로 설정해두면 학습을 계산하는데  악영향을 주지 않으면서 $0$으로 나누는 문제를 방지할 수 있다.
def loss(hypothesis, y):
    # cross entropy
    cross_entropy = tf.reduce_mean(-tf.reduce_sum(y * tf.log(tf.clip_by_value(hypothesis, 1e-10, 1.0)),
                                                  reduction_indices=[1]))


  • 오차 함수 식을 수정한 결과 출력된 그래프는 그림 $11.2$와 같다.

그림 11.2 epoch에 따른 검증 정확도 변화(오차 함수 수정 후)


  • 오차도 함께 그래프에 표시해보자

# 폰트 설정 font_name = matplotlib.font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name() matplotlib.rc('font', family=font_name) # 도화지 생성 fig = plt.figure() # 큰 도화지(fig)를 1x1로 나눈 도화지에 첫 번째 칸에 ax_acc 그래프를 추가한다 ax_acc = fig.add_subplot(111) # 정확도 그래프 그리기 ax_acc.plot(range(epochs), history['val_acc'], label='정확도(%)', color='darkred') # 축 이름 plt.text(3, 14.7, "<----------------정확도(%)", verticalalignment='top', horizontalalignment='right') plt.xlabel('epochs') plt.ylabel('정확도(%)') ax_acc.grid(linestyle='--', color='lavender') # ax_loos 그래프를 x축을 동일하게 사용하여 겹쳐 그리기 선언 ax_loss = ax_acc.twinx() ax_loss.plot(range(epochs), history['val_loss'], label='오차', color='darkblue') plt.text(3, 2.2, "<----------------오차", verticalalignment='top', horizontalalignment='left') plt.ylabel('오차') ax_loss.yaxis.tick_right() ax_loss.grid(linestyle='--', color='lavender') # 그래프 표시 plt.show() plt.legend() plt.savefig('mnist_tensorflow_acc.png')


  • 정확도와 오차를 함께 그린 그래프는 $11.3$과 같다.

그림 11.2 epoch에 따른 검증 정확도와 오차를 함께 그린 그래프



Keras로 구현

  • Keras에서는 model.fit()의 반환값에 학습이 진행된 정도를 나타내는 값이 저장된 결과가 들어있다.
  • 검증 데이터를 사용해서 학습시키는 경우에는 학습시킬 때 다음과 같이 validation_data=를 사용하면 hist 변수에 오차와 정확도가 저장된다.
hist = model.fit(X_train, Y_train, epochs=epochs, batch_size=batch_size, validation_data=(X_val, Y_val))

val_loss = hist.history['val_loss']
val_acc = hist.history['val_acc']


  • 정확도를 그래프로 그리면 그림 $11.3$과 같다.
epochs = 50
batch_size = 200

hist = model.fit(X_train, Y_train, epochs=epochs, batch_size=batch_size, validation_data=(X_val, Y_val))
val_loss = hist.history['val_loss']
val_acc = hist.history['val_acc']

font_name = matplotlib.font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name()
matplotlib.rc('font', family=font_name)

fig = plt.figure()
ax_acc = fig.add_subplot(111)

ax_acc.plot(range(epochs), val_acc, label='정확도(%)', color='darkred')
plt.text(3, 14.7, "<----------------정확도(%)", verticalalignment='top', horizontalalignment='right')
plt.xlabel('epochs')
ax_acc.grid(linestyle='--', color='lavender')

plt.show()


그림 11.3 Keras로 그린 epoch에 따른 검증 정확도 그래프


  • 그림 $11.3$도 어느 순간부터 학습이 제대로 진행되지 않는 것을 알 수 있다. 그 이유는 매개변수를 초기화하는 부분에 있다.
  • TensorFlow에서는 weight를 초기화하기 위하여 tf.truncated_normal(shape, stddev=0.01) 함수를 사용하였지만 Keras에서는 아무 것도 사용하지 않아 학습이 제대로 진행되지 않는 것이다.
  • weight의 초기값을 어떻게 결정하는지가 학습이 제대로 이루어지는지 아니면 실패하는지에 영향을 준다. 초기값을 설정하는 몇 가지 기법이 연구가 된 상태이다.
  • Keras에서는 Dense() 함수에 kernel_initializer=라는 매개변수가 초기값을 결정하는데 영향을 준다. 매개변수를 사용하는 방법은 다음과 같다.
    • Keras에서 미리 alias로 정의되어 있는 것 이외의 것을 사용할 때에는 keras.backend를 통해 처리를 수행한다.
    • weight_variable() 함수 내부에서 K.truncated_normal(shape, stddev=0.01)이 TensorFlow에서 구현했을 때처럼 표준편차가 $0.01$인 절단 정규분포를 따르는 난수를 반환한다.
    • Dense(kernel_initializer=weight_variable) 함수가 실행되면 TensorFlow에서 실행되었던 것처럼 초기값을 가진 weight를 생성한다.
from keras import backend as K

def weight_variable(shape):
    return K.truncated_normal(shape, stddev=0.01)
model = Sequential()

for i, input_dim in enumerate(([n_in] + n_hiddens)[:-1]):
    model.add(Dense(input_dim=input_dim, units=n_hiddens[i], kernel_initializer=weight_variable))
    model.add(LeakyReLU(alpha=alpha))
    model.add(Dropout(p_keep))

model.add(Dense(units=n_out, kernel_initializer=weight_variable))
model.add(Activation('softmax'))


  • 절단정규분포는 keras.initializersTruncatedNormal() 함수를 통해 사용할 수도 있다.
    • 이 때 weight_variable()은 정의하지 않아도 된다.
from keras.initializers import TruncatedNormal

model.add(Dense(units=n_out, kernel_initializer=TruncatedNormal(stddev=0.01)))


  • Keras에서 NumPy로 생성한 난수를 그대로 사용해도 된다.
    • 이 때 weight_variable()은 다음과 같이 정의한다.
def weight_variable(shape):
return np.random.normal(scale=0.01, size=shape)


  • 전체 코드를 실행하면 그림 $11.4$와 같은 그래프를 얻을 수 있다.
from sklearn import datasets
from sklearn.model_selection import train_test_split
import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Activation, Dropout
from keras.optimizers import SGD
from keras import backend as K
from keras.layers.advanced_activations import LeakyReLU
import matplotlib.pyplot as plt
import matplotlib

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

n = len(MNIST.data)
N = int(n * 1.00)  # 70,000개의 데이터를 모두 사용
test_size = 0.2

# 학습 데이터와 테스트 데이터를 나누기 전에 입력 데이터를 무작위로 섞는다
indices = np.random.permutation(range(n))[:N]
X = MNIST.data[indices]
Y = MNIST.target[indices]
Y = np.eye(10)[Y.astype(int)]  # 1-of-K hot coding 표현으로 변환

# 학습 데이터와 테스트 데이터를 80:20으로 나눈다
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=test_size)
X_train, X_val, Y_train, Y_val = train_test_split(X_train, Y_train, test_size=test_size)

n_in = len(X[0])  # 입력 데이터 이미지의 크기 28x28(=784)
n_hiddens = [200, 200, 200]  # 각 은닉층의 뉴런 개수
n_out = len(Y[0])  # 출력 데이터의 개수 (0~9)
alpha = 0.01
p_keep = 0.5        # 드롭아웃 확률의 비율

def weight_variable(shape):
    return K.truncated_normal(shape, stddev=0.01)

model = Sequential()

for i, input_dim in enumerate(([n_in] + n_hiddens)[:-1]):
    model.add(Dense(input_dim=input_dim, units=n_hiddens[i], kernel_initializer=weight_variable))
    model.add(LeakyReLU(alpha=alpha))
    model.add(Dropout(p_keep))

model.add(Dense(units=n_out, kernel_initializer=weight_variable))
model.add(Activation('softmax'))

model.compile(loss='categorical_crossentropy', optimizer=SGD(lr=0.01), metrics=['accuracy'])

epochs = 50
batch_size = 200

hist = model.fit(X_train, Y_train, epochs=epochs, batch_size=batch_size, validation_data=(X_val, Y_val))
val_loss = hist.history['val_loss']
val_acc = hist.history['val_acc']

font_name = matplotlib.font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name()
matplotlib.rc('font', family=font_name)

fig = plt.figure()
ax_acc = fig.add_subplot(111)

ax_acc.plot(range(epochs), val_acc, label='정확도(%)', color='darkred')
plt.text(3, 14.7, "<----------------정확도(%)", verticalalignment='top', horizontalalignment='right')
plt.xlabel('epochs')
plt.ylabel('정확도(%)')
ax_acc.grid(linestyle='--', color='lavender')

ax_loss = ax_acc.twinx()
ax_loss.plot(range(epochs), val_loss, label='오차', color='darkblue')
plt.text(3, 2.2, "<----------------오차", verticalalignment='top', horizontalalignment='left')
plt.ylabel('오차')
ax_loss.yaxis.tick_right()
ax_loss.grid(linestyle='--', color='lavender')

plt.legend()
plt.show()
ax_acc.legend()
plt.savefig('mnist_keras_plot.png')

evaluation = model.evaluate(X_test, Y_test)
print(f'\nloss : {evaluation[0]}, accuracy : {evaluation[1]*100:.6} %')


그림 11.4 weight를 초기화시켜 학습시킨 모델의 그래프



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

신경망 모델 코드로 구현하기

  • 딥러닝 모델 구현 시, 1개의 층을 코드로 작성하면 다음과 같다.
    • TensorFlow
W = tf.Variable(tf.truncated_normal([n_in, n_hidden], stddev=0.01))
b = tf.Variable(tf.zeros([n_hidden]))
h = tf.nn.relu(tf.matmul(x, W) + b)
h_drop = tf.nn.dropout(h, keep_prob)
    • Keras
model = Sequential()
model.add(Dense(input_dim=n_in, units=n_hidden))
model.add(Activation('relu'))
model.add(Dropout(0.5))
  • 딥러닝 모델에서 층을 늘려갈 떄에는 위의 코드를 반복적으로 사용하여 구현하였다.


효율적으로 신경망 모델 구현하기

  • 층의 개수를 더 늘리고 싶거나 모델의 일부분을 변경(예를 들어 활성화 함수를 다른 것으로 변경)하고 싶을 때 유연하게 적용할 수 있도록 신경망 모델을 설계해보자

TensorFlow 설계 방법

  • 신경망 모델을 정의하는 전체적인 과정은 그림 10.1과 같다.

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

  • TensorFlow에서는 위의 과정을 단계별로 함수화할 것을 권장하고 있으며 각각 inference(), loss(), training() 함수로 정의해 사용해야 한다.
    • inference() : 모델 전체를 설정하고 모델의 출력(예측 결과)를 반환하는 함수
    • loss() : 모델의 오차 함수를 정의하고 오차를 반환하는 함수
    • training() : 모델을 학습시키고 학습시킨 결과(진행 상황)을 반환하는 함수
  • 전체적인 코드의 구성은 다음과 같다.
def inference(x):
    # 모델을 정의

def loss(hypothesis, y):
    # 오차함수를 정의

def training(loss):
    # 학습 알고리즘을 정의

if __name__ == '__main__':
    # 1. 데이터를 준비한다
    # 2. 모델을 설정한다
        hypothesis = inference(x)
        loss = loss(hypothesis, y)

    # 3. 모델을 학습시킨다
        train = training(loss)

    # 4. 모델을 평가한다


  • hypohesis = inference(x) 함수 설계
    • 지금까지 각 층을 h0, h1 등으로 함수를 정의했지만 이제는 함수들을 한꺼번에 정의하기 위해 각 층에 있는 뉴런의 개수를 인수로 받게 만들어야 한다.
    • 드롭아웃을 지원하기 위해 x 이외에 keep_prob 변수를 인수로 받아야 하기 때문에 다음과 같이 코드를 작성한다.
def inference(x, keep_prob, n_in, n_hiddens, n_out):
    # 모델을 정의

if __name__ == '__main__':
    # 1. 데이터를 준비한다
    MNIST = datasets.fetch_mldata('MNIST original', data_home='.')

    n = len(MNIST.data)
    N = int(n * 1.00)  # 70,000개의 데이터를 모두 사용
    N_train = int(n * 0.2)
    N_val = int(N_train * 0.2)
    # 학습 데이터와 테스트 데이터를 나누기 전에 입력 데이터를 무작위로 섞는다
    indices = np.random.permutation(range(n))[:N]
    X = MNIST.data[indices]
    Y = MNIST.target[indices]
    Y = np.eye(10)[Y.astype(int)]  # 1-of-K hot coding 표현으로 변환

    # 학습 데이터와 테스트 데이터를 80:20으로 나눈다
    X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=N_train)

    X_train, X_val, Y_train, Y_val = train_test_split(X_train, Y_train, test_size=N_val)

    # 2. 모델을 설정한다
    # 입출력 데이터와 은닉층의 뉴런 개수 설정
    n_in = len(X[0])  # 입력 데이터 이미지의 크기 28x28(=784)
    n_hiddens = [200, 200, 200]  # 각 은닉층의 뉴런 개수
    n_out = len(Y[0])  # 출력 데이터의 개수 (0~9)
    p_keep = 0.5        # 드롭아웃 확률의 비율

    x = tf.placeholder(tf.float32, shape=[None, n_in])
    y = tf.placeholder(tf.float32, shape=[None, n_out])

    keep_prob = tf.placeholder(tf.float32)

    # 가설식을 그래프에 추가
    hypothesis = inference(x, keep_prob, n_in=n_in, n_hiddens=n_hiddens, n_out=n_out)
    • 입력층에서 출력층의 바로 앞 층까지는 모든 출력을 같은 식으로 나타낼 수 있지만 출력층만은 활성화 함수가 소프트맥스 함수(또는 시그모이드 함수)로 지정돼 있어 드음과 같이 코드를 작성한다.
      • weight_variable()bias_variable()을 정의해, 웨이트와 바이어스를 초기화 처리를 한다.
      • for 문의 내부에서 '입력층 - 은닉층'과 '은닉층-은닉층'은 각각 처리가 다르지만 기본적으로 이전 층의 output이 다음 층의 input이 되는 구조를 구현
def inference(x, keep_prob, n_in, n_hiddens, n_out):
    def weight_variable(shape):
        initial = tf.truncated_normal(shape, stddev=0.01)
        return tf.Variable(initial)

    def bias_variable(shape):
        initial = tf.zeros(shape)
        return tf.Variable(initial)

    # 입력층-은닉층과 은닉층-은닉층 설계
    for i, n_hidden in enumerate(n_hiddens):
        if i == 0:  # 입력층-은닉층
            input = x
            input_dim = n_in
        else:   # 은닉층-은닉층
            input = output  # 출력층이 다음 입력층이 된다
            input_dim = n_hiddens[i-1]

        W = weight_variable([input_dim, n_hidden])  # weight의 shape은 입력층 차원(784) x 뉴런의 개수
        b = bias_variable([n_hidden])               # bias의 shape은 뉴런의 개수

        hidden_layer = tf.nn.relu(tf.matmul(input, W) + b)  # 은닉층의 활성화 함수로 ReLU를 사용
        output = tf.nn.dropout(hidden_layer, keep_prob=keep_prob)   # 출력은 드롭아웃을 적용

    # 은닉층-출력층 설계
    W_out = weight_variable([n_hiddens[-1], n_out]) # 출력층의 weight shape은 뉴런의 개수 x 출력층의 차원(10개)
    b_out = bias_variable([n_out])                  # bias의 shape은 출력층의 차원

    hypothesis = tf.nn.softmax(tf.matmul(output, W_out) + b_out)    # 출력 활성화 함수는 softmax를 사용
    
    return hypothesis


  • loss(hypothesis, y) 함수와 training(loss) 함수 설계
def loss(hypothesis, y):
    cross_entropy = tf.reduce_sum(-tf.reduce_sum(y * tf.log(hypothesis), reduction_indices=[1]))

    return cross_entropy

def training(loss):
    optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)
    train = optimizer.minimize(loss)

    return train


  • 모델 평가를 위한 accuracy(hypothesis, y) 함수 설계
def accuracy(hypothesis, y):
    correct_prediction = tf.equal(tf.argmax(hypothesis, 1), tf.argmax(y, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    
    return accuracy


  • 전체 코드
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.utils import shuffle
import numpy as np
import tensorflow as tf

def inference(x, keep_prob, n_in, n_hiddens, n_out):
    def weight_variable(shape):
        initial = tf.truncated_normal(shape, stddev=0.01)
        return tf.Variable(initial)

    def bias_variable(shape):
        initial = tf.zeros(shape)
        return tf.Variable(initial)

    # 입력층-은닉층과 은닉층-은닉층 설계
    for i, n_hidden in enumerate(n_hiddens):
        if i == 0:  # 입력층-은닉층
            input = x
            input_dim = n_in
        else:   # 은닉층-은닉층
            input = output  # 출력층이 다음 입력층이 된다
            input_dim = n_hiddens[i-1]

        W = weight_variable([input_dim, n_hidden])  # weight의 shape은 입력층 차원(784) x 뉴런의 개수
        b = bias_variable([n_hidden])               # bias의 shape은 뉴런의 개수

        hidden_layer = tf.nn.relu(tf.matmul(input, W) + b)  # 은닉층의 활성화 함수로 ReLU를 사용
        output = tf.nn.dropout(hidden_layer, keep_prob=keep_prob)   # 출력은 드롭아웃을 적용

    # 은닉층-출력층 설계
    W_out = weight_variable([n_hiddens[-1], n_out]) # 출력층의 weight shape은 뉴런의 개수 x 출력층의 차원(10개)
    b_out = bias_variable([n_out])                  # bias의 shape은 출력층의 차원

    hypothesis = tf.nn.softmax(tf.matmul(output, W_out) + b_out)    # 출력 활성화 함수는 softmax를 사용

    return hypothesis

def loss(hypothesis, y):
    # cross entropy
    cross_entropy = tf.reduce_mean(-tf.reduce_sum(y * tf.log(hypothesis), reduction_indices=[1]))

    return cross_entropy

def train(loss):
    optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)
    train = optimizer.minimize(loss)

    return train

def accuracy(hypothesis, y):
    correct_prediction = tf.equal(tf.argmax(hypothesis, 1), tf.argmax(y, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

    return accuracy

if __name__ == '__main__':
    # 1. 데이터를 준비한다
    MNIST = datasets.fetch_mldata('MNIST original', data_home='.')

    n = len(MNIST.data)
    N = int(n * 1.00)  # 70,000개의 데이터를 모두 사용
    N_train = int(n * 0.2)
    N_val = int(N_train * 0.2)
    # 학습 데이터와 테스트 데이터를 나누기 전에 입력 데이터를 무작위로 섞는다
    indices = np.random.permutation(range(n))[:N]
    X = MNIST.data[indices]
    Y = MNIST.target[indices]
    Y = np.eye(10)[Y.astype(int)]  # 1-of-K hot coding 표현으로 변환

    # 학습 데이터와 테스트 데이터를 80:20으로 나눈다
    X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=N_train)

    X_train, X_val, Y_train, Y_val = train_test_split(X_train, Y_train, test_size=N_val)

    # 2. 모델을 설정한다
    # 입출력 데이터와 은닉층의 뉴런 개수 설정
    n_in = len(X[0])  # 입력 데이터 이미지의 크기 28x28(=784)
    n_hiddens = [200, 200, 200]  # 각 은닉층의 뉴런 개수
    n_out = len(Y[0])  # 출력 데이터의 개수 (0~9)
    p_keep = 0.5        # 드롭아웃 확률의 비율

    x = tf.placeholder(tf.float32, shape=[None, n_in])
    y = tf.placeholder(tf.float32, shape=[None, n_out])

    keep_prob = tf.placeholder(tf.float32)

    # 가설식을 그래프에 추가
    hypothesis = inference(x, keep_prob, n_in=n_in, n_hiddens=n_hiddens, n_out=n_out)

    # 오차 함수를 그래프에 추가
    loss = loss(hypothesis, y)

    # 학습 함수를 그래프에 추가
    train = train(loss)

    # 정확도 예측 함수 그래프에 추가
    accuracy = accuracy(hypothesis, y)

    # 3. 모델을 학습시키면서 그래프를 실행
    epochs = 50
    batch_size = 200

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

    n_batches = N_train // 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, feed_dict={x: X_[start: end], y: Y_[start: end], keep_prob: p_keep})

        accuracy_rate = accuracy.eval(session=sess, feed_dict={x: X_val, y: Y_val, keep_prob: p_keep})
        print(f'Validation accuracy rate per epoch: {accuracy_rate}')

    # 4. 모델을 평가한다
    accuracy_rate = accuracy.eval(session=sess, feed_dict={x: X_test, y: Y_test, keep_prob: 1.0})
    print(f'Test Accuracy : {accuracy_rate}')


Keras 설계 방법

  • Keras는 단순하게 구현하려고 사용하는 것이기 때문에 TensorFlow에서 했던 것처럼 구현 방침같은 것이 정해져 있지 않다.
  • Keras에서는 각 층을 한꺼번에 정의할 수 있기 때문에 모델을 설정하는 전체적인 코드는 다음과 같다.
    • for 문의 내부에서 ([n_in] + n_hiddens)[:-1]을 반복하여 출력층 바로 앞까지의 각 층의 입력과 출력의 차원을 Dense()에 넘겨준다.
    • '입력층 - 은닉층'과 '은닉층-은닉층'은 각각 처리가 다르지만 기본적으로 이전 층의 output이 다음 층의 input이 되는 구조를 구현
from sklearn import datasets
from sklearn.model_selection import train_test_split
import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Activation, Dropout
from keras.optimizers import SGD

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

n = len(MNIST.data)
N = int(n * 1.00)  # 70,000개의 데이터를 모두 사용
N_train = int(n * 0.2)
N_val = int(N_train * 0.2)

# 학습 데이터와 테스트 데이터를 나누기 전에 입력 데이터를 무작위로 섞는다
indices = np.random.permutation(range(n))[:N]
X = MNIST.data[indices]
Y = MNIST.target[indices]
Y = np.eye(10)[Y.astype(int)]  # 1-of-K hot coding 표현으로 변환

# 학습 데이터와 테스트 데이터를 80:20으로 나눈다
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=N_train)


n_in = len(X[0])  # 입력 데이터 이미지의 크기 28x28(=784)
n_hiddens = [200, 200]  # 각 은닉층의 뉴런 개수
n_out = len(Y[0])  # 출력 데이터의 개수 (0~9)
activation = 'relu'
p_keep = 0.5        # 드롭아웃 확률의 비율

model = Sequential()

for i, input_dim in enumerate(([n_in] + n_hiddens)[:-1]):
    model.add(Dense(input_dim=input_dim, units=n_hiddens[i]))
    model.add(Activation(activation))
    model.add(Dropout(p_keep))

model.add(Dense(units=n_out))
model.add(Activation(activation))

model.compile(loss='categorical_crossentropy', optimizer=SGD(lr=0.01), metrics=['accuracy'])

epochs = 50
batch_size = 200

model.fit(X_train, Y_train, epochs=epochs, batch_size=batch_size)
loss_and_metrics = model.evaluate(X_test, Y_test)
print(f'\nLoss : {loss_and_metrics[0]:6}')
print(f'Accuracy : {loss_and_metrics[1]*100:.7}%')


TensorFlow에서 신경망 모델을 클래스로 설계하기

  • TensorFlow에서는 각각의 과정을 inference(), loss(), training()이라는 함수 3개로 분리했기 떄문에 구현하기가 쉬워졌다.
  • 모델을 실제로 학습을 시키는 부분은 이 3개의 함수에 포함되어 있지 않기 때문에 main 함수 부분에 코드로 작성할 내용이 많아질 수 있기 때문에 학습시키는 부분도 포함해 모든 것을 하나의 클래스로 정리해놓으면 아래 코드처럼 Keras와 비슷하게 코딩을 할 수 있다.
model = DNN()
model.fit(x_train, y_train)
model.evaluate(x_test, y_test)


  • 클래스를 정의하는 전체적인 코드는 다음과 같다.
class DNN(objec):
    def __init__(self):
        # 초기화 처리

    def weight_variable(self, shape):
        initial = tf.truncated_normal(shape, stddev=0.01)
        return tf.Variable(initial)

    def bias_variable(self, shape):
        initial = tf.zeros(shape)
        return tf.Variable(initial)

    def inference(self, x, keep_prob):
        # 모델을 정의한다
        return hypothesis

    def loss(self, hypothesis, y):
        cross_entropy = tf.reduce_mean(-tf.reduce_sum(y * tf.log(hypothesis), reduction_indices=[1]))
        return cross_entropy
    
    def training(self, loss):
        optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)
        train = optimizer.minimize(loss)
        return train
    
    def accuracy(self, hypothesis, y):
        correct_prediction = tf.equal(tf.argmax(hypothesis, 1), tf.argmax(y, 1))
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
        return accuracy
    
    def fit(self, x_train, y_train):
        # 학습 처리

    def evaluate(self, x_test, y_test):
        # 예측 결과 처리


신경망 모델 초기화

  • 신경망 모델 초기화 부분에서는 모델의 구성을 결정하는 것이 바람직하므로 각 층의 차원을 매개변수로 받아야 한다.
  • 모델의 구성도 결정해야 하기 때문에 각 층에 관련된 weight와 bias로 정의한다.
class DNN(objec):
    def __init__(self, n_in, n_hiddens, n_out):
        self.n_in = n_in
        self.n_hiddens = n_hiddens
        self.n_out = n_out
        self.weight = []
        self.bias = []


inference() 함수 정의

  • 초기화 부분에 따라 inference() 함수는 다음과 같이 구현할 수 있다. 기존 코드에서는 각 층의 차원 수를 매개변수를 통해 받아들였지만 self.n_in 등을 통해 대체할 수 있게 되었다.
class DNN(objec):
    def inference(self, x, keep_prob):
        # 입력층 - 은닉층, 은닉층 - 입력층
        for i, n_hidden in enumerate(self.n_hiddens):
            if i == 0:
                input = x
                input_dim = self.n_in
            else:
                input = output
                input_dim = self.n_hiddens[i-1]

        self.weight.append(self.weight_variable([input_dim, n_hidden]))
        self.bias.append(self.bias_variable([n_hidden]))

        h = tf.nn.relu(tf.matmul(input, self.weight[-1]) + self.bias[-1])
        output = tf.nn.dropout(hypothesis, keep_prob)

        # 은닉층 - 출력층
        self.weight.append(self.weight_variable([self.n_hiddens[-1], self.n_out]))
        self.bias.append(self.bias_variable([self.n_out]))

        hypothesis = tf.nn.softmax(tf.matmul(output, self.weight[-1]) + self.bias[-1])

        return hypothesis


fit() 함수 정의

  • Keras처럼 학습 데이터와 epoch 수, batch size 등 매개변수를 받아들일 수 있어야 한다. 또한 dropout 변수도 사용할 수 있어야 한다.
class DNN(objec):
    def fit(self, X_train, Y_train, epochs=100, batch_size=100, p_keep=0.5, verbose=1):
        # 학습처리
        x = tf.placeholder(tf.float32, shape=[None, self.n_in])
        y = tf.placeholder(tf.float32, shape=[None, self.n_out])
        keep_prob = tf.placeholder(tf.float32)

        # 예측을 위한 evauate() 함수용으로 작성해둔다
        self._x = x
        self._y = y
        self._keep_prob = keep_prob

        # 학습을 위한 오차 함수 및 예측 정확도 함수 그래프 생성
        hypothesis = self.inference(x, keep_prob)
        loss = self.loss(hypothesis, y)
        train = self.train(loss)
        # evaluate() 함수에서 참조할 수 있도록 함
        self.accuracy = self.accuracy(hypothesis, y)

        # 그래프 초기화
        init = tf.global_variables_initializer()
        sess = tf.Session()
        sess.run(init)

        # 예측을 위한 evaluate() 함수용으로 작성해둔다
        self._sess = sess

        N_train = len(X_train)
        n_batches = N_train // 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, feed_dict={x: X_[start:end],
                                           y: Y_[start: end],
                                           keep_prob: p_keep})

            loss_ = loss.eval(session=sess, feed_dict={x: X_train,
                                                       y: Y_train,
                                                       keep_prob: p_keep})
            
            # evaluate() 함수에서 참조할 수 있도록 함
            accuracy_ = self.accuracy.eval(session=sess, feed_dict={x: X_train,
                                                               y: Y_train,
                                                               keep_prob: p_keep})

            # 오차와 정확도를 그래프로 그리기 위해 값을 기록한다
            self._history['loss'].append(loss_)
            self._history['accuracy'].append(accuracy_)

            if verbose:
                print(f'epoch : {epoch:03}, loss : {loss_:.7}, accuracy : {accuracy_*100:.6} %')

        return self._history


  • 초기화 부분 다시 정리
    • 테스트할 때 사용되는 변수는 evaluate() 과정에도 필요하기 때문에 클래스 내부에 둬야 한다.
    • 학습 진행 상황을 클래서에서 파악하고 있으면 학습 종료 후에 데이터를 처리하기 쉬워지무로 self.history를 정의해 사용한다.
class DNN(objec):
    def __init__(self, n_in, n_hiddens, n_out):
        self.n_in = n_in
        self.n_hiddens = n_hiddens
        self.n_out = n_out
        self.weight = []
        self.bias = []

        # 학습 진행 상황을 확인하기 위하여 오차와 정확도를 _history 변수에 저장하고 한다
        self._x = None
        self._y = None
        self._keep_prob = None
        self._sess = None
        self._history = {'accuracy': [],
                         'loss': []}


evaluate() 함수 정의

  • 앞에서 정의한 self._sess 등을 사용해 다음과 같이 구현한다.
class DNN(objec):
    def evaluate(self, X_test, Y_test):
        # 모델 예측 결과 처리
        return self.accuracy(session=self._sess, feed_dict={self._x: X_test,
                                                            self._y: Y_test,
                                                            self._keep_prob: 1.0})


정의한 class를 사용하여 모델 설계하기앞서

  • 앞서 정의한 class를 사용한다면 main 함수에서 다음과 같이 코딩하여 사용할 수 있다.
if __name__ == '__main__':

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

    n = len(MNIST.data)
    N = int(n * 1.00)
    N_train = int(n * 0.2)
    N_val = int(N_train * 0.2)
    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, test_size=N_train)
    X_train, X_val, Y_train, Y_val = train_test_split(X_train, Y_train, test_size=N_val)

    n_in = len(X[0])
    n_hiddens = [200, 200, 200]
    n_out = len(Y[0])

    # 신경망 모델링 정의
    model = DNN(n_in=n_in, n_hiddens=n_hiddens, n_out=n_out)

    # 신경망 모델 학습
    model.fit(X_train, Y_train, epochs=50, batch_size=200, p_keep=0.5)

    accuracy = model.evaluate(X_test, Y_test)
    print(f'accuracy : {accuracy*100:.6} %')


  • 위의 예제에서는 fit() 함수가 수행하는 처리 내용을 단순한 형태로 코딩을 했는데 내부에 있는 처리 내용을 다시 분할해서 코딩하면 더욱 범용으로 편리하게 사용할 수 있는 API가 된다.
  • TensorFlow에서는 이러한 높은 수준의 API도 제공하고 있는데, tf.contrib.learn을 통해 이용할 수 있다.


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



+ Recent posts