본문 바로가기
machine learning

[TENSORFLOW] TensorFlow Mechanics 101

by 유주원 2016. 5. 3.


02_TensorFlow_Mechanics101.ipynb

지난 번에 레이어 없는 MNIST를 구현 하였다면 이번에는 Hidden Layer가 있는 MNIST를 구현해 보는 코스이다. 소스는 아래와 같다.



코드가 지난 번 보다는 복잡하다.


우선 MNIST 데이터를 가져오기 위한 input_data.py는 /root/work/deep/code/01_mnist_beginning에 있다고 가정하고 위와 같이 sys.path.append를 정의해 주었다.


실행 순서대로 코드를 분석해 보자면, 우선 run_training을 통해 실제 mnist 데이터를 training하고 test를 진행해 볼수 있다.


run_training 함수를 살펴보자.


data_sets = input_data.read_data_sets("MNIST_data/", False)


위의 함수를 통해 mnist 데이터 셋을 가져온다. 이전 코드에서는 one_hot 코드를 true로 줘서 onehot encoding을 진행하였는데 여기서는 onehot encoding을 진행하지 않고 데이터를 가지고 온게 차이라고 할 수 있다.


with tf.Graph().as_default():


Tensorflow에서는 그래프를 만들고, 해당 그래프안에 각종 노드들을 집어넣은 다음에 session위에 해당 그래프를 올려서 구동시키는 방식이라고 할 수가 있다. 위의 코드는 default 그래프를 쓰겟다는 표현이고, with는 해당 graph의 실효 범위 지정을 위해 사용되었다.


placeholder_inputs이라는 함수를 통해 image_placeholder와 label_placeholder 두개를 만든다. 


이번에는 hidden layer와 classification을 위한 softmax layer를 정의해 보자. inference 함수를 이용해서 레이어를 만들 수가 있다.

inference 함수 소스를 들여다 보면, 각각의 레이어들 마다 name_scope를 주는 것을 확인할 수가 있다.


동일한 변수 명을 가지고 있더라도, name_scope를 정의해주면 해당하는 name_scope 내에서의 변수로 인식을 하게 된다.


예를 들어 아래의 소스 같은 경우에는 hidden1이라는 name_scope 레이어에 해당하는 weight와 biases가 되는 것이다.


with tf.name_scope('hidden1'):

    weights = tf.Variable(tf.truncated_normal([IMAGE_PIXELS, hidden1_units],

                                                 stddev=1.0/math.sqrt(float(IMAGE_PIXELS))),

                                                 name='weight')

    biases = tf.Variable(tf.zeros([hidden1_units]), name='biases')

    hidden1 = tf.nn.relu(tf.matmul(images, weights) + biases)


tf.truncated_normal이라는 생소한 함수가 있다. 그냥 weights를 정규분포 형태로 초기화 해 준다고 생각하면 쉬울 것 같다. 그리고 biases도 0으로 초기화를 해준다. 마지막에 image pixel 값들과 weights들의 행렬 곱을 한 후에 biases를 더한 값에 relu 함수를 매핑하는 hidden 레이어를 생성하면 첫 번째 hidden 레이어가 완성된다. 


inference 함수에서는 두개의 hidden 레이어를 만들고, 마지막엔 확률 변환을 위해 softmax 레이어를 추가하였다. 이전과는 다르게 softmax 함수를 적용하지 않은게 특징이라고 할 수가 있겠다.


logits = tf.matmul(hidden2, weights) + biases


이렇게 완료된 그래프에 이제 loss function을 추가한다. 


def loss(logits, labels):

    labels = tf.to_int64(labels)

    cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits, labels, name='xentropy')

    loss = tf.reduce_mean(cross_entropy, name='xentropy_mean')

    return loss


처음 mnist 데이터를 가져올때 one_hot encoding을 적용하지 않았기 때문에 to_int64를 통해 label 값을 one_hot encoding 값으로 변환을 한다. 그런 후에 cross_entropy를 구하는데, tf.nn.sparse_softmax_cross_entropy_with_logits이라는 함수를 사용하는 것을 볼 수가 있다. 이 함수는 아래의 함수를 축약한 것이라고 볼 수도 있다.


sm = tf.nn.softmax(x)

ce = cross_entropy(sm)


이전에 softmax를 취하지 않았으므로 cross_entropy를 구할때 softmax를 취하고 cross_entropy를 구하는 것이다.

구한 cross_entropy의 평균 값을 계산 한 후 그 값을 리턴하면 loss function에서의 기능은 끝난다.


이제 training을 적용해보자.


def training(loss, learning_rate):

    tf.scalar_summary(loss.op.name, loss)

    optimizer = tf.train.GradientDescentOptimizer(learning_rate)

    global_step = tf.Variable(0, name='global_step', trainable=False)

    train_op = optimizer.minimize(loss, global_step=global_step)

    return train_op


scala_summary라는 것이 있다. tensor_board에 명시할 operation 명과 loss 함수를 입력으로 받는다. GradientDescentOptimizer를 만들고, global_step을 0으로 지정해준다. 

이 global_step이 정확히 무슨 역할을 하는지 몰라서 tensorflowKR에 문의를 한적이 있는데, 누군가가 자세히 설명을 해 주었다.




결론은 tracking을 편리하게 하고, tensor_board를 이용해 결과값을 확인할 때 그 위치를 확인하기 위함 정도라고 생각하면 될 것 같다.

마지막으로 loss가 최소가 되는 train_op를 찾아서 리턴해주면 training의 역할은 종료된다.


이제 training된 모델이 실제로 적합한지를 평가할 수 있는 함수를 만들자.

evaluation이라는 함수에서 그 역할을 담당할 것이다.


tf.nn.in_top_k 함수를 통해 예측된 값 중 가장 큰 값과 label 값 중 가장 큰 값이 일치할 경우에는 True를 그렇지 않을 경우에는 False를 리턴한다. label은 one_hot encoding이기 때문에 1 값을 가진 label이 가장 큰 값이 된다.


이제 인식을 위한 모든 함수의 구현은 끝냈다.

실제 training을 돌려보기 전에, tensor_board와의 연동 및 session 초기화를 위해 아래의 작업들을 해주자.


summary_op = tf.merge_all_summaries()

saver = tf.train.Saver()

sess = tf.Session()


init = tf.initialize_all_variables()

sess.run(init)


summary_writer = tf.train.SummaryWriter("./logs", sess.graph)


tf.merge_all_summaries를 통해 지금까지 요약한 정보들을 합친다. 또한 기존 모델 복원 등을 위한 체크포인트 파일을 만들기 위해 tf.train.Saver를 호출해서 인스턴스를 만든다. tensor flow 세션을 만들고 초기화 함수를 만든 후, session.run을 통해 초기화를 진행 시킨다. 

그리고 이벤트 요약 정보의 작성을 위해 summary_writer를 생성한다.


이제 실제로 training을 해보자.

fill_feed_dict를 통해 image 데이터와 label 데이터를 data_sets.train으로부터 가져온다.

batch_size를 100으로 했기 때문에 한번에 100개씩의 데이터들을 가져올 것이다. 그러니깐 batch 100에 루프 2000, 즉 20만개의 데이터를 학습한다고 생각하면 된다.


session.run으로 기존에 작성한 train_op와 loss_function를 작동시킨다.

step이 100 단위일 때마다 이벤트 요약 정보를 갱신 시키게 코드가 구현되어져 있으며, step이 1000또는 2000일 경우에, validation과 test를 진행하도록 구현되어져 있다.




Num correct가 헷갈리게 출력이 되어져 있는데, 5000 중에 91개가 맞다는 게 아니라 1 step 당 100개씩 가져와서 비교하기 때문에 첫번째는 100개 중 91개 그 다음은 200개중 179개가 맞다고 생각하면 된다. Precision은 실제 개념의 Precision이라기 보다는 그냥 accuracy라고 생각하면 된다. 전체 중에 맞는 비율이 위에서 나타내는 Precision이다. 1이 되면 100%가 된다.

위의 테스트와 validate에서는 각각 0.9032, 0.9048, 즉 90% 정도의 확률이 나타났다.


아래의 그림은 tensor_board로 summary 정보를 보는 화면을 나타내고 있다. 그래프를 통해 step이 진행할 수록 entropy 값이 떨어지는 것을 확인해 볼 수 있으며, 지금 내가 구성한 model을 그래프로 볼 수도 있다.