TensorFlow 기본 개념 (1)


머신러닝의 정의란 Andrew Ng교수님의 말을 인용하면 아래와 같다.

A computer program is said to learn from experience E with respect to some class of taks T and performance tasks in T, as measured by P, improves with experience E, Tom M. Mitchell

Tensor는 뭔가?

많은 데이터를 효과적으로 처리하는 자료구조이다.
다차원 array, list라고 생각하면 된다.
이래서 NumPy를 확장한것이 Tensor인 것이다.

# What is tensor?
tensor1 = 7          # 0-dimensional
tensor2 = [7]        # 1-dimensional
tensor3 = [[1,2,3],  # 2-dimensional
           [4,5,6]]  ...
           ...

Flow

Flow란 결국 Graph이다.
즉 모든 계산을 쉽게 하기 위해서 각각의 연산을 잘게 쪼개고 이것을 Graph로 연결 한 것이다.
미분 Chain Rule 같은것을 생각해보면 왜 연산이 간단해 지는지 알 수 있다.

Graph, Node, Edge

선언부와 실행부가 다르다

TensorFlow코드 상에서 a=1을 선언해도 추후에 이것이 반영되는 것이지 지금 당장 변수 a가 1로 assgin된것은 아니다.
이러한 부분이 기본적인 python 프로그래밍과는 다른점이다. 
이러한 동작 매커니즘을 생각해보면 결국 앞으로 어떻게 동작 할 것이다라는 계획을 Graph로 표현한것이 TensorFlow가 되는 것이다.

  • Operation: 동작을 정의한 것
  • Node: Operation 정의를 포함한 것
  • Edge: Node와 Node를 연결한 것

이러한 구조에서 Tensor데이터 구조가 이동하면서 Operation에 의해서 연산을 하는 방식이다.

import tensotflow as tf

위처럼 TensorFlow를 일단 Import 하면 내부적으로 default_graph_stack에 Default Graph가 생긴다.

tf.get_default_graph()명령어로 접근 가능
이 graph에 저장된 operation을 확인해 보면 []비어 있는 것을 알 수 있다.

상수를 하나 선언 하면 아래처럼 주소가 기록된다.
이러한 의미는 operation이 리스트 형태로 들어가 있는 것이다.

import tensorflow as tf
graph = tf.get_default_graph()
graph.get_operations()
input = tf.constant(1.0)
operations = graph.get_operations()
operations
[<tensorflow.python.framework.ops.Operation at 0x7f94695fbd90>,
 <tensorflow.python.framework.ops.Operation at 0x7f944788a990>]

operation을 감싸고 있는 node를 출력해보자.

>>> operations[0].node_def
## name: "Const"
## op: "Const"
## attr {
##   key: "dtype"
##   value {
##     type: DT_FLOAT
##   }
## }
## attr {
##   key: "value"
##   value {
##     tensor {
##       dtype: DT_FLOAT
##       tensor_shape {
##       }
##       float_val: 1.0
##     }
##   }
## }

TensorFlow는 내부적으로 protocol buffer를 이용한다.
어떤 google style의 JSON이라고 생각하면 쉽다. 위에 출력도 JSON 스럽기 때문이다.

왜 tensorflow는 이처럼 고유한 특징의 구조나 타입들을 가지는 것일까?

Numpy는 기본적으로 matrix을 연산을 위해서 C++로 개발 되었다. 하지만 여전히 많은 overhead를 발생 시킨다.
완전히 Python 영역 밖에서 동작 시킴으로 성능을 끌어 올린다.
이러한 방법은 Theano또는 Torch에서 수행하는 방식이다.

그냥 input을 출력해 보자

In [27]: input
Out[27]: <tf.Tensor 'Const_9:0' shape=() dtype=float32>

32비트 float tensor를 확인 할 수 있다. no dimension 이며 그냥 싱글 숫자이다.

이제 실제로 input값을 session으로 실행하자.
default에 의해서 default graph에서 값을 꺼내오는 방식이다.

>>> sess = tf.Session()
>>> sess.run(input_value)
## 1.0

초 간단 TensorFlow neuron

Hadley Wickham said Names have objects rather than the reverse이다.
표현 하면 아래의 그림과 같다.

여기서 말하는 초간단 neron은 non-identity activation function도 없고 bias도 없는 것을 말한다.

constant는 상수를 의미하고, variable은 계속 변화하는 값을 의미 한다.

weight = tf.Variable(0.8)
#지원하는 operation 이름 확인
for op in graph.get_operations(): print op.name

x에 w를 곱해서 y라는 출력값을 만들어냄
이때 참값은 0이다.

입력값은 1로 고정 시킨다면 당연히 참값 y_ = 0과 일치시키는 w는 0일것이다.

단순한 방정식이다.

$$ 1 \times w = 0 $$

위와 같을 때는 $w$는 0이 최적화된 값일 것이다.

하지만 이러한 해를 wieght값 w를 조정하면서 전체 cost를 줄이는 방법으로 차근 차근 찾아보자.

w의 시작값을 0.8로 설정 한다.
이때의 cost는 $1 \times 0.8 = 0.8$ 이다.
이 cost를 계산하는 것이 최소제곱법이 있다.

$$ cost = minimum(\hat { y }- y)^{2} $$
이것을 최소화로 만들어주는 방법은 y를 결정하는 함수 $f(x) = wx$ 입니다. 결국 w를 조정해서 해를 찾을 수 있게 됩니다.

우선, cost함수를 w에 대해서 편미분하면 chain rule에 의해서 아래와 같이 나옵니다.

$$ \frac{\partial cost}{\partial w} = 2 \times x$$

그리고 이러한 과정에 쓰이는 데이터를 트레이닝 데이터라고 하고 과정을 학습(learning)이라고 합니다.
TensorFlow에서느 사실 이러한 미분을 자동으로 해주게 됩니다. 구지 손으로 저렇게 해서 식으로 적을 필요 없습니다.

그리고 이러한 미분값을 최적화 시키기위해서 조금씩 값을 update하는 방법을 사용하게 됩니다. 
방법의 이름은 gradient descent algorihtm입니다. 알고리즘을 표현하면 아래와 같습니다.

$$ w = w - \alpha \frac{\partial cost}{\partial w} $$

위 알고리즘은 간단하게 TensorFlow에서는 아래와 같은 함수하나만 이용하면 딥니다.

optim = tf.train.GradientDescentOptimizer(learning_rate = 0.025)

learing rate을 0.025를 적용해서 손으로 1단계를 게산해보면 아래와 같습니다.
$1.6 \times 0.025 = 0.04$가 됩니다.

아래 코드는 이러한 과정을 적용한 전체 코드 입니다.

import tensorflow as tf
x = tf.constant(1.0, name='input')
w = tf.Variable(0.8, name='weight')
y = tf.mul(w, x, name='output')
y_ = tf.constant(0.0, name='correct_value')
loss = tf.pow(y - y_, 2, name='loss')

train_step = tf.train.GradientDescentOptimizer(0.025).minimize(loss)

for value in [x, w, y, y_, loss]:
    tf.scalar_summary(value.op.name, value)

summaries = tf.merge_all_summaries()

sess = tf.Session()
summary_writer = tf.train.SummaryWriter('log_simple_stats', sess.graph)

sess.run(tf.initialize_all_variables())
for i in range(100):
    if i % 10 ==0:
        print("epoch {}, output: {}".format(i, sess.run(y)))
    summary_writer.add_summary(sess.run(summaries), i)
    sess.run(train_step)

출력결과

epochs 0, output: 0.800000011921
epochs 10, output: 0.478989481926
epochs 20, output: 0.286788702011
epochs 30, output: 0.171710968018
epochs 40, output: 0.10280970484
epochs 50, output: 0.0615559667349
epochs 60, output: 0.0368558317423
epochs 70, output: 0.0220669470727
epochs 80, output: 0.0132122989744
epochs 90, output: 0.00791069027036

output은 weight 값 w를 의미하며 정답인 0에 거의 근접한것을 알 수 있다.

해당 결과를 Tensor Board를 통해서 시각적으로 확인 할 수도 있다.
프로젝트 디렉터리에서 log_simple_stats에 저장한다고 했으니 그곳에서 아래의 명령어를 수행 한다.

tensorboard --logdir=./log_simple_stats

그다음 크롬을 이용해서 http://192.168.188.129:6006/#graphs아래 주소로 접속 한다.
IP Address는 각자 맞춰서 한다. 
그냥 local에서 작업한다면 localhost:6006/#graphs라고 쓰면 된다.

작성코드

본 내용에 작성된 코드들은 Github를 통해서 공유하겠습니다.
주소: https://github.com/leejaymin/TensorFlowLecture/tree/master/0.Basic

참고자료

https://www.oreilly.com/learning/hello-tensorflow
https://jihobak.github.io/2016-06-26-deeplearning-ninja001/


+ Recent posts