Tensorflow2 API

Nam Eun-Ji·2021년 2월 26일
0

딥러닝 모델을 작성하는 방법
1. Sequential
2. Functional
3. Model Subclassing

1. Sequential

model = keras.Sequential()이라고 선언해준 후 차곡차곡 레이어를 쌓아가는 형태이다.

  • 입력과 출력이 각각 1가지인 경우에 가능하다.
  • 모델의 입력과 출력이 여러개인 경우에는 적합하지 않은 방식이다.
  • 참고 tensorflow 공식문서
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)



2. Functional

keras.Model을 사용하여 더 자유로운 모델을 만들 수 있다.

  • 입력과 출력을 규정함으로써 모델 전체를 규정한다.
  • 입력, 출력은 한 개일 수도, 여러개일 수도 있다.
  • 참고 tensorflow 공식문서
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)



3. Subclassing

Subclassing은 이 세가지 방법 중 가장 자유롭게 모델링할 수 있는 방식이다.

  • keras.Model을 상속받은 모델 클래스를 만든다.
  • init메소드 안에서 레이어 구성을 정의한다.
  • call메소드 안에서 레이어간 forward propagation을 구현한다.
  • 레이어에 대한 깊은 이해도가 필요하다.
  • 참고 tensorflow 공식문서
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)




실습

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



실습 - Sequential

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()
model.add(tf.keras.layers.Conv2D(32, 3, activation='relu'))
model.add(tf.keras.layers.Conv2D(64, 3, activation='relu'))
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(128, activation='relu'))
model.add(tf.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)



실습 - Functional

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)
outputs = keras.layers.Dense(10, activation='softmax')(x)

model = keras.Model(inputs=inputs, outputs=outputs)

# 모델 학습
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)



실습 - Subclassing

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.dense1 = keras.layers.Dense(128, activation='relu')
    self.dense2 = keras.layers.Dense(10, activation='softmax')
    
  def call(self, x):
    x = self.conv1(x)
    x = self.conv2(x)
    x = self.flatten(x)
    x = self.dense1(x)
    x = self.dense2(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)





실습 cifar-100

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



실습 - Sequential

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=2)



실습 - Functional

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((2, 2))(x)
x = keras.layers.Conv2D(32, 3, activation='relu')(x)
x = keras.layers.MaxPool2D((2, 2))(x)
x = keras.layers.Flatten()(x)
x = keras.layers.Dense(256, activation='relu')(x)
outputs = keras.layers.Dense(100, activation='softmax')(x)

model = keras.Model(inputs=inputs, outputs=outputs)

# 모델 학습
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)



실습 - Subclassing

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.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()





Automatic differentiation - GradientTape

model.fit()의 실제 수행 과정은

  • Forward Propagation 수행 및 중간 레이어값 저장
  • Loss 값 계산
  • 중간 레이어값 및 Loss를 활용한 체인룰(chain rule) 방식의 역전파(Backward Propagation) 수행
  • 학습 파라미터 업데이트
    이렇게 4단계로 이루어져있고, 이 step을 여러번 반복한다.

tf.GradientTape는 순전파로 진행된 모든 연산의 중간 레이어값을 tape에 기록하고, 이를 이용해 gradient를 계산한 후 tape를 폐기한다.

compile

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

↓↓↓ 위 스텝을 아래와 같이 바꿀 수 있다.

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

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

fit

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

↓↓↓ 위 스텝을 아래와 같이 바꿀 수 있다.

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)):
        	if step % batch_size == batch_size - 1:
            	x_batch.append(x)
                y_batch.append(y)
                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()

그럼 이렇게 바꿔서 무엇이 좋을까?

  • tf.GradientTape()를 활용하면 model.compile()model.fit() 안에 감추어져 있던 한 스텝의 학습 단계(위 예제에서는 train_step 메소드)를 끄집어내서 자유롭게 재구성할 수 있게 된다.
  • 강화학습 혹은 GAN 학습을 위해서는 train_step 메소드의 재구성이 필수적이므로, tf.GradientTape()를 사용할 줄 알아야한다.
profile
한 줄 소개가 자연스러워지는 그날까지

0개의 댓글