Skip to main content
Version: 1.3

Tuner 사용 준비하기

시작하면서#

이 장에서는 fashion mnist 데이터셋으로 만든 모델의 Validation accuracy를 높이기 위한 하이퍼파라미터 튜닝을 진행합니다. 여러가지 방법으로 Tuner를 사용하는 방법을 배웁니다.
이 튜토리얼은 Notebook과 Tuner에서 진행됩니다.

info

이번 과정은 개발을 위한 준비 챕터에서 만든 handson 노트북서버에서 진행됩니다.
fairing 으로 학습시키기에서 만든 모델을 예제에 사용할 것입니다.
사용되는 코드는 아래 링크에서 보실 수 있습니다.
05.fashion_mnist_tuner.ipynb


Goal#

  • 하이퍼파라미터 대상 설정하기(Tuner 사용준비하기)
  • 모델 학습의 로그포맷 변경하기

하이퍼파라미터 대상 설정 (Tuner 사용준비하기)#

앞 장에서 우리는 85%대의 정확도를 가진 모델을 만들었습니다.
일단 fashion_mnist_tuner.ipynb 라는 이름의 노트북을 만들어서 my_model.py 모델 코드를 옮겨넣겠습니다.

fashion_mnist_tuner.ipynb
# fashion_mnist_tuner.ipynbimport osimport datetimeimport tensorflow as tffrom kubeflow.fairing.kubernetes.utils import mounting_pvc
class MyModel(object):    def train(self):        mnist = tf.keras.datasets.fashion_mnist        (x_train, y_train), (x_test, y_test) = mnist.load_data()
        print("x_train shape:", x_train.shape, "y_train shape:", y_train.shape)        print("x_test shape:", x_test.shape, "y_test shape:", y_test.shape)
        x_train, x_test = x_train / 255.0, x_test / 255.0
        model = tf.keras.models.Sequential([            tf.keras.layers.Flatten(input_shape=(28, 28)),            tf.keras.layers.Dense(128, activation='relu'),            tf.keras.layers.Dropout(0.2),            tf.keras.layers.Dense(10, activation='softmax')        ])
        model.compile(optimizer='sgd',                        loss='sparse_categorical_crossentropy',                        metrics=['acc'])

        date_folder = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")        if os.getenv('FAIRING_RUNTIME', None) is None:            log_dir = "log/fit/" + date_folder        else:            log_dir = "/notebook/log/fit/" + date_folder
        print(f"tensorboard log dir : {log_dir}")
        tensorboard_cb = tf.keras.callbacks.TensorBoard(log_dir=log_dir,                                                        histogram_freq=1)        model.fit(x_train, y_train,                    verbose=1,                    validation_data=(x_test, y_test),                    epochs=10,                    callbacks=[tensorboard_cb])        return model

일반적으로 하이퍼파라미터 튜닝의 대상은

  • optimizer의 learning rate
  • 레이어에서의 dropout rate
  • 레이어층의 수
  • 레이어 노드의 수
  • loss function(cost function) 등이 됩니다.

즉, 사용자가 입력할 수 있는 값들이 대상이 됩니다.

MyModel클래스 에서는

  • 레이어층의 갯수
  • fully connected 레이어의 노드(뉴런) 갯수
  • dropout rate
  • optimizer

정도가 튜닝의 대상이 될 수 있을 것 같습니다.

  • argparse 를 이용해 모델 구조에 큰 변경을 주지 않는 node_amount, dropout rate, optimizer 를
    외부에서 입력받을 수 있게 코드를 수정해놓습니다.
fashion_mnist_tuner.ipynb
# fashion_mnist_tuner.ipynbimport osimport datetimeimport tensorflow as tfimport argparse # 외부에서 입력받기 위해 추가from kubeflow.fairing.kubernetes.utils import mounting_pvc
class MyModel(object):    def train(self):
        parser = argparse.ArgumentParser()        parser.add_argument('--node_amount', required=False, type=int, default=128) # 노드 개수        parser.add_argument('--dropout_rate', required=False, type=float, default=0.2) # dropout rate        parser.add_argument('--optimizer', required=False, type=str, default="sgd") # optimizer        args = parser.parse_args()
        mnist = tf.keras.datasets.fashion_mnist        (x_train, y_train), (x_test, y_test) = mnist.load_data()
        ...        ...        model = tf.keras.models.Sequential([            tf.keras.layers.Flatten(input_shape=(28, 28)),            tf.keras.layers.Dense(args.node_amount, activation='relu'),            tf.keras.layers.Dropout(args.dropout_rate),            tf.keras.layers.Dense(10, activation='softmax')        ])
        model.compile(optimizer=args.optimizer,                        loss='sparse_categorical_crossentropy',                        metrics=['acc'])
argparse

node_amount에 외부에서 아무값이 들어오지 않으면 default로 설정해둔 값인 128 입니다.


Tuner 사용을 위한 로그 포맷 변경#

Tuner는 모델 학습에서 나오는 로그를 수집하여 하이퍼파라미터 튜닝을 진행합니다.
좀 더 자세하게 이야기하면 학습에서 나오는 key=value 형태의 로그를 파싱하는 형태로 메트릭값을 수집합니다.
하지만 현재 MyModel 클래스는 key=value 형태의 로그 출력 형태가 아닙니다.

  • ( loss: 0.3873 - acc: 0.862) 의 형태

    mymodel_log

  • 그렇기 때문에 로그 출력 형태를 key=value 로 변경해주겠습니다.

from tensorflow.python.keras.callbacks import Callback 을 제일 상단에 추가하고
로그메세지를 출력 해주는 p 함수와 keras의 Callback을 이용하여 batch 와 epoch 마다 로그를 가져와줄 LoggingTrain 클래스를 추가하였습니다.

fashion_mnist_tuner.ipynb
# fashion_mnist_tuner.ipynbimport argparseimport osimport datetimeimport tensorflow as tffrom kubeflow.fairing.kubernetes.utils import mounting_pvcfrom tensorflow.python.keras.callbacks import Callback # Callback 추가
class MyModel(object):  ...
def p(msg):    dt_now = datetime.datetime.now()    strftime = dt_now.strftime('%Y-%m-%dT%H:%M:%SZ')    print(f"{strftime} {msg}", flush=True)
class LoggingTrain(Callback):    """logging for train    """    def on_batch_end(self, batch, logs={}):        if batch % 100 == 0:            p(f"batch: {batch}")            p(f"accuracy={logs.get('acc')} loss={logs.get('loss')}")
    def on_epoch_begin(self, epoch, logs={}):        p(f"epoch: {epoch}")
    def on_epoch_end(self, epoch, logs={}):        p(f"Validation-accuracy={logs.get('val_acc')}")        p(f"Validation-loss={logs.get('val_loss')}")        return

이제 LoggingTrain 클래스를 model.fit 의 callbacks 에 추가해줍니다.
fit에 verbose 설정을 0으로 변경해 기존 로그 출력이 LoggingTrain에 의해 출력되는 것과 중복되는 것을 막았습니다.

fashion_mnist_tuner.ipynb
# fashion_mnist_tuner.ipynb        model.fit(x_train, y_train,                    verbose=0, # 0으로 변경                    validation_data=(x_test, y_test),                    epochs=10,                    callbacks=[LoggingTrain(), # logging callback 추가                               tensorboard_cb])        return model
def p(msg):    ...

노트북의 모델을 컨테이너 이미지로 빌드하기#

Notebook에서 만든 모델을 Tuner에서 실행시키기 위해서는 컨테이너를 이미지로 빌드해야합니다.
이전 장에서 배운 fairing을 사용하여 이 문제를 해결해보겠습니다.

fashion_mnist_tuner.ipynb
# fashion_mnist_tuner.ipynbif __name__ == '__main__':    if os.getenv('FAIRING_RUNTIME', None) is None:        from kubeflow import fairing
        fairing.config.set_preprocessor('notebook',                                        notebook_file='fashion_mnist_tuner.ipynb')
        DOCKER_REGISTRY = 'YOURID'  # 도커 허브 아이디        # DOCKER_REGISTRY = 'harbor.dudaji.com/프로젝트 이름'        fairing.config.set_builder(            'append',            image_name='fashion-mnist-tuner',            base_image='dudaji/cap-jupyterlab:tf2.0-cpu',            registry=DOCKER_REGISTRY,            push=True)
        notebook_volume = mounting_pvc(pvc_name="workspace-handson",                                        pvc_mount_path="/notebook")
        fairing.config.set_deployer('job',                                    pod_spec_mutators=[notebook_volume],                                    cleanup=True) # 잡을 실행후 완료시 잡을 삭제할지의 여부를 결정        fairing.config.run()    else:        remote_model = MyModel()        remote_model.train()

  • 기존 fairing과 달리 python 파일 대신 notebook 파일을 가져다 사용합니다.
fashion_mnist_tuner.ipynb
        fairing.config.set_preprocessor('notebook',                                        notebook_file='fashion_mnist_tuner.ipynb')

  • 위 작업으로 fairing은 노트북을 그대로 python 파일로 변경하여 실행합니다.
# 생성된 컨테이너의 inspect 결과"Cmd": [    "python",    "/app/fashion_mnist_tuner.py"],
  • if 절에서 fairing 을 이용해 노트북이 컨테이너 이미지를 빌드하고 클러스터로 job을 요청합니다.
    else 절에서 빌드된 이미지로 실행된 컨테이너가 명령을 실행시켜 학습을 진행하게됩니다.
    else:        remote_model = MyModel()        remote_model.train()

아래는 지금까지의 전체코드 입니다.

fashion_mnist_tuner.ipynb
# fashion_mnist_tuner.ipynbimport osimport datetimeimport tensorflow as tfimport argparse # 외부에서 입력받기 위해 추가from kubeflow.fairing.kubernetes.utils import mounting_pvcfrom tensorflow.python.keras.callbacks import Callback # 추가
class MyModel(object):    def train(self):
        parser = argparse.ArgumentParser()        parser.add_argument('--node_amount', required=False, type=int, default=128) # 노드 개수        parser.add_argument('--dropout_rate', required=False, type=float, default=0.2) # dropout rate        parser.add_argument('--optimizer', required=False, type=str, default="sgd") # optimizer        args = parser.parse_args()
        mnist = tf.keras.datasets.fashion_mnist        (x_train, y_train), (x_test, y_test) = mnist.load_data()
        print("x_train shape:", x_train.shape, "y_train shape:", y_train.shape)        print("x_test shape:", x_test.shape, "y_test shape:", y_test.shape)
        x_train, x_test = x_train / 255.0, x_test / 255.0
        model = tf.keras.models.Sequential([            tf.keras.layers.Flatten(input_shape=(28, 28)),            tf.keras.layers.Dense(args.node_amount, activation='relu'),            tf.keras.layers.Dropout(args.dropout_rate),            tf.keras.layers.Dense(10, activation='softmax')        ])
        model.compile(optimizer=args.optimizer,                        loss='sparse_categorical_crossentropy',                        metrics=['acc'])

        date_folder = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")        if os.getenv('FAIRING_RUNTIME', None) is None:            log_dir = "log/fit/" + date_folder        else:            log_dir = "/notebook/log/fit/" + date_folder
        print(f"tensorboard log dir : {log_dir}")
        tensorboard_cb = tf.keras.callbacks.TensorBoard(log_dir=log_dir,                                                        histogram_freq=1)        model.fit(x_train, y_train,                    verbose=0, # 0으로 변경                    validation_data=(x_test, y_test),                    epochs=10, # 추후 변경 예정                    callbacks=[LoggingTrain(), # logging callback 추가                               tensorboard_cb])        return model
def p(msg):    dt_now = datetime.datetime.now()    strftime = dt_now.strftime('%Y-%m-%dT%H:%M:%SZ')    print(f"{strftime} {msg}", flush=True)
class LoggingTrain(Callback): #로그 형태 변경    """logging for train    """    def on_batch_end(self, batch, logs={}):        if batch % 100 == 0:            p(f"batch: {batch}")            p(f"accuracy={logs.get('acc')} loss={logs.get('loss')}")
    def on_epoch_begin(self, epoch, logs={}):        p(f"epoch: {epoch}")
    def on_epoch_end(self, epoch, logs={}):        p(f"Validation-accuracy={logs.get('val_acc')}")        p(f"Validation-loss={logs.get('val_loss')}")        return
if __name__ == '__main__':    if os.getenv('FAIRING_RUNTIME', None) is None:        from kubeflow import fairing
        fairing.config.set_preprocessor('notebook',                                        notebook_file='fashion_mnist_tuner.ipynb')
        DOCKER_REGISTRY = 'YOURID'  # 도커 허브 아이디        # DOCKER_REGISTRY = 'harbor.dudaji.com/프로젝트 이름'        fairing.config.set_builder(            'append',            image_name='fashion-mnist-tuner',            base_image='dudaji/cap-jupyterlab:tf2.0-cpu',            registry=DOCKER_REGISTRY,            push=True)
        notebook_volume = mounting_pvc(pvc_name="workspace-handson",                                        pvc_mount_path="/notebook")
        fairing.config.set_deployer('job',                                    pod_spec_mutators=[notebook_volume],                                    cleanup=True) # 잡을 실행후 완료시 잡을 삭제할지의 여부를 결정        fairing.config.run()    else:        remote_model = MyModel()        remote_model.train()

위 노트북을 실행시키면 클러스터에서 학습이 진행되는것을 확인할 수 있습니다.

fairing_job logging_train

fairing job이 실행되는 로그를 통해서 yourID/fashion-mnist-tuner:[yourTAG] 이름의 컨테이너 이미지가 만들어졌음을 알 수 있습니다. 그리고 로그 형태도 key=value 형태로 바뀌어져 나오는 것을 확인할 수 있습니다.

logging_train

여기서 생성된 컨테이너 이미지를 가지고 Tuner UI로 이동하여 튜닝을 진행해보겠습니다.