Model sekwencyjny

Zobacz na TensorFlow.org Uruchom w Google Colab Wyświetl źródło na GitHub Pobierz notatnik

Ustawiać

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

Kiedy używać modelu sekwencyjnego?

Sequential model jest odpowiedni dla zwykłego stosu warstw, przy czym każda warstwa ma dokładnie jeden tensor tensor wejście i jedno wyjście.

Schematycznie, co następuje Sequential model:

# 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)

jest odpowiednikiem tej funkcji:

# 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)))

Model sekwencyjny nie jest odpowiednia, gdy:

  • Twój model ma wiele wejść lub wiele wyjść
  • Każda z twoich warstw ma wiele wejść lub wiele wyjść
  • Musisz udostępnić warstwy
  • Potrzebujesz nieliniowej topologii (np. połączenie resztkowe, model wielorozgałęziony)

Tworzenie modelu sekwencyjnego

Możesz utworzyć model Sequential, przekazując listę warstw do konstruktora Sequential:

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

Jej warstwy są dostępne za pośrednictwem layers atrybutu:

model.layers
[<keras.layers.core.Dense at 0x7fdc784478d0>,
 <keras.layers.core.Dense at 0x7fdbbc3c4650>,
 <keras.layers.core.Dense at 0x7fdbbc3c4a10>]

Można również tworzyć sekwencyjnego modelu przyrostowego poprzez add() metody:

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

Należy pamiętać, że istnieje również odpowiadające pop() metoda usuwania warstw: sekwencyjnego modelu zachowuje się bardzo podobnie do listy warstw.

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

Należy również pamiętać, że sekwencyjny konstruktor przyjmuje name argumentu, podobnie jak każdej warstwy lub model w Keras. Jest to przydatne do opisywania wykresów TensorBoard za pomocą semantycznie znaczących nazw.

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"))

Wcześniejsze określenie kształtu wejściowego

Ogólnie rzecz biorąc, wszystkie warstwy w Keras muszą znać kształt swoich danych wejściowych, aby móc tworzyć swoje wagi. Więc kiedy tworzysz taką warstwę, początkowo nie ma ona żadnych wag:

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

Tworzy swoje wagi przy pierwszym wywołaniu na wejściu, ponieważ kształt wag zależy od kształtu danych wejściowych:

# 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.5319189 , -0.8767905 , -0.63919735],
        [-0.6276014 ,  0.1689707 , -0.57695866],
        [ 0.6710613 ,  0.5354214 , -0.00893992],
        [ 0.15670097, -0.15280598,  0.8865864 ]], dtype=float32)>,
 <tf.Variable 'dense_6/bias:0' shape=(3,) dtype=float32, numpy=array([0., 0., 0.], dtype=float32)>]

Oczywiście dotyczy to również modeli sekwencyjnych. Podczas wystąpienia sekwencyjnego modelu bez kształtu wejściowego, to nie jest „zbudowany”: to nie ma masę (i wzywaj model.weights skutkuje błędem podając tylko tego). Wagi są tworzone, gdy model po raz pierwszy widzi dane wejściowe:

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

Gdy model jest „zbudowany”, można nazwać jej summary() metodę, aby wyświetlić jego zawartość:

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
_________________________________________________________________

Jednak podczas budowania modelu sekwencyjnego może być bardzo przydatne, aby móc wyświetlić dotychczasowe podsumowanie modelu, w tym bieżący kształt wyjściowy. W tym przypadku, należy rozpocząć swój model przechodząc Input obiektu do danego modelu, tak, że zna jego kształt wejściowy od początku:

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
_________________________________________________________________

Należy pamiętać, że Input obiektu nie jest wyświetlany jako część model.layers , ponieważ nie jest to warstwa:

model.layers
[<keras.layers.core.Dense at 0x7fdbbc37c390>]

Prostym rozwiązaniem jest po prostu zdać input_shape argument pierwszej warstwie:

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
_________________________________________________________________

Modele zbudowane z predefiniowanym kształtem wejściowym, takim jak ten, zawsze mają wagi (nawet przed wyświetleniem jakichkolwiek danych) i zawsze mają zdefiniowany kształt wyjściowy.

Ogólnie zaleca się, aby zawsze wcześniej określać kształt wejściowy modelu sekwencyjnego, jeśli wiesz, co to jest.

Częstym debugowanie workflow: add() + summary()

Przy budowie nowego Sequential architekturę, jest to przydatne, aby stopniowo układać warstwy z add() i często drukować modelowych podsumowania. Na przykład ten pozwala monitorować stos Conv2D i MaxPooling2D warstw mapy funkcji próbkowania obrazu:

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
_________________________________________________________________

Bardzo praktyczne, prawda?

Co robić, gdy masz modelkę

Gdy architektura Twojego modelu będzie gotowa, będziesz chciał:

Ekstrakcja cech za pomocą modelu sekwencyjnego

Gdy sekwencyjny model został zbudowany, to zachowuje się jak funkcjonalny model API . Oznacza to, że każda warstwa ma input i output atrybut. Te atrybuty mogą być używane do robienia fajnych rzeczy, takich jak szybkie tworzenie modelu, który wyodrębnia dane wyjściowe wszystkich warstw pośrednich w modelu sekwencyjnym:

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)

Oto podobny przykład, który wyodrębnia funkcje tylko z jednej warstwy:

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)

Transfer uczenia się z modelem sekwencyjnym

Uczenie transferu polega na zamrażaniu dolnych warstw w modelu i trenowaniu tylko górnych warstw. Jeśli nie są zaznajomieni z nim, upewnij się, aby przeczytać nasz przewodnik uczenia transferowego .

Oto dwa popularne schematy uczenia się transferów obejmujące modele sekwencyjne.

Najpierw załóżmy, że masz model sekwencyjny i chcesz zamrozić wszystkie warstwy z wyjątkiem ostatniej. W tym przypadku, po prostu iteracyjne nad model.layers i zestaw layer.trainable = False na każdej warstwie, z wyjątkiem ostatniego. Lubię to:

model = keras.Sequential([
    keras.Input(shape=(784)),
    layers.Dense(32, activation='relu'),
    layers.Dense(32, activation='relu'),
    layers.Dense(32, activation='relu'),
    layers.Dense(10),
])

# Presumably you would want to first load pre-trained weights.
model.load_weights(...)

# Freeze all layers except the last one.
for layer in model.layers[:-1]:
  layer.trainable = False

# Recompile and train (this will only update the weights of the last layer).
model.compile(...)
model.fit(...)

Innym powszechnym planem jest użycie modelu sekwencyjnego do ułożenia wstępnie wytrenowanego modelu i niektórych świeżo zainicjowanych warstw klasyfikacji. Lubię to:

# Load a convolutional base with pre-trained weights
base_model = keras.applications.Xception(
    weights='imagenet',
    include_top=False,
    pooling='avg')

# Freeze the base model
base_model.trainable = False

# Use a Sequential model to add a trainable classifier on top
model = keras.Sequential([
    base_model,
    layers.Dense(1000),
])

# Compile & train
model.compile(...)
model.fit(...)

Jeśli uczysz się transferów, prawdopodobnie będziesz często używał tych dwóch wzorców.

To wszystko, co musisz wiedzieć o modelach sekwencyjnych!

Aby dowiedzieć się więcej o budowaniu modeli w Keras, zobacz: