Le modèle séquentiel

Voir sur TensorFlow.org Exécuter dans Google Colab Voir la source sur GitHub Télécharger le cahier

Installer

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

Quand utiliser un modèle séquentiel

Un Sequential modèle est approprié pour un empilement simple de couches où chaque couche a exactement un tenseur d'entrée et une tenseur de sortie.

Schématiquement, le suivant Sequential modèle:

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

équivaut à cette fonction :

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

Un modèle séquentiel ne convient pas de les cas suivants :

  • Votre modèle a plusieurs entrées ou plusieurs sorties
  • Chacune de vos couches a plusieurs entrées ou plusieurs sorties
  • Vous devez faire le partage de couche
  • Vous voulez une topologie non linéaire (par exemple une connexion résiduelle, un modèle multi-branches)

Création d'un modèle séquentiel

Vous pouvez créer un modèle Sequential en passant une liste de couches au constructeur Sequential :

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

Ses couches sont accessibles via les layers attribut:

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

Vous pouvez également créer un modèle séquentiel progressivement par l' add() méthode:

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

A noter qu'il existe aussi un correspondant pop() méthode pour éliminer les couches: un modèle séquentiel se comporte très bien comme une liste de couches.

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

A noter également que le constructeur séquentiel accepte un name argument comme une couche ou d'un modèle en Keras. Ceci est utile pour annoter les graphiques TensorBoard avec des noms sémantiquement significatifs.

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

Spécification de la forme d'entrée à l'avance

Généralement, toutes les couches de Keras ont besoin de connaître la forme de leurs entrées afin de pouvoir créer leurs poids. Ainsi, lorsque vous créez un calque comme celui-ci, au départ, il n'a pas de poids :

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

Il crée ses poids la première fois qu'il est appelé sur une entrée, puisque la forme des poids dépend de la forme des entrées :

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

Naturellement, cela s'applique également aux modèles séquentiels. Lorsque vous instanciez un modèle séquentiel sans une forme d'entrée, il est pas « construit »: il n'a pas de poids (et appelant model.weights résultats dans une erreur indiquant que cela). Les poids sont créés lorsque le modèle voit pour la première fois des données d'entrée :

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

Une fois qu'un modèle est « construit », vous pouvez appeler son summary() méthode pour afficher son contenu:

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
_________________________________________________________________

Cependant, il peut être très utile lors de la création incrémentielle d'un modèle séquentiel de pouvoir afficher le résumé du modèle jusqu'à présent, y compris la forme de sortie actuelle. Dans ce cas, vous devriez commencer votre modèle en passant une Input objet à votre modèle, de sorte qu'il connaît sa forme d'entrée depuis le début:

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
_________________________________________________________________

Notez que l' Input objet n'apparaît pas dans le cadre de model.layers , puisqu'il n'est pas une couche:

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

Une alternative simple est de passer juste un input_shape argument à votre première couche:

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
_________________________________________________________________

Les modèles construits avec une forme d'entrée prédéfinie comme celle-ci ont toujours des poids (même avant de voir des données) et ont toujours une forme de sortie définie.

En général, il est recommandé de toujours spécifier la forme d'entrée d'un modèle séquentiel à l'avance si vous savez de quoi il s'agit.

Un flux de travail de débogage commun: add() + summary()

Lors de la construction d' une nouvelle architecture séquentielle, il est utile d'empiler progressivement des couches avec add() et souvent imprimer des résumés de modèle. Par exemple, cela vous permet de surveiller comment une pile de Conv2D et MaxPooling2D couches est sous - échantillonnage des cartes de caractéristiques d'image:

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
_________________________________________________________________

Très pratique, non ?

Que faire une fois que vous avez un modèle

Une fois l'architecture de votre modèle prête, vous voudrez :

Extraction de caractéristiques avec un modèle séquentiel

Une fois qu'un modèle séquentiel a été construit, il se comporte comme un modèle API fonctionnelle . Cela signifie que chaque couche a une input et output attribut. Ces attributs peuvent être utilisés pour faire des choses intéressantes, comme créer rapidement un modèle qui extrait les sorties de toutes les couches intermédiaires dans un modèle séquentiel :

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)

Voici un exemple similaire qui extrait uniquement les entités d'une couche :

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)

Transférer l'apprentissage avec un modèle séquentiel

L'apprentissage par transfert consiste à figer les couches inférieures dans un modèle et à n'entraîner que les couches supérieures. Si vous n'êtes pas familier avec, assurez - vous de lire notre guide pour l' apprentissage de transfert .

Voici deux modèles courants d'apprentissage par transfert impliquant des modèles séquentiels.

Tout d'abord, supposons que vous ayez un modèle séquentiel et que vous souhaitiez geler tous les calques sauf le dernier. Dans ce cas, vous simplement itérer sur model.layers et ensemble layer.trainable = False sur chaque couche, à l' exception du dernier. Comme ça:

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(...)

Un autre modèle courant consiste à utiliser un modèle séquentiel pour empiler un modèle pré-entraîné et des couches de classification fraîchement initialisées. Comme ça:

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

Si vous transférez l'apprentissage, vous vous retrouverez probablement fréquemment à utiliser ces deux modèles.

C'est à peu près tout ce que vous devez savoir sur les modèles séquentiels !

Pour en savoir plus sur la création de modèles dans Keras, consultez :