2 minute read

History객체 살펴보기

그리고 추가된 코드에서 plt.plot(history.history['loss']) 해당 부분을 발견했는데, 이 부분의 history객체가 궁금해져서 조금 살펴보기로 했습니다.

우선 keras/keras/engins/training.py 파일안에 Model이라는 클래스가 있습니다.

class Model(base_layer.Layer, version_utils.ModelVersionSelector):
  """`Model` groups layers into an object with training and inference features.
  Args:
  ~~~ 여러 설명
  """

이 클래스를 통해 만들어지는 객체인 tf.keras.Model 객체를 통해 모델의 틀을 만들 수 있습니다. 그리고 Model 클래스의 생성자 부분을 살펴보면 self.history를 초기화 해주는 부분을 발견할 수 있습니다. 링크

그리고 Model클래스의 fit메소드를 살펴보면, history 객체를 return 하는 것을 확인할 수 있습니다.

training.py의 1332번째 줄을 살펴보면 History Object에 대한 간략한 설명이 나와있습니다.

Returns: ‘History’객체입니다. ‘History.history’ attribute는 validation loss value와 validation metrics value의 값(해당되는 경우)뿐만 아니라 연속적인 epoch에 대해서 training loss value 및 training metricts values가 저장되어 있는 기록입니다.

위처럼 주석에 설명되어 있습니다.

Class Model의 fit()메소드는 callbacks 변수를 가지고 있습니다.

callbacks: keras.callbacks.Callback 인스턴스의 목록입니다.

  • 훈련 중에 적용할 콜백 목록입니다. tf.keras.callbacks를 참조하세요.
  • 참고 tf.keras.callbacks.ProgbarLoggertf.keras.callbacks.History 콜백은 자동으로 생성되며 model.fit에 전달할 필요가 없습니다.
  • tf.keras.callbacks.ProgbarLoggermodel.fit에 대한 verbose 인수를 기반으로 생성되거나 생성되지 않습니다.
  • 배치 수준 호출이 있는콜백은 현재 tf.distribute.experimental.ParameterServerStrategy에서 지원되지 않으며 사용자는 적절한 steps_per_epoch 값 대신 에포크 수준 호출을 구현하는 것이 좋습니다.

callbacks 매개변수에 대한 설명을 읽어보면 History 콜백은 자동으로 생성된다고 합니다. 이 History 콜백이 자동으로 생성되는 과정은 1388~1397번째 줄에 의해 생성됩니다. 링크

      # Container that configures and calls `tf.keras.Callback`s.
      if not isinstance(callbacks, callbacks_module.CallbackList):
        callbacks = callbacks_module.CallbackList(
            callbacks,
            add_history=True,
            add_progbar=verbose != 0,
            model=self,
            verbose=verbose,
            epochs=epochs,
            steps=data_handler.inferred_steps)

isinstance 메소드는 1번째 인자가 2번째 인자의 instance이면 true, 아니면 false를 반환합니다.

여기서 callbacks_module.CallbackList 객체를 생성할 때 매개변수에 add_history를 true값으로 넘겨주게 되면 반환되는 콜백 리스트에 history를 생성하는 콜백을 넣어서 주게됩니다.

add_history: ‘콜백’ 목록에 이미 존재하지 않는 경우 ‘히스토리’ 콜백을 추가해야 하는지 여부.

그러면 이제 history 객체가 어떻게 생성되는지를 알았으니 가볍게 history객체가 무엇을 하는지를 살펴봅시다.

class History(Callback):
  """Callback that records events into a `History` object.
  This callback is automatically applied to
  every Keras model. The `History` object
  gets returned by the `fit` method of models.
  Example:
  >>> model = tf.keras.models.Sequential([tf.keras.layers.Dense(10)])
  >>> model.compile(tf.keras.optimizers.SGD(), loss='mse')
  >>> history = model.fit(np.arange(100).reshape(5, 20), np.zeros(5),
  ...                     epochs=10)
  >>> print(history.params)
  {'verbose': 1, 'epochs': 10, 'steps': 1}
  >>> # check the keys of history object
  >>> print(history.history.keys())
  dict_keys(['loss'])
  """

  def __init__(self):
    super(History, self).__init__()
    self.history = {}

  def on_train_begin(self, logs=None):
    self.epoch = []

  def on_epoch_end(self, epoch, logs=None):
    logs = logs or {}
    self.epoch.append(epoch)
    for k, v in logs.items():
      self.history.setdefault(k, []).append(v)

    # Set the history attribute on the model after the epoch ends. This will
    # make sure that the state which is set is the latest one.
    self.model.history = self

우선 History클래스는 위와 같이 생겼습니다.

  • 생성자에서 history를 dictionary타입으로 선언하여 초기화해줍니다.
  • Callback클래스의 상속을 받고있고 on_train_begin()메소드와 op_epoch_end()메소드가 오버라이딩 되어 있습니다. on_train_begin()에서 epoch 리스트를 선언합니다.
  • on_epoch_end()메소드는 epoch와 logs를 매개변수로 받습니다. 매개변수로 넘어오는 logs 딕셔너리의 값을 history 딕셔너리에 추가해주는 것 같습니다. (이를 통해 history.history[‘loss’]와 같은 형태로 코드를 사용할 수 있는 것 같습니다.)

저는 logs에 어떤 정보가 저장되어 오는지를 확인하고 싶으므로 이것을 찾아보겠습니다.

우선 History클래스는 콜백리스트에 담겨서 호출되는 클래스이므로, class model의 fit()메소드를 호출할 때 on_train_begin()on_epoch_end()메소드가 사용될 것 같다는 추측을 했습니다.

on_epoch_end()메소드가 호출되는 곳을 찾아본 결과 이 곳에 있었습니다. 여기서 epoch_logs가 어디서 온지를 살펴본다면 logs로 어떤 것이 넘어오는지 알 수 있을 것 같았습니다.

근데 양이 너무 방대해서.. 이까지만 살펴보기로 했습니다. 우선 fit메소드 안을 들여다보면 logs매개변수가 여러 함수를 호출하며 계속 바뀌는 것을 볼 수 있는데, 아마 학습을 하면서 return값으로 logs가 쌓이는 형태인 것 같았습니다. 어떤 값들이 다 return되는지 살펴본다면 정신이 나갈 것 같아서 우선은 넘어가기로 했습니다.