6 분 소요

본 글은 [케라스 창시자에게 배우는 딥러닝] - (박해선 옮김) 책을 개인공부하기 위해 요약, 정리한 내용입니다.
전체 코드는 https://github.com/ingu627/deep-learning-with-python-notebooks에 기재했습니다.(원본 코드 fork)
뿐만 아니라 책에서 설명이 부족하거나 이해가 안 되는 것들은 외부 자료들을 토대로 메꿔 놓았습니다. 즉, 딥러닝은 이걸로 끝을 내보는 겁니다.
오타나 오류는 알려주시길 바라며, 도움이 되길 바랍니다.





7_1. Sequential 모델을 넘어서: 케라스의 함수형 API

  • 어떤 작업은 입력 데이터에서 여러 개의 타깃 속성을 예측해야 한다.
  • 다양한 입력 소스에서 전달된 데이터를 다른 종류의 신경망 층을 사용하여 처리하고 합친다.

  • 다중 입력 모델

image


  • 다중 출력 모델

image


  • 최근에 개발된 많은 신경망 구조는 선형적이지 않은 네트워크 토폴로지가 필요하다.
  • 인셉션 모듈 : 나란히 놓인 합성곱 층으로 구성된 서브그래프

image


  • 잔차 열결 : 하위 층의 출력을 상위 층의 특성 맵에 더해서 아래층의 표현이 네트워크 위쪽으로 흘러갈 수 있도록 한다.
    • 하위 층에서 학습된 정보가 데이터 처리 과정에서 손실되는 것을 방지한다.

image



7_1_1. 함수형 API 소개

  • 함수형 API에서는 직접 텐서들의 입출력을 다룬다. 함수처럼 층을 사용하여 텐서를 입력받고 출력한다.



Sequential vs 함수형 API

  • 케라스는 input_tensor에서 output_tensor로 가는 데 필요한 모든 층을 추출한다.
  • 그 다음 이들을 모아 그래프 데이터 구조인 Model 객체를 만든다.
  • 물론 input_tensor를 반복 변환하여 output_tensor를 만들 수 있어야 한다. 관련되지 않은 입력과 출력으로 모델을 만들면 RuntimError가 발생한다.



7_1_2. 다중 입력 모델

  • 함수형 API는 다중 입력 모델을 만드는 데 사용할 수 있다.
    • add, concatenate등을 사용해 여러 텐서를 연결할 수 있는 층을 사용
  • 예시의 모델은 질문-응답 모델이다. 전형적인 질문-응답 모델은 2개의 입력을 가진다.
  • (자연어 질문, 답변에 필요한 정보가 담긴 텍스트)
  • 그러면 모델은 답을 출력해야 한다. 가장 간단한 구조는 미리 정의한 어휘 사전에서 소프트맥스 함수를 통해 한 단어로 된 답을 출력하는 것이다.

image



2개의 입력을 가진 질문-응답 모델의 함수형 API 구현하기




다중 입력 모델에 데이터 주입하기

  • 이렇게 입력이 2개인 모델은 2가지 방법을 훈련할 수 있다.
  1. 넘파이 배열의 리스트에 주입
  2. 입력 이름과 넘파이 배열로 이루어진 딕셔너리를 모델의 입력으로 주입 (입력 이름 설정시 가능)



7_1_3. 다중 출력 모델

  • 예를 들어 소셜 미디어에서 익명 사용자의 포스트를 입력으로 받아 그 사람의 나이, 성별, 소득 수준 등을 예측한다.

image




다중 출력 모델의 컴파일 옵션: 다중 손실

  • 이런 모델을 예측하기 위해서는 네트워크 출력마다 다른 손실 함수를 지정해야 한다.
  • 손실 값을 합치는 가장 간단한 방법은 모두 더하는 것이다.
  • 케라스에서는 compile 메서드에서 리스트나 딕셔너리를 사용하여 출력마다 다른 손실을 지정할 수 있다. 계산된 손실 값은 전체 손실 하나로 더해지고 훈련 과정에서 최소화된다.



다중 출력 모델의 컴파일 옵션: 손실 가중치

  • 손실 값이 최종 손실에 기여하는 수준을 지정할 수 있다.



다중 출력 모델에 데이터 주입하기

  • 다중 입력 모델과 마찬가지로 넘파이 배열의 리스트나 딕셔너리를 모델에 전달하여 훈련한다.



7_1_4. 층으로 구성된 비순환 유향 그래프

  • 함수형 API를 사용하면 다중 입력이나 다중 출력 모델뿐만 아니라 내부 토폴로지가 복잡한 네트워크도 만들 수 있다.
  • 케라스의 신경망은 층으로 구성된 어떤 비순환 유향 그래프도 만들 수 있다.



인셉션 모듈

  • 인셉션은 합성곱 신경망에서 인기 있는 네트워크 구조이다.
  • 나란히 분리된 가지를 따라 모듈을 쌓아 독립된 작은 네트워크처럼 구성한다.
  • 가장 기본적인 인셉션 모듈 형태는 3~4개의 가지를 가진다.
  • 1x1 합성곱으로 시작해섯 3x3 합성곱이 뒤따르고 마지막에 전체 출력 특성이 합쳐진다.
  • 이런 구성은 네트워크가 따로따로 공간 특성과 채널 방향의 특성을 학습하도록 돕는다.
  • 아래는 인셉션 V3에 있는 모듈의 예이다.

image

  • 위에서 flowchart로 나타낸 모델은 인셉션V3로 케라스에서는 keras.applications.inception_v3.InceptionV3에 준비되어 있으며, ImageNet에서 사전 훈련된 가중치를 포함하고 있다.
  • 이와 비슷한 엑셉션 모델도 케라스에 포함되어 있다. 이 합성곱 구조는 채널 방향의 학습과 공간 방향의 학습을 극단적으로 분리한다는 아이디어에 착안하여 인셉션 모듈을 깊이별 분리 합성곱으로 바꾼다.
  • 이 합성곱은 깊이별 합성곱 다음에 점별 합성곱이 뒤따른다. 인셉션 모듈의 극한 형태로 공간 특성과 채널 방향 특성을 완전히 분리한다. 엑셉션은 인셉션V3와 거의 동일한 개수의 모델 파라미터를 가지지만 실행 속도가 더 빠르고 대규모 데이터셋에서 정확도가 더 높다. (효율적 사용)



잔차 연결

  • 잔차 연결은 하위 층의 출력을 상위 층의 입력으로 사용한다.
  • 순서대로 놓인 네트워크를 질러가는 연결이 만들어진다. 하위 층의 출력이 상위 층의 출력에 연결되는 것이 아니라 더해진다. 따라서 두 출력의 크기가 동일해야 한다. 크기가 다르면 선형 변환을 사용하여 하위 층의 활성화 출력을 목표 크기로 변환한다.



특성 맵의 크기가 다를 때 잔차 연결을 구현



7_1_5. 층 가중치 공유

  • 함수형 API의 중요한 또 하나의 기능은 층 객체를 여러 번 재사용할 수 있다는 것이다.
  • 층 객체를 두 번 호출하면 새로운 층 객체를 만들지 않고 각 호출에 동일한 가중치를 재사용한다. 이런 기능 때문에 공유 가지를 가진 모델을 만들 수 있다.
  • 이런 가지는 가중치를 공유하고 같은 연산을 수행한다.
  • 다시 말해 같은 표현을 공유하고 이런 표현을 다른 입력에서 함께 학습한다.



함수형 APi를 통해 공유 층을 사용하는 모델



7_1_6. 층과 모델

  • 함수형 API에서는 모델을 층처럼 사용할 수 있다.



7_2. 케라스 콜백과 텐서보드를 사용한 딥러닝 모델 검사와 모니터링

7_2_1. 콜백을 사용하여 모델의 훈련 과정 제어하기

  • 검증 손실이 더 이상 향상되지 않을 때 훈련을 멈추는 것이다. 케라스의 콜백을 사용하여 구현할 수 있다.
  • 콜백은 모델의 fit() 메서드가 호출될 때 전달되는 객체(특정 메서드를 구현한 클래스 객체)이다.
  • 훈련되는 동안 모델은 여러 지점에서 콜백을 호출한다.
  • 콜백은 모델의 상태와 성능에 대한 모든 정보에 접근하고 훈련 중지, 모델 저장, 가중치 적재 또는 모델 상태 변경 등을 처리할 수 있다.

콜백을 사용하는 몇 가지 사례

  1. 모델 체크포인트 저장 : 훈련하는 동안 어떤 지점에서 모델의 현재 가중치를 저장한다.
  2. 조기 종료(early stopping) : 검증 손실이 더 이상 향상되지 않을 때 훈련을 중지한다.
  3. 훈련하는 동안 하이퍼파라미터 값을 동적으로 조정한다. : 옵티마이저의 학습률 같은 경우이다.
  4. 훈련과 검증 지표를 로그에 기록하거나 모델이 학습한 표현이 업데이트될 때마다 시각화한다 : 진행 표시줄이 하나의 콜백이다.



ModelCheckpoint와 EarlyStopping 콜백

  • EarlyStopping콜백을 사용하면 정해진 에포크 동안 모니터링 지표가 향상되지 않을 때 훈련을 중지할 수 있다.
    • 일반적으로 이 콜백은 훈련하는 동안 모델을 계속 저장해주는 ModelCheckpoint와 함께 사용한다. (선택적으로 지금까지 가장 좋은 모델만 저장할 수 있다. 에포크 마지막에 다다랐을 때 최고 성능을 달성한 모델이다.)



ReduceLROnPlateau 콜백

  • ReduceLROnPlateau을 사용하면 검증 손실이 향상되지 않을 때 학습률을 작게 할 수 있다.
    • 손실 곡선이 평탄할 때 학습률을 작게 하거나 크게 하면 훈련 도중 지역 최솟값에서 효과적으로 빠져나올 수 있다.



자신만의 콜백 만들기

  • 내장 콜백에서 제공하지 않는 특수한 행동이 훈련 도중 필요하면 자신만의 콜백을 만들 수 있다.
  • 콜백은 keras.callbacks.Callback 클래스를 상속받아 구현한다. 그 다음 훈련하는 동안 호출될 여러 지점을 나타내기 위해 약속된 다음 메서드를 구현한다.
파라미터 설명
on_epoch_begin 각 에포크가 시작할 때 호출한다.
on_epoch_end 각 에포크가 끝날 때 호출한다.
on_batch_begin 각 배치 처리가 시작되기 전에 호출한다.
on_batch_end 각 배치 처리가 끝난 후에 호출한다.
on_train_begin 훈련이 시작될 때 호출한다.
on_train_end 훈련이 끝날 때 호출한다.
  • 이 메서드들은 모두 logs 매개변수와 함께 호출된다.
  • 이 매개변수에는 이전 배치, 에포크에 대한 훈련과 검증 측정값이 담겨 있는 딕셔너리가 전달된다.
  • 또 콜백은 다음 속성을 참조할 수 있다.
파라미터 설명
self.model 콜백을 호출하는 모델 객체
self.validation_data:fit() 메서드에 전달된 검증 데이터



7_2_2. 텐서보드 소개: 텐서플로의 시각화 프레임워크

  • 좋은 연구를 하거나 좋은 모델을 개발하려면 실험하는 모델 내부에서 어떤 일이 일어나는지 자주 그리고 많은 피드백을 받아야 한다.
  • 텐서보드의 핵심 목적은 훈련 모델의 내부에서 일어나는 모든 것을 시각적으로 모니터링할 수 있도록 돕는 것이다.
    • 모델의 최종 손실 외에 더 많은 정보를 모니터링하면 모델 작동에 대한 명확한 그림을 그릴 수 있다.
  1. 훈련하는 동안 측정 지표를 시각적으로 모니터링
  2. 모델 구조를 시각화
  3. 활성화 출력과 그래디언트의 히스토그램
  4. 3D로 임베딩 표현


image



텐서보드를 사용한 텍스트 분류 모델



텐서보드 로그 파일을 위한 디렉터리 생성하기

$ mkdir my_log_dir



텐서보드 콜백과 함께 모델 훈련하기

텐서플로 연산의 그래프 외에 케라스의 keras.utils.plot_model 유틸리티는 모델의 층 그래프를 깔끔하게 그려 주는 기능을 제공한다. 이를 사용하려면 pydot과 pydot-ng, graphviz 라이브러리가 필요한다.

  • 이제 명령행에서 콜백이 사용하는 로그 디렉터리를 지정하여 텐서보드 서버를 실행한다.
$ tensorboard --logdir=my_log_dir

그다음 브라우저에서 http://localhost:6006 주소에 접속하면 훈련 결과를 확인할 수 있다.

11.24 위 주소로 들어가지지만 해당 파일을 찾지 못하는 에러 발생



7_3. 모델의 성능을 최대로 끌어올리기

7_3_1. 고급 구조 패턴

배치 정규화

  • 정규화는 머신 러닝 모델에 주입되는 샘플들을 균일하게 만드는 광범위한 방법이다.
  • 이 방법은 모델이 학습하고 새로운 데이터에 잘 일반화되도록 돕는다.
  • normalized_data = (data - np.mean(data, axis=...)) / np.std(data, axis=...)
  • 배치 정규화(batch normalization)는 훈련하는 동안 평균과 분산이 바귀더라도 이에 적응하여 데이터를 정규화한다. (케라스에서 BatchNormalization) 훈련 과정에 사용된 배치 데이터의 평균과 분산에 대한 지수 이동 평균을 내부에 유지한다.
  • 배치 정규화의 주요 효과는 잔차 연 결과 매우 흡사하게 그래디언트의 전파를 도와주는 것이다. 결국 더 깊은 네트워크를 구성할 수 있다.
  • BatchNormalizaion() 층은 일반적으로 합성곱이나 완전 연결 층 다음에 사용한다.
from tensorflow.keras.layers import Conv2D, BatchNormalization, Dense

conv_model.add(Conv2D(32, 3, activation="relu"))
conv_model.add(BatchNormalization())

dense_model.add(Dense(32, activation='relu'))
dense_model.add(BatchNormalization())



깊이별 분리 합성곱

  • 깊이별 분리 합성곱(depthwise separable convolution) (SeparableConv2D)층은 입력 채널별로 따로따로 공간 방향의 합성곱을 수행한다. 그다음 점별 합성곱(1x1 합성곱)을 통해 출력 채널을 합친다.
  • 이는 공간 특성의 학습과 채널 방향 특성의 학습을 분리하는 효과를 낸다.
  • 입력에서 공간상 위치는 상관관계가 크지만 채널별로는 매우 독립적이라고 가정한다.
  • 합성곱을 통해 더 효율적으로 표현을 학습하기 때문에 적은 데이터로도 더 좋은 표현을 학습하고, 결국 성능이 더 높은 모델을 만든다.

image


from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import SeparableConv2D, MaxPooling2D, GlobalAveragePooling2D, Dense

height = 64
width = 64
channels = 3
num_classes = 10

model = Sequential()
model.add(SeparableConv2D(32, 3,
                          activation='relu',
                          input_shape=(height, width, channels,)))
model.add(SeparableConv2D(64, 3, activation='relu'))
model.add(MaxPooling2D(2))

model.add(SeparableConv2D(64, 3, activation='relu'))
model.add(SeparableConv2D(128, 3, activation='relu'))
model.add(MaxPooling2D(2))

model.add(SeparableConv2D(64, 3, activation='relu'))
model.add(SeparableConv2D(128, 3, activation='relu'))
model.add(GlobalAveragePooling2D())

model.add(Dense(32, activation='relu'))
model.add(Dense(num_classes, activation='softmax'))

model.compile(optimizer='rmsprop', loss='categorical_crossentropy')



7_3_2. 하이퍼파라미터 최적화

  • 딥러닝 모델은 무작위 선택의 연속이다. 층의 수, 유닛과 필터 수, 활성화 함수의 종류 등 설계자의 이유있는 랜덤한 선택의 연속이다.
  • 이런 구조에 관련된 파라미터를 역전파로 훈련되는 모델 파라미터와 구분하여 하이퍼파라미터(hyperparameter) 라고 한다.

전형적인 하이퍼파라미터 최적화 과정

  1. 일련의 하이퍼파라미터를 자동으로 선택합니다.
  2. 선택된 하이퍼파라미터로 모델을 만듭니다.
  3. 훈련 데이터에 학습하고 검증 데이터에서 최종 성능을 측정합니다.
  4. 다음으로 시도할 하이퍼파라미터를 자동으로 선택합니다.
  5. 이 과정을 반복합니다.
  6. 마지막으로 테스트 데이터에서 성능을 측정합니다.
  • 하이퍼파라미터 최적화는 어느 작업에서 최고의 모델을 얻거나 머신 러닝 경연 대회에서 우승하기 위한 강력한 도구이다.



모델 앙상블

  • 모델 앙상블(model ensemble)은 여러 개 다른 모델의 예측을 합쳐 더 좋은 예측을 만든다.
  • 가장 기본적인 앙상블은 추론에 나온 예측을 평균내는 방법이다.
  • 가중치를 주고 평균을 내는 방법도 있다.
preds_a = model_a.predict(x_val)
preds_b = model_b.predict(x_val)
preds_c = model_c.predict(x_val)
preds_d = model_d.predict(x_val)

final_preds = 0.25 * (preds_a + preds_b + preds_c + preds_d) # 예측 평균
final_preds = 0.5 * preds_a + 0.25 * preds_b + 0.1 * preds_c + 0.15 * preds_d # 가중치를 이용한 평균
  • 좋은 앙상블 가중치는 랜덤 서치나 넬더-미드 방법 같은 간단한 최적화 방법이 있다. 이외에도 지수 값을 평균할 수 있다.



References

댓글남기기