안녕하세요.


이번에는 Docker를 이용해 구축한 TensorFlow(이하 텐서플로우)의 학습된 정보 (training)를 Tensorboard(이하 텐서보드)로 시각화는 방법을 다뤄 보겠습니다.


앞서 소개한 바 있는 Docker로 Windows에서 텐서플로우 수행하기 포스트에서 생성한 도커 컨테이너에서는 버그로 인해 텐서보드가 수행되지 않지 않는 현상이 있어 불가피하게 새로운 컨테이너를 만들어야 합니다.

귀찮겠지만 다음과 같이 새로운 컨테이너를 만들도록 합시다.   


1. 0.9 버전의 텐서플로우 컨테이너 만들기


Docker Quickstart Terminal를 실행한 후 현재 실행 중인 컨테이너 정보를 확인합시다.


$ docker ps -a


기존에 설치한 컨테이너가 수행 중이라면 정지하고 새로운 이미지를 다운로드 받아 설치합니다.


$ docker stop {수행중인 컨테이너 이름} $ docker run -p 8888:8888 -p 6006:6006 -it b.gcr.io/tensorflow/tensorflow:r0.9-devel

b.gcr.io/tensorflow/tensorflow 다음 콜론(:) 구분자 다음은 태그를 지칭하고 여기서 r0.9-devel 은 0.9 개발자 버전을 의미합니다.

(글을 작성하는 시점에서 텐서플로우는 활발히 개발 중인 프로젝트임으로 다음 링크에서 최신 버전에 대한 태그를 확인하기 바랍니다.)


또한 -d 옵션은 포트 포워딩을 의미하는데 jupyter가 사용하는 기본 포트인 8888과 텐서보드가 사용하는 포트인 6006를 전달해서 외부(즉, windows) 상에서 접속할 수 있게 해 줍니다.


다운로드가 완료되면 새로운 컨테이너가 생성되고 기본 값으로 bash 가 실행됩니다.


2. jupyter 실행하기


root@{컨테이너 아이디}:/# cd /


루트 폴더로 이동하면 jupyter를 실행할 수 있는 shell script 가 준비되어 있습니다. 이전 이미지는 컨테이너 생성 후 바로 이 스크립트를 수행하도록 지정되어 있으나 이 개발자 버전에서는 사용자가 직접 이 스크립트를 수행해 주어야 합니다.

bash가 종료되더라도 jupyter가 종료되지 않도록 nohup 으로 수행하고 새로운 process로 띄우기 위해 & 옵션을 줍시다.


root@{컨테이너 아이디}:/# nohup ./run_jupyter.sh


이제 windows에서 브라우저로 다음 경로에 접속해 jupyter를 실행합니다: 

http://192.168.99.100:8888/


3. 텐서보드 실행하기


텐서보드는 jupyter처럼 웹서비스로 동작합니다. 따라서 이를 실행하기 위해 run_jupyter.sh 와 같은 run_tensorboard.sh 스크립트를 만들어 보도록 하겠습니다.


root@{컨테이너 아이디}:/# nohup tensorboard --logdir=/logs --port 6006 &


여기서 logdir은 텐서플로우 프로그램에서 지정하는 로그 경로를 의미하고 port 는 사용할 포트를 의미합니다.

이제, windows 의 브라우저로 텐서보드를 접속해봅시다: 

http://192.168.99.100:6006/


아직 텐서플로우로 학습한 바가 없어서 아무런 기록이 없습니다.

4. 샘플코드 수행하기

jupyter에서 새로운 ipython 파일을 생성하고 샘플코드를 입력합시다.


import tensorflow as tf

# reset everything to rerun in jupyter
tf.reset_default_graph()

# config
batch_size = 100
learning_rate = 0.5
training_epochs = 5
logs_path = "/logs"

# load mnist data set
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)

# input images
with tf.name_scope('input'):
    # None -> batch size can be any size, 784 -> flattened mnist image
    x = tf.placeholder(tf.float32, shape=[None, 784], name="x-input") 
    # target 10 output classes
    y_ = tf.placeholder(tf.float32, shape=[None, 10], name="y-input")

# model parameters will change during training so we use tf.Variable
with tf.name_scope("weights"):
    W = tf.Variable(tf.zeros([784, 10]))

# bias
with tf.name_scope("biases"):
    b = tf.Variable(tf.zeros([10]))

# implement model
with tf.name_scope("softmax"):
    # y is our prediction
    y = tf.nn.softmax(tf.matmul(x,W) + b)

# specify cost function
with tf.name_scope('cross_entropy'):
    # this is our cost
    cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))

# specify optimizer
with tf.name_scope('train'):
    # optimizer is an "operation" which we can execute in a session
    train_op = tf.train.GradientDescentOptimizer(learning_rate).minimize(cross_entropy)

with tf.name_scope('Accuracy'):
    # Accuracy
    correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    
# create a summary for our cost and accuracy
tf.scalar_summary("cost", cross_entropy)
tf.scalar_summary("accuracy", accuracy)

# merge all summaries into a single "operation" which we can execute in a session 
summary_op = tf.merge_all_summaries()

with tf.Session() as sess:
    # variables need to be initialized before we can use them
    sess.run(tf.initialize_all_variables())

    # create log writer object
    writer = tf.train.SummaryWriter(logs_path, graph=tf.get_default_graph())
        
    # perform training cycles
    for epoch in range(training_epochs):
        
        # number of batches in one epoch
        batch_count = int(mnist.train.num_examples/batch_size)
        
        for i in range(batch_count):
            batch_x, batch_y = mnist.train.next_batch(batch_size)
            
            # perform the operations we defined earlier on batch
            _, summary = sess.run([train_op, summary_op], feed_dict={x: batch_x, y_: batch_y})
            
            # write log
            writer.add_summary(summary, epoch * batch_count + i)
            
        if epoch % 5 == 0: 
            print "Epoch: ", epoch 
    print "Accuracy: ", accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels})
    print "done"

MNIST 데이터셋(수기로 작성한 숫자를 모아 구분하는 데이터셋)을 이용해 간단한 소프트맥스 로지스틱스 회기 분류로 학습 후 평가하는 모델을 수행해 봅니다.

이때, 텐서보드를 위해 추가되는 코드는 다음과 같습니다.


  • logs_path = "/logs" : 텐서보드가 참조할 로그 정보가 저장될 경로를 지정
  • tf.scalar_summary("cost", cross_entropy)
    tf.scalar_summary("accuracy", accuracy)
    summary_op = tf.merge_all_summaries()

    : EVENTS 항목에 표시할 요약 정보로 cost와 accuracy를 지정
  • writer = tf.train.SummaryWriter(logs_path, graph=tf.get_default_graph())
    : SummaryWriter 개체를 생성해 로그 경로에 현재 그래프를 지정
  • _, summary = sess.run([train_op, summary_op], feed_dict={x: batch_x, y_: batch_y})
    : training과 summary operation을 수행
  • writer.add_summary(summary, epoch * batch_count + i)
    : minibatch 수행 시마다 결과 요약 저장

5. 텐서보드에서 결과 확인

텐서보드는 events, images, audios, graphs, histograms 항목으로 구성되어 있습니다.

이번 샘플 코드에서는 events 와 graphs 에 관한 정보만 저장됩니다.




events 항목에서 학습으로 인해 accuracy가 증가되면서 cost 가 주는 모습을 보여주는 도식을 볼 수 있으며, graphs 항목에서는 모델의 형태와 텐서의 흐름을 역동적으로 볼 수 있습니다.


5. 참고자료



+ Recent posts