오늘의 주제는 Neural Network 최적화입니다. Sigmoid보다 좀 더 효율적이라고 알려져 있는 ReLU, 초기 Weight를 좀 더 효율적으로 초기화시키는 Xavier Initializer, Dropout을 다뤄볼 것입니다.


먼저 Neural Network가 최적화되었음을 확인할 수 있게 간단히 Train & Test가 가능한 MNIST(Mixed National Institute of Standards and Technology database) 데이터셋을 사용합니다. MNIST는 숫자 손글씨를 28x28의 흑백 이미지와 그 숫자 손글씨가 어떤 숫자를 나타내는지 label이 붙어있는 데이터셋입니다. TensorFlow에서 제공하는 MNIST는 55,000개의 train set, 5,000개의 validation set, 10,000개의 test set으로 구성되어 있습니다. train set 이미지를 모아서 보면 아래 이미지와 같습니다.


네 뭐.... 엄청 많습니다. ㅋㅋㅋ


이제 본론으로 돌아와서, 이 MNIST에서 주어지는 train set을 학습시켜서 Neural Network가 test set의 이미지 숫자를 정상적으로 인식해내게 하면 됩니다. 먼저 가장 먼저 했던 Softmax로 위를 분류해보겠습니다. 모델의 종류만 다르고 실제로 학습 & 테스트 시에 dropout_rate 유무를 제외하고는 모든 코드가 같기 때문에 간결한 설명을 위해서 각 모델에 대한 설명만 하겠습니다. 코드는 첨부된 Jupyter Notebook 파일 또는 Python 파일을 참조해주시기 바랍니다.


기본적으로 image는 28x28(=768)의 데이터(1과 0으로 음영 구분), label은 10개의 배열(image에 맞는 숫자 index에 표기)이므로 아래와 같이 input placeholder를 짭니다.

X = tf.placeholder(tf.float32, [None, 28*28], name="x")
Y = tf.placeholder(tf.float32, [None, 10], name="y")

Xavier Initializer를 제외한 모든 Weight 초기화는 tf.random_normal로 했고, 최적화 모델은 기존에 쓰던 GradientDescentOptimizer보다 더 좋은 AdamOptimizer를 사용하였습니다. learning_rate는 0.001입니다.


1. Softmax

DNN Optimization(Softmax).ipynb

DNN Optimization(Softmax).py

간단히 28*28 행렬에서 곱을 하여 10 행렬을 만들어야 하기 때문에 아래와 같이 모델을 만듭니다.

w = tf.Variable(tf.zeros([28*28, 10]), name="Weight")
b = tf.Variable(tf.zeros([10]), name="Bias")

H = tf.nn.softmax(tf.matmul(X, w) + b)

이후 이 모델을 학습시키고 test set을 돌리면 다음과 같은 결과가 나옵니다. Epoch는 55,000개의 train set의 반복한 횟수입니다. (최적화 기술에 대한 비교를 위해 모든 기타 옵션을 똑같이 고정했기 때문에, Softmax의 경우 random_normal 대신 0으로, 최적화 모델도 rate랑 모델도 바꿔보면 92% 정도까지 나옵니다.)

Epoch: 1
Test Accuracy: 0.4371
Epoch: 2
Test Accuracy: 0.5924
Epoch: 3
Test Accuracy: 0.7421
Epoch: 4
Test Accuracy: 0.7831
Epoch: 5
Test Accuracy: 0.8081
Epoch: 6
Test Accuracy: 0.8268
Epoch: 7
Test Accuracy: 0.8429
Epoch: 8
Test Accuracy: 0.858
Epoch: 9
Test Accuracy: 0.8761
Epoch: 10
Test Accuracy: 0.8849
Epoch: 11
Test Accuracy: 0.892
Epoch: 12
Test Accuracy: 0.8962
Epoch: 13
Test Accuracy: 0.9006
Epoch: 14
Test Accuracy: 0.9036
Epoch: 15
Test Accuracy: 0.905


2. 4-Layer DNN, ReLU

DNN Optimization(4-Layer DNN, ReLU).ipynb

DNN Optimization(4-Layer DNN, ReLU).py DNN Optimization(4-Layer DNN, ReLU).ipynb

이번엔 지난 번에 배운 NN을 적용하고, activation function으로 sigmoid 대신 더 효율적이라고 알려져 있는 ReLU를 사용합니다. ReLU는 sigmoid와는 다르게 치역의 범위가 [0, ∞] 이며 간단하게 max(0, x)으로 나타낼 수 있습니다. 어떻게 보면 sigmoid의 1보다 좀 더 가중치를 두게 하여 더 효율적인 학습을 하게 했다고 볼 수 있을 거 같습니다. 모델은 다음과 같습니다.

def nn_layer(input_data, output_size):
    W = tf.Variable(tf.random_normal([input_data.get_shape().as_list()[1], output_size]))
    B = tf.Variable(tf.random_normal([output_size]))
    return tf.matmul(input_data, W) + B

with tf.name_scope("Layer2"):
    L2 = tf.nn.relu(nn_layer(X, 14*14))
    
with tf.name_scope("Layer3"):
    L3 = tf.nn.relu(nn_layer(L2, 14*14))
    
with tf.name_scope("Layer4"):
    H = nn_layer(L3, 10)


실행 결과는 다음과 같습니다. 위의 Softmax보다 좀 더 개선된 모습을 볼 수 있습니다.

Epoch: 1
Test Accuracy: 0.8417
Epoch: 2
Test Accuracy: 0.8828
Epoch: 3
Test Accuracy: 0.8995
Epoch: 4
Test Accuracy: 0.9103
Epoch: 5
Test Accuracy: 0.9194
Epoch: 6
Test Accuracy: 0.9253
Epoch: 7
Test Accuracy: 0.9283
Epoch: 8
Test Accuracy: 0.9276
Epoch: 9
Test Accuracy: 0.935
Epoch: 10
Test Accuracy: 0.9351
Epoch: 11
Test Accuracy: 0.9375
Epoch: 12
Test Accuracy: 0.9401
Epoch: 13
Test Accuracy: 0.9404
Epoch: 14
Test Accuracy: 0.9389
Epoch: 15
Test Accuracy: 0.9427


3. 4-Layer DNN, ReLU, Xavier Initializer

DNN Optimization(4-Layer DNN, ReLU, Xavier Init).ipynb

DNN Optimization(4-Layer DNN, ReLU, Xavier Init).py

과거의 Deep Learning에서는 Weight의 초기값을 단순히 random으로 초기화하거나 학습이 진행만 된다면 0으로 초기화하기도 하였습니다. 그러나 이 초기값 초기화의 중요성이 대두되면서 Xavier Initializer와 같은 새로운 초기화 알고리즘이 등장하게 되었습니다. 꽤 효과적인 Xavier Initializer는 다음과 같은 알고리즘으로 초기화를 합니다.

def xavier_init(n_inputs, n_outputs, uniform=True):
  """Set the parameter initialization using the method described.
  This method is designed to keep the scale of the gradients roughly the same
  in all layers.
  Xavier Glorot and Yoshua Bengio (2010):
           Understanding the difficulty of training deep feedforward neural
           networks. International conference on artificial intelligence and
           statistics.
  Args:
    n_inputs: The number of input nodes into each output.
    n_outputs: The number of output nodes for each input.
    uniform: If true use a uniform distribution, otherwise use a normal.
  Returns:
    An initializer.
  """
  if uniform:
    # 6 was used in the paper.
    init_range = math.sqrt(6.0 / (n_inputs + n_outputs))
    return tf.random_uniform_initializer(-init_range, init_range)
  else:
    # 3 gives us approximately the same limits as above since this repicks
    # values greater than 2 standard deviations from the mean.
    stddev = math.sqrt(3.0 / (n_inputs + n_outputs))
    return tf.truncated_normal_initializer(stddev=stddev)

실제로는 TensorFlow에 구현되어 있기 때문에 아래와 같이 간단하게 사용하여 모델을 작성할 수 있습니다. (본 강의에서는 w만 Xavier Initializer 쓴 거 같던데, 전 그냥 bias까지 썻습니다. 어차피 큰 차이는 없는 거 같기에...)

def nn_layer(name, input_data, output_size):
    W = tf.get_variable(name=name + "_W",
                        shape=[input_data.get_shape().as_list()[1], output_size],
                        initializer=tf.contrib.layers.xavier_initializer())
    B = tf.get_variable(name=name + "_B",
                        shape=[output_size],
                        initializer=tf.contrib.layers.xavier_initializer())
    return tf.matmul(input_data, W) + B

with tf.name_scope("Layer2"):
    L2 = tf.nn.relu(nn_layer("L2", X, 14*14))
    
with tf.name_scope("Layer3"):
    L3 = tf.nn.relu(nn_layer("L3", L2, 14*14))
    
with tf.name_scope("Layer4"):
    H = nn_layer("L4", L3, 10)

실행 결과는 다음과 같습니다. 이전과는 달리, 엄청나게 개선된 모습을 볼 수 있습니다.

Epoch: 1
Test Accuracy: 0.9509
Epoch: 2
Test Accuracy: 0.972
Epoch: 3
Test Accuracy: 0.9754
Epoch: 4
Test Accuracy: 0.9773
Epoch: 5
Test Accuracy: 0.9724
Epoch: 6
Test Accuracy: 0.9776
Epoch: 7
Test Accuracy: 0.9772
Epoch: 8
Test Accuracy: 0.977
Epoch: 9
Test Accuracy: 0.9791
Epoch: 10
Test Accuracy: 0.9791
Epoch: 11
Test Accuracy: 0.9773
Epoch: 12
Test Accuracy: 0.9807
Epoch: 13
Test Accuracy: 0.9811
Epoch: 14
Test Accuracy: 0.9757
Epoch: 15
Test Accuracy: 0.9787


4. 4-Layer DNN, ReLU, Xavier Initializer, Dropout

DNN Optimization(4-Layer DNN, ReLU, Xavier Init, Dropout).ipynb

DNN Optimization(4-Layer DNN, ReLU, Xavier Init, Dropout).py

이번에는 Dropout이라는 기술을 써볼 겁니다. 아주 간단합니다. Neural Network에서 일정 비율의 node들을 비활성화 시킨 채로 학습을 진행하고, 이후에 test 할 때 모두 활성화시키는 기술입니다. 실제로 효과가 꽤 있다고는 하는 거 같다만, 여기서는 그렇게 눈에 띄게 효과가 나타나진 않습니다. 모델과 결과는 다음과 같습니다.

def nn_layer(name, input_data, output_size):
    W = tf.get_variable(name=name + "_W",
                        shape=[input_data.get_shape().as_list()[1], output_size],
                        initializer=tf.contrib.layers.xavier_initializer())
    B = tf.get_variable(name=name + "_B",
                        shape=[output_size],
                        initializer=tf.contrib.layers.xavier_initializer())
    return tf.matmul(input_data, W) + B

dropout_rate = tf.placeholder(tf.float32)

with tf.name_scope("Layer2"):
    _L2 = tf.nn.relu(nn_layer("L2", X, 14*14))
    L2 = tf.nn.dropout(_L2, dropout_rate)
    
with tf.name_scope("Layer3"):
    _L3 = tf.nn.relu(nn_layer("L3", L2, 14*14))
    L3 = tf.nn.dropout(_L3, dropout_rate)
    
with tf.name_scope("Layer4"):
    _H = nn_layer("L4", L3, 10)
    H = tf.nn.dropout(_H, dropout_rate)
Epoch: 1
Test Accuracy: 0.9436
Epoch: 2
Test Accuracy: 0.9665
Epoch: 3
Test Accuracy: 0.9705
Epoch: 4
Test Accuracy: 0.9711
Epoch: 5
Test Accuracy: 0.9767
Epoch:  DNN Optimization(6-Layer DNN, ReLU, Xavier Init, Dropout, Ensemb6
Test Accuracy: 0.9754
Epoch: 7
Test Accuracy: 0.9774
Epoch: 8
Test Accuracy: 0.9802
Epoch: 9
Test Accuracy: 0.9807
Epoch: 10
Test Accuracy: 0.9781
Epoch: 11
Test Accuracy: 0.9798
Epoch: 12
Test Accuracy: 0.9795
Epoch: 13
Test Accuracy: 0.9805
Epoch: 14
Test Accuracy: 0.9804
Epoch: 15
Test Accuracy: 0.979


5. 6-Layer DNN, ReLU, Xavier Initializer, Dropout

DNN Optimization(6-Layer DNN, ReLU, Xavier Init, Dropout).ipynb

DNN Optimization(6-Layer DNN, ReLU, Xavier Init, Dropout).py

모델은 4번과 같으며 여기서 14*14 행렬로 레이어를 2개 더 늘렸습니다. 미미한 정확도 상승을 확인할 수 있습니다.

Epoch: 1
Test Accuracy: 0.9351
Epoch: 2
Test Accuracy: 0.9634
Epoch: 3
Test Accuracy: 0.9696
Epoch: 4
Test Accuracy: 0.9729
Epoch: 5
Test Accuracy: 0.9743
Epoch: 6
Test Accuracy: 0.9754
Epoch: 7
Test Accuracy: 0.975
Epoch: 8
Test Accuracy: 0.9782
Epoch: 9
Test Accuracy: 0.9776
Epoch: 10
Test Accuracy: 0.9776
Epoch: 11
Test Accuracy: 0.9772
Epoch: 12
Test Accuracy: 0.9787
Epoch: 13
Test Accuracy: 0.9795
Epoch: 14
Test Accuracy: 0.9794
Epoch: 15
Test Accuracy: 0.9793


6. 6-Layer DNN, ReLU, Xavier Initializer, Dropout, Ensemble(5 NNs)

DNN Optimization(6-Layer DNN, ReLU, Xavier Init, Dropout, Ensemb

DNN Optimization(6-Layer DNN, ReLU, Xavier Init, Dropout, Ensemb

이번엔 Ensemble(앙상블)을 해보겠습니다. Dropout보다 간단한 기술인데(기술이라고 해야하나..), 여러 개의 NN을 학습시켜서 여러 개의 NN에 test data를 넣고 predicted label을 평균 내는 등의 방법으로 정확도를 좀 더 개선하는 방법입니다. 강의에서는 3% 정도까지 정확도가 상승된다고 하셨는데, 실제로 제가 돌려봤을 때에는 0.5% 정도? 정확도가 상승하는 것을 확인할 수 있었습니다.

NN: 0 Epoch: 1 Test Accuracy: 0.9483 NN: 0 Epoch: 2 Test Accuracy: 0.9664 NN: 0 Epoch: 3 Test Accuracy: 0.9707 NN: 0 Epoch: 4 Test Accuracy: 0.9713 NN: 0 Epoch: 5 Test Accuracy: 0.9752 NN: 0 Epoch: 6 Test Accuracy: 0.9779 NN: 0 Epoch: 7 Test Accuracy: 0.9764 NN: 0 Epoch: 8 Test Accuracy: 0.9766 NN: 0 Epoch: 9 Test Accuracy: 0.9776 NN: 0 Epoch: 10 Test Accuracy: 0.9744 NN: 0 Epoch: 11 Test Accuracy: 0.9775 NN: 0 Epoch: 12 Test Accuracy: 0.9772 NN: 0 Epoch: 13 Test Accuracy: 0.9796 NN: 0 Epoch: 14 Test Accuracy: 0.9798 NN: 0 Epoch: 15 Test Accuracy: 0.9786 NN: 1 Epoch: 1 Test Accuracy: 0.9525 NN: 1 Epoch: 2 Test Accuracy: 0.959 NN: 1 Epoch: 3 Test Accuracy: 0.9666 NN: 1 Epoch: 4 Test Accuracy: 0.972 NN: 1 Epoch: 5 Test Accuracy: 0.9736 NN: 1 Epoch: 6 Test Accuracy: 0.9736 NN: 1 Epoch: 7 Test Accuracy: 0.9746 NN: 1 Epoch: 8 Test Accuracy: 0.9771 NN: 1 Epoch: 9 Test Accuracy: 0.9771 NN: 1 Epoch: 10 Test Accuracy: 0.9766 NN: 1 Epoch: 11 Test Accuracy: 0.978 NN: 1 Epoch: 12 Test Accuracy: 0.9788 NN: 1 Epoch: 13 Test Accuracy: 0.9787 NN: 1 Epoch: 14 Test Accuracy: 0.98 NN: 1 Epoch: 15 Test Accuracy: 0.9787 NN: 2 Epoch: 1 Test Accuracy: 0.9537 NN: 2 Epoch: 2 Test Accuracy: 0.966 NN: 2 Epoch: 3 Test Accuracy: 0.9742 NN: 2 Epoch: 4 Test Accuracy: 0.9731 NN: 2 Epoch: 5 Test Accuracy: 0.976 NN: 2 Epoch: 6 Test Accuracy: 0.9777 NN: 2 Epoch: 7 Test Accuracy: 0.9748 NN: 2 Epoch: 8 Test Accuracy: 0.978 NN: 2 Epoch: 9 Test Accuracy: 0.9766 NN: 2 Epoch: 10 Test Accuracy: 0.9784 NN: 2 Epoch: 11 Test Accuracy: 0.9787 NN: 2 Epoch: 12 Test Accuracy: 0.9767 NN: 2 Epoch: 13 Test Accuracy: 0.9813 NN: 2 Epoch: 14 Test Accuracy: 0.9804 NN: 2 Epoch: 15 Test Accuracy: 0.9815 NN: 3 Epoch: 1 Test Accuracy: 0.9547 NN: 3 Epoch: 2 Test Accuracy: 0.9677 NN: 3 Epoch: 3 Test Accuracy: 0.9722 NN: 3 Epoch: 4 Test Accuracy: 0.9708 NN: 3 Epoch: 5 Test Accuracy: 0.9728 NN: 3 Epoch: 6 Test Accuracy: 0.9753 NN: 3 Epoch: 7 Test Accuracy: 0.9773 NN: 3 Epoch: 8 Test Accuracy: 0.9783 NN: 3 Epoch: 9 Test Accuracy: 0.977 NN: 3 Epoch: 10 Test Accuracy: 0.9775 NN: 3 Epoch: 11 Test Accuracy: 0.9795 NN: 3 Epoch: 12 Test Accuracy: 0.9794 NN: 3 Epoch: 13 Test Accuracy: 0.9792 NN: 3 Epoch: 14 Test Accuracy: 0.9809 NN: 3 Epoch: 15 Test Accuracy: 0.9802 NN: 4 Epoch: 1 Test Accuracy: 0.9469 NN: 4 Epoch: 2 Test Accuracy: 0.9663 NN: 4 Epoch: 3 Test Accuracy: 0.9673 NN: 4 Epoch: 4 Test Accuracy: 0.9742 NN: 4 Epoch: 5 Test Accuracy: 0.9723 NN: 4 Epoch: 6 Test Accuracy: 0.9739 NN: 4 Epoch: 7 Test Accuracy: 0.9763 NN: 4 Epoch: 8 Test Accuracy: 0.9772 NN: 4 Epoch: 9 Test Accuracy: 0.9758 NN: 4 Epoch: 10 Test Accuracy: 0.9797 NN: 4 Epoch: 11 Test Accuracy: 0.9783 NN: 4 Epoch: 12 Test Accuracy: 0.9798 NN: 4 Epoch: 13 Test Accuracy: 0.9805 NN: 4 Epoch: 14 Test Accuracy: 0.9808 NN: 4 Epoch: 15 Test Accuracy: 0.9804


Ensemble Accuracy: 0.9846

이렇게 간단히 MNIST를 사용하여 딥러닝에서 사용되는 여러 기술을 적용해보고 테스트를 해봤습니다. 다음 번에는 CNN, RNN 등을 나갈 예정입니다.


*Ensemble은 생각보다 TensorFlow에서 구현하는 방법이 잘 나오지 않아서 아래 링크를 참조했습니다.

https://github.com/Hvass-Labs/TensorFlow-Tutorials/blob/master/05_Ensemble_Learning.ipynb

  1. Study 2017.10.17 11:39 신고

    DNN 구현할 때 Output Data로 14×14를 넣으신 이유는 Layer를 2번 돌리기 때문일까요?

    • makeapp 2017.10.28 00:10 신고

      네, 중간 레이어 크기를 적당하게 잡아주기 위해서 적당히 14x14를 넣었습니다.

아주 간단한 Deep Learning에 대한 기초를 이전 글들을 통해 쌓아봤습니다. 이제 본격적으로 NN(Neural Network)를 시작할 겁니다. (기대되네요 >_<)


먼저 간단한 NN으로 다층 퍼셉트론을 이용한 XOR 문제를 해결해볼 것입니다. XOR(eXclusive OR: 베타적 논리합)은 논리 연산 중 하나입니다. 컴퓨터 프로그래밍에서는 A^B로 나타낼 수 있습니다. 두 변수가 있고, 이를 XOR 했을 때 결과는 다음 표와 같습니다.


B

A^B

 False

False

False

True

False

True

False

True

True

True

True

False


OR 연산과 다른 점은 A와 B를 연산했을 때 True가 아니라 False가 나온다는 점입니다. True를 1, False를 0이라고 두고 결과값 True를 파란색 점, False를 빨간색 점이라고 두면 다음과 같은 그래프로 나타낼 수 있습니다.

보시다시피, 이렇게 나와있는 형태의 경우 직선으로는 두 종류를 정확도 100%로 분할할 수 없습니다. 따라서 선형 분류가 불가능하다는 소리인데, 이런 경우가 XOR 말고도 꽤 있습니다. 따라서 이런 문제에서는 sigmoid 함수(뉴런)를 한 번만 사용하는 것이 아닌 여러 번을 겹쳐 사용하여 문제를 해결합니다. 간단히 아래와 같이 NN을 설계해보겠습니다.

(그래프 표기는 http://www.asimovinstitute.org/neural-network-zoo/를 따랐습니다. 넘나 이뻐서... 앞으로도 그래프는 이것을 따를 예정입니다)

노란색 노드: 입력, 초록색 노드: 은닉층, 주황색 노드: 출력

노란색 노드를 제외하고는 모두 sigmoid를 가지고 있는 연산노드입니다. 각 노드를 왼쪽부터 순서대로 Layer 1(노란색), Layer 2(초록색), Layer 3(주황색)이라고 하고 각 층마다 행렬로 묶어서 연산을 하면 좀 더 간단하게 연산을 나타낼 수 있습니다. 아래가 그 모습입니다.


이 상태에서 Layer 2, Layer 3에 sigmoid를 적용하면 끝인거죠. 참 쉽죠?


다음으로 이 네트워크를 어떻게 최적화시킬지 알아보겠습니다. 물론 TensorFlow 상에선 cross-entropy를 적용하고 GradientDescentOptimizer를 적용하면 바로 해결됩니다. 그래도 간단히 최적화시키는 기법인 Backpropagation을 알아가면 좋을 거 같아서 한 번 알아보겠습니다.


Backpropagation(역전파법)은 다음과 같은 과정으로 이뤄집니다.

1. Forward Propagation: 임의의 작은 수로 Network의 Weight를 초기화합니다. 그리고 학습 데이터를 입력하여 출력을 만들어냅니다.

2. label과 출력의 에러를 계산해냅니다. 그리고 이를 기반으로 Weight에 대한 미분값을 구합니다.


3. 계산된 에러의 미분값을 이용해 학습률(Learning rate)를 정하고 학습률만큼 수정된 Weight를 구해 업데이트합니다. 그리고 네트워크가 일정한 정확도

     에 도달할 때까지 이 과정을 반복합니다.


수식으로 나타내보고 싶지만, 이게 여간 쉬워보이진 않아서(...) 나중에 기회가 되면 수식으로 정리해보겠습니다.


이렇게 기초적인 NN에 대해 정리를 해봤습니다. 아래 코드를 통해서 이를 직접 테스트할 수 있습니다. Jupyter Notebook 파일도 첨부합니다.

Hello, NN!.ipynb

import tensorflow as tf
import numpy as np

x = [[0, 0], [0, 1], [1, 0], [1 ,1]]
y = [[0], [1], [1], [0]]

X = tf.placeholder(tf.float32, [None, 2], name="X-Input")
Y = tf.placeholder(tf.float32, [None, 1], name="Y-Input")

W1 = tf.Variable(tf.random_uniform([2, 2], -1., 1.), name="W1")
W2 = tf.Variable(tf.random_uniform([2, 1], -1., 1.), name="W2")
B1 = tf.Variable(tf.zeros([2]), name="B1")
B2 = tf.Variable(tf.zeros([1]), name="B2")

with tf.name_scope("Layer2") as scope:
    L2 = tf.sigmoid(tf.matmul(X, W1) + B1)
with tf.name_scope("Layer3") as scope:
    L3 = tf.sigmoid(tf.matmul(L2, W2) + B2)

with tf.name_scope("Cost") as scope:
    cost = -tf.reduce_mean(Y * tf.log(L3) + (1 - Y) * tf.log(1 - L3))
    cost_summ = tf.summary.scalar("Cost", cost)    

with tf.name_scope("Train") as scope:
    train = tf.train.GradientDescentOptimizer(0.5).minimize(cost)

with tf.name_scope("Accuracy") as scope:
    accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.floor(L3 + 0.5), Y), tf.float32))
    accuracy_summ = tf.summary.scalar("Accuracy", accuracy)

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

merged = tf.summary.merge_all()
writer = tf.summary.FileWriter("./logs/", sess.graph)

for step in xrange(2001):
    summary, _ = sess.run([merged, train], feed_dict={X: x, Y: y})
    writer.add_summary(summary, step)
    if step % 500 == 0:
        print sess.run(cost, feed_dict={X: x, Y: y})

print sess.run(accuracy, feed_dict={X: x, Y: y})


추가적으로 설명하자면, Deep Learning에서 Accuracy 등의 변화를 좀 더 편하게 살피고, 네트워크의 구조를 살피기 좋게 TensorFlow에서는 TensorBoard라는 것을 제공합니다. 따라서 앞으로 다루는 NN에 대해서는 TensorBoard를 적용해서 편리하게 네트워크를 보려고 합니다. 그래서 이번 기회에 이 TensorBoard를 소개하고자 합니다. TensorBoard 사용방법은 다음과 같습니다.


1. 그래프에 표기하고 싶은 변수들의 이름을 정한다

X = tf.placeholder(tf.float32, [None, 2], name="X-Input")


2. 기록하고 싶은 부분(Accuracy, Cost, Layer 등)을 scop를 통해 묶는다.

with tf.name_scope("Train") as scope:


3. Scalar 그래프 또는 Histogram 그래프로 보고 싶은 변수는 아래와 같이 이름과 함께 등록해둔다.

cost_summ = tf.summary.scalar("Cost", cost)


4. 모든 Summaries를 Merge하고, 이 Summaries를 기록할 Writer를 만든다. 이때 저장될 log의 경로를 정합니다.

merged = tf.summary.merge_all()

writer = tf.summary.FileWriter("./logs/", sess.graph)


5. merged된 것을 돌리고 매 step마다 writer에 추가합니다.

summary, _ = sess.run([merged, train], feed_dict={X: x, Y: y})

writer.add_summary(summary, step)


6. TensorBoard를 이용해 기록된 log, 네트워크의 graph를 확인합니다. 이때 경로 상에 ','가 있는 경우 그걸 기점으로 경로가 나눠져버리니 이 점 고려하셔야 합니다. (의도는 여러 개의 log를 볼 수 있게 ','를 통해 여러 경로를 받으려는 거 같은데, ""로 경로를 하나 감싸도 이러는 거 보니 버그 맞는 듯) 만약 로그가 정상적으로 로딩이 안 되면, --debug를 붙여서 TensorBoard의 상태를 확인해보시면 됩니다.

tensorboard --logdir=./logs/


아래는 위 코드를 실행시키고 TensorBoard로 Cost, Accuracy, NN의 Graph를 확인한 내용입니다.



이렇게 간단한 NN을 끝내봤습니다. 다음엔 ReLU를 할 거 같습니다.


원래 제 목표가 자비스 만드는 거라(ㄹㅇ) 딥 러닝쪽 공부를 이제서야 시작하게 되었습니다. 원래 여름방학 때부터 하려고 했는데, 그놈의 귀차니즘이.....

이번엔 목표가 제대로 생겨서(고2때 카이스트 입학) 동기부여가 잘 되서, 좀 방향이 잡히더라고요. 여튼 뭐 그래서 제가 공부한 것들 좀 올려보려고 합니다.


일단 앞으로 딥 러닝 공부는 Tensorflow로 할 생각입니다. 제가 Python을 상당히 싫어하지만, 또 써보니 괜찮은 언어이기도 하고 무엇보다 제가 공부하고 있는 곳(http://hunkim.github.io/ml/)을 기준으로 추가적으로 제가 더 찾아보고 그러는 거라서, Tensorflow가 적합한 거 같네요.


- 1. Linear Regression은 무엇인고?


선형 회귀라고도 하며, 두 변수에 대한 선형 관계를 나타낸 것이라고 할 수 있을 듯 합니다.


- 2. Linear Regression을 한 번 해보자!


예를 들어 게임을 하는데 게임을 하는 시간과 그에 따라 얻는 경험치에 대한 표가 있다고 합시다.


 시간(y)

경험치(x)

2

35

60 

70 

100 


그리고 앞으로 게임에서 매일 일정 레벨에 도달하려고 할 때 얼마 만큼의 경험치를 얻기 위해 하루에 몇 시간 정도 게임을 하는 것이 적합한 지에 대해 알고 싶어한다고 합시다. 일반적으로 자연 현상에서는 대부분이 선형 관계이므로 1차 함수꼴(y = ax + b)로 두 변수에 대한 관계를 나타낼 수 있습니다. 따라서 일단 그 관계를 함수로 나타내기 위해서 저 데이터들을 그래프에 나타내봅시다.


이렇게 나옵니다. 이때 저 네 점에 대해 가장 가깝게 지나는 1차 함수를 구해야 합니다. 그래서 나온 것이 최소제곱법입니다. y = ax + b라는 식과 xi, yi 라는 데이터가 있을 때, 최소제곱법의 값은 cost(a, b)라고 하면, 아래와 같은 식이 나옵니다.

이때, 구하고자 하는 가장 최적의 y = ax + b를 만들려면 이때의 cost값이 가장 작아야합니다. TensorFlow에서는 이러한 cost값을 줄여주기 위한 알고리즘으로 GradientDescentOptimizer가 있습니다.  실제로 cost를 줄이는 알고리즘을 구현하고자 하면 조금 까다롭긴 하지만 크게 어렵지는 않습니다.


먼저 y = ax + b에서 우리는 cost를 줄이기 위해 a, b를 변수로 두고 이를 조정해야합니다. 위의 cost(a, b)식을 전개하면 다음과 같이 나옵니다.


휴... 전개가 좀 힘드네요. 여튼 잘 보시면 이 식은 간단하게 z = ax2 + by2 + cxy + d(a, b, c, d는 상수)의 함수꼴로 나타낼 수 있습니다. 일단 cost 함수를 위의 예시대로 데이터를 넣어서 전개해봅시다.

그리고 이 식대로 그래프를 한 번 그려봅시다. (식이 조금 복잡한 관계로, 다른 그래프들과 달리 https://academo.org/demos/3d-surface-plotter/를 사용했습니다.)



?! 아닛, 3D 포물면을 보는 기분이군요(WolframAlpha에서도 같은 함수를 돌렸는데 포물면인가 뭐시기라고 나온 거 같네요) 근데 사실 정확히 포물면은 아니고, 저기에 마우스 대서 값을 보면 최솟값인 부분이 한 군데로 수렴하는 거 같습니다. 뭐 여튼, 이때 cost의 최솟값 부분을 찾아야 하니 이 함수를 미분해서 0인 부분을 찾으면 됩니다! (아마 고등학교 과정의 미적분 수준으로는 힘들 거 같고, 편미분으로 할 수 있지 않을까 싶네요) 편미분으로 하는 과정은 다음 글에 한 번 써보도록 노력을 해보겠습니다. 


일단 본래 목적은 TensorFlow에서 GradientDescentOptimizer를 통해 cost를 줄여, 최종적인 1차 함수꼴을 얻는게 목적이니 이제 TensorFlow를 사용해봅시다. 소스 코드는 다음과 같습니다.

import tensorflow as tf import matplotlib.pyplot as plt x_data = [35, 60, 70, 100] y_data = [2, 4, 5, 9] W = tf.Variable(tf.random_uniform([1], -2.0, 2.0)) b = tf.Variable(tf.random_uniform([1], -2.0, 2.0)) y = W * x_data + b loss = tf.reduce_mean(tf.square(y - y_data)) optimizer = tf.train.GradientDescentOptimizer(0.0002) train = optimizer.minimize(loss) init = tf.initialize_all_variables() sess = tf.Session() sess.run(init) for step in xrange(300001): sess.run(train) if step % 60000 == 0: print(step, sess.run(W), sess.run(b)) print(step, sess.run(loss)) # Graph plt.plot(x_data, y_data, "go") plt.plot(x_data, sess.run(W) * x_data + sess.run(b)) plt.axis([0, 110, 0, 10]) plt.show()

문제는 GradientDescentOptimizer의 특성인지, learning_rate를 0.0002정도로 세팅해야지, 이 이상으로 세팅하면 값이 수렴하지 않고 오히려 INF로 넘어가서 아예 함수를 못 만들게 되는 현상이 일어납니다... learning_rate를 아주 작게 세팅하니 그만큼 수렴 속도도 무진장 느리고요... 원인 파악은 못 했으나 GradientDescentOptimizer 대신 TensorFlow의 다른 Optimizer를 시험삼아 써봤더니 아주 아주 조금 부정확하지만 빨리 수렴하더라고요 흠... 연구가 많이 필요해보입니다. 아마 다음 글에서 편미분하고 알고리즘 직접 작성하면서 알게될 수 있을지도 모르겠다는 생각이 드네요.


그리고 제가 GPU 세팅된 TensorFlow 버전을 사용하는데도 다 돌리는데 1분 넘게 걸리니 웬만하면 step을 1천 정도로 잡아주시는게 간단히 보는데 좋을 거 같습니다. 굳이 좋게 하실려면 GradientDescentOptimizer 대신 AdamOptimizer(0.5)를 돌려주시면 깔끔하게 나올 겁니다. (저건 또 왜인지 모르겠지만 무진장 좋긴 한데, 로그를 보시면 값이 수렴하지 않고 마지막까지 조금씩 바뀌기도 하더군요)


그래프의 변화도 첨부하고 싶었는데, 좀 노잼이여서(... 겁나 느리게 수렴합니다) 그냥 마지막 그래프만 보여드리겠습니다.


Linear Regression은 뭐 이정도면 얼추 끝난 거 같습니다. 다음 글에서는 위에서 말한 편미분을 통한 cost 줄이기 알고리즘을 직접 설계해서 돌려보도록 하겠습니다. 그 다음엔 다중 Linear Regression? 뭐 이런 걸 할 거 같네요. (위에 언급한 머신러닝 강의 순서대로 공부하고 글을 쓸 예정입니다)

+ Recent posts

티스토리 툴바