![]() | ![]() | ![]() | ![]() |
Installer
import tensorflow as tf
from tensorflow import keras
La classe Layer
: la combinaison de l'état (poids) et du calcul
L'une des abstractions centrales de Keras est la classe Layer
. Une couche encapsule à la fois un état (les «poids» de la couche) et une transformation des entrées en sorties (un «appel», la passe avant de la couche).
Voici une couche densément connectée. Il a un état: les variables w
et b
.
class Linear(keras.layers.Layer):
def __init__(self, units=32, input_dim=32):
super(Linear, self).__init__()
w_init = tf.random_normal_initializer()
self.w = tf.Variable(
initial_value=w_init(shape=(input_dim, units), dtype="float32"),
trainable=True,
)
b_init = tf.zeros_initializer()
self.b = tf.Variable(
initial_value=b_init(shape=(units,), dtype="float32"), trainable=True
)
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
Vous utiliseriez une couche en l'appelant sur certaines entrées tensorielles, un peu comme une fonction Python.
x = tf.ones((2, 2))
linear_layer = Linear(4, 2)
y = linear_layer(x)
print(y)
tf.Tensor( [[ 8.9605771e-02 -5.8132906e-02 -2.3558782e-02 -5.9220940e-05] [ 8.9605771e-02 -5.8132906e-02 -2.3558782e-02 -5.9220940e-05]], shape=(2, 4), dtype=float32)
Notez que les poids w
et b
sont automatiquement suivis par le calque lorsqu'ils sont définis comme attributs de calque:
assert linear_layer.weights == [linear_layer.w, linear_layer.b]
Notez que vous avez également accès à un raccourci plus rapide pour ajouter du poids à un calque: la méthode add_weight()
:
class Linear(keras.layers.Layer):
def __init__(self, units=32, input_dim=32):
super(Linear, self).__init__()
self.w = self.add_weight(
shape=(input_dim, units), initializer="random_normal", trainable=True
)
self.b = self.add_weight(shape=(units,), initializer="zeros", trainable=True)
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
x = tf.ones((2, 2))
linear_layer = Linear(4, 2)
y = linear_layer(x)
print(y)
tf.Tensor( [[-0.00164011 -0.02662886 0.00616307 0.0370644 ] [-0.00164011 -0.02662886 0.00616307 0.0370644 ]], shape=(2, 4), dtype=float32)
Les couches peuvent avoir des poids non entraînables
Outre les poids entraînables, vous pouvez également ajouter des poids non entraînables à une couche. Ces poids sont censés ne pas être pris en compte lors de la rétropropagation, lorsque vous entraînez la couche.
Voici comment ajouter et utiliser un poids non entraînable:
class ComputeSum(keras.layers.Layer):
def __init__(self, input_dim):
super(ComputeSum, self).__init__()
self.total = tf.Variable(initial_value=tf.zeros((input_dim,)), trainable=False)
def call(self, inputs):
self.total.assign_add(tf.reduce_sum(inputs, axis=0))
return self.total
x = tf.ones((2, 2))
my_sum = ComputeSum(2)
y = my_sum(x)
print(y.numpy())
y = my_sum(x)
print(y.numpy())
[2. 2.] [4. 4.]
Cela fait partie de layer.weights
, mais il est classé comme un poids non entraînable:
print("weights:", len(my_sum.weights))
print("non-trainable weights:", len(my_sum.non_trainable_weights))
# It's not included in the trainable weights:
print("trainable_weights:", my_sum.trainable_weights)
weights: 1 non-trainable weights: 1 trainable_weights: []
Bonne pratique: différer la création de poids jusqu'à ce que la forme des entrées soit connue
Notre couche Linear
ci-dessus a pris un argument input_dim
qui a été utilisé pour calculer la forme des poids w
et b
dans __init__()
:
class Linear(keras.layers.Layer):
def __init__(self, units=32, input_dim=32):
super(Linear, self).__init__()
self.w = self.add_weight(
shape=(input_dim, units), initializer="random_normal", trainable=True
)
self.b = self.add_weight(shape=(units,), initializer="zeros", trainable=True)
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
Dans de nombreux cas, vous ne connaissez peut-être pas à l'avance la taille de vos entrées et vous souhaitez créer paresseusement des pondérations lorsque cette valeur devient connue, quelque temps après l'instanciation du calque.
Dans l'API Keras, nous vous recommandons de créer des pondérations de couche dans la méthode build(self, inputs_shape)
de votre couche. Comme ça:
class Linear(keras.layers.Layer):
def __init__(self, units=32):
super(Linear, 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
La __call__()
de votre couche exécutera automatiquement build la première fois qu'elle sera appelée. Vous avez maintenant une couche paresseuse et donc plus facile à utiliser:
# At instantiation, we don't know on what inputs this is going to get called
linear_layer = Linear(32)
# The layer's weights are created dynamically the first time the layer is called
y = linear_layer(x)
Les couches sont composables de manière récursive
Si vous affectez une occurrence de calque en tant qu'attribut d'un autre calque, le calque externe commencera à suivre les poids du calque interne.
Nous vous recommandons de créer de telles sous-couches dans la __init__()
(puisque les sous-couches auront généralement une méthode de construction, elles seront construites lorsque la couche externe sera construite).
# Let's assume we are reusing the Linear class
# with a `build` method that we defined above.
class MLPBlock(keras.layers.Layer):
def __init__(self):
super(MLPBlock, self).__init__()
self.linear_1 = Linear(32)
self.linear_2 = Linear(32)
self.linear_3 = Linear(1)
def call(self, inputs):
x = self.linear_1(inputs)
x = tf.nn.relu(x)
x = self.linear_2(x)
x = tf.nn.relu(x)
return self.linear_3(x)
mlp = MLPBlock()
y = mlp(tf.ones(shape=(3, 64))) # The first call to the `mlp` will create the weights
print("weights:", len(mlp.weights))
print("trainable weights:", len(mlp.trainable_weights))
weights: 6 trainable weights: 6
La méthode add_loss()
Lors de l'écriture de la méthode call()
d'une couche, vous pouvez créer des tenseurs de perte que vous voudrez utiliser plus tard, lors de l'écriture de votre boucle d'entraînement. Ceci est faisable en appelant self.add_loss(value)
:
# A layer that creates an activity regularization loss
class ActivityRegularizationLayer(keras.layers.Layer):
def __init__(self, rate=1e-2):
super(ActivityRegularizationLayer, self).__init__()
self.rate = rate
def call(self, inputs):
self.add_loss(self.rate * tf.reduce_sum(inputs))
return inputs
Ces pertes (y compris celles créées par n'importe quelle couche interne) peuvent être récupérées via layer.losses
. Cette propriété est réinitialisée au début de chaque __call__()
vers la couche de niveau supérieur, de sorte que layer.losses
contienne toujours les valeurs de perte créées lors de la dernière passe avant.
class OuterLayer(keras.layers.Layer):
def __init__(self):
super(OuterLayer, self).__init__()
self.activity_reg = ActivityRegularizationLayer(1e-2)
def call(self, inputs):
return self.activity_reg(inputs)
layer = OuterLayer()
assert len(layer.losses) == 0 # No losses yet since the layer has never been called
_ = layer(tf.zeros(1, 1))
assert len(layer.losses) == 1 # We created one loss value
# `layer.losses` gets reset at the start of each __call__
_ = layer(tf.zeros(1, 1))
assert len(layer.losses) == 1 # This is the loss created during the call above
En outre, la propriété de loss
contient également des pertes de régularisation créées pour les poids de toute couche interne:
class OuterLayerWithKernelRegularizer(keras.layers.Layer):
def __init__(self):
super(OuterLayerWithKernelRegularizer, self).__init__()
self.dense = keras.layers.Dense(
32, kernel_regularizer=tf.keras.regularizers.l2(1e-3)
)
def call(self, inputs):
return self.dense(inputs)
layer = OuterLayerWithKernelRegularizer()
_ = layer(tf.zeros((1, 1)))
# This is `1e-3 * sum(layer.dense.kernel ** 2)`,
# created by the `kernel_regularizer` above.
print(layer.losses)
[<tf.Tensor: shape=(), dtype=float32, numpy=0.001774033>]
Ces pertes sont destinées à être prises en compte lors de l'écriture de boucles d'entraînement, comme ceci:
# Instantiate an optimizer.
optimizer = tf.keras.optimizers.SGD(learning_rate=1e-3)
loss_fn = keras.losses.SparseCategoricalCrossentropy(from_logits=True)
# Iterate over the batches of a dataset.
for x_batch_train, y_batch_train in train_dataset:
with tf.GradientTape() as tape:
logits = layer(x_batch_train) # Logits for this minibatch
# Loss value for this minibatch
loss_value = loss_fn(y_batch_train, logits)
# Add extra losses created during this forward pass:
loss_value += sum(model.losses)
grads = tape.gradient(loss_value, model.trainable_weights)
optimizer.apply_gradients(zip(grads, model.trainable_weights))
Pour un guide détaillé sur l'écriture de boucles d'entraînement, consultez le guide d'écriture d'une boucle d'entraînement à partir de zéro .
Ces pertes fonctionnent également de manière transparente avec fit()
(elles sont automatiquement additionnées et ajoutées à la perte principale, le cas échéant):
import numpy as np
inputs = keras.Input(shape=(3,))
outputs = ActivityRegularizationLayer()(inputs)
model = keras.Model(inputs, outputs)
# If there is a loss passed in `compile`, the regularization
# losses get added to it
model.compile(optimizer="adam", loss="mse")
model.fit(np.random.random((2, 3)), np.random.random((2, 3)))
# It's also possible not to pass any loss in `compile`,
# since the model already has a loss to minimize, via the `add_loss`
# call during the forward pass!
model.compile(optimizer="adam")
model.fit(np.random.random((2, 3)), np.random.random((2, 3)))
1/1 [==============================] - 0s 111ms/step - loss: 0.3057 1/1 [==============================] - 0s 44ms/step - loss: 0.0246 <tensorflow.python.keras.callbacks.History at 0x7f557862c4e0>
La méthode add_metric()
Comme pour add_loss()
, les couches ont également une méthode add_metric()
pour suivre la moyenne mobile d'une quantité pendant l'entraînement.
Considérez la couche suivante: une couche de «point final logistique». Il prend comme entrées des prédictions et des cibles, il calcule une perte qu'il suit via add_loss()
, et il calcule un scalaire de précision, qu'il suit via add_metric()
.
class LogisticEndpoint(keras.layers.Layer):
def __init__(self, name=None):
super(LogisticEndpoint, self).__init__(name=name)
self.loss_fn = keras.losses.BinaryCrossentropy(from_logits=True)
self.accuracy_fn = keras.metrics.BinaryAccuracy()
def call(self, targets, logits, sample_weights=None):
# Compute the training-time loss value and add it
# to the layer using `self.add_loss()`.
loss = self.loss_fn(targets, logits, sample_weights)
self.add_loss(loss)
# Log accuracy as a metric and add it
# to the layer using `self.add_metric()`.
acc = self.accuracy_fn(targets, logits, sample_weights)
self.add_metric(acc, name="accuracy")
# Return the inference-time prediction tensor (for `.predict()`).
return tf.nn.softmax(logits)
Les métriques suivies de cette manière sont accessibles via layer.metrics
:
layer = LogisticEndpoint()
targets = tf.ones((2, 2))
logits = tf.ones((2, 2))
y = layer(targets, logits)
print("layer.metrics:", layer.metrics)
print("current accuracy value:", float(layer.metrics[0].result()))
layer.metrics: [<tensorflow.python.keras.metrics.BinaryAccuracy object at 0x7f5578831518>] current accuracy value: 1.0
Tout comme pour add_loss()
, ces métriques sont suivies par fit()
:
inputs = keras.Input(shape=(3,), name="inputs")
targets = keras.Input(shape=(10,), name="targets")
logits = keras.layers.Dense(10)(inputs)
predictions = LogisticEndpoint(name="predictions")(logits, targets)
model = keras.Model(inputs=[inputs, targets], outputs=predictions)
model.compile(optimizer="adam")
data = {
"inputs": np.random.random((3, 3)),
"targets": np.random.random((3, 10)),
}
model.fit(data)
1/1 [==============================] - 0s 248ms/step - loss: 0.8016 - binary_accuracy: 0.0000e+00 <tensorflow.python.keras.callbacks.History at 0x7f557860d470>
Vous pouvez éventuellement activer la sérialisation sur vos couches
Si vous avez besoin que vos couches personnalisées soient sérialisables dans le cadre d'un modèle fonctionnel , vous pouvez éventuellement implémenter une méthode get_config()
:
class Linear(keras.layers.Layer):
def __init__(self, units=32):
super(Linear, 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}
# Now you can recreate the layer from its config:
layer = Linear(64)
config = layer.get_config()
print(config)
new_layer = Linear.from_config(config)
{'units': 64}
Notez que la __init__()
de la classe de base Layer
prend certains arguments de mot-clé, en particulier un name
et un dtype
. Il est __init__()
de transmettre ces arguments à la classe parente dans __init__()
et de les inclure dans la configuration de la couche:
class Linear(keras.layers.Layer):
def __init__(self, units=32, **kwargs):
super(Linear, self).__init__(**kwargs)
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):
config = super(Linear, self).get_config()
config.update({"units": self.units})
return config
layer = Linear(64)
config = layer.get_config()
print(config)
new_layer = Linear.from_config(config)
{'name': 'linear_8', 'trainable': True, 'dtype': 'float32', 'units': 64}
Si vous avez besoin de plus de flexibilité lors de la désérialisation de la couche de sa configuration, vous pouvez également remplacer la méthode de classe from_config()
. Voici l'implémentation de base de from_config()
:
def from_config(cls, config):
return cls(**config)
Pour en savoir plus sur la sérialisation et l'enregistrement, consultez le guide complet d'enregistrement et de sérialisation des modèles .
Argument d' training
privilégié dans la méthode call()
Certaines couches, en particulier la couche BatchNormalization
et la couche Dropout
, ont des comportements différents pendant l'entraînement et l'inférence. Pour de telles couches, il est de pratique courante d'exposer un argument d' training
(booléen) dans la méthode call()
.
En exposant cet argument dans call()
, vous activez les boucles d'entraînement et d'évaluation intégrées (par exemple fit()
) pour utiliser correctement la couche dans l'apprentissage et l'inférence.
class CustomDropout(keras.layers.Layer):
def __init__(self, rate, **kwargs):
super(CustomDropout, self).__init__(**kwargs)
self.rate = rate
def call(self, inputs, training=None):
if training:
return tf.nn.dropout(inputs, rate=self.rate)
return inputs
Argument de mask
privilégié dans la méthode call()
L'autre argument privilégié pris en charge par call()
est l'argument mask
.
Vous le trouverez dans toutes les couches Keras RNN. Un masque est un tenseur booléen (une valeur booléenne par pas de temps dans l'entrée) utilisé pour ignorer certains pas de temps d'entrée lors du traitement des données de séries temporelles.
Keras passera automatiquement l'argument de mask
correct à __call__()
pour les calques qui le supportent, lorsqu'un masque est généré par un calque précédent. Les calques générant un masque sont le calque d' Embedding
configuré avec mask_zero=True
et le calque de Masking
.
Pour en savoir plus sur le masquage et comment écrire des calques activés pour le masquage, consultez le guide «Comprendre le remplissage et le masquage» .
La classe Model
En général, vous utiliserez la classe Layer
pour définir des blocs de calcul internes et vous utiliserez la classe Model
pour définir le modèle externe - l'objet que vous allez entraîner.
Par exemple, dans un modèle ResNet50, vous auriez plusieurs blocs ResNet sous-classant la Layer
et un seul Model
englobant tout le réseau ResNet50.
La classe Model
a la même API que Layer
, avec les différences suivantes:
- Il expose des boucles
model.fit()
formation, d'évaluation et de prédiction (model.fit()
,model.evaluate()
,model.predict()
). - Il expose la liste de ses couches internes, via la propriété
model.layers
. - Il expose les API de sauvegarde et de sérialisation (
save()
,save_weights()
...)
En effet, la classe Layer
correspond à ce que nous appelons dans la littérature une "couche" (comme dans "convolution layer" ou "recurrent layer") ou comme un "block" (comme dans "ResNet block" ou "Inception block" ).
Pendant ce temps, la classe Model
correspond à ce que la littérature appelle un "modèle" (comme dans "deep learning model") ou comme un "réseau" (comme dans "deep neural network").
Donc, si vous vous demandez, "devrais-je utiliser la classe Layer
ou la classe Model
?", Demandez-vous: est-ce que je devrai appeler fit()
dessus? Dois-je appeler save()
dessus? Si tel est le cas, choisissez Model
. Sinon (soit parce que votre classe n'est qu'un bloc dans un système plus grand, soit parce que vous écrivez vous-même la formation et enregistrez le code), utilisez Layer
.
Par exemple, nous pourrions prendre notre exemple de mini-resnet ci-dessus et l'utiliser pour construire un Model
que nous pourrions entraîner avec fit()
, et que nous pourrions enregistrer avec save_weights()
:
class ResNet(tf.keras.Model):
def __init__(self, num_classes=1000):
super(ResNet, self).__init__()
self.block_1 = ResNetBlock()
self.block_2 = ResNetBlock()
self.global_pool = layers.GlobalAveragePooling2D()
self.classifier = Dense(num_classes)
def call(self, inputs):
x = self.block_1(inputs)
x = self.block_2(x)
x = self.global_pool(x)
return self.classifier(x)
resnet = ResNet()
dataset = ...
resnet.fit(dataset, epochs=10)
resnet.save(filepath)
Tout rassembler: un exemple de bout en bout
Voici ce que vous avez appris jusqu'à présent:
- Une
Layer
encapsule un état (créé dans__init__()
oubuild()
) et des calculs (définis danscall()
). - Les couches peuvent être imbriquées de manière récursive pour créer de nouveaux blocs de calcul plus volumineux.
- Les couches peuvent créer et suivre des pertes (généralement des pertes de régularisation) ainsi que des métriques, via
add_loss()
etadd_metric()
- Le conteneur extérieur, ce que vous voulez entraîner, est un
Model
. UnModel
est comme unLayer
, mais avec des utilitaires de formation et de sérialisation supplémentaires.
Mettons toutes ces choses ensemble dans un exemple de bout en bout: nous allons implémenter un Variational AutoEncoder (VAE). Nous allons l'entraîner sur les chiffres MNIST.
Notre VAE sera une sous-classe de Model
, construite comme une composition imbriquée de couches qui sous-classe Layer
. Il comportera une perte de régularisation (divergence KL).
from tensorflow.keras import layers
class Sampling(layers.Layer):
"""Uses (z_mean, z_log_var) to sample z, the vector encoding a digit."""
def call(self, inputs):
z_mean, z_log_var = inputs
batch = tf.shape(z_mean)[0]
dim = tf.shape(z_mean)[1]
epsilon = tf.keras.backend.random_normal(shape=(batch, dim))
return z_mean + tf.exp(0.5 * z_log_var) * epsilon
class Encoder(layers.Layer):
"""Maps MNIST digits to a triplet (z_mean, z_log_var, z)."""
def __init__(self, latent_dim=32, intermediate_dim=64, name="encoder", **kwargs):
super(Encoder, self).__init__(name=name, **kwargs)
self.dense_proj = layers.Dense(intermediate_dim, activation="relu")
self.dense_mean = layers.Dense(latent_dim)
self.dense_log_var = layers.Dense(latent_dim)
self.sampling = Sampling()
def call(self, inputs):
x = self.dense_proj(inputs)
z_mean = self.dense_mean(x)
z_log_var = self.dense_log_var(x)
z = self.sampling((z_mean, z_log_var))
return z_mean, z_log_var, z
class Decoder(layers.Layer):
"""Converts z, the encoded digit vector, back into a readable digit."""
def __init__(self, original_dim, intermediate_dim=64, name="decoder", **kwargs):
super(Decoder, self).__init__(name=name, **kwargs)
self.dense_proj = layers.Dense(intermediate_dim, activation="relu")
self.dense_output = layers.Dense(original_dim, activation="sigmoid")
def call(self, inputs):
x = self.dense_proj(inputs)
return self.dense_output(x)
class VariationalAutoEncoder(keras.Model):
"""Combines the encoder and decoder into an end-to-end model for training."""
def __init__(
self,
original_dim,
intermediate_dim=64,
latent_dim=32,
name="autoencoder",
**kwargs
):
super(VariationalAutoEncoder, self).__init__(name=name, **kwargs)
self.original_dim = original_dim
self.encoder = Encoder(latent_dim=latent_dim, intermediate_dim=intermediate_dim)
self.decoder = Decoder(original_dim, intermediate_dim=intermediate_dim)
def call(self, inputs):
z_mean, z_log_var, z = self.encoder(inputs)
reconstructed = self.decoder(z)
# Add KL divergence regularization loss.
kl_loss = -0.5 * tf.reduce_mean(
z_log_var - tf.square(z_mean) - tf.exp(z_log_var) + 1
)
self.add_loss(kl_loss)
return reconstructed
Écrivons une simple boucle d'entraînement sur MNIST:
original_dim = 784
vae = VariationalAutoEncoder(original_dim, 64, 32)
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3)
mse_loss_fn = tf.keras.losses.MeanSquaredError()
loss_metric = tf.keras.metrics.Mean()
(x_train, _), _ = tf.keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype("float32") / 255
train_dataset = tf.data.Dataset.from_tensor_slices(x_train)
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)
epochs = 2
# Iterate over epochs.
for epoch in range(epochs):
print("Start of epoch %d" % (epoch,))
# Iterate over the batches of the dataset.
for step, x_batch_train in enumerate(train_dataset):
with tf.GradientTape() as tape:
reconstructed = vae(x_batch_train)
# Compute reconstruction loss
loss = mse_loss_fn(x_batch_train, reconstructed)
loss += sum(vae.losses) # Add KLD regularization loss
grads = tape.gradient(loss, vae.trainable_weights)
optimizer.apply_gradients(zip(grads, vae.trainable_weights))
loss_metric(loss)
if step % 100 == 0:
print("step %d: mean loss = %.4f" % (step, loss_metric.result()))
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz 11493376/11490434 [==============================] - 0s 0us/step Start of epoch 0 step 0: mean loss = 0.3603 step 100: mean loss = 0.1262 step 200: mean loss = 0.0997 step 300: mean loss = 0.0895 step 400: mean loss = 0.0845 step 500: mean loss = 0.0810 step 600: mean loss = 0.0789 step 700: mean loss = 0.0773 step 800: mean loss = 0.0761 step 900: mean loss = 0.0750 Start of epoch 1 step 0: mean loss = 0.0748 step 100: mean loss = 0.0741 step 200: mean loss = 0.0736 step 300: mean loss = 0.0731 step 400: mean loss = 0.0728 step 500: mean loss = 0.0724 step 600: mean loss = 0.0721 step 700: mean loss = 0.0718 step 800: mean loss = 0.0715 step 900: mean loss = 0.0713
Notez que dans la mesure où le VAE sous-classe le Model
, il comporte des boucles d'apprentissage intégrées. Vous auriez donc pu aussi l'entraîner comme ceci:
vae = VariationalAutoEncoder(784, 64, 32)
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3)
vae.compile(optimizer, loss=tf.keras.losses.MeanSquaredError())
vae.fit(x_train, x_train, epochs=2, batch_size=64)
Epoch 1/2 938/938 [==============================] - 3s 2ms/step - loss: 0.0945 Epoch 2/2 938/938 [==============================] - 2s 2ms/step - loss: 0.0678 <tensorflow.python.keras.callbacks.History at 0x7f54dc15f550>
Au-delà du développement orienté objet: l'API fonctionnelle
Cet exemple était-il trop de développement orienté objet pour vous? Vous pouvez également créer des modèles à l'aide de l' API fonctionnelle . Surtout, choisir un style ou un autre ne vous empêche pas de tirer parti des composants écrits dans l'autre style: vous pouvez toujours mélanger et assortir.
Par exemple, l'exemple d'API fonctionnelle ci-dessous réutilise la même couche d' Sampling
nous avons définie dans l'exemple ci-dessus:
original_dim = 784
intermediate_dim = 64
latent_dim = 32
# Define encoder model.
original_inputs = tf.keras.Input(shape=(original_dim,), name="encoder_input")
x = layers.Dense(intermediate_dim, activation="relu")(original_inputs)
z_mean = layers.Dense(latent_dim, name="z_mean")(x)
z_log_var = layers.Dense(latent_dim, name="z_log_var")(x)
z = Sampling()((z_mean, z_log_var))
encoder = tf.keras.Model(inputs=original_inputs, outputs=z, name="encoder")
# Define decoder model.
latent_inputs = tf.keras.Input(shape=(latent_dim,), name="z_sampling")
x = layers.Dense(intermediate_dim, activation="relu")(latent_inputs)
outputs = layers.Dense(original_dim, activation="sigmoid")(x)
decoder = tf.keras.Model(inputs=latent_inputs, outputs=outputs, name="decoder")
# Define VAE model.
outputs = decoder(z)
vae = tf.keras.Model(inputs=original_inputs, outputs=outputs, name="vae")
# Add KL divergence regularization loss.
kl_loss = -0.5 * tf.reduce_mean(z_log_var - tf.square(z_mean) - tf.exp(z_log_var) + 1)
vae.add_loss(kl_loss)
# Train.
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3)
vae.compile(optimizer, loss=tf.keras.losses.MeanSquaredError())
vae.fit(x_train, x_train, epochs=3, batch_size=64)
Epoch 1/3 938/938 [==============================] - 3s 2ms/step - loss: 0.0950 Epoch 2/3 938/938 [==============================] - 2s 2ms/step - loss: 0.0677 Epoch 3/3 938/938 [==============================] - 2s 2ms/step - loss: 0.0676 <tensorflow.python.keras.callbacks.History at 0x7f54d00b9630>
Pour plus d'informations, assurez-vous de lire le guide de l'API fonctionnelle .