![]() | ![]() | ![]() | ![]() |
Installer
import tensorflow as tf
from tensorflow import keras
La Layer
de classe: la combinaison de l' État (poids) et un certain calcul
L' un de l'abstraction centrale dans Keras est la Layer
classe. 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 dispose d' un: 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 de tenseur, un peu comme une fonction Python.
x = tf.ones((2, 2))
linear_layer = Linear(4, 2)
y = linear_layer(x)
print(y)
tf.Tensor( [[ 0.00962844 -0.01307489 -0.1452128 0.0538918 ] [ 0.00962844 -0.01307489 -0.1452128 0.0538918 ]], shape=(2, 4), dtype=float32)
On notera que les coefficients de pondération w
et b
sont détectés automatiquement par la couche à être mis sous forme d' attributs de couche:
assert linear_layer.weights == [linear_layer.w, linear_layer.b]
Notez que vous avez également accès à un plus rapide raccourci pour ajouter du poids à une couche: la add_weight()
méthode:
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.05790994 0.060931 -0.0402256 -0.09450993] [ 0.05790994 0.060931 -0.0402256 -0.09450993]], 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. De tels poids sont destiné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 se catégorisé comme un poids non trainable:
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 intrants soit connue
Notre Linear
couche ci - dessus a un input_dim
argument 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 poids lorsque cette valeur est connue, quelque temps après l'instanciation de la couche.
Dans l'API Keras, nous vous recommandons de créer des poids de la couche dans la build(self, inputs_shape)
méthode 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__()
méthode de votre couche construirons exécute automatiquement la première fois qu'il est appelé. Vous avez maintenant un calque paresseux 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)
La mise en œuvre build()
séparément , comme indiqué ci - dessus se sépare bien que la création de poids une fois d'utiliser des poids dans tous les appels. Cependant, pour certaines couches personnalisées avancées, il peut devenir difficile de séparer la création et le calcul de l'état. Layer implémenteurs sont autorisés à différer la création de poids à la première __call__()
, mais besoin de prendre soin que plus tard les appels utilisent les mêmes poids. De plus, étant donné que __call__()
est susceptible d'être exécuté pour la première fois dans une tf.function
, toute création de variable qui a lieu dans __call__()
devrait être enveloppé dans un tf.init_scope
.
Les calques sont récursivement composables
Si vous attribuez une instance de couche en tant qu'attribut d'une autre couche, la couche externe commencera à suivre les poids créés par la couche interne.
Nous vous recommandons de créer ces sous - couches dans la __init__()
méthode et le laisser à la première __call__()
pour déclencher la construction de leur poids.
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
Le add_loss()
Méthode
Lors de l' écriture l' call()
méthode d'une couche, vous pouvez créer des tenseurs de perte que vous voulez utiliser plus tard, lors de l' écriture de votre boucle de formation. 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 ceux qui sont créés par une couche interne) peuvent être récupérées par l' intermédiaire d' layer.losses
. Cette propriété est remis à zéro au début de chaque __call__()
à la couche de niveau supérieur, de sorte que layer.losses
contient toujours les valeurs de perte créées au cours de la dernière passe en 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 loss
propriété 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.0024520475>]
Ces pertes sont censé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 des boucles de formation, consultez le guide pour écrire une boucle de formation à partir de zéro .
Ces pertes fonctionnent également de manière transparente avec fit()
(ils se résument automatiquement et ajoutés à 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 209ms/step - loss: 0.1948 1/1 [==============================] - 0s 49ms/step - loss: 0.0298 <keras.callbacks.History at 0x7fce9052d290>
Le add_metric()
Méthode
De manière similaire à add_loss()
, les couches ont également un add_metric()
méthode pour le suivi de la moyenne d'une quantité en mouvement pendant l' entraînement.
Considérez la couche suivante : une couche « point final logistique ». Il faut que les prévisions et les cibles entrées, il calcule une perte dont 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)
Metrics chenillés 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: [<keras.metrics.BinaryAccuracy object at 0x7fce90578490>] current accuracy value: 1.0
Tout comme pour add_loss()
, ces mesures 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 274ms/step - loss: 0.9291 - binary_accuracy: 0.0000e+00 <keras.callbacks.History at 0x7fce90448c50>
Vous pouvez éventuellement activer la sérialisation sur vos couches
Si vous avez besoin de vos couches personnalisées sérialisable dans le cadre d'un modèle fonctionnel , vous pouvez éventuellement mettre en œuvre un get_config()
méthode:
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__()
méthode de la base Layer
classe prend des arguments clés, notamment un name
et un dtype
. Il est bon 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 d'une plus grande flexibilité lors de la désérialisation la couche de sa configuration, vous pouvez également remplacer la from_config()
méthode de classe. Ceci est la mise en œuvre de la base from_config()
:
def from_config(cls, config):
return cls(**config)
Pour en savoir plus sur la sérialisation et de sauver, voir le complet guide pour sauver et modèles sérialisation .
Privileged training
argument dans l' call()
méthode
Certaines couches, en particulier la BatchNormalization
couche et la Dropout
de Dropout
couche, ont des comportements différents au cours de la formation et l' inférence. Pour ces couches, il est pratique courante d'exposer une training
argument (booléen) dans l' call()
méthode.
En exposant cet argument dans l' call()
, vous activez intégré dans les boucles de formation et d' évaluation (par exemple en fit()
) d'utiliser correctement la couche dans la formation 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
Privilégié mask
argument dans l' call()
Méthode
L'autre argument privilégié soutenu par call()
est le mask
argument.
Vous le trouverez dans toutes les couches Keras RNN. Un masque est un tenseur booléen (une valeur booléenne par pas de temps en entrée) utilisé pour ignorer certains pas de temps en entrée lors du traitement des données de la série temporelle.
Keras passera automatiquement le bon mask
argument pour __call__()
pour les couches qui le supportent, lorsqu'un masque est générée par une couche antérieure. Masque couches génératrices sont Embedding
couche configurée avec mask_zero=True
, et le Masking
couche.
Pour en savoir plus sur le masquage et la façon d'écrire des couches de masquage activé, s'il vous plaît consulter le guide « comprendre le rembourrage et le masquage » .
Le Model
classe
En général, vous utiliserez la Layer
classe pour définir des blocs de calcul internes et utilisera le Model
de classe pour définir le modèle externe - l'objet que vous formerez.
Par exemple, dans un modèle ResNet50, vous auriez plusieurs blocs ResNet sous - classement Layer
, et un seul Model
englobant l'ensemble du réseau ResNet50.
Le Model
classe a la même API que la Layer
, avec les différences suivantes:
- Il expose intégré dans la formation, l' évaluation et des boucles de prédiction (
model.fit()
,model.evaluate()
,model.predict()
). - Il expose la liste de ses couches internes, par la
model.layers
propriété. - Il expose l' épargne et les API de sérialisation (
save()
,save_weights()
...)
En effet, la Layer
correspond de classe à ce que nous appelons dans la littérature comme une « couche » (comme dans « la couche de convolution » ou « couche récurrente ») ou comme un « bloc » (comme dans « ResNet bloc » ou « bloc Inception » ).
Pendant ce temps, le Model
correspond de classe à ce qu'on appelle dans la littérature comme un « modèle » (comme dans « modèle d'apprentissage en profondeur ») ou comme un « réseau » (comme dans « réseau de neurones profond »).
Donc , si vous vous demandez, « dois - je utiliser la Layer
de classe ou Model
classe? », Demandez - vous: dois - je appeler en fit()
là - dessus? Ai - je besoin d'appeler save()
là - dessus? Si oui, rendez - vous avec le Model
. Dans le cas contraire ( que ce soit parce que votre classe est juste un bloc dans un système plus grand, ou parce que vous écrivez vous - même le code et la formation d' économie), l' utilisation de Layer
.
Par exemple, nous pourrions prendre notre exemple mini-ResNet ci - dessus, et l' utiliser pour construire un Model
que nous pourrions former avec fit()
, et que nous pourrions sauver 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 assembler : un exemple de bout en bout
Voici ce que vous avez appris jusqu'à présent :
- Une
Layer
encapsuler un état (créé en__init__()
oubuild()
) et un certain calcul (défini dans l'call()
). - Les couches peuvent être imbriquées de manière récursive pour créer de nouveaux blocs de calcul plus gros.
- Les couches peuvent créer et pertes de piste (généralement des pertes de régularisation), ainsi que des mesures, via
add_loss()
etadd_metric()
- Le récipient extérieur, la chose que vous voulez former, est un
Model
. UnModel
est tout comme uneLayer
, mais avec formation ajoutée et les services publics sérialisation.
Regroupons toutes ces choses 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
, construit comme une composition imbriquée de couches que la 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 boucle d'entraînement simple 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()))
Start of epoch 0 step 0: mean loss = 0.3184 step 100: mean loss = 0.1252 step 200: mean loss = 0.0989 step 300: mean loss = 0.0890 step 400: mean loss = 0.0841 step 500: mean loss = 0.0807 step 600: mean loss = 0.0787 step 700: mean loss = 0.0771 step 800: mean loss = 0.0759 step 900: mean loss = 0.0749 Start of epoch 1 step 0: mean loss = 0.0746 step 100: mean loss = 0.0740 step 200: mean loss = 0.0735 step 300: mean loss = 0.0730 step 400: mean loss = 0.0727 step 500: mean loss = 0.0723 step 600: mean loss = 0.0720 step 700: mean loss = 0.0717 step 800: mean loss = 0.0715 step 900: mean loss = 0.0712
Notez que depuis la VAE est le sous - classement Model
, il fonctionnalités intégrées dans les boucles de formation. Donc, vous auriez 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 3ms/step - loss: 0.0745 Epoch 2/2 938/938 [==============================] - 3s 3ms/step - loss: 0.0676 <keras.callbacks.History at 0x7fce90282750>
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 en utilisant 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 de l' API fonctionnelle ci - dessous réutilise le même Sampling
de la couche nous avons défini 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 3ms/step - loss: 0.0748 Epoch 2/3 938/938 [==============================] - 3s 3ms/step - loss: 0.0676 Epoch 3/3 938/938 [==============================] - 3s 3ms/step - loss: 0.0676 <keras.callbacks.History at 0x7fce90233cd0>
Pour plus d' informations, assurez - vous de lire le guide de l' API fonctionnelle .