본문 바로가기

DEEP LEARNING/Tensorflow Training

Basic

TensorFlow 2.x Basics

ML 모델을 개발하고 학습시키는 데 도움이 되는 핵심 오픈소스 라이브러리로 TensorFlow와 Keras는 모두 약 4년전쯤 릴리즈 되었음 (Keras는 2015년 3월, TensorFlow는 2015년 11월). 딥러닝 세계의 관점에서 볼 때, 꽤 오랜시간이라고 볼 수 있음.

TensorFlow 1.x + Keras의 문제점

  • TensorFlow를 사용한다는것은 정적인 계산 그래프 조작을 의미하는 것으로, Imperative 코딩 스타일을 사용하는 프로그래머에게 어렵고 불편한 느낌을 받게 함
  • TensorFlow API : 매우 강력하면서도 유연하지만 빠른 코드의 작성의 가능성 결여 및 사용법이 어렵고 혼란스러움
  • Keras: 매우 생산적이고 사용이 쉽지만 유연성 결여

TensorFlow 2.0

TensorFlow와 Keras를 대대적으로 새로이 디자인한 것으로, 지난 4년간의 사용자 피드백과 기술의 진보가 모두 고려

  • 사용자들이 계산을 eager mode로 수행할 수 있게 해줌
    • Numpy 사용법과 유사
    • 프로그래밍이 직관적
    • pythonic
  • 1.x에서의 컴파일된 그래프의 엄청난 이점을 그대로 보존
    • 성능, 분산, 그리고 배포를 위함
    • TensorFlow를 빠르고, 분산 구조에서의 확장 가능하며, 상용화에 준비될 수 있도록 해 줌
  • Keras를 딥러닝의 고수준 API로 채택
    • TensorFlow를 이해하기 쉬우면서 높은 생산성을 가질 수 있게 만들어 줌
    • 매우 고수준(더 쉬운 사용성, 약간 부족한 유연성)에서부터 매우 저수준(더 깊은 전문성, 매우 뛰어난 유연성)의 다양한 범위의 작업으로까지 Keras를 확장

Google Colab에서 TensorFlow 2.x 버전 사용하기

try:
  # %tensorflow_version only exists in Colab.
#   %tensorflow_version 2.x
except Exception:
  pass

### TensorFlow library import
import tensorflow as tf
import numpy as np
from tensorflow import keras

### version 확인
print(tf.__version__)
print(keras.__version__)

Tensors

Tensor는 multi-dimensional array를 나타내는 말로, TensorFlow의 기본 data type

## Hello World
hello = tf.constant("Hello World")
print(hello)
tf.Tensor(b'Hello World', shape=(), dtype=string)

상수형 tensor 만들기

x = tf.constant([[1.0, 2.0],
                 [3.0, 4.0]])
print(x)
tf.Tensor(
[[1. 2.]
 [3. 4.]], shape=(2, 2), dtype=float32)
## numpy ndarray나 python의 list도 tensor로 바꾸기
x_np = np.array([[1.0, 2.0],
                [3.0, 4.0]])
x_np = tf.convert_to_tensor(x_np)

x_list = [[1.0, 2.0], 
         [3.0, 4.0]]
x_list = tf.convert_to_tensor(x_list)


## tensor를 numpy ndarray로 바꾸기
x.numpy()

많이 사용되는 방법

print(tf.ones(shape=(2,2)))
print(tf.zeros(shape=(2,2)))
tf.Tensor(
[[1. 1.]
 [1. 1.]], shape=(2, 2), dtype=float32)
tf.Tensor(
[[0. 0.]
 [0. 0.]], shape=(2, 2), dtype=float32)

Random한 상수형 tensor 만들기

## 표준정규분포로부터 random 상수 생성
tf.random.normal(shape=(2,2), mean=0., stddev=1.)

## 균등(uniform)분포로부터 random 상수 생성
tf.random.uniform(shape=(2, 2), minval=0, maxval=10, dtype='int32')

tensor 연산 : element-wise 연산 기본

a = tf.ones((2,2))*2
b = tf.ones((2,2))*6
print (a.numpy())
print (b.numpy())

## 덧셈
print (tf.add(a, b).numpy())
print ((a + b).numpy())

## 뺄셈
print (tf.subtract(b, a).numpy())
print ((b - a).numpy())

## 곱셈
print (tf.multiply(a, b).numpy())
print ((a * b).numpy())

## 나눗셈
print (tf.divide(b, a).numpy())
print ((b / a).numpy())
[[2. 2.]
 [2. 2.]]
[[6. 6.]
 [6. 6.]]

[[8. 8.]
 [8. 8.]]
[[8. 8.]
 [8. 8.]]

[[4. 4.]
 [4. 4.]]
[[4. 4.]
 [4. 4.]]

[[12. 12.]
 [12. 12.]]
[[12. 12.]
 [12. 12.]]

[[3. 3.]
 [3. 3.]]
[[3. 3.]
 [3. 3.]]
## Tensor와 numpy ndarray 호환
ndarray = np.ones((3, 3))
print(ndarray)

## Tensor연산에 입력으로 tensor가 아닌 ndarray가 입력으로 들어갈 수 있음
tensor = tf.multiply(ndarray, 10)
print(tensor)

## numpy ndarray연산에 입력으로 tensor가 들어갈 수도 있습니다
print(np.add(tensor, 2))
[[1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]]

tf.Tensor(
[[10. 10. 10.]
 [10. 10. 10.]
 [10. 10. 10.]], shape=(3, 3), dtype=float64)

[[12. 12. 12.]
 [12. 12. 12.]
 [12. 12. 12.]]

Variables (변수)

  • 변할 수 있는 상태를 저장하는데 사용되는 특별한 텐서
  • 대부분의 경우 학습해야 할 가중치(weight, parameter)들을 variable로 생성
  • 변수 텐서의 값을 바꿀 때는 assign, assign_add, assign_sub 메서드 사용
    • assign: 값을 완전히 할당. =에 해당
    • assign_add: 값을 증가. +=에 해당
    • assign_sub: 값을 감소. -=에 해당
## 초기값 사용
initial_value = tf.random.normal(shape=(2, 2))
weight = tf.Variable(initial_value)

## variable 초기화해주는 initializer 사용
weight = tf.Variable(tf.random_normal_initializer(stddev=0.1)(shape=(2,2)))

## .assign(value)`, `.assign_add(increment)`, 또는 `.assign_sub(decrement)`와 같은 메소드로 Variable 갱신
new_value = tf.random.normal(shape=(2,2))
weight.assign(new_value)

added_value = tf.ones(shape=(2,2))
weight.assign_add(added_value)

Shape

t = tf.constant([[[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],
                  [[13, 14, 15, 16], [17, 18, 19, 20], [21, 22, 23, 24]]]])
print(t.shape)
(1, 2, 3, 4)

Broadcasting

  1. 배열의 랭크가 같지 않으면 두 모양이 같은 길이가 될 때까지 배열의 낮은 랭크쪽에 1을 붙인다.
  2. 두 배열은 차원에서 크기가 같거나 배열 중 하나의 차원에 크기가 1 인 경우 차원에서 호환 가능하다.
  3. 배열은 모든 차원에서 호환되면 함께 브로드캐스트 될 수 있다.
  4. 브로드캐스트 후 각 배열은 두 개의 입력 배열의 요소 모양 최대 개수와 동일한 모양을 가진 것처럼 동작한다.
  5. 한 배열의 크기가 1이고 다른 배열의 크기가 1보다 큰 차원에서 첫 번째 배열은 마치 해당 차원을 따라 복사 된 것처럼 작동한다.
matrix1 = tf.constant([[3., 3.]])
matrix2 = tf.constant([[2.],[2.]])

print(matrix1)
print(matrix2)
tf.Tensor([[3. 3.]], shape=(1, 2), dtype=float32)
tf.Tensor(
[[2.]
 [2.]], shape=(2, 1), dtype=float32)
print(matrix1+matrix2)
print(matrix1/matrix2)
tf.Tensor(
[[5. 5.]
 [5. 5.]], shape=(2, 2), dtype=float32)

tf.Tensor(
[[1.5 1.5]
 [1.5 1.5]], shape=(2, 2), dtype=float32)

Tensor Operations

Indexing, Slicing

x = tf.constant([[1,2,3,4], [5,6,7,8], [9,10,11,12]])

## indexing - 항상 차원 감소
print(x[0])
print(x[1, 2])
print(x[2, 3])

## slicing
print(x[:2, 1:3])
print(x[1:3, 3:])
tf.Tensor([1 2 3 4], shape=(4,), dtype=int32)
tf.Tensor(7, shape=(), dtype=int32)
tf.Tensor(12, shape=(), dtype=int32)

tf.Tensor(
[[2 3]
 [6 7]], shape=(2, 2), dtype=int32)
tf.Tensor(
[[ 8]
 [12]], shape=(2, 1), dtype=int32)

Reshape, Expend_dims

t = tf.constant([[[0, 1, 2],
                [3, 4, 5]],
               [[6, 7, 8],
                [9, 10, 11]]])
print(t.shape)

print(tf.reshape(t, shape=[-1, 3]))
print(tf.reshape(t, shape=[-1, 1, 3]))
(2, 2, 3)

tf.Tensor(
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]], shape=(4, 3), dtype=int32)

tf.Tensor(
[[[ 0  1  2]]
 [[ 3  4  5]]
 [[ 6  7  8]]
 [[ 9 10 11]]], shape=(4, 1, 3), dtype=int32) 
x = tf.constant([0, 1, 2])
print(x.shape)

print(tf.expand_dims(x, axis=0))
print(tf.expand_dims(x, axis=1))
(3,)

tf.Tensor([[0 1 2]], shape=(1, 3), dtype=int32)
tf.Tensor(
[[0]
 [1]
 [2]], shape=(3, 1), dtype=int32)

Reduce Mean/Sum

x = tf.constant([[1., 2.],
                [3., 4.]])
print(tf.reduce_mean(x))
print(tf.reduce_mean(x, axis=0))    ### x.shape[axis]: x.shape[0] 에 대해 연산
print(tf.reduce_mean(x, axis=1))    ### x.shape[axis]: x.shape[1] 에 대해 연산
print(tf.reduce_mean(x, axis=-1))
tf.Tensor(2.5, shape=(), dtype=float32)
tf.Tensor([2. 3.], shape=(2,), dtype=float32)
tf.Tensor([1.5 3.5], shape=(2,), dtype=float32)
tf.Tensor([1.5 3.5], shape=(2,), dtype=float32)
print(tf.reduce_sum(x))
print(tf.reduce_sum(x, axis=0))
print(tf.reduce_sum(x, axis=-1))
print(tf.reduce_mean(tf.reduce_sum(x, axis=-1)))
tf.Tensor(10.0, shape=(), dtype=float32)
tf.Tensor([4. 6.], shape=(2,), dtype=float32)
tf.Tensor([3. 7.], shape=(2,), dtype=float32)
tf.Tensor(5.0, shape=(), dtype=float32)
print(t)

print(tf.reduce_sum(t, axis=0))
print(tf.reduce_sum(t, axis=1))
print(tf.reduce_sum(t, axis=-1))
tf.Tensor(
[[[ 0  1  2]
  [ 3  4  5]]
 [[ 6  7  8]
  [ 9 10 11]]], shape=(2, 2, 3), dtype=int32)

tf.Tensor(
[[ 6  8 10]
 [12 14 16]], shape=(2, 3), dtype=int32)
tf.Tensor(
[[ 3  5  7]
 [15 17 19]], shape=(2, 3), dtype=int32)
tf.Tensor(
[[ 3 12]
 [21 30]], shape=(2, 2), dtype=int32)

Argmax

x = [[0, 1, 2],
     [2, 1, 0]]

print(tf.argmax(x, axis=0))
print(tf.argmax(x, axis=1))
print(tf.argmax(x, axis=-1))
tf.Tensor([1 0 0], shape=(3,), dtype=int64)
tf.Tensor([2 0], shape=(2,), dtype=int64)
tf.Tensor([2 0], shape=(2,), dtype=int64)

One-hot Encoding

label = tf.constant([0, 1, 2, 0])
onehot1 = tf.one_hot(label, depth=3)
onehot2 = keras.utils.to_categorical(label, num_classes=3)

print(onehot1, type(onehot1))
print(onehot2, type(onehot2))
tf.Tensor(
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]
 [1. 0. 0.]], shape=(4, 3), dtype=float32) <class 'tensorflow.python.framework.ops.EagerTensor'>
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]
 [1. 0. 0.]] <class 'numpy.ndarray'>

Type Casting

print(tf.cast([1.8, 2.2, 3.3, 4.9], tf.int32))
print(tf.cast([True, False, 1 == 1, 0 == 1], tf.int32))
tf.Tensor([1 2 3 4], shape=(4,), dtype=int32)
tf.Tensor([1 0 1 0], shape=(4,), dtype=int32)