이 페이지는 Cloud Translation API를 통해 번역되었습니다.
Switch to English

기능적 API

TensorFlow.org에서보기 Google Colab에서 실행 GitHub에서 소스보기 노트북 다운로드

설정

import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

소개

Keras 기능 APItf.keras.Sequential API보다 더 유연한 모델을 생성하는 방법입니다. 기능적 API는 비선형 토폴로지, 공유 레이어, 심지어 여러 입력 또는 출력으로 모델을 처리 할 수 ​​있습니다.

주요 아이디어는 딥 러닝 모델이 일반적으로 계층의 방향성 비순환 그래프 (DAG)라는 것입니다. 따라서 기능적 API는 레이어의 그래프를 작성 하는 방법입니다.

다음 모델을 고려하십시오.

(input: 784-dimensional vectors)
       ↧
[Dense (64 units, relu activation)]
       ↧
[Dense (64 units, relu activation)]
       ↧
[Dense (10 units, softmax activation)]
       ↧
(output: logits of a probability distribution over 10 classes)

이것은 3 개의 레이어가있는 기본 그래프입니다. 기능적 API를 사용하여이 모델을 빌드하려면 먼저 입력 노드를 생성합니다.

inputs = keras.Input(shape=(784,))

데이터의 모양은 784 차원 벡터로 설정됩니다. 각 샘플의 모양 만 지정되므로 배치 크기는 항상 생략됩니다.

예를 들어 (32, 32, 3) 모양의 이미지 입력이있는 경우 다음을 사용합니다.

# Just for demonstration purposes.
img_inputs = keras.Input(shape=(32, 32, 3))

반환되는 inputs 에는 dtype 에 제공하는 입력 데이터의 모양 및 dtype 에 대한 정보가 포함됩니다. 모양은 다음과 같습니다.

inputs.shape
TensorShape([None, 784])

다음은 dtype입니다.

inputs.dtype
tf.float32

inputs 객체에서 레이어를 호출하여 레이어 그래프에 새 노드를 만듭니다.

dense = layers.Dense(64, activation="relu")
x = dense(inputs)

"레이어 호출"작업은 "입력"에서 만든이 레이어로 화살표를 그리는 것과 같습니다. 입력을 dense 레이어로 "전달"하고 출력으로 x 를 얻습니다.

레이어 그래프에 레이어를 몇 개 더 추가해 보겠습니다.

x = layers.Dense(64, activation="relu")(x)
outputs = layers.Dense(10)(x)

이 시점에서 레이어 그래프에서 입력과 출력을 지정하여 Model 을 만들 수 있습니다.

model = keras.Model(inputs=inputs, outputs=outputs, name="mnist_model")

모델 요약이 어떻게 생겼는지 확인해 보겠습니다.

model.summary()
Model: "mnist_model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 784)]             0         
_________________________________________________________________
dense (Dense)                (None, 64)                50240     
_________________________________________________________________
dense_1 (Dense)              (None, 64)                4160      
_________________________________________________________________
dense_2 (Dense)              (None, 10)                650       
=================================================================
Total params: 55,050
Trainable params: 55,050
Non-trainable params: 0
_________________________________________________________________

모델을 그래프로 플로팅 할 수도 있습니다.

keras.utils.plot_model(model, "my_first_model.png")

png

선택적으로 플로팅 된 그래프에서 각 레이어의 입력 및 출력 모양을 표시합니다.

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

png

이 그림과 코드는 거의 동일합니다. 코드 버전에서 연결 화살표는 호출 작업으로 대체됩니다.

"계층 그래프"는 딥 러닝 모델에 대한 직관적 인 정신 이미지이며, 기능적 API는이를 밀접하게 반영하는 모델을 만드는 방법입니다.

훈련, 평가 및 추론

훈련, 평가 및 추론은 Sequential 모델과 기능 API를 사용하여 빌드 된 모델에 대해 정확히 동일한 방식으로 작동합니다.

여기에서 MNIST 이미지 데이터를로드하고, 벡터로 재구성하고, 데이터에 모델을 맞춘 다음 (검증 분할에서 성능을 모니터링하는 동안) 테스트 데이터에서 모델을 평가합니다.

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()

x_train = x_train.reshape(60000, 784).astype("float32") / 255
x_test = x_test.reshape(10000, 784).astype("float32") / 255

model.compile(
    loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    optimizer=keras.optimizers.RMSprop(),
    metrics=["accuracy"],
)

history = model.fit(x_train, y_train, batch_size=64, epochs=2, validation_split=0.2)

test_scores = model.evaluate(x_test, y_test, verbose=2)
print("Test loss:", test_scores[0])
print("Test accuracy:", test_scores[1])
Epoch 1/2
750/750 [==============================] - 2s 2ms/step - loss: 0.3475 - accuracy: 0.9017 - val_loss: 0.2012 - val_accuracy: 0.9401
Epoch 2/2
750/750 [==============================] - 2s 2ms/step - loss: 0.1524 - accuracy: 0.9552 - val_loss: 0.1336 - val_accuracy: 0.9619
313/313 - 0s - loss: 0.1297 - accuracy: 0.9610
Test loss: 0.12966538965702057
Test accuracy: 0.9610000252723694

자세한 내용은 교육 및 평가 가이드를 참조하세요.

저장 및 직렬화

모델 저장 및 직렬화는 기능적 API를 사용하여 빌드 된 모델에 대해 Sequential 모델과 동일한 방식으로 작동합니다. 기능 모델을 저장하는 표준 방법은 model.save() 를 호출하여 전체 모델을 단일 파일로 저장하는 것입니다. 나중에 모델을 빌드 한 코드를 더 이상 사용할 수없는 경우에도이 파일에서 동일한 모델을 다시 만들 수 있습니다.

이 저장된 파일에는 다음이 포함됩니다.

  • 모델 아키텍처
  • 모델 가중치 값 (훈련 중에 학습 됨)
  • 모델 학습 구성 (있는 경우) ( compile 로 전달됨)
  • 옵티 마이저 및 상태 (있는 경우) (중단 한 지점에서 훈련을 다시 시작)
model.save("path_to_my_model")
del model
# Recreate the exact same model purely from the file:
model = keras.models.load_model("path_to_my_model")
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Model.state_updates (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Layer.updates (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.
INFO:tensorflow:Assets written to: path_to_my_model/assets

자세한 내용은 모델 직렬화 및 저장 가이드를 참조하세요.

동일한 레이어 그래프를 사용하여 여러 모델 정의

Functional API에서 모델은 레이어 그래프에 입력 및 출력을 지정하여 생성됩니다. 즉, 단일 레이어 그래프를 사용하여 여러 모델을 생성 할 수 있습니다.

아래 예에서는 동일한 레이어 스택을 사용하여 이미지 입력을 16 차원 벡터로 변환하는 encoder 모델과 학습을위한 엔드 autoencoder 엔드 autoencoder encoder 모델의 두 모델을 인스턴스화합니다.

encoder_input = keras.Input(shape=(28, 28, 1), name="img")
x = layers.Conv2D(16, 3, activation="relu")(encoder_input)
x = layers.Conv2D(32, 3, activation="relu")(x)
x = layers.MaxPooling2D(3)(x)
x = layers.Conv2D(32, 3, activation="relu")(x)
x = layers.Conv2D(16, 3, activation="relu")(x)
encoder_output = layers.GlobalMaxPooling2D()(x)

encoder = keras.Model(encoder_input, encoder_output, name="encoder")
encoder.summary()

x = layers.Reshape((4, 4, 1))(encoder_output)
x = layers.Conv2DTranspose(16, 3, activation="relu")(x)
x = layers.Conv2DTranspose(32, 3, activation="relu")(x)
x = layers.UpSampling2D(3)(x)
x = layers.Conv2DTranspose(16, 3, activation="relu")(x)
decoder_output = layers.Conv2DTranspose(1, 3, activation="relu")(x)

autoencoder = keras.Model(encoder_input, decoder_output, name="autoencoder")
autoencoder.summary()
Model: "encoder"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
img (InputLayer)             [(None, 28, 28, 1)]       0         
_________________________________________________________________
conv2d (Conv2D)              (None, 26, 26, 16)        160       
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 24, 24, 32)        4640      
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 8, 8, 32)          0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 6, 6, 32)          9248      
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 4, 4, 16)          4624      
_________________________________________________________________
global_max_pooling2d (Global (None, 16)                0         
=================================================================
Total params: 18,672
Trainable params: 18,672
Non-trainable params: 0
_________________________________________________________________
Model: "autoencoder"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
img (InputLayer)             [(None, 28, 28, 1)]       0         
_________________________________________________________________
conv2d (Conv2D)              (None, 26, 26, 16)        160       
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 24, 24, 32)        4640      
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 8, 8, 32)          0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 6, 6, 32)          9248      
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 4, 4, 16)          4624      
_________________________________________________________________
global_max_pooling2d (Global (None, 16)                0         
_________________________________________________________________
reshape (Reshape)            (None, 4, 4, 1)           0         
_________________________________________________________________
conv2d_transpose (Conv2DTran (None, 6, 6, 16)          160       
_________________________________________________________________
conv2d_transpose_1 (Conv2DTr (None, 8, 8, 32)          4640      
_________________________________________________________________
up_sampling2d (UpSampling2D) (None, 24, 24, 32)        0         
_________________________________________________________________
conv2d_transpose_2 (Conv2DTr (None, 26, 26, 16)        4624      
_________________________________________________________________
conv2d_transpose_3 (Conv2DTr (None, 28, 28, 1)         145       
=================================================================
Total params: 28,241
Trainable params: 28,241
Non-trainable params: 0
_________________________________________________________________

여기서 디코딩 아키텍처는 인코딩 아키텍처와 엄격하게 대칭이므로 출력 모양은 입력 모양 (28, 28, 1) 합니다.

(A)의 반대 Conv2D 층은 인 Conv2DTranspose 층, 및의 역 MaxPooling2D 층은 인 UpSampling2D 층.

모든 모델은 레이어처럼 호출 가능합니다.

Input 또는 다른 레이어의 출력에서 ​​모델을 호출하여 모든 모델을 마치 레이어처럼 취급 할 수 있습니다. 모델을 호출하면 모델의 아키텍처를 재사용하는 것이 아니라 가중치도 재사용하게됩니다.

이 동작을 확인하기 위해 다음은 인코더 모델, 디코더 모델을 생성하고이를 두 번의 호출로 연결하여 오토 인코더 모델을 얻는 오토 인코더 예제에 대한 다른 방식입니다.

encoder_input = keras.Input(shape=(28, 28, 1), name="original_img")
x = layers.Conv2D(16, 3, activation="relu")(encoder_input)
x = layers.Conv2D(32, 3, activation="relu")(x)
x = layers.MaxPooling2D(3)(x)
x = layers.Conv2D(32, 3, activation="relu")(x)
x = layers.Conv2D(16, 3, activation="relu")(x)
encoder_output = layers.GlobalMaxPooling2D()(x)

encoder = keras.Model(encoder_input, encoder_output, name="encoder")
encoder.summary()

decoder_input = keras.Input(shape=(16,), name="encoded_img")
x = layers.Reshape((4, 4, 1))(decoder_input)
x = layers.Conv2DTranspose(16, 3, activation="relu")(x)
x = layers.Conv2DTranspose(32, 3, activation="relu")(x)
x = layers.UpSampling2D(3)(x)
x = layers.Conv2DTranspose(16, 3, activation="relu")(x)
decoder_output = layers.Conv2DTranspose(1, 3, activation="relu")(x)

decoder = keras.Model(decoder_input, decoder_output, name="decoder")
decoder.summary()

autoencoder_input = keras.Input(shape=(28, 28, 1), name="img")
encoded_img = encoder(autoencoder_input)
decoded_img = decoder(encoded_img)
autoencoder = keras.Model(autoencoder_input, decoded_img, name="autoencoder")
autoencoder.summary()
Model: "encoder"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
original_img (InputLayer)    [(None, 28, 28, 1)]       0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 26, 26, 16)        160       
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 24, 24, 32)        4640      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 8, 8, 32)          0         
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 6, 6, 32)          9248      
_________________________________________________________________
conv2d_7 (Conv2D)            (None, 4, 4, 16)          4624      
_________________________________________________________________
global_max_pooling2d_1 (Glob (None, 16)                0         
=================================================================
Total params: 18,672
Trainable params: 18,672
Non-trainable params: 0
_________________________________________________________________
Model: "decoder"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
encoded_img (InputLayer)     [(None, 16)]              0         
_________________________________________________________________
reshape_1 (Reshape)          (None, 4, 4, 1)           0         
_________________________________________________________________
conv2d_transpose_4 (Conv2DTr (None, 6, 6, 16)          160       
_________________________________________________________________
conv2d_transpose_5 (Conv2DTr (None, 8, 8, 32)          4640      
_________________________________________________________________
up_sampling2d_1 (UpSampling2 (None, 24, 24, 32)        0         
_________________________________________________________________
conv2d_transpose_6 (Conv2DTr (None, 26, 26, 16)        4624      
_________________________________________________________________
conv2d_transpose_7 (Conv2DTr (None, 28, 28, 1)         145       
=================================================================
Total params: 9,569
Trainable params: 9,569
Non-trainable params: 0
_________________________________________________________________
Model: "autoencoder"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
img (InputLayer)             [(None, 28, 28, 1)]       0         
_________________________________________________________________
encoder (Functional)         (None, 16)                18672     
_________________________________________________________________
decoder (Functional)         (None, 28, 28, 1)         9569      
=================================================================
Total params: 28,241
Trainable params: 28,241
Non-trainable params: 0
_________________________________________________________________

보시다시피 모델은 중첩 될 수 있습니다. 모델은 하위 모델을 포함 할 수 있습니다 (모델은 레이어와 같으므로). 모델 중첩의 일반적인 사용 사례는 앙상블 입니다. 예를 들어 다음은 모델 집합을 예측을 평균하는 단일 모델로 통합하는 방법입니다.

def get_model():
    inputs = keras.Input(shape=(128,))
    outputs = layers.Dense(1)(inputs)
    return keras.Model(inputs, outputs)


model1 = get_model()
model2 = get_model()
model3 = get_model()

inputs = keras.Input(shape=(128,))
y1 = model1(inputs)
y2 = model2(inputs)
y3 = model3(inputs)
outputs = layers.average([y1, y2, y3])
ensemble_model = keras.Model(inputs=inputs, outputs=outputs)

복잡한 그래프 토폴로지 조작

여러 입력 및 출력이있는 모델

기능적 API를 사용하면 여러 입력 및 출력을 쉽게 조작 할 수 있습니다. 이것은 Sequential API로 처리 할 수 ​​없습니다.

예를 들어 우선 순위에 따라 고객 발행 티켓의 순위를 매기고 올바른 부서로 라우팅하는 시스템을 구축하는 경우 모델에는 세 가지 입력이 있습니다.

  • 티켓 제목 (텍스트 입력),
  • 티켓의 텍스트 본문 (텍스트 입력)
  • 사용자가 추가 한 모든 태그 (카테고리 입력)

이 모델에는 두 가지 출력이 있습니다.

  • 0과 1 사이의 우선 순위 점수 (스칼라 시그 모이 드 출력) 및
  • 티켓을 처리해야하는 부서 (부서 세트에 대한 softmax 출력).

기능적 API를 사용하여이 모델을 몇 줄로 빌드 할 수 있습니다.

num_tags = 12  # Number of unique issue tags
num_words = 10000  # Size of vocabulary obtained when preprocessing text data
num_departments = 4  # Number of departments for predictions

title_input = keras.Input(
    shape=(None,), name="title"
)  # Variable-length sequence of ints
body_input = keras.Input(shape=(None,), name="body")  # Variable-length sequence of ints
tags_input = keras.Input(
    shape=(num_tags,), name="tags"
)  # Binary vectors of size `num_tags`

# Embed each word in the title into a 64-dimensional vector
title_features = layers.Embedding(num_words, 64)(title_input)
# Embed each word in the text into a 64-dimensional vector
body_features = layers.Embedding(num_words, 64)(body_input)

# Reduce sequence of embedded words in the title into a single 128-dimensional vector
title_features = layers.LSTM(128)(title_features)
# Reduce sequence of embedded words in the body into a single 32-dimensional vector
body_features = layers.LSTM(32)(body_features)

# Merge all available features into a single large vector via concatenation
x = layers.concatenate([title_features, body_features, tags_input])

# Stick a logistic regression for priority prediction on top of the features
priority_pred = layers.Dense(1, name="priority")(x)
# Stick a department classifier on top of the features
department_pred = layers.Dense(num_departments, name="department")(x)

# Instantiate an end-to-end model predicting both priority and department
model = keras.Model(
    inputs=[title_input, body_input, tags_input],
    outputs=[priority_pred, department_pred],
)

이제 모델을 플로팅합니다.

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

png

이 모델을 컴파일 할 때 각 출력에 다른 손실을 할당 할 수 있습니다. 총 훈련 손실에 대한 기여도를 조절하기 위해 각 손실에 다른 가중치를 할당 할 수도 있습니다.

model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss=[
        keras.losses.BinaryCrossentropy(from_logits=True),
        keras.losses.CategoricalCrossentropy(from_logits=True),
    ],
    loss_weights=[1.0, 0.2],
)

출력 레이어의 이름이 다르기 때문에 다음과 같이 손실을 지정할 수도 있습니다.

model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss={
        "priority": keras.losses.BinaryCrossentropy(from_logits=True),
        "department": keras.losses.CategoricalCrossentropy(from_logits=True),
    },
    loss_weights=[1.0, 0.2],
)

입력 및 대상의 NumPy 배열 목록을 전달하여 모델을 훈련시킵니다.

# Dummy input data
title_data = np.random.randint(num_words, size=(1280, 10))
body_data = np.random.randint(num_words, size=(1280, 100))
tags_data = np.random.randint(2, size=(1280, num_tags)).astype("float32")

# Dummy target data
priority_targets = np.random.random(size=(1280, 1))
dept_targets = np.random.randint(2, size=(1280, num_departments))

model.fit(
    {"title": title_data, "body": body_data, "tags": tags_data},
    {"priority": priority_targets, "department": dept_targets},
    epochs=2,
    batch_size=32,
)
Epoch 1/2
40/40 [==============================] - 0s 11ms/step - loss: 1.3184 - priority_loss: 0.7015 - department_loss: 3.0842
Epoch 2/2
40/40 [==============================] - 0s 10ms/step - loss: 1.3183 - priority_loss: 0.6988 - department_loss: 3.0971

<tensorflow.python.keras.callbacks.History at 0x7fcc3fd61e80>

Dataset 객체로 fit을 호출 할 때 ([title_data, body_data, tags_data], [priority_targets, dept_targets]) 와 같은 목록의 튜플 또는 ({'title': title_data, 'body': body_data, 'tags': tags_data}, {'priority': priority_targets, 'department': dept_targets}) 와 같은 사전의 튜플을 생성해야합니다 ({'title': title_data, 'body': body_data, 'tags': tags_data}, {'priority': priority_targets, 'department': dept_targets}) .

자세한 설명은 교육 및 평가 가이드를 참조하십시오.

장난감 ResNet 모델

다중 입력 및 출력이있는 모델 외에도 기능적 API를 사용하면 비선형 연결 토폴로지를 쉽게 조작 할 수 있습니다. 이는 순차적으로 연결되지 않은 계층이있는 모델이며 Sequential API가 처리 할 수 ​​없습니다.

이에 대한 일반적인 사용 사례는 잔여 연결입니다. 이를 증명하기 위해 CIFAR10 용 장난감 ResNet 모델을 구축해 보겠습니다.

inputs = keras.Input(shape=(32, 32, 3), name="img")
x = layers.Conv2D(32, 3, activation="relu")(inputs)
x = layers.Conv2D(64, 3, activation="relu")(x)
block_1_output = layers.MaxPooling2D(3)(x)

x = layers.Conv2D(64, 3, activation="relu", padding="same")(block_1_output)
x = layers.Conv2D(64, 3, activation="relu", padding="same")(x)
block_2_output = layers.add([x, block_1_output])

x = layers.Conv2D(64, 3, activation="relu", padding="same")(block_2_output)
x = layers.Conv2D(64, 3, activation="relu", padding="same")(x)
block_3_output = layers.add([x, block_2_output])

x = layers.Conv2D(64, 3, activation="relu")(block_3_output)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(256, activation="relu")(x)
x = layers.Dropout(0.5)(x)
outputs = layers.Dense(10)(x)

model = keras.Model(inputs, outputs, name="toy_resnet")
model.summary()
Model: "toy_resnet"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
img (InputLayer)                [(None, 32, 32, 3)]  0                                            
__________________________________________________________________________________________________
conv2d_8 (Conv2D)               (None, 30, 30, 32)   896         img[0][0]                        
__________________________________________________________________________________________________
conv2d_9 (Conv2D)               (None, 28, 28, 64)   18496       conv2d_8[0][0]                   
__________________________________________________________________________________________________
max_pooling2d_2 (MaxPooling2D)  (None, 9, 9, 64)     0           conv2d_9[0][0]                   
__________________________________________________________________________________________________
conv2d_10 (Conv2D)              (None, 9, 9, 64)     36928       max_pooling2d_2[0][0]            
__________________________________________________________________________________________________
conv2d_11 (Conv2D)              (None, 9, 9, 64)     36928       conv2d_10[0][0]                  
__________________________________________________________________________________________________
add (Add)                       (None, 9, 9, 64)     0           conv2d_11[0][0]                  
                                                                 max_pooling2d_2[0][0]            
__________________________________________________________________________________________________
conv2d_12 (Conv2D)              (None, 9, 9, 64)     36928       add[0][0]                        
__________________________________________________________________________________________________
conv2d_13 (Conv2D)              (None, 9, 9, 64)     36928       conv2d_12[0][0]                  
__________________________________________________________________________________________________
add_1 (Add)                     (None, 9, 9, 64)     0           conv2d_13[0][0]                  
                                                                 add[0][0]                        
__________________________________________________________________________________________________
conv2d_14 (Conv2D)              (None, 7, 7, 64)     36928       add_1[0][0]                      
__________________________________________________________________________________________________
global_average_pooling2d (Globa (None, 64)           0           conv2d_14[0][0]                  
__________________________________________________________________________________________________
dense_6 (Dense)                 (None, 256)          16640       global_average_pooling2d[0][0]   
__________________________________________________________________________________________________
dropout (Dropout)               (None, 256)          0           dense_6[0][0]                    
__________________________________________________________________________________________________
dense_7 (Dense)                 (None, 10)           2570        dropout[0][0]                    
==================================================================================================
Total params: 223,242
Trainable params: 223,242
Non-trainable params: 0
__________________________________________________________________________________________________

모델을 플로팅합니다.

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

png

이제 모델을 학습 시키십시오.

(x_train, y_train), (x_test, y_test) = keras.datasets.cifar10.load_data()

x_train = x_train.astype("float32") / 255.0
x_test = x_test.astype("float32") / 255.0
y_train = keras.utils.to_categorical(y_train, 10)
y_test = keras.utils.to_categorical(y_test, 10)

model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss=keras.losses.CategoricalCrossentropy(from_logits=True),
    metrics=["acc"],
)
# We restrict the data to the first 1000 samples so as to limit execution time
# on Colab. Try to train on the entire dataset until convergence!
model.fit(x_train[:1000], y_train[:1000], batch_size=64, epochs=1, validation_split=0.2)
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170500096/170498071 [==============================] - 11s 0us/step
13/13 [==============================] - 0s 20ms/step - loss: 2.3143 - acc: 0.1013 - val_loss: 2.3050 - val_acc: 0.1150

<tensorflow.python.keras.callbacks.History at 0x7fcc3e6aa1d0>

공유 레이어

기능적 API의 또 다른 좋은 용도는 공유 레이어 를 사용하는 모델입니다. 공유 레이어는 동일한 모델에서 여러 번 재사용되는 레이어 인스턴스로, 레이어 그래프의 여러 경로에 해당하는 기능을 학습합니다.

공유 레이어는 종종 유사한 공간 (예 : 유사한 어휘를 특징으로하는 두 개의 다른 텍스트)에서 입력을 인코딩하는 데 사용됩니다. 이를 통해 서로 다른 입력간에 정보를 공유 할 수 있으며 더 적은 데이터로 이러한 모델을 훈련 할 수 있습니다. 주어진 단어가 입력 중 하나에 표시되면 공유 레이어를 통과하는 모든 입력 처리에 도움이됩니다.

기능적 API에서 계층을 공유하려면 동일한 계층 인스턴스를 여러 번 호출하십시오. 예를 들어 다음은 두 가지 다른 텍스트 입력에서 공유되는 Embedding 레이어입니다.

# Embedding for 1000 unique words mapped to 128-dimensional vectors
shared_embedding = layers.Embedding(1000, 128)

# Variable-length sequence of integers
text_input_a = keras.Input(shape=(None,), dtype="int32")

# Variable-length sequence of integers
text_input_b = keras.Input(shape=(None,), dtype="int32")

# Reuse the same layer to encode both inputs
encoded_input_a = shared_embedding(text_input_a)
encoded_input_b = shared_embedding(text_input_b)

레이어 그래프에서 노드 추출 및 재사용

조작중인 레이어의 그래프는 정적 데이터 구조이므로 액세스하고 검사 할 수 있습니다. 이것이 기능 모델을 이미지로 표시하는 방법입니다.

이는 또한 중간 레이어 (그래프의 "노드")의 활성화에 액세스하여 다른 곳에서 재사용 할 수 있음을 의미합니다. 이는 특징 추출과 같은 작업에 매우 유용합니다.

예를 살펴 보겠습니다. 다음은 ImageNet에서 가중치가 사전 훈련 된 VGG19 모델입니다.

vgg19 = tf.keras.applications.VGG19()
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg19/vgg19_weights_tf_dim_ordering_tf_kernels.h5
574717952/574710816 [==============================] - 15s 0us/step

다음은 그래프 데이터 구조를 쿼리하여 얻은 모델의 중간 활성화입니다.

features_list = [layer.output for layer in vgg19.layers]

다음 기능을 사용하여 중간 계층 활성화 값을 반환하는 새로운 기능 추출 모델을 만듭니다.

feat_extraction_model = keras.Model(inputs=vgg19.input, outputs=features_list)

img = np.random.random((1, 224, 224, 3)).astype("float32")
extracted_features = feat_extraction_model(img)

이것은 무엇보다도 신경 스타일 전송 과 같은 작업에 유용합니다.

사용자 정의 레이어를 사용하여 API 확장

tf.keras 에는 다음과 같은 다양한 내장 레이어가 포함됩니다.

  • Conv1D 레이어 : Conv1D , Conv2D , Conv3D , Conv2DTranspose
  • 풀링 레이어 : MaxPooling1D , MaxPooling2D , MaxPooling3D , AveragePooling1D
  • RNN 레이어 : GRU , LSTM , ConvLSTM2D
  • BatchNormalization , Dropout , Embedding

그러나 필요한 것을 찾지 못한 경우 자체 레이어를 만들어 API를 쉽게 확장 할 수 있습니다. 모든 레이어는 Layer 클래스를 하위 클래스로 만들고 다음을 구현합니다.

  • call 메서드는 계층에 의해 수행되는 계산을 지정합니다.
  • 레이어의 가중치를 생성하는 build 메소드 ( __init__ 에서도 가중치를 생성 할 수 있으므로 이것은 스타일 규칙 일뿐입니다).

처음부터 레이어를 만드는 방법에 대해 자세히 알아 보려면 사용자 지정 레이어 및 모델 가이드를 읽어보세요.

다음은 tf.keras.layers.Dense 의 기본 구현입니다.

class CustomDense(layers.Layer):
    def __init__(self, units=32):
        super(CustomDense, self).__init__()
        self.units = units

    def build(self, input_shape):
        self.w = self.add_weight(
            shape=(input_shape[-1], self.units),
            initializer="random_normal",
            trainable=True,
        )
        self.b = self.add_weight(
            shape=(self.units,), initializer="random_normal", trainable=True
        )

    def call(self, inputs):
        return tf.matmul(inputs, self.w) + self.b


inputs = keras.Input((4,))
outputs = CustomDense(10)(inputs)

model = keras.Model(inputs, outputs)

사용자 정의 계층에서 직렬화를 지원하려면 계층 인스턴스의 생성자 인수를 반환하는 get_config 메서드를 정의합니다.

class CustomDense(layers.Layer):
    def __init__(self, units=32):
        super(CustomDense, self).__init__()
        self.units = units

    def build(self, input_shape):
        self.w = self.add_weight(
            shape=(input_shape[-1], self.units),
            initializer="random_normal",
            trainable=True,
        )
        self.b = self.add_weight(
            shape=(self.units,), initializer="random_normal", trainable=True
        )

    def call(self, inputs):
        return tf.matmul(inputs, self.w) + self.b

    def get_config(self):
        return {"units": self.units}


inputs = keras.Input((4,))
outputs = CustomDense(10)(inputs)

model = keras.Model(inputs, outputs)
config = model.get_config()

new_model = keras.Model.from_config(config, custom_objects={"CustomDense": CustomDense})

선택적으로 해당 구성 사전이 제공된 레이어 인스턴스를 다시 만들 때 사용되는 클래스 메서드 from_config(cls, config) 를 구현합니다. from_config 의 기본 구현은 from_config 과 같습니다.

def from_config(cls, config):
  return cls(**config)

기능적 API를 사용하는 경우

Keras 기능 API를 사용하여 새 모델을 만들어야합니까, 아니면 Model 클래스를 직접 하위 클래스로 만들어야합니까? 일반적으로 기능적 API는 더 높은 수준이고 더 쉽고 안전하며 하위 클래스 모델이 지원하지 않는 여러 기능이 있습니다.

그러나 모델 하위 분류는 레이어의 방향성 비순환 그래프로 쉽게 표현할 수없는 모델을 빌드 할 때 더 큰 유연성을 제공합니다. 예를 들어, 기능적 API를 사용하여 Tree-RNN을 구현할 수 없으며 Model 직접 하위 클래스 화해야합니다.

Functional API와 모델 서브 클래 싱의 차이점을 자세히 살펴 보려면 TensorFlow 2.0의 Symbolic 및 Imperative API 란 무엇인가요?를 읽어보세요 . .

기능적 API 강점 :

다음 속성은 Sequential 모델 (데이터 구조이기도 함)에도 적용되지만 하위 클래스 모델 (데이터 구조가 아닌 Python 바이트 코드)에는 적용되지 않습니다.

덜 장황함

super(MyClass, self).__init__(...) , def call(self, ...): 등이 없습니다.

비교:

inputs = keras.Input(shape=(32,))
x = layers.Dense(64, activation='relu')(inputs)
outputs = layers.Dense(10)(x)
mlp = keras.Model(inputs, outputs)

서브 클래 싱 된 버전 :

class MLP(keras.Model):

  def __init__(self, **kwargs):
    super(MLP, self).__init__(**kwargs)
    self.dense_1 = layers.Dense(64, activation='relu')
    self.dense_2 = layers.Dense(10)

  def call(self, inputs):
    x = self.dense_1(inputs)
    return self.dense_2(x)

# Instantiate the model.
mlp = MLP()
# Necessary to create the model's state.
# The model doesn't have a state until it's called at least once.
_ = mlp(tf.zeros((1, 32)))

연결성 그래프를 정의하는 동안 모델 검증

기능적 API에서 입력 사양 (shape 및 dtype)은 미리 생성됩니다 ( Input 사용). 계층을 호출 할 때마다 계층은 전달 된 사양이 가정과 일치하는지 확인하고 그렇지 않은 경우 유용한 오류 메시지를 표시합니다.

이렇게하면 기능적 API로 빌드 할 수있는 모든 모델이 실행됩니다. 수렴 관련 디버깅을 제외한 모든 디버깅은 실행 시간이 아닌 모델 생성 중에 정적으로 발생합니다. 이것은 컴파일러의 유형 검사와 유사합니다.

기능 모델은 플로팅 및 검사 가능

모델을 그래프로 플로팅 할 수 있으며이 그래프의 중간 노드에 쉽게 액세스 할 수 있습니다. 예를 들어, 중간 계층의 활성화를 추출하고 재사용하려면 (이전 예제에서 볼 수 있음) :

features_list = [layer.output for layer in vgg19.layers]
feat_extraction_model = keras.Model(inputs=vgg19.input, outputs=features_list)

기능 모델은 직렬화 또는 복제 가능

기능 모델은 코드가 아닌 데이터 구조이기 때문에 안전하게 직렬화 할 수 있으며 원본 코드에 액세스하지 않고도 정확히 동일한 모델을 다시 만들 수있는 단일 파일로 저장할 수 있습니다. 직렬화 및 저장 가이드를 참조하세요.

서브 클래스 모델을 직렬화하려면 구현 from_config() 모델 수준에서 get_config()from_config() 메서드를 지정해야합니다.

기능적 API 약점 :

동적 아키텍처를 지원하지 않습니다.

기능적 API는 모델을 계층의 DAG로 취급합니다. 이는 대부분의 딥 러닝 아키텍처에 적용되지만 전부는 아닙니다.

믹스 앤 매치 API 스타일

기능적 API 또는 모델 서브 클래 싱 중에서 선택하는 것은 모델의 한 범주로 제한하는 이진 결정이 아닙니다. tf.keras API의 모든 모델은 Sequential 모델이든, 기능적 모델이든, 처음부터 작성된 하위 클래스 모델이든간에 서로 상호 작용할 수 있습니다.

항상 기능 모델 또는 Sequential 모델을 하위 클래스 모델 또는 계층의 일부로 사용할 수 있습니다.

units = 32
timesteps = 10
input_dim = 5

# Define a Functional model
inputs = keras.Input((None, units))
x = layers.GlobalAveragePooling1D()(inputs)
outputs = layers.Dense(1)(x)
model = keras.Model(inputs, outputs)


class CustomRNN(layers.Layer):
    def __init__(self):
        super(CustomRNN, self).__init__()
        self.units = units
        self.projection_1 = layers.Dense(units=units, activation="tanh")
        self.projection_2 = layers.Dense(units=units, activation="tanh")
        # Our previously-defined Functional model
        self.classifier = model

    def call(self, inputs):
        outputs = []
        state = tf.zeros(shape=(inputs.shape[0], self.units))
        for t in range(inputs.shape[1]):
            x = inputs[:, t, :]
            h = self.projection_1(x)
            y = h + self.projection_2(state)
            state = y
            outputs.append(y)
        features = tf.stack(outputs, axis=1)
        print(features.shape)
        return self.classifier(features)


rnn_model = CustomRNN()
_ = rnn_model(tf.zeros((1, timesteps, input_dim)))
(1, 10, 32)

다음 패턴 중 하나를 따르는 call 메서드를 구현하는 한 기능 API에서 모든 하위 클래스 계층 또는 모델을 사용할 수 있습니다.

  • call(self, inputs, **kwargs) - inputs 은 텐서 또는 텐서의 중첩 구조 (예 : 텐서 목록)이고 **kwargs 는 비 텐서 인수 (비 입력)입니다.
  • call(self, inputs, training=None, **kwargs) -여기서 training 은 계층이 훈련 모드와 추론 모드에서 동작해야하는지 여부를 나타내는 부울입니다.
  • call(self, inputs, mask=None, **kwargs) -여기서 mask 는 부울 마스크 텐서입니다 (예를 들어 RNN에 유용).
  • call(self, inputs, training=None, mask=None, **kwargs) -물론 마스킹과 훈련 관련 동작을 동시에 가질 수 있습니다.

또한 사용자 지정 레이어 또는 모델에 get_config 메서드를 구현하는 경우 생성 한 기능 모델은 여전히 ​​직렬화 및 복제 가능합니다.

다음은 기능 모델에서 사용되는 사용자 지정 RNN의 간단한 예입니다.

units = 32
timesteps = 10
input_dim = 5
batch_size = 16


class CustomRNN(layers.Layer):
    def __init__(self):
        super(CustomRNN, self).__init__()
        self.units = units
        self.projection_1 = layers.Dense(units=units, activation="tanh")
        self.projection_2 = layers.Dense(units=units, activation="tanh")
        self.classifier = layers.Dense(1)

    def call(self, inputs):
        outputs = []
        state = tf.zeros(shape=(inputs.shape[0], self.units))
        for t in range(inputs.shape[1]):
            x = inputs[:, t, :]
            h = self.projection_1(x)
            y = h + self.projection_2(state)
            state = y
            outputs.append(y)
        features = tf.stack(outputs, axis=1)
        return self.classifier(features)


# Note that you specify a static batch size for the inputs with the `batch_shape`
# arg, because the inner computation of `CustomRNN` requires a static batch size
# (when you create the `state` zeros tensor).
inputs = keras.Input(batch_shape=(batch_size, timesteps, input_dim))
x = layers.Conv1D(32, 3)(inputs)
outputs = CustomRNN()(x)

model = keras.Model(inputs, outputs)

rnn_model = CustomRNN()
_ = rnn_model(tf.zeros((1, 10, 5)))