Vue d'ensemble du contenu
- établissement
- Introduction
- Formation, évaluation et inference
- Enregistrer et sérialiser
- Utilisez le même graphique de couches pour définir plusieurs modèles
- Tous les modèles un callable, tout comme les couches
- Manipuler des topologies graphiques complexes
- Modèles avec plusieurs entrées et sorties
- Un modèle de jouet ResNet
établissement
import numpy as np
import tensorflow as tf
from tensorflow import keras
from keras import layers
Introduction
Les KerasAPI fonctionnelest une façon de créer des modèles qui sont plus flexibles que leskeras.Sequential
L'API fonctionnelle peut gérer des modèles avec une topologie non linéaire, des couches partagées et même plusieurs entrées ou sorties.
L'idée principale est qu'un modèle d'apprentissage profond est généralement un graphique acyclique dirigé (DAG) de couches.Les graphiques des layers.
Considérez le modèle suivant :
[Dense (64 unités, activation relu)] [Dense (64 unités, activation relu)] [Dense (10 unités, activation softmax)] (sortie: logits d'une répartition de probabilité sur 10 classes)
Ceci est un graphique de base avec trois couches. Pour construire ce modèle en utilisant l'API fonctionnelle, commencez par créer un nœud d'entrée:
inputs = keras.Input(shape=(784,))
La forme des données est définie comme un vecteur de 784 dimensions. La taille du lot est toujours omise car seule la forme de chaque échantillon est spécifiée.
Si, par exemple, vous avez une entrée d'image avec une forme(32, 32, 3)
Vous allez utiliser :
# Just for demonstration purposes.
img_inputs = keras.Input(shape=(32, 32, 3))
Leinputs
qui est retourné contient des informations sur la forme etdtype
des données d'entrée que vous fournissez à votre modèle. voici la forme:
inputs.shape
TensorShape([None, 784])
Voici le dtype :
inputs.dtype
tf.float32
Vous créez un nouveau nœud dans le graphique des couches en appelant une couche sur ceinputs
Objet :
dense = layers.Dense(64, activation="relu")
x = dense(inputs)
L'action "call layer" est comme tirer une flèche de "inputs" à cette couche que vous avez créée.dense
Laissez, et vous obtiendrezx
comme la production.
Ajoutez quelques couches supplémentaires au graphique des couches :
x = layers.Dense(64, activation="relu")(x)
outputs = layers.Dense(10)(x)
À ce stade, vous pouvez créer uneModel
en spécifiant ses entrées et ses sorties dans le graphique des couches :
model = keras.Model(inputs=inputs, outputs=outputs, name="mnist_model")
Voyons voir à quoi ressemble le résumé du modèle :
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: 55050 (215.04 KB)
Trainable params: 55050 (215.04 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
Vous pouvez également utiliser le modèle comme graphique :
keras.utils.plot_model(model, "my_first_model.png")
Et, en option, afficher les formes d'entrée et de sortie de chaque couche dans le graphique tracé:
keras.utils.plot_model(model, "my_first_model_with_shape_info.png", show_shapes=True)
Cette figure et le code sont presque identiques. Dans la version du code, les flèches de connexion sont remplacées par l'opération d'appel.
Un « graphique de couches » est une image mentale intuitive pour un modèle d'apprentissage profond, et l'API fonctionnelle est un moyen de créer des modèles qui reflètent de près cela.
Formation, évaluation et inference
La formation, l'évaluation et l'inférence fonctionnent exactement de la même manière pour les modèles construits à l'aide de l'API fonctionnelle que pour les modèlesSequential
des modèles.
LeModel
La classe propose un circuit d’entraînement intégré (lefit()
méthode) et un circuit d’évaluation intégré (leevaluate()
A noter que vous pouvez facilementTélécharger ces loopsmettre en œuvre des routines de formation au-delà de l’apprentissage supervisé (par ex.Gansé) de
Ici, chargez les données d'image MNIST, réformez-les en vecteurs, ajoutez le modèle sur les données (en surveillant la performance sur une division de validation), puis évaluez le modèle sur les données de test:
(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=[keras.metrics.SparseCategoricalAccuracy()],
)
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 [==============================] - 4s 3ms/step - loss: 0.3556 - sparse_categorical_accuracy: 0.8971 - val_loss: 0.1962 - val_sparse_categorical_accuracy: 0.9422
Epoch 2/2
750/750 [==============================] - 2s 2ms/step - loss: 0.1612 - sparse_categorical_accuracy: 0.9527 - val_loss: 0.1461 - val_sparse_categorical_accuracy: 0.9592
313/313 - 0s - loss: 0.1492 - sparse_categorical_accuracy: 0.9556 - 463ms/epoch - 1ms/step
Test loss: 0.14915992319583893
Test accuracy: 0.9556000232696533
Pour une lecture plus approfondie, voirFormation et évaluationLe Guide.
Enregistrer et sérialiser
Enregistrer le modèle et la sérialisation fonctionnent de la même manière pour les modèles construits à l'aide de l'API fonctionnelle qu'ils le font pourSequential
modèles.Le moyen standard de sauvegarder un modèle fonctionnel consiste à appelermodel.save()
to save the entire model as a single file. You can later recreate the same model from this file, even if the code that built the model is no longer available.
Ce fichier sauvegardé comprend :
- Modèle d’architecture
- valeurs de poids de modèle (apprises au cours de l'entraînement)
- modèles de formation config, le cas échéant (comme passé à compiler)
- optimisateur et son état, le cas échéant (pour redémarrer la formation où vous avez cessé)
model.save("path_to_my_model.keras")
del model
# Recreate the exact same model purely from the file:
model = keras.models.load_model("path_to_my_model.keras")
Pour plus de détails, lisez le modèleSérialisation et épargneLe Guide.
Utilisez le même graphique de couches pour définir plusieurs modèles
Dans l'API fonctionnelle, les modèles sont créés en spécifiant leurs entrées et leurs sorties dans un graphique de couches. Cela signifie qu'un seul graphique de couches peut être utilisé pour générer plusieurs modèles.
Dans l'exemple ci-dessous, vous utilisez la même pile de couches pour instantier deux modèles: unencoder
modèle qui transforme les entrées d'image en vecteurs 16 dimensions, et un end-to-endautoencoder
Modèle de formation.
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 (MaxPooling2 (None, 8, 8, 32) 0
D)
conv2d_2 (Conv2D) (None, 6, 6, 32) 9248
conv2d_3 (Conv2D) (None, 4, 4, 16) 4624
global_max_pooling2d (Glob (None, 16) 0
alMaxPooling2D)
=================================================================
Total params: 18672 (72.94 KB)
Trainable params: 18672 (72.94 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
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 (MaxPooling2 (None, 8, 8, 32) 0
D)
conv2d_2 (Conv2D) (None, 6, 6, 32) 9248
conv2d_3 (Conv2D) (None, 4, 4, 16) 4624
global_max_pooling2d (Glob (None, 16) 0
alMaxPooling2D)
reshape (Reshape) (None, 4, 4, 1) 0
conv2d_transpose (Conv2DTr (None, 6, 6, 16) 160
anspose)
conv2d_transpose_1 (Conv2D (None, 8, 8, 32) 4640
Transpose)
up_sampling2d (UpSampling2 (None, 24, 24, 32) 0
D)
conv2d_transpose_2 (Conv2D (None, 26, 26, 16) 4624
Transpose)
conv2d_transpose_3 (Conv2D (None, 28, 28, 1) 145
Transpose)
=================================================================
Total params: 28241 (110.32 KB)
Trainable params: 28241 (110.32 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
Ici, l'architecture de décodage est strictement symétrique à l'architecture de codage, de sorte que la forme de sortie est la même que la forme d'entrée(28, 28, 1)
.
L’inverse d’uneConv2D
Layer est unConv2DTranspose
L’inverse, et l’inverse d’unMaxPooling2D
Layer est unUpSampling2D
à Layer.
Tous les modèles sont appelables, tout comme les couches
Vous pouvez traiter n'importe quel modèle comme s'il s'agissait d'une couche en l'invoquant sur unInput
En appelant un modèle, vous ne réutilisez pas seulement l'architecture du modèle, vous réutilisez également ses poids.
Pour voir cela en action, voici un autre exemple de l'auto-encodeur qui crée un modèle d'encodeur, un modèle de décodeur, et les chaîne en deux appels pour obtenir le modèle d'auto-encodeur:
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 (MaxPoolin (None, 8, 8, 32) 0
g2D)
conv2d_6 (Conv2D) (None, 6, 6, 32) 9248
conv2d_7 (Conv2D) (None, 4, 4, 16) 4624
global_max_pooling2d_1 (Gl (None, 16) 0
obalMaxPooling2D)
=================================================================
Total params: 18672 (72.94 KB)
Trainable params: 18672 (72.94 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
Model: "decoder"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
encoded_img (InputLayer) [(None, 16)] 0
reshape_1 (Reshape) (None, 4, 4, 1) 0
conv2d_transpose_4 (Conv2D (None, 6, 6, 16) 160
Transpose)
conv2d_transpose_5 (Conv2D (None, 8, 8, 32) 4640
Transpose)
up_sampling2d_1 (UpSamplin (None, 24, 24, 32) 0
g2D)
conv2d_transpose_6 (Conv2D (None, 26, 26, 16) 4624
Transpose)
conv2d_transpose_7 (Conv2D (None, 28, 28, 1) 145
Transpose)
=================================================================
Total params: 9569 (37.38 KB)
Trainable params: 9569 (37.38 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
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: 28241 (110.32 KB)
Trainable params: 28241 (110.32 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
Comme vous pouvez le voir, le modèle peut être niché : un modèle peut contenir des sous-modèles (car un modèle est comme une couche).EnsemblePar exemple, voici comment regrouper un ensemble de modèles en un seul modèle qui moyenne leurs prévisions:
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)
Manipuler des topologies graphiques complexes
Modèles avec plusieurs entrées et sorties
L'API fonctionnelle facilite la manipulation de plusieurs entrées et sorties.Sequential
Le feu.
For example, if you're building a system for ranking customer issue tickets by priority and routing them to the correct department, then the model will have three inputs:
- le titre du billet (entrée de texte),
- le corps texte du billet (entrée de texte), et
- tous les tags ajoutés par l'utilisateur (entrée catégorique)
Ce modèle aura deux sorties :
- le score de priorité compris entre 0 et 1 (sortie sigmoïde scalaire), et
- le département qui doit gérer le billet (softmax de sortie sur l'ensemble des départements).
Vous pouvez construire ce modèle en quelques lignes avec l’API fonctionnelle :
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],
)
Définissez le modèle :
keras.utils.plot_model(model, "multi_input_and_output_model.png", show_shapes=True)
Lors de la compilation de ce modèle, vous pouvez attribuer des pertes différentes à chaque sortie. Vous pouvez même attribuer des poids différents à chaque perte - pour moduler leur contribution à la perte totale d'entraînement.
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],
)
Étant donné que les couches de sortie ont des noms différents, vous pouvez également spécifier les pertes et les poids de perte avec les noms de couches correspondants:
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={"priority": 1.0, "department": 0.2},
)
Formez le modèle en passant des listes de matrices NumPy d'entrées et de cibles:
# 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 [==============================] - 8s 112ms/step - loss: 1.2982 - priority_loss: 0.6991 - department_loss: 2.9958
Epoch 2/2
40/40 [==============================] - 3s 64ms/step - loss: 1.3110 - priority_loss: 0.6977 - department_loss: 3.0666
<keras.src.callbacks.History at 0x7f08d51fab80>
Lorsque l’on appelle à uneDataset
objet, il devrait produire soit un tuple de listes comme([title_data, body_data, tags_data], [priority_targets, dept_targets])
ou un grand nombre de dictionnaires comme({'title': title_data, 'body': body_data, 'tags': tags_data}, {'priority': priority_targets, 'department': dept_targets})
.
Pour une explication plus détaillée, consultez leFormation et évaluationLe Guide.
Un modèle de jouet ResNet
En plus des modèles avec plusieurs entrées et sorties, l'API fonctionnelle facilite la manipulation des topologies de connectivité non linéaire - ce sont des modèles avec des couches qui ne sont pas reliées séquentiellement, ce quiSequential
Le feu ne peut pas gérer.
Un cas d'utilisation courant pour cela est les connexions résiduelles. Construisons un modèle de jeu ResNet pour CIFAR10 pour le démontrer:
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 (MaxPoolin (None, 9, 9, 64) 0 ['conv2d_9[0][0]']
g2D)
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 ( (None, 64) 0 ['conv2d_14[0][0]']
GlobalAveragePooling2D)
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: 223242 (872.04 KB)
Trainable params: 223242 (872.04 KB)
Non-trainable params: 0 (0.00 Byte)
__________________________________________________________________________________________________
Découvrez le modèle :
keras.utils.plot_model(model, "mini_resnet.png", show_shapes=True)
Maintenant, prenez le modèle :
(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)
13/13 [==============================] - 4s 39ms/step - loss: 2.3086 - acc: 0.0988 - val_loss: 2.3020 - val_acc: 0.0850
<keras.src.callbacks.History at 0x7f078810c880>
Publié à l'origine sur le site Web de TensorFlow, cet article apparaît ici sous un nouveau titre et est sous licence CC BY 4.0.
Publié à l'origine sur le site Web de TensorFlow, cet article apparaît ici sous un nouveau titre et est sous licence CC BY 4.0.
TensorFlow