Sequential 모델

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

설정

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

Sequential 모델을 사용하는 경우

Sequential 모델은 각 레이어에 정확히 하나의 입력 텐서와 하나의 출력 텐서가 있는 일반 레이어 스택에 적합합니다.

개략적으로 다음과 같은 Sequential 모델은

# Define Sequential model with 3 layers
model = keras.Sequential(
    [
        layers.Dense(2, activation="relu", name="layer1"),
        layers.Dense(3, activation="relu", name="layer2"),
        layers.Dense(4, name="layer3"),
    ]
)
# Call model on a test input
x = tf.ones((3, 3))
y = model(x)

다음 함수와 동일합니다.

# Create 3 layers
layer1 = layers.Dense(2, activation="relu", name="layer1")
layer2 = layers.Dense(3, activation="relu", name="layer2")
layer3 = layers.Dense(4, name="layer3")

# Call layers on a test input
x = tf.ones((3, 3))
y = layer3(layer2(layer1(x)))

Sequential 모델은 다음의 경우에 적합하지 않습니다.

  • 모델에 다중 입력 또는 다중 출력이 있습니다
  • 레이어에 다중 입력 또는 다중 출력이 있습니다
  • 레이어 공유를 해야 합니다
  • 비선형 토폴로지를 원합니다(예: 잔류 연결, 다중 분기 모델)

Sequential 모델 생성하기

레이어의 목록을 Sequential 생성자에 전달하여 Sequential 모델을 만들 수 있습니다.

model = keras.Sequential(
    [
        layers.Dense(2, activation="relu"),
        layers.Dense(3, activation="relu"),
        layers.Dense(4),
    ]
)

속한 레이어는 layers 속성을 통해 접근할 수 있습니다.

model.layers
[<tensorflow.python.keras.layers.core.Dense at 0x7fb6712fb0f0>,
 <tensorflow.python.keras.layers.core.Dense at 0x7fb65f1e0e80>,
 <tensorflow.python.keras.layers.core.Dense at 0x7fb65f1eaa90>]

add() 메서드를 통해 Sequential 모델을 점진적으로 작성할 수도 있습니다.

model = keras.Sequential()
model.add(layers.Dense(2, activation="relu"))
model.add(layers.Dense(3, activation="relu"))
model.add(layers.Dense(4))

레이어를 제거하는 pop() 메서드도 있습니다. Sequential 모델은 레이어의 리스트와 매우 유사하게 동작합니다.

model.pop()
print(len(model.layers))  # 2
2

또한 Sequential 생성자는 Keras의 모든 레이어 또는 모델과 마찬가지로 name 인수를 허용합니다. 이것은 의미론적으로 유의미한 이름으로 TensorBoard 그래프에 주석을 달 때 유용합니다.

model = keras.Sequential(name="my_sequential")
model.add(layers.Dense(2, activation="relu", name="layer1"))
model.add(layers.Dense(3, activation="relu", name="layer2"))
model.add(layers.Dense(4, name="layer3"))

미리 입력 형상 지정하기

일반적으로 Keras의 모든 레이어는 가중치를 만들려면 입력의 형상을 알아야 합니다. 따라서 다음과 같은 레이어를 만들면 처음에는 가중치가 없습니다.

layer = layers.Dense(3)
layer.weights  # Empty
[]

가중치는 모양이 입력의 형상에 따라 달라지기 때문에 입력에서 처음 호출될 때 가중치를 만듭니다.

# Call layer on a test input
x = tf.ones((1, 4))
y = layer(x)
layer.weights  # Now it has weights, of shape (4, 3) and (3,)
[<tf.Variable 'dense_6/kernel:0' shape=(4, 3) dtype=float32, numpy=
 array([[-0.6729593 , -0.8124825 ,  0.27425182],
        [-0.28105432, -0.747067  ,  0.58314645],
        [-0.48650968, -0.7678964 , -0.17685282],
        [ 0.10437417, -0.40062302,  0.610639  ]], dtype=float32)>,
 <tf.Variable 'dense_6/bias:0' shape=(3,) dtype=float32, numpy=array([0., 0., 0.], dtype=float32)>]

당연히 이것은 Sequential 모델에도 적용됩니다. 입력 형상이 없는 Sequential 모델을 인스턴스화할 때는 "빌드"되지 않습니다. 가중치가 없습니다(그리고 model.weights를 호출하면 오류가 발생함). 모델에 처음 입력 데이터가 표시되면 가중치가 생성됩니다.

model = keras.Sequential(
    [
        layers.Dense(2, activation="relu"),
        layers.Dense(3, activation="relu"),
        layers.Dense(4),
    ]
)  # No weights at this stage!

# At this point, you can't do this:
# model.weights

# You also can't do this:
# model.summary()

# Call the model on a test input
x = tf.ones((1, 4))
y = model(x)
print("Number of weights after calling the model:", len(model.weights))  # 6
Number of weights after calling the model: 6

모델이 "빌드"되면, 그 내용을 표시하기 위해 summary() 메서드를 호출할 수 있습니다.

model.summary()
Model: "sequential_3"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_7 (Dense)              (1, 2)                    10        
_________________________________________________________________
dense_8 (Dense)              (1, 3)                    9         
_________________________________________________________________
dense_9 (Dense)              (1, 4)                    16        
=================================================================
Total params: 35
Trainable params: 35
Non-trainable params: 0
_________________________________________________________________

그러나 현재 출력 형상을 포함하여 지금까지 모델의 요약을 표시할 수 있도록 Sequential 모델을 점진적으로 빌드할 때 매우 유용할 수 있습니다. 이 경우 Input 객체를 모델에 전달하여 모델의 시작 형상을 알 수 있도록 모델을 시작해야 합니다.

model = keras.Sequential()
model.add(keras.Input(shape=(4,)))
model.add(layers.Dense(2, activation="relu"))

model.summary()
Model: "sequential_4"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_10 (Dense)             (None, 2)                 10        
=================================================================
Total params: 10
Trainable params: 10
Non-trainable params: 0
_________________________________________________________________

Input 객체는 레이어가 아니므로 model.layers의 일부로 표시되지 않습니다.

model.layers
[<tensorflow.python.keras.layers.core.Dense at 0x7fb6711bab38>]

간단한 대안은 첫 번째 레이어에 input_shape 인수를 전달하는 것입니다.

model = keras.Sequential()
model.add(layers.Dense(2, activation="relu", input_shape=(4,)))

model.summary()
Model: "sequential_5"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_11 (Dense)             (None, 2)                 10        
=================================================================
Total params: 10
Trainable params: 10
Non-trainable params: 0
_________________________________________________________________

이처럼 사전 정의된 입력 모양으로 빌드된 모델은 항상 가중치를 가지며(데이터를 보기 전에도) 항상 정의된 출력 형상을 갖습니다.

일반적으로 Sequential 모델의 입력 형상을 알고 있는 경우 항상 Sequential 모델의 입력 형상을 지정하는 것이 좋습니다.

일반적인 디버깅 워크플로우: add() + summary()

새로운 Sequential 아키텍처를 구축할 때는 add() 하여 레이어를 점진적으로 쌓고 모델 요약을 자주 인쇄하는 것이 유용합니다. 예를 들어 Conv2DMaxPooling2D 레이어의 스택이 이미지 특성 맵을 다운 샘플링 하는 방법을 모니터링할 수 있습니다.

model = keras.Sequential()
model.add(keras.Input(shape=(250, 250, 3)))  # 250x250 RGB images
model.add(layers.Conv2D(32, 5, strides=2, activation="relu"))
model.add(layers.Conv2D(32, 3, activation="relu"))
model.add(layers.MaxPooling2D(3))

# Can you guess what the current output shape is at this point? Probably not.
# Let's just print it:
model.summary()

# The answer was: (40, 40, 32), so we can keep downsampling...

model.add(layers.Conv2D(32, 3, activation="relu"))
model.add(layers.Conv2D(32, 3, activation="relu"))
model.add(layers.MaxPooling2D(3))
model.add(layers.Conv2D(32, 3, activation="relu"))
model.add(layers.Conv2D(32, 3, activation="relu"))
model.add(layers.MaxPooling2D(2))

# And now?
model.summary()

# Now that we have 4x4 feature maps, time to apply global max pooling.
model.add(layers.GlobalMaxPooling2D())

# Finally, we add a classification layer.
model.add(layers.Dense(10))
Model: "sequential_6"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 123, 123, 32)      2432      
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 121, 121, 32)      9248      
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 40, 40, 32)        0         
=================================================================
Total params: 11,680
Trainable params: 11,680
Non-trainable params: 0
_________________________________________________________________
Model: "sequential_6"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 123, 123, 32)      2432      
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 121, 121, 32)      9248      
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 40, 40, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 38, 38, 32)        9248      
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 36, 36, 32)        9248      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 12, 12, 32)        0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 10, 10, 32)        9248      
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 8, 8, 32)          9248      
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 4, 4, 32)          0         
=================================================================
Total params: 48,672
Trainable params: 48,672
Non-trainable params: 0
_________________________________________________________________

매우 실용적이죠?

모델이 완성되면 해야 할 일

모델 아키텍처가 준비되면 다음을 수행할 수 있습니다.

Sequential 모델을 사용한 특성 추출

Sequential 모델이 빌드되면 Functional API 모델처럼 동작합니다. 이는 모든 레이어가 inputoutput 속성을 갖는다는 것을 의미합니다. 이러한 속성을 사용하면 Sequential 모델 내의 모든 중간 레이어들의 출력을 추출하는 모델을 빠르게 생성하는 등 깔끔한 작업을 수행할 수 있습니다.

initial_model = keras.Sequential(
    [
        keras.Input(shape=(250, 250, 3)),
        layers.Conv2D(32, 5, strides=2, activation="relu"),
        layers.Conv2D(32, 3, activation="relu"),
        layers.Conv2D(32, 3, activation="relu"),
    ]
)
feature_extractor = keras.Model(
    inputs=initial_model.inputs,
    outputs=[layer.output for layer in initial_model.layers],
)

# Call feature extractor on test input.
x = tf.ones((1, 250, 250, 3))
features = feature_extractor(x)

다음은 한 레이어에서 특성만 추출하는 것과 유사한 예입니다.

initial_model = keras.Sequential(
    [
        keras.Input(shape=(250, 250, 3)),
        layers.Conv2D(32, 5, strides=2, activation="relu"),
        layers.Conv2D(32, 3, activation="relu", name="my_intermediate_layer"),
        layers.Conv2D(32, 3, activation="relu"),
    ]
)
feature_extractor = keras.Model(
    inputs=initial_model.inputs,
    outputs=initial_model.get_layer(name="my_intermediate_layer").output,
)
# Call feature extractor on test input.
x = tf.ones((1, 250, 250, 3))
features = feature_extractor(x)

Sequential 모델을 통한 전이 학습

전이 학습은 모델에서 맨 아래 레이어를 동결하고 맨 위 레이어만 훈련하는 것으로 구성됩니다. 익숙하지 않은 경우, 전이 학습 가이드를 읽어보세요.

다음은 Sequential 모델과 관련된 두 가지 일반적인 전이 학습 청사진입니다.

먼저 Sequential 모델이 있고 마지막 모델을 제외한 모든 레이어를 동결하려고 한다고 가정합니다. 이 경우 다음과 같이 단순히 model.layers를 반복하고 마지막 레이어를 제외하고 각 레이어에서 layer.trainable = False를 설정합니다.

model = keras.Sequential([<br>    keras.Input(shape=(784))<br>    layers.Dense(32, activation='relu'),<br>    layers.Dense(32, activation='relu'),<br>    layers.Dense(32, activation='relu'),<br>    layers.Dense(10),<br>])<br> <br># Presumably you would want to first load pre-trained weights.<br>model.load_weights(...)<br> <br># Freeze all layers except the last one.<br>for layer in model.layers[:-1]:<br>  layer.trainable = False<br> <br># Recompile and train (this will only update the weights of the last layer).<br>model.compile(...)<br>model.fit(...)

또 다른 일반적인 청사진은 다음과 같이 Sequential 모델을 사용하여 사전 훈련된 모델과 새로 초기화된 분류 레이어를 쌓는 것입니다.

# Load a convolutional base with pre-trained weights<br>base_model = keras.applications.Xception(<br>    weights='imagenet',<br>    include_top=False,<br>    pooling='avg')<br> <br># Freeze the base model<br>base_model.trainable = False<br> <br># Use a Sequential model to add a trainable classifier on top<br>model = keras.Sequential([<br>    base_model,<br>    layers.Dense(1000),<br>])<br> <br># Compile & train<br>model.compile(...)<br>model.fit(...)

전이 학습을 한다면 아마도 이 두 가지 패턴을 자주 사용하게 될 것입니다.

이것이 Sequential 모델에 대해 알아야 할 전부입니다!

Keras에서 모델을 빌드하는 방법에 대한 자세한 내용은 다음을 참조하세요.