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.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.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) 의 형태
그렇기 때문에 로그 출력 형태를 key=value 로 변경해주겠습니다.
from tensorflow.python.keras.callbacks import Callback
을 제일 상단에 추가하고
로그메세지를 출력 해주는 p 함수와 keras의 Callback을 이용하여 batch 와 epoch 마다 로그를 가져와줄 LoggingTrain 클래스를 추가하였습니다.
# 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 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.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 파일을 가져다 사용합니다.
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.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이 실행되는 로그를 통해서 yourID/fashion-mnist-tuner:[yourTAG] 이름의 컨테이너 이미지가 만들어졌음을 알 수 있습니다. 그리고 로그 형태도 key=value 형태로 바뀌어져 나오는 것을 확인할 수 있습니다.
여기서 생성된 컨테이너 이미지를 가지고 Tuner UI로 이동하여 튜닝을 진행해보겠습니다.