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

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)

 

Regularization

정규화는 overfitting을 방지하게 됩니다. 정규화항을 통해 모델에 미치는 차원의 수의 수를 감소시키기 때문입니다. 

대표적으로 회귀모델에서 정규화항을 붙이는 방법은 3가지가 있습니다.

사실 수식적으로 정규화에 대한 의미를 해석할 수도 있겠지만, 그러기가 쉽지는 않으니 쉬운방법으로 설명해보겠습니다. 

일반적인 회귀방법에서 비용함수는 MSE를 최소화하는 방향으로 나아가게 됩니다. 일반적인 회귀방법에서 데이터의 특징수가 많아질수록(차원이 증가할수록) overfitting에 대한 위험성이 커지게 됩니다

이를 막기위해 정규화 항을 사용하게 되는데요. MSE + regular-term으로 비용함수를 재정의하게 됩니다.

그렇다면 비용함수를 최소화하는 방향에선 regular-term또한 최소화가 되어야 할겁니다. 

최소화를 진행하게 되면서 가중치가 낮은 항은 정규화 방법에 따라 0으로 수렴하여 사용하지 않게되거나 0에 가까운 수가 되어 모델에 미치는 영향이 덜해지게 됩니다.

 

릿지 회귀(능선회귀, Lidge), 라쏘회귀(Lasso), 엘라스틱넷(elasticNet)

이 세가지에 대해서 간단히 적어보겠습니다. 


릿지 회귀

릿지 회귀는 L2-Norm을 사용한 회귀입니다. 이 회귀방법은 일반적으로 영향을 거의 미치지 않는 특성에 대하여 0에 가까운 가중치를 주게 됩니다. 

 

라쏘 회귀

라쏘 회귀는 L1-Norm을 사용한 회귀입니다. 특성값의 계수가 매우 낮다면 0으로 수렴하게 하여 특성을 지워버립니다. 특성이 모델에 미치는 영향을 0으로 만든다는 것은 bias를 증가 시켜 overfitting을 방지한다는 의미가 되겠죠?

 

엘라스틱넷

엘라스틱 넷은 라쏘회귀와 릿지회귀의 최적화 지점이 서로 다르기 때문에 두 정규화 항을 합쳐서 r로 규제정도를 조절하여 준다.

 

 

밑의 그림을 보면, L1-Norm을 사용한 것은 마름모꼴이 되면서 가중치 중 하나가 0이 되는 걸 볼 수 있다.

그에 반해, L2-Norm은 두 가중치를 서서히 줄어들게 해서 작은 수의 가중치를 가지는 것을 볼 수 있다.

 

Reference

핸즈온 머신러닝 1권

 

 

위의 이미지를 떠올리면서 개념을 이해하면 더 쉽습니다. 


Bias

여기서 말하는 편향은 회귀모델의 상수항이 아닙니다. 

편향이란, y_pred의 평균과 y_true와의 관계입니다. 다르게 말하면 외부적으로 얼마나 영향을 받는지를 뜻합니다. 외부란 정답이라고 생각하면 쉽겠죠?

다르게 말해서, y_pred의 값들과 y_true의 값들이 떨어져 있는 정도가 클 경우를 '편향이 높다' 라고 표현합니다.

편향이 클 경우엔 정답값들과의 거리가 멀테니 이를 과소적합이라고 표현할 수 있습니다 - underfitting


Variance

분산이란, 예측값들 간의 관계입니다. 즉, 밑의 식에 따라 예측값과 예측값들의 평균의 차이에 대한 평균입니다.

즉, 예측값들끼리 얼마나 떨어져 있는가 입니다. 

예측값들이 자기들끼리 떨어져 있는 정도가 클 경우를 '분산이 높다' 라고 표현합니다.

높은 분산을 가질 경우 과대적합이라고 표현할 수 있습니다. - overfitting

그리는 선의 종류가 구불구불하게 복잡해져 새로운 데이터를 예측하기가 쉽지 않기 떄문이죠.

 


Trade-off between Bias and Variance

이러한 편향과 분산에는 트레이드 오프가 존재합니다. 

또한, 이는 모델 복잡도와도 연관이 있습니다. 

모델이 복잡해질수록 편향은 줄어들지만 분산은 높아집니다.

모델이 단순해질수록 편향은 높아지지만 분산은 줄어듭니다.

실제로 첫 번째 그림은 bias가 높고 variance가 낮습니다.

예측값들은 한 직선 위에 있으니 분산이 낮고, 데이터들이 모델과 멀어져 있으니 편향이 높습니다.

세 번째 그림은 구불구불한 직선 위에 있으니 분산이 높고, 모델과 거리가 가까우니 편향이 낮게 됩니다. 

실제 데이터를 다룰 때, 데이터가 충분히 많다는 가정하에 bias가 높아 underfitting으로 고생하는 연구자들은 잘 보지 못했지만, overfitting때문에 고생하는 분들은 상당수 보았습니다. 

Overfitting은 모델 학습을 수행할 때 training error와 validation error가 함께 같은 방향으로 잘 진행되다가 어느 순간 갑자기 벌어지는 현상에서 포착할 수 있습니다.

일반적으로 데이터를 전처리에는 세 가지 방법을 활용합니다. 

경험상으로 일단 결과를 대충 보고싶을 때, 이 세 가지를 먼저 전처리에 사용한 후에 모델을 만들어 테스트해보고 다른 전처리로 넘어가는 것을 추천드립니다.

(빠르고 쉽기떄문)

 

MinMax(Normalization)

일정 범위로 scaling을 적용해줍니다. 대표적으로 [0, 1]을 많이 사용합니다.

 

Robust Normalization

평균과 표준편차 대신에 사분위값을 이용합니다.

사분위값을 이용하므로 이상치에 영향을 덜 받습니다.

 

내 기억에 정규화는 Vanishing, explode 문제 떄문에 해줬던 것 같다. 실제 신경망이 아닌 다른 알고리즘 사용에서도 큰 값은 피하지만..

 

Standardization

mean 차감을 통해 zero-centered화를 시켜주고 std로 나누어줌으로써 데이터가 일정 범위 안에 머무르게 합니다. 

cs231n에 따르면 평균, 표준편차는 train set의 값을 써야한다는 점입니다. 즉, 데이터셋을 train,val,test로 나눈 뒤, train set의 mean, std를 구하여 val, test set 각각에 적용시켜주어야 합니다. 

 

Reference

http://aikorea.org/cs231n/neural-networks-2-kr/