2017년 12월 25일 월요일

ML 초보자를 위한 MNIST(MNIST For ML Beginners)(4)


출처 : https://www.tensorflow.org/get_started/mnist/beginners

이글은 영어 원본을 읽고 공부하면서 불필요한 내용 빼고 이해하기 쉽도록 적절히 내맘대로 작성해보았습니다. 이해가 잘못되어 원저자의 의도대로 번역이 안되어 있을 수도 있습니다. 이점 참고해서 읽어 주시면 고맙겠습니다


앞서 작성한 글
ML 초보자를 위한 MNIST(MNIST For ML Beginners)(1)
ML 초보자를 위한 MNIST(MNIST For ML Beginners)(2)
ML 초보자를 위한 MNIST(MNIST For ML Beginners)(3)

이 글은 앞서 작성한 글에 이어지는 글입니다.



훈련


우리는 훈련을 하기 위해서 모델이 충분히 좋은 것이 어떤 것인지 정의를 해야합니다. 실제로, 기계 학습에서 우리는 전형적으로 모델이 나쁜 것을 의미하는 것을 정의합니다. 우우리는 이것을 비용 또는 손실이라 부르며 모델이 원하는 결과와 얼마나 멀리 떨어져 있는지 나타냅니다. 우리는 오류를 최소화하려고 노력하며 오류 마진이 작을수록 모델이 좋습니다.
(정리하자면 모델에 데이터를 넣어 훈련을 해서 오류를 최소화 하도록 한다는 뜻입니다.)
모델의 손실을 결정하는 매우 일반적인 함수 중 하나 인 "교차 엔트로피 (cross-entropy)"라고 합니다.
교차 엔트로피는 다음과 같이 정의 됩니다.
여기서 y는 우리의 예측된 확률 분포이고, y'는 실제 분포 (숫자 레이블을 갖는 원 핫 벡터)입니다. 크로스 엔트로피는 우리의 예측이 실제 값을 묘사하는 데 얼마나 비효율적인지를 측정합니다.(말이 어렵습니다. ) 크로스 엔트로피에 대한 자세한 내용은이 튜토리얼의 범위를 벗어나지만 이해할만한 가치가 있습니다.
교차 엔트로피를 구현하려면 정답을 입력하기 위해 먼저 새로운 "placeholder"를 추가해야합니다
y_ = tf.placeholder(tf.float32, [None, 10])
그런 다음 교차 엔트로피 함수를 구현할 수 있습니다.
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
먼저, tf.log는 y의 각 원소의 로그값를 계산합니다. 다음으로, y_의 각 요소에 tf.log (y)의 해당 요소를 곱합니다. 그런 다음 tf.reduce_sum은 reduction_indices = [1] 매개 변수로 인해 y의 두 번째 차원에 요소들을 더합니다. 마지막으로 tf.reduce_mean은 배치의 모든 예제에 대한 평균을 계산합니다.
소스 코드에서는 수치적으로 불안정하기 때문에 이 공식을 사용하지 않았습니다.(수치적으로 불안정하다는 말은 이해하기가 난감하네요. ) 대신, 정규화되지 않은 로그에 tf.nn.softmax_cross_entropy_with_logits를 적용합니다
(예를 들어 tf.matmul (x, W) + b에서 softmax_cross_entropy_with_logits를 호출합니다.)
왜냐하면 더 수치적으로 안정된 함수가 softmax 활성화를 내부적으로 계산하기 때문입니다. 당신의 코드에서 tf.nn.softmax_cross_entropy_with_logits 를 사용하는것을 고려해 보세요.
소스 코드를 살펴보면 동일한 내용의 주석이 있습니다. 정리하자면,
-tf.reduce_sum(y_ * tf.log(tf.nn.softmax(y)), reduction_indices=[1]) 이 부분 대신 tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y) 이렇게 사용하라는 의미 입니다.

  # The raw formulation of cross-entropy,
  #
  #   tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(tf.nn.softmax(y)),
  #                                 reduction_indices=[1]))
  #
  # can be numerically unstable.
  #
  # So here we use tf.nn.softmax_cross_entropy_with_logits on the raw
  # outputs of 'y', and then average across the batch.
  cross_entropy = tf.reduce_mean(
      tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))

여기에서 궁금한점이 생겼습니다. 수치적으로 안정화되었다는게 무엇인지... 참 알다가도 모를 일입니다. 그래서 원본 소스에서 softmax_cross_entropy_with_logits 를 사용하지 않고 reduce_sum 를 사용해서 결과를 살펴보았습니다. 결과는 크게 차이가 없는것 같았습니다. 전문가 분이 수치적으로 안정화 되어있다는 말을 그냥 믿어야 할것 같습니다.
이제 우리 모델이 하고자하는 것이 무엇인지 알았으므로 TensorFlow가 그렇게 하도록 훈련시키는 것은 매우 쉽습니다. TensorFlow는 계산의 전체 그래프를 알고 있기 때문에 그것은 변수가 최소화하도록 요청한 손실(loss 또는 error를 의미)에 효율적으로 결정하기 위해서 backpropagation (역전파) 알고리즘을 자동으로 사용할 수 있습니다.
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
위 경우, 학습 속도가 0.5 인 그래디언트 디센트(경사 하강)알고리즘을 사용하여 cross_entropy를 최소화하도록 TensorFlow에 요청합니다. 경사 하강법은 간단한 절차입니다. TensorFlow는 각 변수의 비용을 줄이는 방향으로 조금씩 옮깁니다. TensorFlow는 다른 많은 최적화 알고리즘도 제공하는데 한 줄 바꾸는것 만큼 간단합니다.
뒤쪽에서 Tensorflow에서 실제로 하는 것은, 역전파와 경사 하강를 구현하는 그래프에 새로운 작업을 추가하는 것입니다. 그리고 나서 그것이 동작을 다시 한번 해 줍니다. 그것이 동작을 할 때, 약간의 경사 하강 train을 하고, 손실을 줄이기 위해 변수를 약간 변경합니다. (원문 내용이 뭔가 계속적으로 동일하고 비슷한 말만 하고 있습니다.)
이제 이 모델을 InteractiveSession(상호 작용)으로 시작할 수 있습니다.
sess = tf.InteractiveSession()
먼저 생성한 변수를 초기화하는 작업을 생성해야 합니다.
InteractiveSession을 사용하는것은 기본 세션을 설정한다는 의미로 받아들이면 조금 쉽습니다. 그래서 아래와같이 run을 곧장 수행할 수가 있습니다.
tf.global_variables_initializer().run()
위 내용은 앞에서도 몇차례 등장했던 내용입니다.
InteractiveSession 은 https://www.tensorflow.org/api_docs/python/tf/InteractiveSession 링크에서도 확인이 가능합니다.
Session 사용 예제)
  sess = tf.Session()
  sess.run(tf.global_variables_initializer())
InteractiveSession 사용 예제)
  sess = tf.InteractiveSession()
  tf.global_variables_initializer().run()

자, 이제 훈련을 1000번 해 봅시다!
for _ in range(1000):
  batch_xs, batch_ys = mnist.train.next_batch(100)
  sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
루프의 각각의 step(단계) 에서, 우리는 훈련 데이터셋에서 100개의 랜덤 데이터 포인트 batch를 얻습니다. feed_dict에 값을 넣어 placeholders의 값(x, y_)을 변경하여 train_step(위에서 언급한 train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy))을 실행합니다. x,y_는 placeholder로 외부에서 값을 변경하기 위한 변수입니다.
참고로 앞에서 x,y_는 아래와 같이 정의 하였습니다.
  x = tf.placeholder(tf.float32, [None, 784])
  y_ = tf.placeholder(tf.float32, [None, 10])

무작위 데이터의 작은 일괄(batch) 처리를 사용하는 확률적 훈련 - 위 경우(mnist.train.next_batch(100) 처럼 사용)에는 확률적 경사 하강이라고 합니다. 이상적으로, 우리는 우리가 해야 할 일에 대해 모든 훈련 단계에 대해서 전체 데이터를 사용하면 좋지만, 비용이 많이 듭니다. 그래서, 대신 매번 다른 부분 집합을 사용합니다. 이렇게 하는 것은 저렴하고 많은 이점을 가지고 있습니다.

댓글 없음:

댓글 쓰기