본문 바로가기

ML & AI Theory

텐서플로우 #1] 텐서플로우의 시작과 배열 구조 다루기

반응형

일반적인 내용은 넘어가겠습니다.

 

텐서플로는 노드 집합으로 구성된 계산 그래프를 바탕으로 합니다. 각 노드는 0개 이상의 입력이나 출력을 가지는 연산을 나타냅니다. 계산 그래프의 에지를 따라 이동하는 값을 텐서(tensor)라고 합니다.

 

텐서플로 1.5.0 버전에는 계산 그래프를 만들지 않고 텐서를 즉시 평가할 수 있는 즉시 실행(eager execution) 기능이 추가되었습니다. 텐서플로 2.x에서는 즉시 실행 모드가 기본으로 활성화되어 있어서 텐서 값을 바로 확인할 수 있습니다. 이 절에서는 저수준 API를 사용하여 텐서플로 1.x와 2.x 차이를 알아보겠습니다.

 

텐서는 스칼라(sclar), 벡터, 행렬 등이 일반화된 것으로 생각할 수 있습니다. 구체적으로 말해서 스칼라는 랭크 0 텐서로 정의합니다. 벡터는 랭크 1 텐서, 행렬은 랭크 2 텐서로 정의합니다. 행렬을 세 번째 차원으로 쌓아 올리면 랭크 3 텐서가 됩니다.

 

텐서플로 1.x에서는 계산 그래프가 구성된 후 텐서플로의 Session을 통해 그래프에 있는 각 노드를 실행합니다. 텐서플로 2.x 버전에서 이와 관련된 연산은 tensorflow.compat.v1 모듈 아래로 이동되었습니다. 텐서플로 1.x 버전의 API를 사용해 보고 바로 2.x 버전의 API로 동일한 계산을 구현해 보겠습니다.

 

텐서플로의 스칼라를 사용한 첫 번째 예제로 1차원 데이터셋 x와 가중치 w, 절편 b로부터 최종 입력 z를 계산해 보겠습니다.

 

z=w x w + b

 


tensorflow 1.x version

 

다음 코드는 텐서플로 1.x 방식의 저수준 API를 사용하여 이 식을 구현한 것입니다.

import tensorflow as tf

g = tf.Graph()

with g.as_default():
    x = tf.compat.v1.placeholder(dtype=tf.float32, shape=(None), name='x')
    w = tf.Variable(2.0, name='weight')
    b = tf.Variable(0.7, name='bias')
    
    z = w*x+b
    
    init = tf.compat.v1.global_variables_initializer()
    

with tf.compat.v1.Session(graph=g) as sess:
    sess.run(init)
    
    for t in [1.0, 0.6, -1.8]:
        print('x=%4.1f --> z=%4.1f'%(t, sess.run(z, feed_dict={x:t})))

텐서플로 1.x 방식의 저수준 API로 개발할 때 입력 데이터(x, y 또는 튜닝이 필요한 다른 파라미터)를 위해 플레이스 홀더(placeholder)를 정의합니다. 그다음 가중치 행렬을 정의하고 입력에서부터 출력까지 연결된 모델을 만듭니다. 최적화 문제인 경우에는 손실 함수 또는 비용 함수를 정의하고 어떤 최적화 알고리즘을 사용할지 결정합니다. 텐서플로의 그래프는 정의된 모든 요소를 노드로 포함시킵니다.

 

그다음 세션을 만들고 변수를 초기화합니다. 앞서 shape=(None)으로 플레이스홀더 x를 만들었습니다. 입력 데이터 크기를 정의하지 않았으므로 다음과 같이 배치 데이터를 한 번에 전달하여 원소 하나씩 차례대로 모델에 주입할 수 있습니다.

 

with tf.compat.v1.Session(graph=g) as sess:
    sess.run(init)
    print(sess.run(z, feed_dict={x:[1.,2.,3.]}))

print(z)

이 코드에 있는 텐서z를 출력해 봅시다.

[2.7 4.7 6.7]
Tensor("add:0", dtype=float32)

 

텐서 이름 "add:0"은 z가 덧셈 연산의 첫 번째 출력이라는 것을 알려 줍니다. 텐서 z에는 실제 어떤 값도 들어 있지 않습니다. 세션을 열고 텐서 z를 평가해야 비로소 값을 얻을 수 있습니다. 이렇게 텐서플로 1.x 방식에서는 그래프 정의 단계와 실행 단계로 크게 나누어져 있습니다. z를 평가할 때 필요한 데이터가 있다면 feed_dict매개변수를 통해 플레이스홀더에 데이터를 주입해야 합니다.

 

 


tensorflow 1.x version

 

tensorflow 2.x 방식으로 위와 동일한 계산을 만들어 보겠습니다.

#tensorflow 2.x
w = tf.Variable(2.0, name = 'weight')
b = tf.Variable(2.0, name = 'bias')

#z를 평가합니다.
for x in [1.0, 0.6,-1.8]:
    z = w*x+b
    print('x=%4.1f --> z=%4.1f'%(x,z))
    
print(z)
print(z.numpy())

Session 객체를 만들어 플레이스홀더에 데이터를 주입하는 대신 파이썬 리스트를 사용하여 직접 z값을 계산했습니다. 텐서플로 2.x방식에서는 변수 초기화 과정도 불필요합니다. 앞에서처럼 z를 출력해 보겠습니다. 또한 텐서 값을 넘파이 배열로 출력하려면 numpy()메서드를 사용합니다.

 

x= 1.0 --> z= 4.0
x= 0.6 --> z= 3.2
x=-1.8 --> z=-1.6
tf.Tensor(-1.5999999, shape=(), dtype=float32)
-1.5999999

 


배열 구조 다루기

 

텐서프로에서 배열 구조를 어떻게 다루는지 알아보겠습니다. 다음 코드를 실행하면 3X2X3 크기의 간단한 랭크 3 텐서를 만듭니다. 그다음 크기를 바꾸고 텐서플로의 최적화된 연산을 사용하여 각 열의 합을 계산합니다.

import tensorflow as tf
import numpy as np

x_array = np.arange(18).reshape(3,2,3)
x2 = tf.reshape(x_array, shape=(-1,6))

print(x_array)
print(x2)

#각 열의 합을 계산합니다.
xsum = tf.reduce_sum(x2, axis=0)
print(xsum)

xmean = tf.reduce_mean(x2, axis=0)
print(xmean)

xmean = tf.reduce_mean(x2, axis=1)
print(xmean)

reshpe 메서드의 첫 번째 차원으로 -1을 사용했습니다. 텐서 크기를 바꿀 때 특정 차원에 -1을 사용하면 텐서 전체 크기와 남은 차원에 따라 계산됩니다. axis 매개변수는 축소될 차원을 지정합니다. axis=0은 행 차원을 축소시켜 각 열의 합이나 평균을 계산합니다. axis=1은 열 차원을 축소시켜 각 행의 합이나 편균을 계산합니다.

[[[ 0  1  2]
  [ 3  4  5]]

 [[ 6  7  8]
  [ 9 10 11]]

 [[12 13 14]
  [15 16 17]]]
tf.Tensor(
[[ 0  1  2  3  4  5]
 [ 6  7  8  9 10 11]
 [12 13 14 15 16 17]], shape=(3, 6), dtype=int32)
tf.Tensor([18 21 24 27 30 33], shape=(6,), dtype=int32)
tf.Tensor([ 6  7  8  9 10 11], shape=(6,), dtype=int32)
tf.Tensor([ 2  8 14], shape=(3,), dtype=int32)

 

반응형