7장 케라스 모델 활용법

감사말: 프랑소와 숄레의 Deep Learning with Python, Second Edition 7장에 사용된 코드에 대한 설명을 담고 있으며 텐서플로우 2.6 버전에서 작성되었습니다. 소스코드를 공개한 저자에게 감사드립니다.

tensorflow 버전과 GPU 확인

주요 내용

7.1 케라스 활용성

케라스를 이용하여 매우 단순한 모델부터 매우 복잡한 모델까지 구성 및 훈련이 가능하다. 케라스의 모델과 층은 모두 각각 Model 클래스와 Layer 클래스를 상속하기에 다른 모델에서 사용된 요소들을 재활용하기에도 용이하다.

여기서는 주어진 문제에 따른 케라스 모델 구성법과 훈련법의 다양한 방식을 살펴본다.

7.2 케라스 모델 구성법

케라스를 이용하여 모델을 세 가지 방식으로 구성할 수 있다.

가장 간단한 모델부터 아주 복잡한 모델까지 모두 구성할 수 있으며 사용자가 직접 정의한 모델과 레이어도 활용할 수 있다.

모델 구성법 1: Sequential 모델

층으로 스택을 쌓아 만든 모델이며 가장 단순하다.

Sequential 클래스

층의 추가는 add 메서드를 이용할 수도 있다. 더해진 순서대로 층이 쌓인다.

build() 메서드

모델 훈련에 사용되는 층별 가중치는 모델이 처음 활용될 때 호출되는 build() 메서드에 의해 초기화된다. 이유는 입력값이 들어와야 가중치 텐서의 모양(shape)을 정할 수 있기 때문이다. 아래 코드 샘플은 3장에서 SimpleDense를 선언할 때 사용된 build() 메서드를 보여주며, 훈련이 시작되면서 첫 배치 데이터셋이 입력될 때 특성 수를 확인하여 가중치와 편향 텐서를 생성과 동시에 초기화한다.

def build(self, input_shape):
    input_dim = input_shape[-1]   # 입력 샘플의 특성 수
    self.W = self.add_weight(shape=(input_dim, self.units),
                             initializer="random_normal")
    self.b = self.add_weight(shape=(self.units,),
                             initializer="zeros")

따라서 지금 당장 가중치를 확인하려 하면 오류가 발생한다.

>>> model.weights
...
ValueError: Weights for model sequential_1 have not yet been created. 
Weights are created when the Model is first called on inputs or 
`build()` is called with an `input_shape`.

반면에 입력값 대신 build() 메서드를 특성 수 정보를 이용하여 직접 호출하면 가중치 텐서가 무작위로 초기화된 형식으로 생성된다. 즉, 모델 빌드가 완성된다.

모델 빌드가 완성되면 weights 속성에 생성된 모델 훈련에 필요한 모든 가중치와 편향이 저장된다. 위 모델에 대해서 층별로 가중치와 편향 텐서 하나씩 총 4 개의 텐서가 생성된다.

summary() 메서드

완성된 모델의 요악한 내용은 확인할 수 있다.

name 인자

모델 또는 층을 지정할 때 생성자 메서등의 name 키워드 인자를 이용하여 이름을 지정할 수도 있다.

Input() 함수, KerasTensor, 모델 디버깅

모델 구성 중간에 구성 과정을 확인하려면 Input()함수를 이용하여 케라스텐서(KerasTensor) 객체를 가장 먼저 모델에 추가한다. 그러면 층을 추가할 때마다 summary()를 실행할 수 있다. Input() 함수는 모델 훈련에 사용되는 데이터 샘플의 모양(shape) 정보를 제공하는 가상의 텐서인 KerasTensor 객체를 생성한다.

주의사항: shape 키워드 인자에 사용되는 값은 각 샘플의 특성 수이며, 앞서 build() 메서드의 인자와 다른 형식으로 사용된다.

모델 구성법 2: 함수형 API

다중 입력과 다중 출력을 지원하려면 함수형 API를 활용하여 모델을 구성해야 하며, 가장 많이 사용되는 모델 구성법이다. 사용법은 간단하다.

Model(inputs, outputs)

기본 활용법

앞서 살펴 본 Sequential 모델을 함수형 API를 이용하여 구성하면 다음과 같다.

사용된 단계들을 하나씩 살펴보자.

생성된 값은 KerasTensor이다.

케라스텐서(KerasTensor)의 모양에서 None은 배치 사이즈, 즉 하나의 훈련 스텝에 사용되는 샘플의 수를 대상으로 하며, 임의의 크기의 배치를 처리할 수 있다는 의미로 사용된다.

KerasTensor의 역할

앞서 보았듯이 케라스텐서는 모델 훈련에 사용되는 텐서의 모양에 대한 정보를 제공하는 가상의 텐서이다. 빌드되는 모델은 입력 케라스텐서부터 출력 케라스텐서까지 각 층에 저장된 텐서의 모양 정보를 이용하여 가중치 텐서와 편향 텐서를 생성하고 초기화한다.

다중 입력, 다중 출력 모델 구성법

고객의 요구사항이 적힌 티켓을 처리할 때 필요한 우선순위와 담당부서를 지정하는 시스템을 구현하려 한다. 시스템에 사용될 모델은 세 개의 입력과 두 개의 출력을 사용한다.

모델 훈련을 위해 적절한 개수의 입력 텐서와 타깃 텐서를 지정해야 한다. 여기서는 훈련 과정을 설명하기 위해 적절한 모양의 입력 텐서 3개와 타깃 텐서 2개를 무작위로 생성해서 사용한다.

모델 컴파일 과정에서 지정된 타깃 수만큼 손실함수와 측정 기준을 지정해야 한다.

모델 훈련은 fit() 함수에 세 개의 훈련 텐서로 이루어진 리스트와 두 개의 타깃 텐서로 이루어진 리스트를 지정한 후에 실행한다. 여기서는 시험삼아 한 번의 에포크만 사용한다.

평가도 훈련과 동일한 방식의 인자가 사용된다.

예측은 입력값만 리스트로 지정하고 실행하면 두 개의 어레이 출력값으로 구성된 리스트가 반환된다.

사전 객체 활용

입력층과 출력층의 이름을 이용하여 사전 형식으로 입력값과 출력값을 지정할 수 있다.

층 연결 구조 확인

plot_model()을 이용하여 층 연결 구조를 그래프로 나타낼 수 있다.

>>> keras.utils.plot_model(model, "ticket_classifier.png")

주의사항: pydot 파이썬 모듈과 graphviz 라는 프로그램이 컴퓨터에 설치되어 있어야 한다.

입력 텐서와 출력 텐서의 모양을 함께 표기할 수도 있다.

>>> keras.utils.plot_model(model, "ticket_classifier_with_shape_info.png", show_shapes=True)

모델 재활용

훈련된 모델의 특성을 이용하여 새로운 모델을 빌드할 수 있다. 먼저 모델의 layers 속성을 이용하여 사용된 층에 대한 정보를 확인한다. layers 속성은 사용된 층들의 객체로 이루어진 리스트를 가리킨다.

예를 들어, 3번 인덱스에 해당하는 층의 입력값과 출력값에 대한 정보는 아래처럼 확인할 수 있다.

출력층을 제외한 나머지 층을 재활용해보자. 출력층은 5번과 6번 인덱스에 위치하기에 4번 인덱스가 가리키는 (은닉)층의 출력 정보를 따로 떼어낸다.

이제 출력층에 문제해결의 어려움 정도를 "quick", "medium", "difficult"로 구분하는 어려움(difficulty) 정도를 판별하는 층을 추가해보자. 먼저, difficulty 층을 준비한다.

준비된 'difficulty' 층을 출력층으로 추가하여 priority, department, difficulty 세 개의 출력값을 생성하는 새로운 모델을 구성한다.

새로 생성된 모델은 기존에 훈련된 모델의 가중치, 즉, 은닉층에 사용된 가중치는 그대로 사용되며, 모델 구성 그래프는 다음과 같다.

>>> keras.utils.plot_model(new_model, "updated_ticket_classifier.png", show_shapes=True)

요약 결과는 다음과 같다.

모델 구성법 3: 서브클래싱

케라스 모델과 호환되는 모델 클래스를 직접 선언하여 활용하려면 keras.Model 클래스를 상속해야 한다. 이런 방식을 서브클래싱(subclassing)이라 부르며 keras.Model 클래스를 상속하면서 기본적으로 아래 두 메서드를 목적에 맞추어 재정의(overriding)하면 된다.

앞서 함수형 API로 구성한 티켓 모델을 서브클래싱을 기법을 이용하여 구현하면 다음과 같다.

참고: keras.layers.Layer를 상속하여 사용자 정의 층을 선언하는 방식과 거의 유사하다(3장 6절 참조).

모델 구성은 해당 모델의 객체를 생성하면 된다. 다만 Layer의 경우처럼 가중치는 실제 데이터와 함께 호출되지 전까지 생성되지 않는다.

컴파일, 훈련, 평가, 예측은 이전과 완전히 동일한 방식으로 실행된다.

서브클래싱 기법의 장단점

모델 구성법 혼합

소개된 세 가지 방식을 임의로 혼합하여 활용할 수 있다.

예제: 서브클래싱 모델을 함수형 모델에 활용하기 (강추!!!)

예제: 함수형 모델을 서브클래싱 모델에 활용하기

7.3 훈련 모니터링

케라스 모델의 구성, 훈련, 평가, 예측은 정해진 방식으로 차례대로 이루어진다. 아래 코드는 MNIST 데이터셋을 이용한 모델 훈련 전반 과정을 보여준다.

사용자 정의 평가지표(metrics) 활용

Metric 클래스 상속

아래 세 개의 메서드를 재정의(overriding)해야 한다.

아래 코드는 평균제곱근오차(RMSE)를 평가지표로 사용하는 클래스를 이용하는 모델 훈련을 소개한다.

콜백(callback) 활용

콜백(callback)은 모델 훈련 도중에 부가적으로 호출되는 객체이며 학습 과정을 모니터링 하면서 일부 제어기능을 수행하는 다양한 메서드를 제공한다. 콜백이 활용되는 주요 기능은 다음과 같다.

keras.callbacks.ModelCheckpoint
keras.callbacks.EarlyStopping
keras.callbacks.LearningRateScheduler
keras.callbacks.ReduceLROnPlateau
keras.callbacks.CSVLogger

여기서는 EarlyStoppingModelCheckpoint 두 콜백의 기능을 살펴본다.

fit() 메서드에서 callbacks 인자 사용하기

아래 코드에 사용된 옵션은 다음과 같다.

조기종료 후 훈련과정에서 저장된 최고 성능의 모델을 불러오면 다음과 같다.

사용자 정의 콜백 활용

Callback 클래스 상속

매 에포크와 매 배치 훈련 단계의 시작과 종료 지점에서 수행해야 할 기능을 정의해야 하며 아래 메서드를 재정의하는 방식으로 이루어진다.

on_epoch_begin(epoch, logs)
on_epoch_end(epoch, logs)
on_batch_begin(batch, logs)
on_batch_end(batch, logs)
on_train_begin(logs)
on_train_end(logs)

각 메서드에 사용되는 인자는 훈련 과정 중에 자동으로 생성된 객체로부터 값을 받아온다.

다음 LossHistory 콜백 클래스는 배치 훈련이 끝날 때마다 손실값을 저장하고 에포크가 끝날 때마다 배치별 손실값을 그래프로 저장하여 훈련이 종료된 후 시각화하여 보여주도록 한다.

텐서보드(TensorBoard) 활용

텐서보드(TensorBoard)는 모델 훈련과정을 모니터링하는 최고의 어플이며 텐서플로우와 함께 기본적으로 설치된다.

주의사항: 텐서보드 데이터의 저장경로를

/full_path_to_your_log_dir

대신에

./tensorboard_log_dir

등을 사용해야 리눅스, 맥 운영체제에서 오류가 발생하지 않는다.

텐서보드를 주피터 노트북에서 아래처럼 실행할 수 있다.

텐서보드를 독립적으로 실행하여 훈련과정을 실시간으로 모니터링 하려면 아래 명령어를 터미널 창에서 실행하고 반환된 주소로 접속하면 된다.

tensorboard --logdir ./full_path_to_your_log_dir

7.4 사용자 정의 훈련 알고리즘: fit() 메서드 대체

Training versus inference

Low-level usage of metrics

A complete training and evaluation loop

Writing a step-by-step training loop: the training step function

Writing a step-by-step training loop: resetting the metrics

Writing a step-by-step training loop: the loop itself

Writing a step-by-step evaluation loop

Make it fast with tf.function

Adding a tf.function decorator to our evaluation step function

Leveraging fit() with a custom training loop

Implementing a custom training step to use with fit()