강좌/MachineLearning

TensorFlow #004 TensorFlow 의 상수, 변수, 치환자 3부

여름나라겨울이야기 2016. 12. 5. 18:12
728x90

Python 은 상수가 없다. 하지만 텐서플로우는 상수 텐서를 써야하는데...



Python 은 상수가 없다. 위에 코드를 보면 a 라고 하는 식별자에 텐서플로우의 상수를 2번 대입하는 것을 볼 수 있다. 하지만 Python 은 상수가 없기에 a 에 2번 대입이 가능한 것이다. 하지만 텐서플로우는 상수를 써야하기 때문에 tf.constant 를 썼다. 그럼 모순이 되지 않을까?

텐서플로우는 새로운 상수 텐서가 대입된 a 를, 이전의 a 와 서로 다르게 취급하는 것을 알 수 있다(a 텐서에 주어진 첫 번째 주어진 이름은 Const 이고, 두 번째 주어진 이름은 Cosnt_1 인 것이다).

이를 더 확인해 보기 위해 아래 코드를 추가해 보자.

b = tf.constant(3, name='b')
b
b = tf.constant(4, name='b')
b

결과는 다음과 같다.

역시나 텐서플로우는 내부적으로 b, b_1 이라는 이름으로 두 상수를 구분한 것을 볼 수 있다. 아래 소스를 실행해 보는 것은 역시 읽는 자는 몫으로...

c = tf.constant(5, name='b') 
c


텐서플로우 변수에 새로운 값을 지정하기

변수 텐서에 대해서도 살펴 보자.

연산 결과만 보고서는 확실하게 알기가 쉽지 않다. 텐서보드를 통해 그래프를 확인해 보자.

소스와 그래프를 비교해 보면 상수 텐서나 변수 텐서나 별반 차이가 없어 보인다. 변수 텐서에 새로운 값을 대입하는 방법은 무엇일까?

 sess.run(tf.assign(변수 텐서명, 새로운 값))

위에 처럼 텐서플로우의 assign 함수를 사용하고, 그것을 세션에서 run 해줘야 한다.

혹여나

a = tf.Variable(10

a = a + 1 

을 하게 되는 경우 a 변수의 값이 1 증가하는 것이 아닌가라는 생각을 할 수 있다. 실제 그런지 테스트 해보도록 하자. 아래 코드를 실행해 보자.

import tensorflow as tf

a = tf.Variable(1)
print a

a = a + 1
print a 

실행 결과는 아래와 같다.

<tensorflow.python.ops.variables.Variable object at 0x7fde6cbe6610> 

Tensor("add:0", shape=(), dtype=int32) 

첫 번째 print a 의 결과는 <tensorflow.python.ops.variables.Variable object at 0x7fde6cbe6610> 이고,

두 번째 print a 의 결과는 Tensor("add:0", shape=(), dtype=int32)  이다.

여기서 Python 은 동적 타입 자료형을 쓰면, 모든 것은 객체이고, 모든 변수명은 레퍼런스이다 등등의 사전 지식이 필요해 진다. 하지만 여기서는 깊이 있게 다루지는 않겠다. 그래서 Python 에 대한 지식이 있다고 가정하겠다. 다음 코드의 실행 결과를 예상해 보자.

import tensorflow as tf

a = tf.Variable(1)
b = a * 3
a = a + 1

sess = tf.Session()
sess.run(tf.initialize_all_variables())

print sess.run(a)
print sess.run(b)
print sess.run(a) 

출력 결과를 보기 전에 세 개의 print 문이 각각 출력하는 값이 얼마일지 꼭 생각해 보자.

출력 결과가 예상과 맞아 떨어졌는가? 빨강 a초록 a 는 같은 Python 변수이지만 각각 다른 텐서 객체를 참조하고 있다는 것을 꼭 기억하기 바란다. 그럼 텐서 변수의 값을 변경하고자 한다면 어떻게 해야 할까? 바로 텐서플로우가 제공하는 tf.assign 메소드를 이용하면 된다. 형식은 다음과 같다.

tf.assign(텐서 객체 참조 변수, 새로운 값)

소스를 통해 이해해 보자.

import tensorflow as tf

a = tf.Variable(1)
b = a * 3

sess = tf.Session()
sess.run(tf.initialize_all_variables())

print sess.run(a)

sess.run(tf.assign(a, a + 1))

print sess.run(b)
print sess.run(a) 


위 두 경우의 그래프도 비교해서 보도록 하자.




이래 저래 보기가 복잡해 지는 것 같다. 마지막 소스 코드를 좀더 텐서플로우답게 수정해 보자.

import tensorflow as tf

a = tf.Variable(1, name="a")
b = tf.mul(a, 3, name="b")
c = tf.assign(a, tf.add(a, 1, name="d"), name="c")

print "a: ", a
print "b: ", b
print "c: ", c

sess = tf.Session()
sess.run(tf.initialize_all_variables())

print sess.run(a)

sess.run(c)

print sess.run(b)
print sess.run(a)

tf.merge_all_summaries()
tf.train.SummaryWriter('/tmp/log', sess.graph)

실행 결과 화면을 보자.

이번에는 그래프를 보자.


작명의 소중함을 다시 확인할 수 있다.


그럼 반복문을 다룰 때 변수는 불편하지 않을까?

당연히 변수 텐서의 값을 assign 해가면서 반복을 돌리는 것은 불편하다. 이 때 유용하게 쓸 수 있는 것이 치환자 텐서이다. 변수 텐서를 이용해서 1부터 10까자의 합을 구하는 소스를 만들어 보자.

import tensorflow as tf

a = tf.Variable(1, name="a")
sum = tf.Variable(0, name="sum")

sess = tf.Session()
sess.run(tf.initialize_all_variables())

for i in xrange(1, 11):
    sess.run(tf.assign(a, i))
    sess.run(tf.assign(sum, tf.add(sum, a)))

print "sum: ", sess.run(sum) 

조금 억지스럽긴한다. 이번에는 텐서 치환자를 이용한 소스를 만들어 보자.

import tensorflow as tf

data = xrange(1, 11)
a = tf.placeholder(tf.int32, shape=[], name="a")
sum = tf.Variable(0, name="sum")

sess = tf.Session()
sess.run(tf.initialize_all_variables())

for i in xrange(0, 10):
    sess.run(tf.assign(sum, tf.add(sum, a)), feed_dict={a: data[i]})

print "sum: ", sess.run(sum) 

음 두 개의 소스만으로는 더 편해졌다고 느끼지지 않는군.. ㅡㅡ; 데이터를 외부 파일로부터 가져오는 경우 변수 텐서를 assign 하는 것 보다는 치환자 텐서를 이용해 feed_dict 하는 것이 더 편한다(믿습니까? 믿으라!).



반응형