TensorFlow 패키지 소개

TensorFlow 패키지는 Theano 패키지와 마찬가지로 선형 대수 심볼 컴파일러(Symbolic Linear Algebra Compiler)이다. Theano에 비해 분산처리 기능이 강화되어 있다.

TensorFlow 기본 사용법

Theano와 유사하게 TensorFlow도 다음과 같은 과정을 거쳐 사용한다.

  1. 심볼 변수 정의
  2. 심볼 관계 정의
  3. 세션 정의
  4. 세션 사용

세션(Session)은 Theano의 함수와 유사한 역할을 하며 실제 심볼 변수의 관계를 분석하고 값을 계산해준다.

심볼 변수 정의

TensorFlow는 Variable 명령으로 정의하며 Theano와 달리 각 심볼 변수의 형태를 스칼라, 벡터, 행렬 등으로 명시적으로 정의하지 않는 대신 초기값을 이용해서 형태를 지정한다.

심볼 변수는 보통 입력한 값들에 대한 정의다


In [1]:
import tensorflow as tf

In [2]:
x = tf.Variable(1.0)
y = tf.Variable(2.0)

In [3]:
type(x), type(y)


Out[3]:
(tensorflow.python.ops.variables.Variable,
 tensorflow.python.ops.variables.Variable)

심볼 관계 정의

이미 만들어진 심볼 변수에 일반 사칙연산이나 TensorFlow 수학 함수를 사용하여 종속 변수 역할을 하는 심볼 변수를 만들 수 있다.


In [4]:
z = x + y

In [5]:
type(z)


Out[5]:
tensorflow.python.framework.ops.Tensor

In [6]:
u = tf.log(z)

In [7]:
type(u)


Out[7]:
tensorflow.python.framework.ops.Tensor

세션

TensorFlow의 세션은 Theano의 함수와 유사한 역할을 하며 실제로 계산 그래프를 생성하고 값을 계산하기 위한 환경을 제공한다. Theano의 함수와 달리 세션 생성과 실행 시작, 종료를 명시해야 한다.

  • 세션 생성: Session 객체 생성
  • 세션 사용: run 메서드
  • 세션 종료: close 메서드. with 문으로 대체하기도 한다.

세션은 지금 만든 심볼을 뒤쪽에서 쓸 수 있도록 정의한다


In [8]:
with tf.Session() as sess:
    tf.initialize_all_variables().run()
    print(sess.run(z))    
    print(sess.run(u))


3.0
1.09861

미분

TensorFlow 도 심볼릭 연산에 의한 미분 계산이 가능하다.


In [9]:
f = x ** 2
fx = tf.gradients(f, [x])

with tf.Session() as sess:
    tf.initialize_all_variables().run()
    print(sess.run(f))    
    print(sess.run(fx))


1.0
[2.0]

최적화

TensorFlow에는 Theano와 달리 최적화를 위한 GradientDescentOptimizer 등의 클래스가 미리 구현되어 있으므로 사용자가 구현할 필요가 없다.

선형 회귀


In [10]:
# Create 100 phony x, y data points in NumPy, y = x * 0.1 + 0.3
x_data = np.random.rand(100).astype(np.float32)
y_data = x_data * 0.1 + 0.3   #w가 0.1, b가 0.3

# Try to find values for W and b that compute y_data = W * x_data + b
# (We know that W should be 0.1 and b 0.3, but Tensorflow will
# figure that out for us.)
W = tf.Variable(tf.random_uniform([1], -1.0, 1.0))   #w와 b를 우리가 찾아야 하기 때문에 심볼로 정의했다.
b = tf.Variable(tf.zeros([1]))     #b는 0으로 이니셜라이즈 했다.
y = W * x_data + b    #y도 심볼 변수가 된 것이다.
# 여기서 중요한 것은 y와 loss가 핵심이다.
# Minimize the mean squared errors.
loss = tf.reduce_mean(tf.square(y - y_data))   #MSE나 RSS와 같은 방식의 에러를 정의한 것이다.
optimizer = tf.train.GradientDescentOptimizer(0.5)   #데아노 같은 경우에는 최적화 과정을 구하는 것이 어려웠으나
                                                     #여기서는 이미 구현된 것 사용
train = optimizer.minimize(loss)    #그러면 트리이닝 객체가 만들어진다.

# Before starting, initialize the variables.  We will 'run' this first.
init = tf.initialize_all_variables()

# Launch the graph.
sess = tf.Session()
sess.run(init)

# Fit the line.
for step in range(201):   #iteration을 201번 돌렸다. 
    sess.run(train)
    if step % 20 == 0:   #20의 배수가 될 때마다 상태를 확인한다.
        print(step, sess.run(loss), sess.run(W), sess.run(b))   #W와 b값이 현재 어디까지 왔는지 확인한다.

# Learns best fit is W: [0.1], b: [0.3]
sess.close()


0 0.0888317 [-0.43195009] [ 0.82341039]
20 0.00217394 [-0.05888844] [ 0.38601264]
40 0.000138049 [ 0.05996076] [ 0.32167485]
60 8.76638e-06 [ 0.08991029] [ 0.30546197]
80 5.56689e-07 [ 0.09745742] [ 0.3013764]
100 3.5349e-08 [ 0.0993593] [ 0.30034685]
120 2.245e-09 [ 0.09983855] [ 0.30008742]
140 1.42601e-10 [ 0.09995931] [ 0.30002204]
160 9.05917e-12 [ 0.09998976] [ 0.30000556]
180 5.7419e-13 [ 0.09999742] [ 0.30000141]
200 3.8245e-14 [ 0.09999934] [ 0.30000037]

MNIST 예제


In [11]:
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import tensorflow as tf

from tensorflow.examples.tutorials.mnist import input_data

import argparse
tf.app.flags.FLAGS = tf.python.platform.flags._FlagValues()
tf.app.flags._global_parser = argparse.ArgumentParser()

#이건 사실 아무 상관이 없는 코드다. argument 주는 부분이다.
flags = tf.app.flags
FLAGS = flags.FLAGS
flags.DEFINE_boolean('fake_data', False, 'If true, uses fake data for unit testing.')
flags.DEFINE_integer('max_steps', 200, 'Number of steps to run trainer.')
flags.DEFINE_float('learning_rate', 0.001, 'Initial learning rate.')
flags.DEFINE_float('dropout', 0.9, 'Keep probability for training dropout.')
flags.DEFINE_string('data_dir', '/home/dockeruser/data', 'Directory for storing data')
flags.DEFINE_string('summaries_dir', '/home/dockeruser/logs', 'Summaries directory')


def train():
    # Import data
    mnist = input_data.read_data_sets(FLAGS.data_dir,
                                      one_hot=True,
                                      fake_data=FLAGS.fake_data)

    sess = tf.InteractiveSession()

    # Create a multilayer model.

    # Input placehoolders
    with tf.name_scope('input'):
        x = tf.placeholder(tf.float32, [None, 784], name='x-input')    #placeholder 나중에 시작할 때 들어가는 값 변경 가능
        y_ = tf.placeholder(tf.float32, [None, 10], name='y-input')

    with tf.name_scope('input_reshape'):
        image_shaped_input = tf.reshape(x, [-1, 28, 28, 1])
        tf.image_summary('input', image_shaped_input, 10)

    # We can't initialize these variables to 0 - the network will get stuck.
    def weight_variable(shape):
        """Create a weight variable with appropriate initialization."""
        initial = tf.truncated_normal(shape, stddev=0.1)
        return tf.Variable(initial)

    def bias_variable(shape):
        """Create a bias variable with appropriate initialization."""
        initial = tf.constant(0.1, shape=shape)
        return tf.Variable(initial)

    def variable_summaries(var, name):
        """Attach a lot of summaries to a Tensor."""
        with tf.name_scope('summaries'):
            mean = tf.reduce_mean(var)
            tf.scalar_summary('mean/' + name, mean)
        with tf.name_scope('stddev'):
            stddev = tf.sqrt(tf.reduce_sum(tf.square(var - mean)))
        tf.scalar_summary('sttdev/' + name, stddev)
        tf.scalar_summary('max/' + name, tf.reduce_max(var))
        tf.scalar_summary('min/' + name, tf.reduce_min(var))
        tf.histogram_summary(name, var)

    def nn_layer(input_tensor, input_dim, output_dim, layer_name, act=tf.nn.relu):
        """Reusable code for making a simple neural net layer.
        It does a matrix multiply, bias add, and then uses relu to nonlinearize.
        It also sets up name scoping so that the resultant graph is easy to read,
        and adds a number of summary ops.
        """
        # Adding a name scope ensures logical grouping of the layers in the graph.
        with tf.name_scope(layer_name):
            # This Variable will hold the state of the weights for the layer
            with tf.name_scope('weights'):
                weights = weight_variable([input_dim, output_dim])
                variable_summaries(weights, layer_name + '/weights')
            with tf.name_scope('biases'):
                biases = bias_variable([output_dim])
                variable_summaries(biases, layer_name + '/biases')
            with tf.name_scope('Wx_plus_b'):
                preactivate = tf.matmul(input_tensor, weights) + biases
                tf.histogram_summary(layer_name + '/pre_activations', preactivate)
            activations = act(preactivate, 'activation')
            tf.histogram_summary(layer_name + '/activations', activations)
            return activations

    hidden1 = nn_layer(x, 784, 500, 'layer1')

    with tf.name_scope('dropout'):
        keep_prob = tf.placeholder(tf.float32)
        tf.scalar_summary('dropout_keep_probability', keep_prob)
        dropped = tf.nn.dropout(hidden1, keep_prob)

    y = nn_layer(dropped, 500, 10, 'layer2', act=tf.nn.softmax)

    with tf.name_scope('cross_entropy'):
        diff = y_ * tf.log(y)
        with tf.name_scope('total'):
            cross_entropy = -tf.reduce_mean(diff)
        tf.scalar_summary('cross entropy', cross_entropy)

    with tf.name_scope('train'):
        train_step = tf.train.AdamOptimizer(FLAGS.learning_rate).minimize(cross_entropy)

    with tf.name_scope('accuracy'):
        with tf.name_scope('correct_prediction'):
            correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
        with tf.name_scope('accuracy'):
            accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
        tf.scalar_summary('accuracy', accuracy)

    # Merge all the summaries and write them out to /tmp/mnist_logs (by default)
    merged = tf.merge_all_summaries()
    train_writer = tf.train.SummaryWriter(FLAGS.summaries_dir + '/train', sess.graph)
    test_writer = tf.train.SummaryWriter(FLAGS.summaries_dir + '/test')
    tf.initialize_all_variables().run()

    # Train the model, and also write summaries.
    # Every 10th step, measure test-set accuracy, and write test summaries
    # All other steps, run train_step on training data, & add training summaries

    def feed_dict(train):
        """Make a TensorFlow feed_dict: maps data onto Tensor placeholders."""
        if train or FLAGS.fake_data:
            xs, ys = mnist.train.next_batch(100, fake_data=FLAGS.fake_data)
            k = FLAGS.dropout
        else:
            xs, ys = mnist.test.images, mnist.test.labels
            k = 1.0
        return {x: xs, y_: ys, keep_prob: k}

    for i in range(FLAGS.max_steps):
        if i % 10 == 0:  # Record summaries and test-set accuracy
            summary, acc = sess.run([merged, accuracy], feed_dict=feed_dict(False))
            test_writer.add_summary(summary, i)
            print('Accuracy at step %s: %s' % (i, acc))
        else:  # Record train set summaries, and train
            if i % 100 == 99:  # Record execution stats
                run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
                run_metadata = tf.RunMetadata()
                summary, _ = sess.run([merged, train_step],
                                      feed_dict=feed_dict(True),
                                      options=run_options,
                                      run_metadata=run_metadata)
                train_writer.add_run_metadata(run_metadata, 'step%d' % i)
                train_writer.add_summary(summary, i)
                print('Adding run metadata for', i)
            else:  # Record a summary
                summary, _ = sess.run([merged, train_step], feed_dict=feed_dict(True))
                train_writer.add_summary(summary, i)


def main(_):
    if tf.gfile.Exists(FLAGS.summaries_dir):
        tf.gfile.DeleteRecursively(FLAGS.summaries_dir)
    tf.gfile.MakeDirs(FLAGS.summaries_dir)
    train()
    
    
main("")


Extracting /home/dockeruser/data/train-images-idx3-ubyte.gz
Extracting /home/dockeruser/data/train-labels-idx1-ubyte.gz
Extracting /home/dockeruser/data/t10k-images-idx3-ubyte.gz
Extracting /home/dockeruser/data/t10k-labels-idx1-ubyte.gz
Accuracy at step 0: 0.1367
Accuracy at step 10: 0.7056
Accuracy at step 20: 0.8268
Accuracy at step 30: 0.8642
Accuracy at step 40: 0.881
Accuracy at step 50: 0.8834
Accuracy at step 60: 0.8907
Accuracy at step 70: 0.8894
Accuracy at step 80: 0.8932
Accuracy at step 90: 0.8883
Adding run metadata for 99
Accuracy at step 100: 0.9044
Accuracy at step 110: 0.9167
Accuracy at step 120: 0.9185
Accuracy at step 130: 0.9191
Accuracy at step 140: 0.9279
Accuracy at step 150: 0.9236
Accuracy at step 160: 0.9305
Accuracy at step 170: 0.9265
Accuracy at step 180: 0.9235
Accuracy at step 190: 0.9281
Adding run metadata for 199

TensorBoard

TensorFlow는 theano와 달리 모형 내부와 결과를 모니터링 할 수 있는 TensorBoard 라는 웹서버를 제공한다.

TensorBoard 웹서버는 포트 6006을 사용하므로 만약 도커를 사용하는 경우에는 다음과 같이 포트를 열고 실행해야 한다.

docker run --name rpython -it -p 8888:8888 -p 6006:6006 datascienceschool/rpython

TensorBoard를 가동하기 위해서는 콘솔에서 다음과 같이 로그 디렉토리를 설정하여 실행한다.

$ tensorboard --logdir=/home/dockeruser/logs &

위와 같이 실행한 다음에는 다음 주소로 연결하면 TensorBoard 화면을 볼 수 있다.

http://192.168.99.100:6006

TensorFlow는 Theano처럼 theano.printing.pydotprint 명령을 지원하지 않는 대신 TensorBoard를 통해 그래프를 보여줄 수 있다.

  • 그래프 만드는 명령어 볼 수 없다. 그래프 보려면 텐서보드에서 보아야 한다.
  • function을 정의하는 것 대신에 세션을 정의해야 한다. function이 없기 때문에 call을 할 수가 없다.
  • 함수와 세션의 차이는? 실행 컨테이너라고 부르는 것을 teano에서는 바깥에서 정의를 했기 때문에 함수를 쓰지 않았다. 하지만 함수를 정의할 때는 인풋아웃풋의 관계? 때문은 아니었다. 세션에서는 그럴 필요가 없다. 관계가 된 아웃풋을 구하면 끝이다. 필요할 때 쓰는 것이다. 세션할 때는 이니셜라이즈(초기화) 항상 해주어야 한다.
  • MNIST 예제
    • 노트북에서 실행 원래 안 되는데 코드 좀 바꾸어서 실행 되게 만들었다.
    • 코드 실행하고 TensorBoard에 들어가면 내용이 뜬다.
    • cross entropy를 cost function으로 썼다. 줄고 있는가? 좀 더 천천히 내려가는 특성이 있다. 계속 내려간다. 어느정도 내리면 별 의미가 없다.
    • dropout? 이건 이따가 설명
    • 28X28 들어가는 것이라서 몇 백개 되는 weight 어렵다. 그래서 max값이나 mean값의 움직임만 보여주는 것이다. layer가 2개이기 때문에 나누어졌다. 맨 밑에는 stadard deviation이다.