Data Science/Tensorflow2.0, Pytorch

Learning Tensorflow) 1. 텐서플로의 기본 이해하기

HJChung 2020. 4. 5. 22:17

텐서플로우의 동작의 구성

1. 만들고

2. 실행하기

 

1) 그래프 -> 세션 -> 실행 ; 만들고 실행하기 

import tensorflow as tf로 tensorflow를 import하면 그 시점에 있는 기본 그래프가 만들어지면 우리가 만드는 노드는 이 기본그래프에 자동으로 연결된다.

그래서 노드를 어떻게 만드는가?!

a = tf.constant(5)
b = tf.constant(2)

 

그러면 만들어진 노드로 연산을 진행하는 연산 그래프는 어떻게 만드는가?!

흔히 사용되는 tensorflow 연산이나 축약형 연산자를 이용한다. 

d = tf.multiply(a, b) #a*b
c = tf.add(a, b)#a+b

 

 

이렇게 노드와, 연산 그래프를 만드고 나면 연산 그래프를 실행할 준비가 된 것이며, 연산을 실행하려면 세션을 만들고 실행하면 된다. 

sess = tf.Session() #연산 그래프를 시작하는 Session객체 
#- Session객체는 텐서플로 API의 일부로, 파이썬 객체와 데이터, 객체의 메모리가 할당되어 있는 실행 환경 사이를 연결하며, 중간 결과를 저장하고 최종 결과를 작업환경으로 보내준다. 
outs = sess.run(f) #연산 그래프가 실행(run) 즉, 계산이 된다. 
#- 이때 계산은, 출력이 나와야 하는 노드에서 시작해 역방향으로 처리하여 의존관계 집합에 따라 실행되어야 하는 노드의 연산을 수행한다. 이 f라는 것은, 노드 f의 계산을 실행요청 한다는 것을 의미한다. 
sess.close() #연산 작업이 마무리되면, 
#이 명령을 사용하여 Session을 닫아서 해당 session에서 사용하는 자원을 해제하는 것이 좋다. 
print("out = {}".format(outs)) #결과 출력

여기서 연산 그래프가 실행(run) 될때 출력이 나와야 하는 노드(이 예에서는 f)에서 시작해 역방향으로 처리하여 의존관계 집합에 따라 실행되어야 하는 노드의 연산을 수행한다.고 하였다. 즉 sess.run(노드) 에서 하나의 특정 노드의 실행을 요청한 것인데 이 인수를 fetches라고 한다. fetches는 노드들이 담긴 리스트가 될 수도 있으며 여러 노드의 출력을 요청한다.


2) 만들기 - 새로운 그래프 생성과 관리

앞에서 살펴보았듯이 tensorflow를 import하면 그 시점에 있는 기본 그래프가 만들어지지만 이와 별개로 그래프를 추가로 생성하고 관계를 직접 제어할 수도 있다. 

기본 그래프가 아닌 새로운 그래프의 생성은

import tensorflow as tf

g = tf.Graph() #.Graph()를 사용하여 새로운 그래프를 생성
print(g) 

이렇게 새로운 그래프를 많이 생성하다보면, 연산의 제어가 필요할 것이다. 즉, 원하는 그래프와 노드를 명확히 연결하고, 그에 관련된 연산을 실행 시킬 수 있는 것이 중요하다. 이를 위해서는 1. tf.get_default_graph()를 사용해서 특정 노드가 어떤 그래프와 연결되어 있는지를 확인하고, 2. 파이썬의 with구문을 이용하여 노드와 원하는 그래프와 명확한 연결을 수행할 수 있다. 또한 3. with구문을 as_default()명령과 함께 사용하면 해당 그래프가 기본 그래프인 콘텍스트 관리자를 반환한다. 

* with 구문을 사용하여 세션을 열면 모든 연산이 완료된 후 자동으로 세션이 닫힌다, 

graph1 = tf.get_default_graph()
graph2 = tf.Graph()

print(g1 is tf.get_default_graph()) #True

with g2.as_default():
	print(g1 is tf.get_default_graph()) #False

print(g1 is tf.get_default_graph()) #True

#즉, with g2.as_default(): 구문 안에서는 g2 그래프가 기본 그래프가 되는 것이다.
#이 with구문을 명시적으로 닫지 않고 세션을 시작할 수도 있다. 

3) 텐서의 흐름

앞에서 '이렇게 노드와, 연산 그래프를 만드고 나면 연산 그래프를 실행할 준비가 된 것이며, 연산을 실행하려면 세션을 만들고 실행하면 된다. '라고 했던 것과 같이 그래프에서 노드를 만들 때, 실제로는 연산 인스턴스가 생성되어 준비가 된 것이다. 생성된 연산 인스턴스는 그래프가 실행되기 전까지는 실제 값을 내놓지 않고 흐름(flow)골격이 형성된것에 불과하다. 이런 흐름은 계산된 결과를 다른 노드로 전달할 수 있는 handle(;edge)로 연결되며, 이 edge는 Tensor object라고 한다. 

이후 세션이 실행되면, 그때 그래프에 데이터가 입력되고 연산이 시작된다. 

이제 '1) 그래프 -> 세션 -> 실행; 만들고 실해하기' 를 다시 보면 이해가 더 잘 될것이다. 

 


4) 더 자세히 ..

이제 그래프 -> 세션 -> 실행의 각각의 단계에서 더욱 자세하고 깊게 알아보고자 한다. 

1. 텐서 객체 

  • 1. 데이터 타입

그래프에 전달되는 데이터는 숫자, boolean, string 등의 다양한 데이터 타입을 가진다. 따라서 텐서 객체를 만들 때 데이터 타입을 지정하여 명시적으로 선택할 수 있다. 

c = tf.constant(4.0, dtype = tf.float64)
print(c.dtype) #.dtype을 사용하면 타입을 확인 할 수 있다. 
  • 2. 형 변환

연산시 데이터 타입이 일치하도록 하는 것은 매우 중요하다. 

tf.cast(해당 텐서, 변환할 데이터 타입) 을 이용하여 형 변환을 진행해준다. ==> 정말 자주 쓰인다!!

x = tf.constant([1, 2, 3], name = 'x', dtype = tf.float32)
x = tf.cast(x, tf.int64)

2. 텐서 배열과 형태

Tensorflow에서의 'Tensor'는 두가지 의미가 있다. 

  • 1. 이때까지 사용한 '그래프에서 연산의 결과에 대한 handle로 파이싼 API에서 사용하는 객체 이름'
  • 2. n차원 배열을 가리키는 수학 용어로, Tensorflow에서는 다차원 배열, 벡터, 행렬, 스칼라 등 그래프에서 전달되는 모든 단위 데이터를 Tensor로 간주한다. 

이번에는 그래프에서 전달되는 모든 단위 데이터인 Tensor를 다루어 보도록 한다. 파이썬의 자료형인 list와 Numpy 배열을 사용하여 Tensor를 초기화하고 이용할 수 있다. 

import numpy as np
import tensorflow as tf

c = tf.constant([[1, 2, 3], [4, 5, 6]]) #List를 사용한 Tensor
print('List input:', c.get_shape())

c = tf.constant(np.array([
			[[1, 2, 3], [4, 5, 6]],
            [[1, 1, 1],[2, 2, 2]] ]) #Numpy array를 사용한 Tensor
print('Numpy array input:', c.get_shape())

이 외에도 다양한 Tensor 초기화 함수들이 있다. 

tf.constant(value) 인수 value에 지정한 값 또는 값들로 채워진 텐서를 생성
tf.fill(shape, value) shape에 지정한 형태의 텐서를 만들고, value에 지정한 값으로 초기화
tf.zeros(shape) shape에 지정한 형태의 텐서를 만들고, 모든 원소의 값을 0으로 초기화
tf.zeros_like(tensor) tensor와 동일한 타입과 형태의 텐서를 만들고, 모든 원소의 값을 0으로 초기화
tf.ones(shape) shape에 지정한 형태의 텐서를 만들고, 모든 원소의 값을 1로 초기화
tf.ones_like(tensor) tensor와 동일한 타입과 형태의 텐서를 만들고, 모든 원소의 값을 1로 초기화
tf.random_normal(shape, mean, stddev) 정규분포를 따르는 난수를 생성
tf.truncated_normal(shape, mean, stddev) 절단정규분포(평균을 기준으로 표준편차보다 크거나 작은 데이터를 제외)를 따르는 난수를 생성
tf.random_uniform(shape, minval, maxval) [minval, maxval) 구간의 균등분포의 값을 생성
tf.random_shuffle(tensor) 첫 번째 차원에 따라 텐서를 무작위로 뒤섞음

표 출처: https://excelsior-cjh.tistory.com/151?category=940399

 

3. Tensor의 연산 - 행렬 곱

Tensor에서는 두 Tensor 객체 A, B에 대해 tf.matmul(A, B) 로 행렬곱을 수행한다. => 매우 유용한 산술 연산이다. 

주의 할 것은, matmul()을 사용하기 전에 양쪽 Tensor의 차원이 같은지, 순서가 맞춰져있는지 확인해야 한다. 

A = tf.constant([[1, 2, 3], [4, 5, 6]])
print(A.get_shape()) #(2, 3)

x = tf.constant([1, 0, 1])
print(x.get_shape()) #(3, )

#Ax = b 연산을 수행하기 위해서는 x에 차원을 추가하여 1차원 벡터에 하나의 열을 가진 2차원 행렬로 변환해야 한다.

4. Tensor의 연산 - 차원의 추가

위 예의 경우, Ax = b 연산을 수행하기 위해서는 x에 차원을 추가하여 1차원 벡터에 하나의 열을 가진 2차원 행렬로 변환해야 한다. tf.expand_dims()를 사용하여 차원을 하나 추가할 수 있다. => CNN등에서 매우 자주 사용된다.

x = tf.expand_dims(x, 1)
print(x.get_shape())

b = tf.matmul(A, x)

sess = tf.InteractiveSession()

print('matmul result:'.format(b.eval())
sess.close()

#matmul result:
[[4] [10]]

5. Tensor의 연산 - 배열 뒤집기(전치)

열 벡터를 행 벡터로 바꾸거나 그 반대로 바꾸는 식으로 배열을 뒤집으려면 tf.transpose()를 사용한다.