본문 바로가기

Computer Science/AI Fundamental

[F-21] TF2 API

728x90

Intro

이번 시간에는 우리가 사용할 TF V2 API의 구성상 개요를 파악하고, 보다 다양하고 깊이있게 TF를 활용할 기본기를 갖추자.

학습 목표

  • Tensorflow V2의 개요와 특징을 파악한다.
  • Tensorflow V2의 3가지 주요 API 구성 방식을 이해하고 활용할 수 있다.
  • GradientTape를 활용해 보고 좀 더 로우 레벨의 딥러닝 구현 방식을 이해한다.

TensorFlow2 API로 모델 구성하기

0) TensorFlow2 API 알아보기

TensorFlow2를 활용함에 있어 딥러닝 모델을 3가지 방법으로 작성할 수 있다. 경우에 따라 적합한 모델링 방식을 택해서 사용할 수 있다는 점이 장점이다.

딥러닝 모델을 작성하는 방법들에는 Sequential, Functional, Model Subclassing이 있다. Functional은 Sequential보다 일반화된 개념이고, Model Subclassing은 클래스로 구현된 기존 모델을 상속받아 자신만의 모델을 만들어나가는 방식이다.

1) TensorFlow2 Sequential Model

import tensorflow as tf
from tensorflow import keras
model = keras.Sequential()   
model.add(__넣고싶은 레이어__)   
model.add(__넣고싶은 레이어__)   
model.add(__넣고싶은 레이어__)   
model.fit(x, y, epochs=10, batch_size=32)

Sequential 모델을 활용하면 쉽게 딥러닝 모델을 쌓아나갈 수 있다.

  • 장점: 초보자가 접근하기 쉬움
  • 단점: 모델의 입출력이 여러 개인 경우 적합하지 않음(반드시 입력 1가지/ 출력 1가지를 전제로 함)

텐서플로 2.0 시작하기: 초보자용

2) TensorFlow2 Functional API

import tensorflow as tf
from tensorflow import keras
inputs = keras.Input(shape=(__원하는 입력값 모양__))
x = keras.layers.__넣고싶은 레이어__(관련 파라미터)(input)
x = keras.layers.__넣고싶은 레이어__(관련 파라미터)(x)
outputs = keras.layers.__넣고싶은 레이어__(관련 파라미터)(x)
model = keras.Model(inputs=inputs, outputs=outputs)
model.fit(x,y, epochs=10, batch_size=32)

Keras.Model을 사용한다는 점이 Sequential Model을 쓰는 것보다 일반적인 접근이 되게 한다. Functional API를 활용하면 앞서 배운 Sequential Model보다 더 자유로운 모델링을 진행할 수 있다.

Funtional이라는 뜻은: 함수형으로 모델을 구성한다는 것, 즉 입출력을 규정함으로써 모델 전체를 규정한다는 생각이다. 따라서 이번에는 Input을 규정한다. 그리고 input과 layer들을 엮어 Output까지 규정하면 된다.

The Keras functional API

3) TensorFlow2 Subclassing

import tensorflow as tf
from tensorflow import keras
class CustomModel(keras.Model):
    def __init__(self):
        super(CustomModel, self).__init__()
        self.__정의하고자 하는 레이어__()
        self.__정의하고자 하는 레이어__()
        self.__정의하고자 하는 레이어__()
    def call(self, x):
        x = self.__정의하고자 하는 레이어__(x)
        x = self.__정의하고자 하는 레이어__(x)
        x = self.__정의하고자 하는 레이어__(x)
        return x
model = CustomModel()
model.fit(x,y, epochs=10, batch_size=32)

Subclassing을 활용하면 가장 자유로운 모델링이 가능해진다. 본질적으로는 Functional한 접근과 차이는 없다. 두 방법 모두 keras.Model을 상속받은 모델 클래스를 만드는 것이기 때문이다. keras.Model은 __init__()이라는 메서드 내에서 레이어 구성을 정의한다. 그리고 call()이라는 메서드 내에서 레이어 간의 forward propagation을 구현한다. 이것으로 끝이지만, 이는 각 레이어에 대한 깊은 이해를 필요로 하기 때문에 초심자에게 의도치 않은 버그를 유발할 수 있다.

텐서플로 2.0 시작하기: 전문가용

Tensorflow2 API로 모델 작성하기: MNIST (1) Sequential API 활용

TF2의 다양한 API에 대해 둘러보았다. 이제 이를 활용해서 이미지 문제를 풀어보며 자세하게 이해해보자.

총 2가지 문제를 3가지 API를 활용해 구현할 예정이고, 해당 과정은 최대한 스스로 진행하는 것을 목표로 한다.

첫 번째는 MNIST 문제를 구현해보자.

import tensorflow as tf
from tensorflow import keras
import numpy as np
# 데이터 구성부분
mnist = keras.datasets.mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

x_train=x_train[...,np.newaxis]
x_test=x_test[...,np.newaxis]

print(len(x_train), len(x_test))
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step
11501568/11490434 [==============================] - 0s 0us/step
60000 10000

✔ np.newaxis의 기능: used to increase the dimension of the existing array by one more dimension, when used once.

"numpy.newaxis"는 무엇이고 언제 사용하는가

✔ ...(Ellipsis 객체)을 사용하는 이유: used in numpy to slice higher-dimensional data structures. It's designed to mean at this point, insert as many full slices (:) to extend the multi-dimensional slice to all dimensions.

How do you use the ellipsis slicing syntax in Python?

Ellipsis 객체1

Ellipsis 객체2

# Sequential Model을 구성해주세요.
"""
Spec:
1. 32개의 채널을 가지고, 커널의 크기가 3, activation function이 relu인 Conv2D 레이어
2. 64개의 채널을 가지고, 커널의 크기가 3, activation function이 relu인 Conv2D 레이어
3. Flatten 레이어
4. 128개의 아웃풋 노드를 가지고, activation function이 relu인 Fully-Connected Layer(Dense)
5. 데이터셋의 클래스 개수에 맞는 아웃풋 노드를 가지고, activation function이 softmax인 Fully-Connected Layer(Dense)
"""

# 여기에 모델을 구성해주세요
model = keras.Sequential([
    keras.layers.Conv2D(32, 3, activation='relu'),
    keras.layers.Conv2D(64, 3, activation='relu'),
    keras.layers.Flatten(),
    keras.layers.Dense(128, activation='relu'),
    keras.layers.Dense(10, activation='softmax')
])
# 모델 학습 설정

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(x_train, y_train, epochs=5)

model.evaluate(x_test,  y_test, verbose=2)
Epoch 1/5
1875/1875 [==============================] - 26s 3ms/step - loss: 0.1093 - accuracy: 0.9663
Epoch 2/5
1875/1875 [==============================] - 6s 3ms/step - loss: 0.0351 - accuracy: 0.9891
Epoch 3/5
1875/1875 [==============================] - 6s 3ms/step - loss: 0.0194 - accuracy: 0.9935
Epoch 4/5
1875/1875 [==============================] - 6s 3ms/step - loss: 0.0130 - accuracy: 0.9956
Epoch 5/5
1875/1875 [==============================] - 6s 3ms/step - loss: 0.0096 - accuracy: 0.9971
313/313 - 1s - loss: 0.0489 - accuracy: 0.9875





[0.04890325665473938, 0.987500011920929]

Tensorflow2 API로 모델 작성하기: MNIST (2) Functional API 활용

이번에는 keras.Model을 직접 활용해서 keras.Input으로 정의된 input/output 레이어 구성을 통해 model을 구현하자.

import tensorflow as tf
from tensorflow import keras
import numpy as np
mnist = keras.datasets.mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

x_train=x_train[...,np.newaxis]
x_test=x_test[...,np.newaxis]

print(len(x_train), len(x_test))
60000 10000
"""
Spec:
0. (28X28X1) 차원으로 정의된 Input
1. 32개의 채널을 가지고, 커널의 크기가 3, activation function이 relu인 Conv2D 레이어
2. 64개의 채널을 가지고, 커널의 크기가 3, activation function이 relu인 Conv2D 레이어
3. Flatten 레이어
4. 128개의 아웃풋 노드를 가지고, activation function이 relu인 Fully-Connected Layer(Dense)
5. 데이터셋의 클래스 개수에 맞는 아웃풋 노드를 가지고, activation function이 softmax인 Fully-Connected Layer(Dense)
"""

# 여기에 모델을 구성해 주세요.
inputs = keras.Input(shape=(28,28,1))

x = keras.layers.Conv2D(32,3, activation = 'relu')(inputs)
x = keras.layers.Conv2D(64,3, activation = 'relu')(x)
x = keras.layers.Flatten()(x)
x = keras.layers.Dense(128, activation='relu')(x)
predictions = keras.layers.Dense(10, activation='softmax')(x)

model = keras.Model(inputs=inputs, outputs=predictions)
# 모델 학습 설정

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(x_train, y_train, epochs=5)

model.evaluate(x_test,  y_test, verbose=2)
Epoch 1/5
1875/1875 [==============================] - 7s 3ms/step - loss: 0.1024 - accuracy: 0.9685
Epoch 2/5
1875/1875 [==============================] - 6s 3ms/step - loss: 0.0339 - accuracy: 0.9897
Epoch 3/5
1875/1875 [==============================] - 6s 3ms/step - loss: 0.0197 - accuracy: 0.9938
Epoch 4/5
1875/1875 [==============================] - 7s 3ms/step - loss: 0.0118 - accuracy: 0.9961
Epoch 5/5
1875/1875 [==============================] - 6s 3ms/step - loss: 0.0088 - accuracy: 0.9973
313/313 - 1s - loss: 0.0479 - accuracy: 0.9876





[0.0479215532541275, 0.9876000285148621]

이번 스텝 결과는 이전 스텝과 비교해서 큰 차이가 나지 않음을 확인할 수 있을 것이다.

Tensorflow2 API로 모델 작성하기: MNIST (3) Subclassing 활용

마지막 SubClassing방법은 keras.Model을 상속받은 클래스를 만드는 것이다.

__init__()메서드 안에서 레이어를 선언하고, call() 메서드 안에서 forward propagation을 구현하는 방식임을 기억하자.

Functional 방식과 비교하면, call()의 입력이 Input이고, call()의 return값이 Output이 되는 것이다.

import tensorflow as tf
from tensorflow import keras
import numpy as np
# 데이터 구성부분
mnist = keras.datasets.mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

x_train=x_train[...,np.newaxis]
x_test=x_test[...,np.newaxis]

print(len(x_train), len(x_test))
60000 10000
# Subclassing을 활용한 Model을 구성해주세요.
"""
Spec:
0. keras.Model 을 상속받았으며, __init__()와 call() 메서드를 가진 모델 클래스
1. 32개의 채널을 가지고, 커널의 크기가 3, activation function이 relu인 Conv2D 레이어
2. 64개의 채널을 가지고, 커널의 크기가 3, activation function이 relu인 Conv2D 레이어
3. Flatten 레이어
4. 128개의 아웃풋 노드를 가지고, activation function이 relu인 Fully-Connected Layer(Dense)
5. 데이터셋의 클래스 개수에 맞는 아웃풋 노드를 가지고, activation function이 softmax인 Fully-Connected Layer(Dense)
6. call의 입력값이 모델의 Input, call의 리턴값이 모델의 Output
"""

# 여기에 모델을 구성해주세요

class CustomModel(keras.Model):
    def __init__(self):
        super().__init__()
        self.conv1 = keras.layers.Conv2D(32, 3, activation='relu')
        self.conv2 = keras.layers.Conv2D(64, 3, activation='relu')
        self.flatten = keras.layers.Flatten()
        self.fc1 = keras.layers.Dense(128, activation='relu')
        self.fc2 = keras.layers.Dense(10, activation='softmax')
        
    def call(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.flatten(x)
        x = self.fc1(x)
        x = self.fc2(x)
        
        return x
        
model = CustomModel()
# 모델 학습 설정

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(x_train, y_train, epochs=5)

model.evaluate(x_test,  y_test, verbose=2)
Epoch 1/5
1875/1875 [==============================] - 7s 3ms/step - loss: 0.1119 - accuracy: 0.9662
Epoch 2/5
1875/1875 [==============================] - 6s 3ms/step - loss: 0.0338 - accuracy: 0.9893
Epoch 3/5
1875/1875 [==============================] - 6s 3ms/step - loss: 0.0199 - accuracy: 0.9934
Epoch 4/5
1875/1875 [==============================] - 6s 3ms/step - loss: 0.0133 - accuracy: 0.9955
Epoch 5/5
1875/1875 [==============================] - 6s 3ms/step - loss: 0.0094 - accuracy: 0.9970
313/313 - 1s - loss: 0.0423 - accuracy: 0.9892





[0.042330220341682434, 0.9891999959945679]

위 3가지 방법 모두 본질적으로는 동일하다!!

TensorFlow2 API로 모델 작성 및 학습하기: CIFAR-100 (1) Sequential API 활용

먼저, 아래 링크를 방문해 CIFAR-100 문제가 무엇인지 확인해 보자.

CIFAR-100 dataset

import tensorflow as tf
from tensorflow import keras
# 데이터 구성부분
cifar100 = keras.datasets.cifar100

(x_train, y_train), (x_test, y_test) = cifar100.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
print(len(x_train), len(x_test))
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-100-python.tar.gz
169009152/169001437 [==============================] - 3s 0us/step
169017344/169001437 [==============================] - 3s 0us/step
50000 10000
# Sequential Model을 구성해주세요.
"""
Spec:
1. 16개의 채널을 가지고, 커널의 크기가 3, activation function이 relu인 Conv2D 레이어
2. pool_size가 2인 MaxPool 레이어
3. 32개의 채널을 가지고, 커널의 크기가 3, activation function이 relu인 Conv2D 레이어
4. pool_size가 2인 MaxPool 레이어
5. 256개의 아웃풋 노드를 가지고, activation function이 relu인 Fully-Connected Layer(Dense)
6. 데이터셋의 클래스 개수에 맞는 아웃풋 노드를 가지고, activation function이 softmax인 Fully-Connected Layer(Dense)
"""

# 여기에 모델을 구성해주세요

model = keras.Sequential([
    keras.layers.Conv2D(16, 3, activation='relu'),
    keras.layers.MaxPool2D((2,2)),
    keras.layers.Conv2D(32, 3, activation='relu'),
    keras.layers.MaxPool2D((2,2)),
    keras.layers.Flatten(),
    keras.layers.Dense(256, activation='relu'),
    keras.layers.Dense(100, activation='softmax')
])
model.compile(optimizer = 'adam',
             loss = 'sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(x_train, y_train, epochs=5)

model.evaluate(x_test, y_test, verbose=1)
Epoch 1/5
1563/1563 [==============================] - 5s 3ms/step - loss: 1.9267 - accuracy: 0.4889
Epoch 2/5
1563/1563 [==============================] - 5s 3ms/step - loss: 1.8034 - accuracy: 0.5165
Epoch 3/5
1563/1563 [==============================] - 5s 3ms/step - loss: 1.6984 - accuracy: 0.5412
Epoch 4/5
1563/1563 [==============================] - 5s 3ms/step - loss: 1.5894 - accuracy: 0.5676
Epoch 5/5
1563/1563 [==============================] - 5s 3ms/step - loss: 1.4942 - accuracy: 0.5865
313/313 [==============================] - 1s 2ms/step - loss: 2.8394 - accuracy: 0.3697





[2.8394346237182617, 0.36970001459121704]

Tensorflow2 API로 모델 작성 및 학습하기: CIFAR-100 (2) Functional API 활용

이전 스텝과 차이가 없지만, 이번엔 keras.Model을 직접 활용해야 하므로, keras.Input으로 정의된 Input, Output 레이어 구성을 통해 model을 구현해야 한다.

import tensorflow as tf
from tensorflow import keras
cifar100 = keras.datasets.cifar100

(x_train, y_train), (x_test, y_test) = cifar100.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
print(len(x_train), len(x_test))
50000 10000
# Functional API를 활용한 Model을 구성해주세요.
"""
Spec:
0. (32X32X3) 차원으로 정의된 Input
1. 16개의 채널을 가지고, 커널의 크기가 3, activation function이 relu인 Conv2D 레이어
2. pool_size가 2인 MaxPool 레이어
3. 32개의 채널을 가지고, 커널의 크기가 3, activation function이 relu인 Conv2D 레이어
4. pool_size가 2인 MaxPool 레이어
5. 256개의 아웃풋 노드를 가지고, activation function이 relu인 Fully-Connected Layer(Dense)
6. 데이터셋의 클래스 개수에 맞는 아웃풋 노드를 가지고, activation function이 softmax인 Fully-Connected Layer(Dense)
"""

# 여기에 모델을 구성해주세요

inputs = keras.Input(shape=(32,32,3))

x = keras.layers.Conv2D(16, 3, activation='relu')(inputs)
x = keras.layers.MaxPool2D(pool_size=(2,2))(x)
x = keras.layers.Conv2D(32, 3, activation='relu')(x)
x = keras.layers.MaxPool2D(pool_size=(2,2))(x)
x = keras.layers.Flatten()(x)
x = keras.layers.Dense(256, activation='relu')(x)
predictions = keras.layers.Dense(100, activation='softmax')(x)

model = keras.Model(inputs = inputs, outputs = predictions)
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy']
             )

model.fit(x_train, y_train, epochs=5)
model.evaluate(x_test, y_test, verbose=1)
Epoch 1/5
1563/1563 [==============================] - 5s 3ms/step - loss: 2.9193 - accuracy: 0.2801
Epoch 2/5
1563/1563 [==============================] - 5s 3ms/step - loss: 2.6502 - accuracy: 0.3338
Epoch 3/5
1563/1563 [==============================] - 5s 3ms/step - loss: 2.4510 - accuracy: 0.3743
Epoch 4/5
1563/1563 [==============================] - 5s 3ms/step - loss: 2.2922 - accuracy: 0.4084
Epoch 5/5
1563/1563 [==============================] - 5s 3ms/step - loss: 2.1514 - accuracy: 0.4399
313/313 [==============================] - 1s 2ms/step - loss: 2.6098 - accuracy: 0.3559





[2.6097517013549805, 0.35589998960494995]

Tensorflow2 API로 모델 작성 및 학습하기: CIFAR-100 (3) Subclassing 활용

keras.Model을 상속받은 클래스를 만드는 것이다. 여전히 정확도는 40% 미만일텐데, 이번엔 Subclassing과 함께, 딥러닝 기법을 다양하게 적용해보자.

import tensorflow as tf
from tensorflow import keras
# 데이터 구성부분
cifar100 = keras.datasets.cifar100

(x_train, y_train), (x_test, y_test) = cifar100.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
print(len(x_train), len(x_test))
50000 10000
# Subclassing을 활용한 Model을 구성해주세요.
"""
Spec:
0. keras.Model 을 상속받았으며, __init__()와 call() 메서드를 가진 모델 클래스
1. 16개의 채널을 가지고, 커널의 크기가 3, activation function이 relu인 Conv2D 레이어
2. pool_size가 2인 MaxPool 레이어
3. 32개의 채널을 가지고, 커널의 크기가 3, activation function이 relu인 Conv2D 레이어
4. pool_size가 2인 MaxPool 레이어
5. 256개의 아웃풋 노드를 가지고, activation function이 relu인 Fully-Connected Layer(Dense)
6. 데이터셋의 클래스 개수에 맞는 아웃풋 노드를 가지고, activation function이 softmax인 Fully-Connected Layer(Dense)
7. call의 입력값이 모델의 Input, call의 리턴값이 모델의 Output
"""

# 여기에 모델을 구성해주세요

class CustomModel(keras.Model):
    def __init__(self):
        super().__init__()
        self.conv1 = keras.layers.Conv2D(16, 3, activation='relu')
        self.pool1 = keras.layers.MaxPool2D((2,2))        
        self.conv2 = keras.layers.Conv2D(32, 3, activation='relu')
        self.pool2 = keras.layers.MaxPool2D((2,2))
        self.flatten = keras.layers.Flatten()
        self.fc1 = keras.layers.Dense(256, activation='relu')
        self.fc2 = keras.layers.Dense(100, activation='softmax')
        
    def call(self, x):
        x= self.conv1(x)
        x= self.pool1(x)
        x= self.conv2(x)
        x= self.pool2(x)
        x= self.flatten(x)
        x= self.fc1(x)
        x= self.fc2(x)
        return x
        
model = CustomModel()
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(x_train, y_train, epochs=5)

model.evaluate(x_test,  y_test, verbose=2)
Epoch 1/5
1563/1563 [==============================] - 5s 3ms/step - loss: 3.5767 - accuracy: 0.1630
Epoch 2/5
1563/1563 [==============================] - 5s 3ms/step - loss: 2.8711 - accuracy: 0.2893
Epoch 3/5
1563/1563 [==============================] - 5s 3ms/step - loss: 2.5653 - accuracy: 0.3523
Epoch 4/5
1563/1563 [==============================] - 5s 3ms/step - loss: 2.3550 - accuracy: 0.3989
Epoch 5/5
1563/1563 [==============================] - 5s 3ms/step - loss: 2.1705 - accuracy: 0.4346
313/313 - 1s - loss: 2.5499 - accuracy: 0.3658





[2.5498905181884766, 0.36579999327659607]

GradientTape의 활용

Automatic differentiation - GradientTape

우리는 좀전까지 아주 비슷한 테스크 2개, 본질적으로 큰 차이가 없는 3개의 모델 구성방법을 활용해 딥러닝으로 구현해보았다.

그동안 동일하게 구성된 부분은 바로 아래 부분이다.

#모델 학습 설정
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
model.fit(x_train, y_train, epochs=5)

Numpy만 가지고 딥러닝을 구현하는 것을 회상해보자. model.fit()이라는 한 줄의 딥러닝 모델 훈련과정은 실제로 어땠는가??

  1. Forward Propagation 수행 및 중간 레이어값 저장
  2. Loss 값 계산
  3. 중간 레이어값 및 Loss를 활용한 체인룰(chain rule) 방식의 역전파(Backward Propagation) 수행
  4. 학습 파라미터 업데이트

위 4단계의 train_step을 반복했다. 이 과정은 TF2 API에는 model.fit()이라는 메서드 하나에 모두 추상화되어 있다.

TF에서 제공하는 tf.GradientTape는 순전파로 진행된 모든 연산의 중간 레이어 값을 tape에 기록하고, 이를 이용해 gradient를 계산한 후 tape를 폐기하는 기능을 수행한다. 이번엔 이전 스텝에서 진행한 학습을 tf.GradientTape를 이용한 것으로 변형해보자. tf.GradientTape는 이후 그래디언트를 좀더 고급스럽게 활용하는 여러 기법을 통해 만날 게 될 것이다.

# 앞부분과 동일

import tensorflow as tf
from tensorflow import keras

# 데이터 구성부분
cifar100 = keras.datasets.cifar100

(x_train, y_train), (x_test, y_test) = cifar100.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
print(len(x_train), len(x_test))

# 모델 구성부분
class CustomModel(keras.Model):
    def __init__(self):
        super().__init__()
        self.conv1 = keras.layers.Conv2D(16, 3, activation='relu')
        self.maxpool1 = keras.layers.MaxPool2D((2,2))
        self.conv2 = keras.layers.Conv2D(32, 3, activation='relu')
        self.maxpool2 = keras.layers.MaxPool2D((2,2))
        self.flatten = keras.layers.Flatten()
        self.fc1 = keras.layers.Dense(256, activation='relu')
        self.fc2 = keras.layers.Dense(100, activation='softmax')

    def call(self, x):
        x = self.conv1(x)
        x = self.maxpool1(x)
        x = self.conv2(x)
        x = self.maxpool2(x)
        x = self.flatten(x)
        x = self.fc1(x)
        x = self.fc2(x)

        return x

model = CustomModel()
50000 10000
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

위와 같이 학습을 진행하면 내부적으로는 매 스텝 학습이 진행될 때마다 발생하는 loss및 그래디언트가 어떻게 학습 파라미터를 업데이트하게 되는지를 지정해주는 작업이 model.compile()에서 자동으로 진행되었다.

아래 코드는 tape.gradient()를 통해 매 스텝 학습이 진행될 때마다 발생하는 그래디언트를 추출한 후 optimizer.apply_gradients()를 통해 발생한 그래디언트가 업데이터해야 할 파라미터 model.trainable_variables를 지정해주는 과정을 기술한 것이다.

# 매 스텝 진행되는 학습의 실제 동작을 train_step으로 구현하기

loss_func = tf.keras.losses.SparseCategoricalCrossentropy()
optimizer = tf.keras.optimizers.Adam()

# tf.GradientTape()를 활용한 train_step
def train_step(features, labels):
    with tf.GradientTape() as tape:
        predictions = model(features)
        loss = loss_func(labels, predictions)
        gradients = tape.gradient(loss, model.trainable_variables)
    optimizer.apply_gradients(zip(gradients, model.trainable_variables))
    return loss
model.fit(x_train, y_train, epochs=5, batch_size=32)

이렇게 진행된 배치 학습 과정은, 매 스텝마다 train_step()가 호출되는 과정으로 바꾸어 구현할 수 있다.

model.fit()호출 시에 결정되는 batch_size만 결정해 주면 된다.

# fit
import time
def train_model(batch_size=32):
    start = time.time()
    for epoch in range(5):
        x_batch = []
        y_batch = []
        for step, (x, y) in enumerate(zip(x_train, y_train)):
            x_batch.append(x)
            y_batch.append(y)
            if step % batch_size == batch_size-1:
                loss = train_step(np.array(x_batch, dtype=np.float32), np.array(y_batch, dtype=np.float32))
                x_batch = []
                y_batch = []
        print('Epoch %d: last batch loss = %.4f' % (epoch, float(loss)))
    print("It took {} seconds".format(time.time() - start))

train_model()
Epoch 0: last batch loss = 3.3150
Epoch 1: last batch loss = 3.0266
Epoch 2: last batch loss = 2.9100
Epoch 3: last batch loss = 2.7622
Epoch 4: last batch loss = 2.5931
It took 77.00022721290588 seconds

위에서 구현한 train_model() 메서드가 model.fit() 메서드와 기능적으로 같댜는 게 확인될 것이다!!

이렇듯 tf.GradientTape()를 활용하면 model.compile()과 model.fit()안에 있던 한 스텝의 학습 단계를 끄집어내서 원하는 대로 재구성할 수 있게 된다. 그동안 다뤄왔던 지도학습 방식과 다른 강화학습/Gan의 학습을 위해서는 train_step 메서드의 재구성이 필수이므로 tf.GradientTape()의 활용법을 꼭 숙지해야한다!!!

# evaluation
prediction = model.predict(x_test, batch_size=x_test.shape[0], verbose=1)
temp = sum(np.squeeze(y_test) == np.argmax(prediction, axis=1))
temp/len(y_test)  # Accuracy
1/1 [==============================] - 1s 848ms/step





0.3399

그래디언트를 활용할 필요가 없는 evaluation 단계는 predict 메서드를 활용해보았다.

728x90

'Computer Science > AI Fundamental' 카테고리의 다른 글

[F-23] Convolution  (0) 2022.02.22
[F-22] Deep Network  (0) 2022.02.22
[F-20] Unsupervised Learning  (0) 2022.02.22
[F-19] Regression  (0) 2022.02.21
[F-18]Understanding of Deep learning  (0) 2022.02.21