Path: blob/master/site/ko/guide/keras/custom_layers_and_models.ipynb
25118 views
Copyright 2020 The TensorFlow Authors.
하위 클래스화를 통한 새로운 레이어 및 모델 만들기
!pip install -U tf-hub-nightly
import tensorflow_hub as hub
from tensorflow.keras import layers
Layer
클래스: 상태(가중치)와 일부 계산의 조합
Keras의 주요 추상화 중 하나는 Layer
클래스입니다. 레이어는 상태(레이어의 "가중치")와 입력에서 출력으로의 변환("호출, 레이어의 정방향 패스")을 모두 캡슐화합니다.
다음은 밀집 레이어입니다. 상태는 변수 w
및 b
입니다.
파이썬 함수와 매우 유사한 일부 텐서 입력에서 레이어를 호출하여 레이어를 사용합니다.
가중치 w
와 b
는 레이어 속성으로 설정될 때 레이어에 의해 자동으로 추적됩니다.
레이어에 가중치를 추가하는 더 빠른 바로 가기에 액세스할 수도 있습니다. add_weight()
메서드:
레이어는 훈련 불가능한 가중치를 가질 수 있습니다
훈련 가능한 가중치 외에도 훈련 불가능한 가중치를 레이어에 추가할 수 있습니다. 이러한 가중치는 레이어를 훈련할 때 역전파 동안 고려되지 않아야 합니다.
훈련 불가능한 가중치를 추가 및 사용하는 방법은 다음과 같습니다.
layer.weights
의 일부이지만, 훈련 불가능한 가중치로 분류됩니다.
모범 사례: 입력 형상이 알려질 때까지 가중치 생성 지연하기
위의 Linear
레이어는 __init__()
에서 가중치 w
및 b
의 형상을 계산하는 데 사용되는 input_dim
인수를 사용했습니다.
대부분의 경우, 입력의 크기를 미리 알지 못할 수 있으며, 레이어를 인스턴스화한 후 얼마 지나지 않아 해당 값을 알게 되면 가중치를 지연 생성하고자 합니다.
Keras API에서는 레이어의 build(self, inputs_shape)
메서드에서 레이어 가중치를 만드는 것이 좋습니다. 다음과 같습니다.
레이어의 __call__()
메서드는 처음 호출될 때 자동으로 빌드를 실행합니다. 지연되어 사용하기 쉬운 레이어입니다.
위에 표시된 대로 build()
를 별도로 구현하면 가중치를 한 번만 생성하는 것과 모든 호출에서 가중치를 사용하는 것을 잘 구분할 수 있습니다. 그러나 일부 고급 사용자 지정 레이어의 경우 상태 생성과 계산을 분리하는 것이 비실용적일 수 있습니다. 레이어 구현자는 가중치 생성을 첫 번째 __call__()
로 연기할 수 있지만 이후 호출에서 동일한 가중치를 사용하도록 주의해야 합니다. 또한 __call__()
은 tf.function
내부에서 처음으로 실행될 가능성이 높기 때문에 __call__()
에서 발생하는 모든 변수 생성은 tf.init_scope
로 래핑되어야 합니다.
재귀적으로 구성 가능한 레이어
또 다른 인스턴스의 속성으로 Layer 인스턴스를 할당하면 외부 레이어가 내부 레이어로 생성한 가중치를 추적하기 시작합니다.
__init__()
메서드에서 이러한 서브 레이어를 만들고 가중치를 빌드하도록 트리거할 수 있게 첫 번째 __call__()
에 그대로 두는 것이 좋습니다.
add_loss()
메서드
레이어의 call()
메서드를 작성할 때는 훈련 루프를 작성할 때 나중에 사용하려는 손실 텐서를 만들 수 있습니다. self.add_loss(value)
를 호출하면 됩니다.
이러한 손실(내부 레이어에서 생성된 손실 포함)은 layer.losses
를 통해 검색할 수 있습니다. 이 속성은 모든 __call__()
이 시작될 때 최상위 레이어로 재설정되므로 layer.losses
에는 항상 마지막 정방향 패스에서 생성된 손실값이 포함됩니다.
또한, loss
속성에는 내부 레이어의 가중치에 대해 생성된 정규화 손실도 포함됩니다.
이러한 손실은 다음과 같이 훈련 루프를 작성할 때 고려됩니다.
훈련 루프 작성에 대한 자세한 가이드는 처음부터 훈련 루프 작성하기 가이드를 참조하세요.
이러한 손실은 fit()
에서도 완벽하게 작동합니다(손실이 있는 경우, 자동으로 합산되어 주 손실에 추가됨).
add_metric()
메서드
add_loss()
와 마찬가지로, 레이어에는 훈련 중 수량의 이동 평균을 추적하기 위한 add_metric()
메서드도 있습니다.
다음 "로지스틱 엔드포인트" 레이어를 고려합니다. 입력 예측 및 목표치로 사용하여 add_loss()
를 통해 추적하는 손실을 계산하고 add_metric()
을 통해 추적하는 정확도 스칼라를 계산합니다.
이러한 방식으로 추적되는 메트릭은 layer.metrics
를 통해 액세스할 수 있습니다.
add_loss()
와 마찬가지로, 이러한 메트릭은 fit()
의해 추적됩니다.
레이어에서 선택적으로 직렬화를 활성화할 수 있습니다
함수 모델의 일부로 사용자 정의 레이어를 직렬화해야 하는 경우, 선택적으로 get_config()
메서드를 구현할 수 있습니다.
기본 Layer
클래스의 __init__()
메서드는 일부 키워드 인수, 특히 name
및 dtype
를 사용합니다. 이러한 인수를 __init__()
의 부모 클래스에 전달하고 레이어 구성에 포함하는 것이 좋습니다.
구성에서 레이어를 역직렬화할 때 유연성이 더 필요한 경우, from_config()
클래스 메서드를 재정의할 수도 있습니다. 다음은 from_config()
의 기본 구현입니다.
직렬화 및 저장에 대한 자세한 내용은 모델 저장 및 직렬화 가이드를 참조하세요.
call()
메서드의 권한 있는 training
인수
일부 레이어, 특히 BatchNormalization
레이어와 Dropout
레이어는 훈련 및 추론 중에 서로 다른 동작을 갖습니다. 이러한 레이어의 경우, call()
메서드에서 training
(boolean) 인수를 노출하는 것이 표준 관행입니다.
이 인수를 call()
에서 노출하면 내장 훈련 및 평가 루프(예: fit()
)를 사용하여 훈련 및 추론에서 레이어를 올바르게 사용할 수 있습니다.
call()
메서드의 권한 있는 mask
인수
call()
에서 지원되는 다른 권한 있는 인수는 mask
인수입니다.
이 인수는 모든 Keras RNN 레이어에서 볼 수 있습니다. 마스크는 시계열 데이터를 처리할 때 특정 입력 타임스텝을 건너뛰는 데 사용되는 부울 텐서(입력의 타임스텝당 하나의 부울 값)입니다.
Keras는 이전 레이어에서 마스크가 생성될 때 이를 지원하는 레이어에 대해 올바른 mask
인수를 __call__()
에 자동으로 전달합니다. 마스크 생성 레이어는 mask_zero=True
레이어와 Masking
레이어로 구성된 Embedding
입니다.
마스킹 및 마스킹 지원 레이어를 작성하는 방법에 대한 자세한 내용은 "패딩 및 마스킹 이해하기" 가이드를 확인하세요.
Model
클래스
일반적으로, Layer
클래스를 사용하여 내부 계산 블록을 정의하고 Model
클래스를 사용하여 훈련할 객체인 외부 모델을 정의합니다.
예를 들어, ResNet50 모델에는 Layer
를 하위 클래스화하는 여러 ResNet 블록과 전체 ResNet50 네트워크를 포괄하는 단일 Model
이 있습니다.
Model
클래스는 Layer
와 같은 API를 가지며, 다음과 같은 차이점이 있습니다.
내장 훈련, 평가 및 예측 루프(
model.fit()
,model.evaluate()
,model.predict()
)를 제공합니다.model.layers
속성을 통해 내부 레이어의 목록을 노출합니다.저장 및 직렬화 API(
save()
,save_weights()
...)를 노출합니다.
효과적으로, Layer
클래스는 문서에서 일컫는 "레이어"("컨볼루션 레이어" 또는 "되풀이 레이어"에서와 같이) 또는 "블록"("ResNet 블록" 또는 "Inception 블록"에서와 같이)에 해당합니다.
한편, Model
클래스는 문서에서 "모델"("딥 러닝 모델"에서) 또는 "네트워크"( "딥 신경망"에서)로 지칭되는 것에 해당합다.
"Layer
클래스를 사용해야 할까요? 아니면 Model
클래스를 사용해야 할까요?"라는 질문이 있다면 자문해 보세요. fit()
을 호출해야 할까? save()
를 호출해야 할까? 만약 그렇다면 Model
를 사용하세요. 그렇지 않다면(클래스가 더 큰 시스템의 블록이거나 직접 훈련을 작성하고 코드를 저장하기 때문에) Layer
를 사용하세요.
예를 들어, 위의 mini-resnet 예제를 사용하여 fit()
으로 훈련하고 save_weights()
로 저장할 수 있는 Model
을 빌드할 수 있습니다.
종합: 엔드 투 엔드 예제
지금까지 배운 내용은 다음과 같습니다.
Layer
는 상태(__init__()
또는build()
) 및 일부 계산(call()
에서 정의)을 캡슐화합니다.레이어를 재귀적으로 중첩하여 새롭고 더 큰 계산 블록을 만들 수 있습니다.
레이어는
add_loss()
및add_metric()
을 통해 메트릭뿐만 아니라 손실(일반적으로, 정규화 손실)을 생성 및 추적할 수 있습니다.훈련하려는 외부 컨테이너는
Model
입니다.Model
은Layer
와 비슷하지만, 훈련 및 직렬화 유틸리티가 추가되었습니다.
이 모든 것을 엔드 투 엔드 예제에 넣어봅시다. VAE(Variational AutoEncoder)를 구현할 것이며, MNIST 숫자로 훈련할 것입니다.
VAE는 Model
의 서브 클래스가 될 것이며 Layer
를 하위 클래스화하는 중첩된 레이어 구성으로 빌드됩니다. 정규화 손실(KL 확산)을 제공합니다.
MNIST에 간단한 훈련 루프를 작성해 봅시다.
VAE는 Model
을 하위 클래스화하기 때문에 내장된 훈련 루프를 제공합니다. 따라서 다음과 같이 훈련할 수도 있습니다.
객체 지향 개발을 넘어: 함수형 API
이 예제가 너무 지나친 객체 지향 개발입니까? 함수형 API(Functional API)를 사용하여 모델을 빌드할 수도 있습니다. 중요한 것은 하나의 스타일을 선택한다고 해서 다른 스타일로 작성된 구성 요소를 활용하지 못하는 것은 아닙니다. 항상 목적에 따라 다르게 선택할 수 있습니다.
예를 들어, 아래의 함수형 API 예제는 위 예제에서 정의한 것과 같은 Sampling
레이어를 재사용합니다.
자세한 정보는 함수형 API 가이드를 참고하세요.