딥러닝 입력 데이터 정규화 (Normalizing inputs)


학습을 수행하기 전에 값의 범위를 normalization 하는 것은 중요하다. 그 이유는 아래와 같다.

입력 변수가 MLP에서와 같이 선형 적으로 결합된다면 적어도 이론 상으로는 입력을 표준화하는 것이 거의 필요하지 않습니다.
그 이유는 해당 weight bais를 변경하여 입력 벡터를 재조정하면 이전과 완전히 똑같은 결과를 남길 수 있기 때문입니다.
그러나 입력을 Standardization하면 학습을 더 빨리하고 지역 최적의 상태에 빠지게 될 가능성을 줄이는 다양한 실용적인 이유가 있습니다.
또한, 표준화 된 입력을 통해 Gradient Descent  Bayesian estimation을 보다 편리하게 수행 할 수 있습니다.

Normalization

수식 : (요소값 - 최소값) / (최대값 - 최소값)
설명 : 전체 구간을 0~1사이의 값으로 맞춰 준다.

Standardization

수식 : (요소값 - 평균) / 표준편차
설명 : 평균은 0 표준편차는

$$\mu = \frac{1}{m} \sum_{i=1}^{m}{x^{(i)}}$$
$$x := x -\mu$$
$$\sigma^{2}=\frac{1}{m}\sum_{i=1}^{m}{(x^{(i)}-\mu)^{2}}$$

직관적 이해

아래와 같이 Unnormalized된 상태에서는 Learning Rate을 매우 작게 설정해야 정상적을 학습이 된다.
이유는 cost 그래프가 elongated하기 때문이다. 따라서 elongated contour의 모습을 가진다.
아래와 같이 Input의 Range가 서로 다르다면 Gradient Descent Algorithm을 적용하는것이 매우 까다로워지는 상황이 발생 한다.

하지만 normalization을 적용하면 좀 더 spherical contour를 가지게 된다.
이렇게 하면 좀 더 Gradient Descent Algorithm으로 쉽게 그리고 빠르게 최적화 지점을 찾게 된다.

구현 코드

아래와 같은 코드를 가질 때
x가 1.0
y가 0.0
이렇게 1.0~0 사이의 값으로 입력과 출력이 정규화가 잘되면 학습이 잘 이뤄 진다.
Learning rate을 적절히 값을 잡아도 학습이 잘된다.

import tensorflow as tf
x = tf.constant(1.0, name='input')
w = tf.Variable(0.8, name='weight')
y = tf.mul(w, x, name='output')
y_ = tf.constant(0.0, name='correct_value')
loss = tf.pow(y - y_, 2, name='loss')
train_step = tf.train.GradientDescentOptimizer(0.025).minimize(loss)

for value in [x, w, y, y_, loss]:
    tf.scalar_summary(value.op.name, value)

summaries = tf.merge_all_summaries()

sess = tf.Session()
summary_writer = tf.train.SummaryWriter('log_simple_stats', sess.graph)

sess.run(tf.initialize_all_variables())
for i in range(100):
    if i % 10 ==0:
        print("epoch {}, output: {}".format(i, sess.run(y)))
    summary_writer.add_summary(sess.run(summaries), i)
    sess.run(train_step)
epoch 80, output: 0.01321229338645935
epoch 90, output: 0.007910688407719135

하지만 x의 범위르 10배만 넓혀서 10으로 하면 학습이 실패 한다.
이 때는 Learning rate을 더 작게 주어야 한다.

import tensorflow as tf
x = tf.constant(10.0, name='input')
w = tf.Variable(0.8, name='weight')
y = tf.mul(w, x, name='output')
y_ = tf.constant(0.0, name='correct_value')
loss = tf.pow(y - y_, 2, name='loss')
train_step = tf.train.GradientDescentOptimizer(0.025).minimize(loss)

for value in [x, w, y, y_, loss]:
    tf.scalar_summary(value.op.name, value)

summaries = tf.merge_all_summaries()

sess = tf.Session()
summary_writer = tf.train.SummaryWriter('log_simple_stats', sess.graph)

sess.run(tf.initialize_all_variables())
for i in range(100):
    if i % 10 ==0:
        print("epoch {}, output: {}".format(i, sess.run(y)))
    summary_writer.add_summary(sess.run(summaries), i)
    sess.run(train_step)
epoch 70, output: nan

통상의 Learning Rate의 문제와 비슷하게 발생 한다.

참고 사이트

http://stackoverflow.com/questions/4674623/why-do-we-have-to-normalize-the-input-for-an-artificial-neural-network


  1. sds888 2018.04.23 14:38 신고

    유익한 글 감사합니다. 저도 현재 DNN을 이용해 공부를 하고 있는데 Normalize에 대해 궁금한 점이 있어 질문드립니다.
    제 input data se t중에 음수(Neagative)값이 포함되어있는데 이러한 경우 Normalize를 0~1로 하는 것 보다 -1~1로 하는 것이 더욱 효율적이라고 생각하는데 이렇게 Normalize하는 것은 어떤가요?

    • JAYNUX 2018.04.23 16:58 신고

      댓글 감사합니다.
      음 저도 예전에 그렇게 생각해서 [-1,1]과 [0,1] 두개로 나눠서 해봤는데 별 차이는 없었던걸로 기억합니다.

      그리고 output이 [0,1] 사이의 값이라면 binary classification이던 softmax classification이던 보통 그렇게 되니 input도 [0,1] range로 맞춰 주는게 보통 좋더라고요. 학습도 더 잘 성공하고요.

      아마 covariate shift문제를 해결한 Bath Normalization이 그런 원리인것 같습니다. 요즘은 Bath Norm.을 기본으로 많이 쓰니 그것을 적용하시는게 더 좋을 것 같습니다.

    • sds888 2018.04.25 10:52 신고

      아 그렇군요 답변 감사합니다!

+ Recent posts