이글은 다음 문서를 참조합니다.

www.tensorflow.org/guide/keras/functional

(번역은 자력 + 파파고 + 구글 번역기를 사용하였으니, 부자연스럽더라도 양해바랍니다.)


The Keras Functional API in Tensorflow

!pip install -q pydot
!pip install graphviz(apt-get install graphviz)

pydot, graphviz를 설치해줍니다. 나중에 model_plot 그릴때 사용됩니다. (graphviz import error 는 밑쪽에)

 

Introduction

우리는 이미 keras.Sequential()을 이용하여 모델 만들기에 익숙해져있습니다. 함수형 API는 Sequential을 통한 모델 생성보다 더 유연하게 모델을 구축할 수 있습니다: 비선형 구조, 층 공유, 다중 입출력 모델을 다룰 수 있습니다.

 딥러닝은 DAG(directed acyclic graph) 아이디어로 구성되어있다. 함수형 API는 층에 대한 그래프를 만들기 위한 도구이다.

다음 모델을 고려해보자:

(input: 784-dimensional vectors)
       ↧
[Dense (64 units, relu activation)]
       ↧
[Dense (64 units, relu activation)]
       ↧
[Dense (10 units, softmax activation)]
       ↧
(output: probability distribution over 10 classes)

3개의 단순한 층으로 이루어진 그래프이다.

함수형 API로는 다음과 같이 만들 수 있고, 입력 노드는 다음과 같다.

from tensorflow import keras

inputs = keras.Input(shape=(784,))

우리의 데이터가 784차원임을 나타내준다. 배치 크기가 항상 생략할 수 있고, 우리는 그저 각 데이터의 구체적인 shape만 알려주면 된다. 만약 데이터가 (32, 32, 3)으로 이루어져 있다면 다음과 같을 것이다.

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

Input 함수의 반환은 input data의 dtype과 shape에 관한 정보를 포함합니다.

inputs.shape = TensorShape([None, 784])
inputs.dtype = tf.float32

 

층 그래프에 inputs object로 새로운 노드를 다음과 같이 만들 수 있습니다.

(층 그래프란 것은 쉽게 말해서 텐서플로우에서 Session안에 층을 구성하면 한 그래프에 층이 쌓이게(stack) 됩니다. 그래프는 통이고 층은 내용물이라고 생각하면 쉽겠네요.)

from tensorflow.keras import layers

dense = layers.Dense(64, activation='relu')
x = dense(inputs)

"layer call"은 우리가 만든 layer로 inputs에서 화살표를 그리는 것과 같습니다. dense layer에 입력을 통과시키고 x를 얻습니다.

더 추가해봅시다.

x = layers.Dense(64, activation='relu')(x)
outputs = layers.Dense(10, activation='softmax')(x)

입력과 출력을 지정하여 Model을 만들 수 있습니다.

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

복습합시다. 전체 과정은 다음과 같습니다.

inputs = keras.Input(shape=(784,), name='img')
x = layers.Dense(64, activation='relu')(inputs)
x = layers.Dense(64, activation='relu')(x)
outputs = layers.Dense(10, activation='softmax')(x)

model = keras.Model(inputs=inputs, outputs=outputs, name='mnist_model')

여러분이 만든 모델은 model.summary()를 이용하여 파라미터 수와 층이 어떻게 구성되어있는지 확인할 수 있습니다.

model.summary()

또한, keras.utils.plot_model(model, 'my_first_model.png')를 사용하면 예쁘게 그림도 그려주고 저장도 해줍니다.

 

ImportError: You must install pydot and graphviz for `pydotprint` to work.

-> 저는 pip말고 brew install graphviz로 설치하니까 되더군요.

keras.utils.plot_model()

선택적으로 shape도 보여줍니다.

keras.utils.plot_model(model, 'my_first_model_with_shape_info.png', show_shapes=True)

 

이러한 그림들과 코드는 사실상 동일함을 나타냅니다. 코드에서 연결화살표는 함수 호출을 의미합니다.

"graph of layers"는 딥러닝 모델을 직관적으로 나타내는 이미지이며, 함수형 API는 이러한 것을 나타내는데 도움을 줍니다.


Training, evaluation, and inference

훈련, 평가, 추론은 Sequential 모델에서 작동하듯이 함수형 API에서도 똑같이 작동합니다.

바로 예시를 보죠.

MNIST 데이터가 있고, validation_spilit을 통해 모니터링하면서 fit시킨 후 평가합니다.

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype('float32') / 255
x_test = x_test.reshape(10000, 784).astype('float32') / 255

model.compile(loss='sparse_categorical_crossentropy',
              optimizer=keras.optimizers.RMSprop(),
              metrics=['accuracy'])
history = model.fit(x_train, y_train,
                    batch_size=64,
                    epochs=5,
                    validation_split=0.2)
test_scores = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', test_scores[0])
print('Test accuracy:', test_scores[1])


Saving and Serialization

이 기능 또한 함수형 API에서도 사용할 수 있습니다.

model.save() 를 통해 모델을 저장할 수 있고, 코드가 있지 않아도 같은 구조의 모델을 load할 수 있습니다.

이 파일에는 다음과 같은 내용이 포함됨: - 모델의 아키텍처 - 모델의 weights 값(교육 중에 학습됨) - 모델의 training config(model.compile) 구성(편집하기 위해 전달된 내용), 있는 경우 - optimizer와 해당 상태(이 경우, 중단한 곳에서 교육을 다시 시작할 수 있음)

model.save('path_to_my_model.h5')
del model
# Recreate the exact same model purely from the file:
model = keras.models.load_model('path_to_my_model.h5')

 

Using the same graph of layers to define multiple models

함수형 API는 구체적으로 입출력을 지정할 수 있습니다. 이는 다중 입출력 모델 또한 구성 가능하다는 것을 뜻합니다.

아래 예시는 2 Model로 auto-encoder를 구성하는 것을 나타냅니다.

encoder_input = keras.Input(shape=(28, 28, 1), name='img')
x = layers.Conv2D(16, 3, activation='relu')(encoder_input)
x = layers.Conv2D(32, 3, activation='relu')(x)
x = layers.MaxPooling2D(3)(x)
x = layers.Conv2D(32, 3, activation='relu')(x)
x = layers.Conv2D(16, 3, activation='relu')(x)
encoder_output = layers.GlobalMaxPooling2D()(x)

encoder = keras.Model(encoder_input, encoder_output, name='encoder')
encoder.summary()

x = layers.Reshape((4, 4, 1))(encoder_output)
x = layers.Conv2DTranspose(16, 3, activation='relu')(x)
x = layers.Conv2DTranspose(32, 3, activation='relu')(x)
x = layers.UpSampling2D(3)(x)
x = layers.Conv2DTranspose(16, 3, activation='relu')(x)
decoder_output = layers.Conv2DTranspose(1, 3, activation='relu')(x)

autoencoder = keras.Model(encoder_input, decoder_output, name='autoencoder')
autoencoder.summary()

(꼭 summary()를 확인하세요!)

 

이러한 구성은 input_shape를 가진 output을 만들어내기 위해 필요합니다.

Conv2D의 반대는 Conv2DTranspose(가중치 학습 가능), MaxPooling2D의 받내는 UpSampling2D(가중치 학습 x)

All models are callable, just like layers

어떠한 모델이던 다른 층의 출력이나 입력을 층으로서 활용이 가능합니다. 이때, 모델 구조를 재사용하는 것이 아닌 가중치를 재사용한다는 것을 명심하세요

encoder_input = keras.Input(shape=(28, 28, 1), name='original_img')
x = layers.Conv2D(16, 3, activation='relu')(encoder_input)
x = layers.Conv2D(32, 3, activation='relu')(x)
x = layers.MaxPooling2D(3)(x)
x = layers.Conv2D(32, 3, activation='relu')(x)
x = layers.Conv2D(16, 3, activation='relu')(x)
encoder_output = layers.GlobalMaxPooling2D()(x)

encoder = keras.Model(encoder_input, encoder_output, name='encoder')
encoder.summary()

decoder_input = keras.Input(shape=(16,), name='encoded_img')
x = layers.Reshape((4, 4, 1))(decoder_input)
x = layers.Conv2DTranspose(16, 3, activation='relu')(x)
x = layers.Conv2DTranspose(32, 3, activation='relu')(x)
x = layers.UpSampling2D(3)(x)
x = layers.Conv2DTranspose(16, 3, activation='relu')(x)
decoder_output = layers.Conv2DTranspose(1, 3, activation='relu')(x)

decoder = keras.Model(decoder_input, decoder_output, name='decoder')
decoder.summary()

autoencoder_input = keras.Input(shape=(28, 28, 1), name='img')
encoded_img = encoder(autoencoder_input)
decoded_img = decoder(encoded_img)
autoencoder = keras.Model(autoencoder_input, decoded_img, name='autoencoder')
autoencoder.summary()

(꼭 summary()를 확인하세요!)

 

모델 앙상블링은 다음과 같이 구성됩니다.

def get_model():
  inputs = keras.Input(shape=(128,))
  outputs = layers.Dense(1, activation='sigmoid')(inputs)
  return keras.Model(inputs, outputs)

model1 = get_model()
model2 = get_model()
model3 = get_model()

inputs = keras.Input(shape=(128,))
y1 = model1(inputs)
y2 = model2(inputs)
y3 = model3(inputs)
outputs = layers.average([y1, y2, y3])
ensemble_model = keras.Model(inputs=inputs, outputs=outputs)

Manipulating complex graph topologies

Models with multiple inputs and outputs

함수형 API는 다중 입출력을 구성할 수 있습니다. 

다음 예제를 보겠습니다. 

티켓 우선순위에 따라 올바른 부서에 전달하는 시스템을 구축하고 있다고 생각합시다.

우리는 3개의 input을 가집니다.

  • Title of the ticket (text input)
  • Text body of the ticket (text input)
  • Any tags added by the user (categorical input)

또한, 2가지 output을 가집니다.

  • Priority score between 0 and 1 (scalar sigmoid output)
  • The department that should handle the ticket (softmax output over the set of departments)

코드를 볼까요.

num_tags = 12  # Number of unique issue tags
num_words = 10000  # Size of vocabulary obtained when preprocessing text data
num_departments = 4  # Number of departments for predictions

title_input = keras.Input(shape=(None,), name='title')  # Variable-length sequence of ints
body_input = keras.Input(shape=(None,), name='body')  # Variable-length sequence of ints
tags_input = keras.Input(shape=(num_tags,), name='tags')  # Binary vectors of size `num_tags`

# Embed each word in the title into a 64-dimensional vector
title_features = layers.Embedding(num_words, 64)(title_input)
# Embed each word in the text into a 64-dimensional vector
body_features = layers.Embedding(num_words, 64)(body_input)

# Reduce sequence of embedded words in the title into a single 128-dimensional vector
title_features = layers.LSTM(128)(title_features)
# Reduce sequence of embedded words in the body into a single 32-dimensional vector
body_features = layers.LSTM(32)(body_features)

# Merge all available features into a single large vector via concatenation
x = layers.concatenate([title_features, body_features, tags_input])

# Stick a logistic regression for priority prediction on top of the features
priority_pred = layers.Dense(1, activation='sigmoid', name='priority')(x)
# Stick a department classifier on top of the features
department_pred = layers.Dense(num_departments, activation='softmax', name='department')(x)

# Instantiate an end-to-end model predicting both priority and department
model = keras.Model(inputs=[title_input, body_input, tags_input],
                    outputs=[priority_pred, department_pred])

 

loss 또한, 각각 output에 대해 적절한 함수를 적용할 수 있습니다.

추가로 가중치를 줘서 각각 loss값이 total loss에 미치는 영향도를 조절할 수도 있습니다.

model.compile(optimizer=keras.optimizers.RMSprop(1e-3),
              loss=['binary_crossentropy', 'categorical_crossentropy'],
              loss_weights=[1., 0.2])

이름도 붙여 줄 수 있습니다.

model.compile(optimizer=keras.optimizers.RMSprop(1e-3),
              loss={'priority': 'binary_crossentropy',
                    'department': 'categorical_crossentropy'},
              loss_weights=[1., 0.2])

 

넘파이 배열을 이용하여 fit 시켜보겠습니다.

import numpy as np

# Dummy input data
title_data = np.random.randint(num_words, size=(1280, 10))
body_data = np.random.randint(num_words, size=(1280, 100))
tags_data = np.random.randint(2, size=(1280, num_tags)).astype('float32')
# Dummy target data
priority_targets = np.random.random(size=(1280, 1))
dept_targets = np.random.randint(2, size=(1280, num_departments))

model.fit({'title': title_data, 'body': body_data, 'tags': tags_data},
          {'priority': priority_targets, 'department': dept_targets},
          epochs=2,
          batch_size=32)

data, label을 fit시킬 떄는 두 가지 방법이 가능합니다.

  1. ([title_data, body_data, tags_data], [priority_targets, dept_targets])
  2.  ({'title': title_data, 'body': body_data, 'tags': tags_data}, {'priority': priority_targets, 'department': dept_targets})

A toy resnet model

함수형 API는 resnet과 같은 비선형적 모델 구조를 구성하기 쉽습니다.

잔차 연결의 예시를 봅시다.

inputs = keras.Input(shape=(32, 32, 3), name='img')
x = layers.Conv2D(32, 3, activation='relu')(inputs)
x = layers.Conv2D(64, 3, activation='relu')(x)
block_1_output = layers.MaxPooling2D(3)(x)

x = layers.Conv2D(64, 3, activation='relu', padding='same')(block_1_output)
x = layers.Conv2D(64, 3, activation='relu', padding='same')(x)
block_2_output = layers.add([x, block_1_output])

x = layers.Conv2D(64, 3, activation='relu', padding='same')(block_2_output)
x = layers.Conv2D(64, 3, activation='relu', padding='same')(x)
block_3_output = layers.add([x, block_2_output])

x = layers.Conv2D(64, 3, activation='relu')(block_3_output)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(256, activation='relu')(x)
x = layers.Dropout(0.5)(x)
outputs = layers.Dense(10, activation='softmax')(x)

model = keras.Model(inputs, outputs, name='toy_resnet')
model.summary()

(꼭 summary()를 확인하세요!)

plot_image도 꼭 확인하세요!

train 시켜 보겠습니다.

(x_train, y_train), (x_test, y_test) = keras.datasets.cifar10.load_data()
x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
y_train = keras.utils.to_categorical(y_train, 10)
y_test = keras.utils.to_categorical(y_test, 10)

model.compile(optimizer=keras.optimizers.RMSprop(1e-3),
              loss='categorical_crossentropy',
              metrics=['acc'])
model.fit(x_train, y_train,
          batch_size=64,
          epochs=1,
          validation_split=0.2)


Sharing layers

함수형 API는 공유 층을 사용하기에도 좋습니다.

밑의 예시는 한가지 임베딩 층에 두가지 입력을 넣어 재사용성을 높였고, 여러 입력을 학습합니다.

공유 계층은 이러한 서로 다른 입력에 걸친 정보의 공유를 가능하게 하고, 그러한 모형을 더 적은 데이터로 훈련시킬 수 있게 하기 때문에 유사한 공간(예: 유사한 어휘를 특징으로 하는 두 개의 다른 텍스트)에서 오는 입력을 인코딩하는 데 자주 사용된다. 입력 중 하나에서 특정 단어가 보이는 경우, 공유 계층을 통과하는 모든 입력의 처리에 도움이 될 것이다.

# Embedding for 1000 unique words mapped to 128-dimensional vectors
shared_embedding = layers.Embedding(1000, 128)

# Variable-length sequence of integers
text_input_a = keras.Input(shape=(None,), dtype='int32')

# Variable-length sequence of integers
text_input_b = keras.Input(shape=(None,), dtype='int32')

# We reuse the same layer to encode both inputs
encoded_input_a = shared_embedding(text_input_a)
encoded_input_b = shared_embedding(text_input_b)

 

--> 2019/03/30 - [ML/tensorflow2.0(keras)] - tensorflow 2.0 keras Funtional API (2)

이글은 다음 문서를 참조합니다.

https://www.tensorflow.org/alpha/guide/keras/overview

이 카테고리의 목적은 overview는 한글번역이 되어 있으나, 그 외에 것은 영어로 되있어 공부할 겸 번역한것입니다. 

그렇다고 overview를 빼놓긴 좀 그러니 추가해서 저 사이트의 가이드에 따라 끝까지 진행해보겠습니다.


이 글에는 즉시실행, Estimator, pipeline이 포함됩니다.


고급 모델 만들기

함수형 API

tf.keras.Sequential 모델은 단순히 층을 쌓은 것으로 임의의 구조를 표현할 수 없습니다. 함수형 APi는 다음과 같은 복잡한 모델 구조를 만들 수 있습니다.

- 다중 입력 모델

- 다중 출력 모델

- 층 공유

- 데이터 흐름이 차례대로 진행되지 않는 모델 ( ex: resnet)


함수형 API로 모델을 만드는 방식은 다음과 같습니다.

1. 하나의 층 객체는 호출 가능하고 텐서를 반환합니다.

2. tf.keras.Model 객체를 정의하기 위해 입력 텐서와 출력 텐서를 사용합니다.

3. 이 모델은 Sequential 모델과 동일한 방식으로 훈련됩니다.


다음 코드는 함수형 API를 사용하여 간단한 완전 연결 네트워크 예시입니다.

inputs = tf.keras.Input(shape=(32,))  # 입력 플레이스홀더를 반환합니다.

# 층 객체는 텐서를 사용하여 호출되고 텐서를 반환합니다.
x = layers.Dense(64, activation='relu')(inputs)
x = layers.Dense(64, activation='relu')(x)
predictions = layers.Dense(10, activation='softmax')(x)
model = tf.keras.Model(inputs=inputs, outputs=predictions)

# 컴파일 단계는 훈련 과정을 설정합니다.
model.compile(optimizer=tf.keras.optimizers.RMSprop(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# 5번의 에포크 동안 훈련합니다.
model.fit(data, labels, batch_size=32, epochs=5)

tf.keras.Sequential 모델과 다르게 Input과 Model객체를 통해 입력과 출력을 객체로 만들어 사용하고 있습니다.


모델 클래스 상속

tf.keras.Model 클래스를 상속하고 자신만의 forward pass를 정의하여 완전히 커스터마이징 된 모델을 만들 수 있습니다. __init__ 메서드에서 층을 만들어 클래스 객체의 속성으로 지정합니다. 

정방향 패스는 call 메서드에 정의합니다.

이 API에서는 가능한 한 함수형 API를 사용하라고 권장합니다. 

다음은 tf.keras.Model의 클래스를 상속하여 명령형 프로그래밍 방식으로 실행할 필요가 없는 정방향 패스를 구현한 예입니다.

class MyModel(tf.keras.Model): def __init__(self, num_classes=10): super(MyModel, self).__init__(name='my_model') self.num_classes = num_classes # 층을 정의합니다. self.dense_1 = layers.Dense(32, activation='relu') self.dense_2 = layers.Dense(num_classes, activation='sigmoid') def call(self, inputs): # 정방향 패스를 정의합니다. # `__init__` 메서드에서 정의한 층을 사용합니다. x = self.dense_1(inputs) return self.dense_2(x)

__init__에 모델 객체를 상속받아 모델 구성할 준비를 끝내고, 층을 쌓은뒤 call에서 먹이를 주고 결과를 반환받는다고 생각하시면 됩니다.

model = MyModel(num_classes=10)

# 컴파일 단계는 훈련 과정을 설정합니다.
model.compile(optimizer=tf.keras.optimizers.RMSprop(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# 5번의 에포크 동안 훈련합니다.
model.fit(data, labels, batch_size=32, epochs=5)


맞춤형 층(Custom layer)

tf.keras.layers.Layer 클래스를 상속하고 다음 메서드를 구현합니다.

- __init__ : 이 층에서 사용되는 하위 층을 정의합니다

- build : 층의 가중치를 만듭니다. add_weight를  사용해 가중치를 추가합니다.

- call : 정방향 패스를 구현합니다.


다음은 입력과 커널 행렬의 matmul 계산을 구현한 Custom layer의 예시입니다.

class MyLayer(layers.Layer):

  def __init__(self, output_dim, **kwargs):
    self.output_dim = output_dim
    super(MyLayer, self).__init__(**kwargs)

  def build(self, input_shape):
    # 이 층에서 훈련할 가중치 변수를 만듭니다.
    self.kernel = self.add_weight(name='kernel',
                                  shape=(input_shape[1], self.output_dim),
                                  initializer='uniform',
                                  trainable=True)

  def call(self, inputs):
    return tf.matmul(inputs, self.kernel)

  def get_config(self):
    base_config = super(MyLayer, self).get_config()
    base_config['output_dim'] = self.output_dim
    return base_config

  @classmethod
  def from_config(cls, config):
    return cls(**config)
model = tf.keras.Sequential([
    MyLayer(10),
    layers.Activation('softmax')])

# 컴파일 단계는 훈련 과정을 설정합니다.
model.compile(optimizer=tf.keras.optimizers.RMSprop(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# 5번의 에포크 동안 훈련합니다.
model.fit(data, labels, batch_size=32, epochs=5)

get_config는 생략하셔도 무방합니다. 나중에 더 알아보시면 됩니다. 중요한 것은 build에서 가중치를 통제한다는 것입니다. 

나머지 모델 구성 및 사용방법은 똑같습니다.


콜백

Callback은 훈련하는 동안 모델의 동작을 변경하고 확장하기 위해 전달하는 객체입니다. 자신만의 콜백을 작성하거나 다음과 같은 내장 tf.keras.callbacks를 사용할 수 있습니다.

-    tf.keras.callbacks.ModelCheckpoint : 일정 간격으로 모델의 체크포인트를 저장합니다. (가장 좋은 모델의 가중치, loss, acc 등등)

-    tf.keras.callbacks.LearningRateScheduler : 학습률을 동적으로 변경합니다.

-    tf.keras.callbacks.EarlyStopping : 검증 성능이 향상되지 않으면 훈련을 중지합니다.

-    tf.keras.callbacks.TensorBoard : 텐서보드를 사용하여 모델을 모니터링 합니다.


fit 메서드에 매개변수로 전달하여 사용할 수 있습니다.

callbacks = [
  # `val_loss`가 2번의 에포크에 걸쳐 향상되지 않으면 훈련을 멈춥니다.
  tf.keras.callbacks.EarlyStopping(patience=2, monitor='val_loss'),
  # `./logs` 디렉토리에 텐서보드 로그를 기록니다.
  tf.keras.callbacks.TensorBoard(log_dir='./logs')
]
model.fit(data, labels, batch_size=32, epochs=5, callbacks=callbacks,
          validation_data=(val_data, val_labels))


저장과 복원

가중치

tf.keras.Model.save_weights를 사용하여 모델의 가중치를 저장하고 복원합니다.

# 가중치를 텐서플로의 체크포인트 파일로 저장합니다.
model.save_weights('./weights/my_model')

# 모델의 상태를 복원합니다.
# 모델의 구조가 동일해야 합니다.
model.load_weights('./weights/my_model')

기본적으로 모델의 가중치는 텐서플로 체크포인트 파일 포맷으로 저장됩니다. 이와 다르게 케라스는 기본적으로 .hdf5 형식을 사용합니다


설정

케라스는 JSON과 YAML 포맷을 지원합니다.

# 모델을 JSON 포맷으로 직렬화합니다.
json_string = model.to_json()
json_string

YAML 포맷으로 직렬화하려면 텐서플로를 임포트하기 전에 pyyaml을 설치해야 합니다.

# JSON 파일로부터 (완전히 새로 초기화된) 모델을 만듭니다. fresh_model = tf.keras.models.model_from_json(json_string) # YAML 포맷으로 직렳화 합니다. yaml_string = model.to_yaml() print(yaml_string) # YAML파일로부터 모델을 다시 만듭니다 fresh_model = tf.keras.models.model_from_yaml(yaml_string)


(나머지는 복붙 및 코드로 대체하겠습니다. 넘어가셔도 됩니다)

즉시 실행

즉시 실행은 연산을 즉각 평가하는 명령형 프로그래밍(imperative programming) 환경입니다. 케라스에서는 즉시 실행이 필수가 아니지만 tf.keras는 이를 지원합니다. 이 기능은 프로그램을 검사하고 디버깅하는데 유용합니다.

모든 tf.keras 모델링 API는 즉시 실행과 호환됩니다. Sequential이나 함수형 API와 사용할 수 있지만 즉시 실행은 특히 모델 상속과 맞춤형 층을 만들 때 장점이 나타납니다. 이런 API는 (기존의 층을 조합하여 모델을 만드는 대신) 직접 정방향 패스의 코드를 작성하기 때문입니다.

즉시 실행 가이드에서 맞춤형 훈련 반복과 tf.GradientTape를 케라스 모델에 같이 사용하는 예를 참고하세요. 또한 간단하지만 완전한 예제를 여기에서 볼 수 있습니다.


분산처리

다중GPU

tf.keras 모델은 tf.distribute.Strategy를 사용하여 다중 GPU에서 실행할 수 있습니다. 이 API는 기존 코드를 거의 수정하지 않고 다중 GPU에서 훈련을 분산시킬 수 있습니다.

현재는 tf.distribute.MirroredStrategy가 유일하게 지원되는 분산 전략입니다. MirroredStrategy는 한 대의 장치에서 계산 결과를 모두 수집하는 방식인 그래프 내 복제(in-graph replication)를 수행합니다. distribute.Strategy를 사용하려면 Strategy의 .scope() 안에 옵티마이저 객체 생성, 모델 구성, 컴파일 단계를 포함시킨 다음 모델을 훈련합니다.

다음 코드는 한 대의 컴퓨터에서 다중 GPU를 사용해 tf.keras.Model을 분산 처리하는 예입니다.

먼저, MirroredStrategy의 scope() 안에서 모델을 정의합니다

strategy = tf.distribute.MirroredStrategy()

with strategy.scope():
  model = tf.keras.Sequential()
  model.add(layers.Dense(16, activation='relu', input_shape=(10,)))
  model.add(layers.Dense(1, activation='sigmoid'))

  optimizer = tf.keras.optimizers.SGD(0.2)

  model.compile(loss='binary_crossentropy', optimizer=optimizer)

model.summary()


이 글에는 즉시실행, Estimator, pipeline이 포함됩니다.

print(tf.__version__)
print(keras.__version__)

2.0.0-alpha0
2.2.4-tf

사용한 버전은 다음과 같습니다.

케라스는 Sequential을 사용하여 층을 차례대로 쌓습니다.  tf.keras.Sequential

 

층 설정

 

tf.keras.layers 에 있는 클래스는 공통으로 몇가지 매개변수를 가지고 있습니다.

- activation : default는 활성화 함수를 지정하지 않습니다.

- kernel_initializer / bias_initializer : default 는 'glorot_uniform' 입니다.

- kernel_regularizer / bias_regularizer : defualt는 규제를 적용하지 않습니다.

 

다음은 여러가지 매개변수를 사용한 Dense층 객체 예시입니다.

 # 시그모이드 활성화
layers.Dense(64, activation='sigmoid')
# 위와 같은 문장입니다.
layers.Dense(64, activation = tf.keras.activations.sigmoid)

# 규제항 추가 입니다.
layers.Dense(64, kernel_regularizer=tf.keras.regularizers.l1(0.01))
layers.Dense(64, bias_regularizer=tf.keras.regularizers.l2(0.01))

# 커널을 랜덤한 직교 행렬로 초기화한 층입니다.
layers.Dense(64, kernel_initializer='orthogonal')

# 절편 벡터를 상수 2.0으로 설정
layers.Dense(64, bias_initializer=tf.keras.initializers.Constant(2.0))

 

훈련과 평가

훈련 준비

Sequential과 같은 메서드로 층을 구성한 뒤 compile 메서드를 호출하여 학습 과정을 설명합니다. 밑의 코드를 보면 배열 형태로 층을 쌓듯이 앞에서부터 쌓아주는 것을 볼 수 있습니다. 

model = tf.keras.Sequential([
# 64개의 유닛을 가진 완전 연결 층을 모델에 추가합니다:
layers.Dense(64, activation='relu', input_shape=(32,)),
# 또 하나를 추가합니다:
layers.Dense(64, activation='relu'),
# 10개의 출력 유닛을 가진 소프트맥스 층을 추가합니다:
layers.Dense(10, activation='softmax')])

model.compile(optimizer=tf.keras.optimizers.Adam(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

tf.keras.Model.compile에는 세 개의 중요한 매개변수가 있습니다.

-    optimizer : tf.keras.optimizers.Adam(SGD)와 같은 tf.keras.optimizers 아래의 옵티마이저 객체를 전달합니다. 'adam', 'sgd'처럼 문자열로 지정할 수도 있습니다.

-    loss : mse, categorical_crossentropy, binary_crossentropy 등이 자주 사용됩니다. 손실 함수를 커스터마이즈할 수 있으며, tf.keras.losses 모듈 아래의 객체를 전달합니다.

-    metrics : tf.keras.metrics 모듈을 사용합니다.

- 더하여서 훈련과 평가를 즉시 실행하려면 run_eagerly=True 매개변수를 전달할 수 있습니다.

다음은 훈련의 예시입니다. 

# 평균 제곱 오차로 회귀 모델을 설정합니다.
model.compile(optimizer=tf.keras.optimizers.Adam(0.01),
              loss='mse',       # 평균 제곱 오차
              metrics=['mae'])  # 평균 절댓값 오차

# 크로스엔트로피 손실 함수로 분류 모델을 설정합니다.
model.compile(optimizer=tf.keras.optimizers.RMSprop(0.01),
              loss=tf.keras.losses.CategoricalCrossentropy(),
              metrics=[tf.keras.metrics.CategoricalAccuracy()])

 

Numpy 데이터를 사용한 훈련

데이터 셋이 작은 경우엔 numpy 배열을 메모리에 적재하여 모델을 훈련하고 평가할 수 있습니다.

 

import numpy as np

data = np.random.random((1000, 32))
labels = np.random.random((1000, 10))

model.fit(data, labels, epochs=10, batch_size=32)

tf.keras.Model.fit 에는 세 개의 중요한 매개변수가 있습니다.

-    epochs : 전체 입력 데이터를 얼마나 순회할 것인가

-    batch_size : 데이터를 작은 배치로 나누어서 순회합니다.

-    validation_data : (x_val, y_val) 형태로 넣어주어야 합니다.

 

다음은 validation_data를 사용하는 예입니다.

 import numpy as np

data = np.random.random((1000, 32))
labels = np.random.random((1000, 10))

val_data = np.random.random((100, 32))
val_labels = np.random.random((100, 10))

model.fit(data, labels, epochs=10, batch_size=32,
          validation_data=(val_data, val_labels))

 

tf.data 데이터셋을 사용한 훈련

tf.data는 대규모 데이터셋이나 분산 작업을 할때 사용하면 훨씬 빠르고 편리합니다. fit 메서드에 tf.data.Dataset 객체를 전달합니다.

# 예제 `Dataset` 객체를 만듭니다:
dataset = tf.data.Dataset.from_tensor_slices((data, labels))
dataset = dataset.batch(32)

# Dataset에서 `fit` 메서드를 호출할 때 `steps_per_epoch` 설정을 잊지 마세요.
model.fit(dataset, epochs=10, steps_per_epoch=30)

여기서 fit 메서드는 steps_per_epoch 매개변수를 사용합니다. 

다음 에포크로 넘어가기 전에 모델이 수행할 훈련 단계 횟수입니다.

! 이 예제가 완벽히 돌아가지 않습니다. steps_per_ecoch이 가진 데이터의 양에 비해 너무 크기 때문이죠. 이 예제가 끝까지 완성되는 것을 보고 싶다면 적어도 (10 * 30)개의 배치가 필요합니다. 하지만 1000개의 데이터에서 32개의 데이터를 가진 배치가 300개가 만들어지지 않으니 깔끔하게 끝까지 돌아가지진 않겠죠? 

Dataset.batch를 이용하여 배치 데이터를 생성하기 때문에 batch_size가 필요하지 않습니다.

Dataset은 검증 데이터에도 사용할 수 있습니다.

dataset = tf.data.Dataset.from_tensor_slices((data, labels))
dataset = dataset.batch(32)

val_dataset = tf.data.Dataset.from_tensor_slices((val_data, val_labels))
val_dataset = val_dataset.batch(32)

model.fit(dataset, epochs=10,
          validation_data=val_dataset)

 

평가와 예측

tf.keras.Model.evaluate 와 tf.keras.Model.predict 메서드에는 넘파이 배열이나 tf.data.Dataset을 사용할 수 있습니다.

data = np.random.random((1000, 32))
labels = np.random.random((1000, 10))

model.evaluate(data, labels, batch_size=32)

model.evaluate(dataset, steps=30)
result = model.predict(data, batch_size=32)
print(result.shape)