728x90
Tensorflow는 현존하는 머신러닝 프레임워크 중 가장 유명한 프레임 워크이다.

필자가 딥러닝을 배우기시작하면서 선택한 프레임워크인데 생각보다 어렵지는 않다.


그러나 필자가 블로그에 포스팅 하는 부분들 중에서는 거의 해본적없는 분야이고

빨리 빨리 바뀌기에 이 포스터는 항상 맞을수 없다고 볼 수 있다.

그러기 때문에 읽는 입장에서 그 부분을 고려해서 읽길 바란다.


이 포스터는 Python3.6으로 Tensorflow1.2로 테스팅 했으며 작업환경은 PyCharm이며 특별한 경우가 아니면 쉘에서 실행하지 않음.


이번 장에서는 스칼라 값을 사용해서 텐서보드에 그래프를 표현하는 예제를 볼것이다.

이제야 뭔가 텐서플로우 다운 예제가 나오게 되는데 그 이유는 마지막에 완성된 텐서보드를 보면 알 수 있다.


이제 예제는 namescope별로 하게 될것이다.

텐서플로우로 머신 러닝을 할때는 일반적으로 다음과정들을 거치게된다.

요약해서 보자.


variables : 사용할 변수를 선언하는 스코프

transformation : 실제로 텐서플로우가 돌아가게될 연산을 하는 스코프 이는 다시 4개의 스코프로 나뉜다.

input : transformation의 하위 스코프, 외부로 부터 값을 입력받는다.

intermediate : transformation의 하위 스코프, 텐서들의 계산이 실행되는 곳

output : transformation의 하위 스코프, 외부로 보낼 값을 결정하는 곳

update : transformation의 하위 스코프, 다시 실행하기 전에 하는 작업



이 구조 어디서 많이 본거 같지 않은가?? c스타일 for문구조와 매우 유사하다.


for(int I=0;i<num;i++)이라는 구조가있다면


for(variables;범위 지정;update){

intermediate

output

}


같은 구조와 유사하다는 것이다.

그럼 하나씩 작성해보도록 하자.


※아래의 예제는 홍릉과학출판사 Sam Abrahams,Danijar Hanfner,Erik Erwitt,Ariel Scarpinelli 공저, 정기철 역의 엣지있게 설명한 텐서플로우의 101페이지 예제를 참고하였습니다.


variables : 변수 선언


이 때까지 텐서플로우를 사용하면서 변수가 뭔지를 배웠을 것이다.

우리가 아는 변수랑은조금 다르지만 어쨋든 값이 변할 수 있다는 것은 알것이다.


앞으로 사용할 변수들을 지정하는 장소이다.

만약 변수를 사용한다면 이 namespace안에 지정하는 경우가 많다.


with tf.name_scope('variables'):
global_step = tf.Variable(0, dtype=tf.int32, trainable=False, name='global_step')
total_output = tf.Variable(0.0, dtype=tf.float32, trainable=False, name='total_output')


여기서 global_step의 경우는 거의 항상 존재하는데 이유는 for문으로 따지면 i와 같기 때문이다.

그리고 total_output의 경우에도 거의 존재하는 경우가 많다.

global_step의 경우 매 턴을 의미하며 1씩 증가시킬 것이고,

total_output의 경우 이떄까지의 output의 총 합을 의미하는 것이다.

아직까지는 얘들이 어떤 결과를 낼지 모르지만 뒤에 로직들을 보면 이해할 수 있다.


input : 외부에서 받아들일 placeholder를 지정


이 input부분은 placeholder를 지정하는 곳이다.

외부에서 값을 받는 부분이기 때문에 매우 중요하기 때문이다.


with tf.name_scope('input'):
a = tf.placeholder(tf.float32, shape=[None], name='input_placeholder_a')


나중에 함수를 추가로 만들때 입력부분을 맞게된다.


여기서 변수 a는 placeholder로써 텐서모양을 보면 벡터로 되어 있다.

따라서 삽입되는 input은 무조건 벡터형이여야 한다는 것을 알 수 있다.

즉 한개의 숫자를 넣더라도 1차원 텐서로 넣어야한다는 의미 이다.


intermediate : 계산이 실행되는 부분


이 부분은 계산이 실행되는 곳이다.

다른 부분에서는 크게 계산이 실행되지 않는다. 따라서 계산에 관련된 코드는 여기다 적는 경우가 많다.


with tf.name_scope('intermediate'):
b = tf.reduce_prod(a, name='product_b')
c = tf.reduce_sum(a, name='sum_c')


이 계산에서 reduce_sum과 reduce_prod이라는 메소드가 사용된다.

reduce메소드들은 차원을 줄여서 결과를 내어 준다.



사용법은 위와 같다. 아무것도 쓰지 않는다면 어떠한 텐서든 0차원 텐서, 즉 스칼라로 변환이 된다.

우리는 그 외에 차원을 적어주면 해당차원으로 줄여서 계산한다.

그리고 차원을 줄이면서 계산을 하게되는데 reduce_sum을 한다면 더하면서 차원이 줄고 reduce_prod는 곱하면서 차원이 줄어든다.

우리는 스칼라를 사용해서 그래프를 만듦으로 reduce의 결과는 스칼라가 되어야한다.

따라서 우리는 그냥 파라미터 없이 사용하자.


output : 결과를 만드는 부분


결과가 출력되는 부분이다.

이 결과라는 건 최종적으로 출력되는 부분이라는게 아니라 이 프로그램이 최종적으로 출력되길 원하는 output을 의미한다.

여기에 적힌것만 당연히 출력할수 있는건 아니다.


with tf.name_scope('output'):
output = tf.add(b, c, name='output')


여기서 output으로 intermediate에서 계산했던 b와 c를 사용한다.


update : 영속성있는 변수들을 업데이트 시키는 부분


결과를 출력하고 나서 마지막으로 변수를 업데이트 시켜주는 부분이다.

우리는 위에서 총 두가지의 변수를 선언 했었고 이 두 변수를 업데이트 시켜 주는 것이다.


with tf.name_scope('update'):
update_total = total_output.assign_add(output)
increment_step = global_step.assign_add(1)


여기서 우리는 step을 1씩 증가시킬것이므로 global_step을 1증가시켜준다.

또한 total_output역시 우리는 증가시킬것이므로 output을 더해주자.


이까지 했으면 계산 과정은 끝났다.

이제는 두가지 프로세스만 더 생각하면 되는데 화면에 출력될 부분, 그리고 추가적인 명령어를 수행할 부분이다.

이 목록은 아래와 같다.


summaries : 마지막에 텐서보드로 출력할 부분

global_ops : 전역으로 실행될 명령어를 정의


여기서 전역으로 실행될 명령어라는 부분이라는 건 sees.run으로 호출할때 원래는 input으로 호출하는데

플레이스 홀더가 아닌 각각의 노드로 호출할때 따로 호출해야할 부분이라는 뜻이다.

예를 들어서 init같은 초기화 노드는 반드시 한번 호출해주어야 한다.


summaries : 텐서보드에 보여줄 부분을 만드는 부분


텐서보드에 보여줄 부분을 만드는 부분으로 이 결과가 나중에 차트로 보여주게된다.


with tf.name_scope('summaries'):
avg = tf.div(update_total, tf.cast(increment_step, tf.float32), name='average')
tf.summary.scalar('output_summary', output)
tf.summary.scalar('total_summary', update_total)
tf.summary.scalar('average_summary', avg)


여기서 우리는 총 3개의 스칼라를 텐서보드에 보여줄 것이다.


global_ops : 전역으로 실행될 명령어를 정의


sess.run은 여러분들이 아시다시피 사실 모든 노드를 실행할 수 있고 placeholder를 지정해서 실행할 수 있다.

그 중에서 내부로직에 들어가진 않지만 외부에서 실행해줘야하는 명령어들이 몇 존재한다.

그 명령어들을 묶어서 같이 저장하는 곳이다.


with tf.name_scope('global_ops'):
init = tf.initialize_all_variables()
merged_summaries = tf.summary.merge_all()


마지막으로 summary.merge_all의 경우 지금까지의 요약차트에 마지막 값을 더해라는 것이다.

즉 위의 summaries네임스페이스에서 만든 summary를 지금까지의 summary에 합산해라는 뜻이다.


이제 그래프는 완성되어서 준비는 끝났다.

실행하는 일만 남았으며 실행하는 부분은 일회부와 반복부로 나뉜다.


일회부 : 단한번만 실행

반복부 : 여러번 실행


의미는 여러분이 생각하는 그대로이다.

일회부는 맨처음 하면서 단 한번만 실행될 do를 하는 것이다.

반복부는 앞으로 계속 반복해서 호출하게될 부분이다. 보통 이부분은 함수로 제작하게 된다.

이제 예제를 보자.


일회부 : 한번 실행, 초기회 구문


sess = tf.Session(graph=graph)
writer = tf.summary.FileWriter('./improved_graph', graph)
sess.run(init)


이 구문의 결과로 우리는 세션에서 그래프를 선택하며 텐서보드를 만들고 init구문을 이용해 초기화를 시켜준다.

이 구문들은 2번 사용할일이 없으므로 말그대로 초기화 구문이 된다.


반복부 : 여러번 실행


def run_graph(input_tensor):
feed_dict = {a: input_tensor}
_, step, summary = sess.run([output, increment_step, merged_summaries], feed_dict=feed_dict)
writer.add_summary(summary, global_step=step)


이 구문은 앞으로 여러번 호출하게될 구문이다. 따라서 함수로 제작한다.

그래프 실행시 받을 input을 플레이스 홀더값에 feed_dict로 건내주는 부분이다.

또한 sees.run의 결과값을 받아서 add_summary구문을 사용해서 요약에 추가시켜준다.

sess.run은 총 3가지 값을 반환하게 여기서 만들었는데 이는 각각 output, increment_step,merged_summaries에 대응된다.

현재 output노드의 값은 필요 없어서 _로 버리고 increment_step의 결과를 받아서 현재 반복이 몇번째인지 확인해주고

summary를 받아서 텐서보드에 출력해준다.


이하 전체 코드는 아래와 같다.


import tensorflow as tf

graph = tf.Graph()

with graph.as_default():
with tf.name_scope('variables'):
global_step = tf.Variable(0, dtype=tf.int32, trainable=False, name='global_step')
total_output = tf.Variable(0.0, dtype=tf.float32, trainable=False, name='total_output')

with tf.name_scope('transformation'):
with tf.name_scope('input'):
a = tf.placeholder(tf.float32, shape=[None], name='input_placeholder_a')

with tf.name_scope('intermediate'):
b = tf.reduce_prod(a, name='product_b')
c = tf.reduce_sum(a, name='sum_c')

with tf.name_scope('output'):
output = tf.add(b, c, name='output')

with tf.name_scope('update'):
update_total = total_output.assign_add(output)
increment_step = global_step.assign_add(1)

with tf.name_scope('summaries'):
avg = tf.div(update_total, tf.cast(increment_step, tf.float32), name='average')
tf.summary.scalar('output_summary', output)
tf.summary.scalar('total_summary', update_total)
tf.summary.scalar('average_summary', avg)

with tf.name_scope('global_ops'):
init = tf.initialize_all_variables()
merged_summaries = tf.summary.merge_all()

sess = tf.Session(graph=graph)
writer = tf.summary.FileWriter('./improved_graph', graph)
sess.run(init)


def run_graph(input_tensor):
feed_dict = {a: input_tensor}
_, step, summary = sess.run([output, increment_step, merged_summaries], feed_dict=feed_dict)
writer.add_summary(summary, global_step=step)


run_graph([2, 8])
run_graph([3, 1, 3, 3])
run_graph([8])
run_graph([1, 2, 3])
run_graph([11, 4])
run_graph([4, 1])
run_graph([7, 3, 1])
run_graph([6, 3])
run_graph([0, 2])
run_graph([4, 5, 6])

writer.flush()
writer.close()
sess.close()


이제 텐서보드를 실행시켜 보자.



우리는 summary값을 총 3개를 받았었다. 따라서 텐서보드에서는 3개의 summary가 출력된다.

이제 뭔가 그럴싸한 프로그램을 만든 느낌이든다.



그래프 탭을 누르면 이렇게 namespace로 확인할 수 있다.


여기서 혹시나 노드가 어떻게 돌아가는지 모르는 사람을 위해서 한번 이야기를 하자면,

노드는 재귀적이게 호출된다. 위의 그래프를 예로들자면 output노드를 실행한다 가정해보자.

output노드는 실행에 반드시 b,c 노드가 필요하다

따라서 b,c노드도 실행된다.

b,c노드를 실행할려고 하면 반드시 a노드가 필요하다.

따라서 a노드도 실행된다.

a노드는 실행되기위해서 필요한 노드가 없다. 따라서 더이상 노드를 실행하지 않는다.


output노드를 실행시키는데 필요한 노드를 제외하고 그 어떠한 노드들도 실행하지 않는다.

따라서 우리는 현재 몇번째 진행중인지 알 수 있는 increment_step노드는 따로 실행해주어야한다.

merged_summaries노드역시 마찬가지이다.

merged_summaries를 만들기위해서 tf.summary.scalar들을 실행시켜야하고 이들을 실행시키기위해서

scalar를 만드는 3개의 노드를 모두 실행시켜야한다.

그래서 사실은 output노드는 따로 호출하지 않아도 이 그래프는 작동을 한다.



'Programming > Python-Tensorflow' 카테고리의 다른 글

[Tensorflow-07] Name Scope  (0) 2017.08.04
[Tensorflow-06] 변수  (0) 2017.08.04
[Tensorflow-05] Fetches, Feed Dictionary, Place Holder  (0) 2017.08.02
[Tensorflow-04] 그래프  (0) 2017.08.02
[Tensorflow-03] 텐서  (0) 2017.08.02

+ Recent posts