![]() | ![]() | ![]() | ![]() |
Configuración
import tensorflow as tf
from tensorflow import keras
La Layer
clase: la combinación de Estado (pesos) y algunos cómputo
Uno de los abstracción central en Keras es la Layer
la clase. Una capa encapsula tanto un estado (los "pesos" de la capa) como una transformación de entradas a salidas (una "llamada", el paso hacia adelante de la capa).
Aquí hay una capa densamente conectada. Tiene un estado: las variables w
y 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
Usaría una capa llamándola en algunas entradas de tensor, como una función de 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)
Tenga en cuenta que los pesos w
y b
se realiza un seguimiento de forma automática por la capa en su constitución como atributos de capa:
assert linear_layer.weights == [linear_layer.w, linear_layer.b]
Tenga en cuenta que también tiene acceso a un atajo más rápido para añadir peso a una capa: la add_weight()
método:
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)
Las capas pueden tener pesos no entrenables
Además de pesos entrenables, también puede agregar pesos no entrenables a una capa. Dichos pesos no deben tenerse en cuenta durante la retropropagación, cuando está entrenando la capa.
Aquí se explica cómo agregar y usar un peso no entrenable:
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.]
Es parte de layer.weights
, pero se clasifica como un peso no entrenable:
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: []
Práctica recomendada: aplazar la creación de peso hasta que se conozca la forma de las entradas
Nuestra Linear
capa por encima tomó una input_dim
argumento que se utilizó para calcular la forma de los pesos w
y b
en __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
En muchos casos, es posible que no sepa de antemano el tamaño de sus entradas y le gustaría crear ponderaciones perezosamente cuando se conozca ese valor, algún tiempo después de instanciar la capa.
En la API Keras, se recomienda la creación de pesos de capa en la build(self, inputs_shape)
método de su capa. Como esto:
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
El __call__()
método de su capa se ejecutará automáticamente construir la primera vez que se llama. Ahora tiene una capa que es perezosa y, por lo tanto, más fácil de usar:
# 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 implementación de build()
por separado como se muestra arriba bien separa la creación de pesas sólo una vez desde el uso de pesos en cada llamada. Sin embargo, para algunas capas personalizadas avanzadas, puede resultar poco práctico separar la creación y el cálculo del estado. Ejecutores de capa se permite aplazar la creación peso a la primera __call__()
, pero es necesario tener cuidado de que las llamadas posteriores utilizan los mismos pesos. Además, puesto que __call__()
es susceptible de ser ejecutado por primera vez dentro de una tf.function
, cualquier creación variable que tiene lugar en __call__()
debe ser envuelto en un tf.init_scope
.
Las capas se pueden componer recursivamente
Si asigna una instancia de Capa como un atributo de otra Capa, la capa externa comenzará a rastrear los pesos creados por la capa interna.
Recomendamos la creación de tales subcapas en la __init__()
método y dejarlo a la primera __call__()
de desencadenar la construcción de sus pesos.
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
El add_loss()
método
Al escribir la call()
método de una capa, puede crear tensores de pérdida que usted tendrá que usar más tarde, al escribir su bucle de entrenamiento. Esto es factible llamando 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
Estas pérdidas (incluyendo los creados por cualquier capa interior) pueden ser recuperados a través layer.losses
. Esta propiedad se pone a cero al comienzo de cada __call__()
a la capa de nivel superior, de manera que layer.losses
contiene siempre los valores de pérdida creados durante el último pase hacia adelante.
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
Además, la loss
propiedad contiene también las pérdidas de regularización creadas para los pesos de cualquier capa interna:
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>]
Estas pérdidas deben tenerse en cuenta al escribir bucles de entrenamiento, como este:
# 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))
Para una guía detallada sobre cómo escribir bucles de formación, consulte la guía para escribir un bucle de formación a partir de cero .
Estas pérdidas también funcionan a la perfección con fit()
(consiguen suman automáticamente y se agregan a la pérdida principal, en su caso):
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>
El add_metric()
método
De manera similar a add_loss()
, las capas también tienen un add_metric()
método para el seguimiento de la media móvil de una cantidad durante el entrenamiento.
Considere la siguiente capa: una capa de "punto final logístico". Se toma como entradas las predicciones y objetivos, se calcula una pérdida que hace un seguimiento de vía add_loss()
, y se calcula un escalar precisión, lo que hace un seguimiento de vía 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)
Métricas de orugas de esta manera se puede acceder a través de 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
Al igual que para add_loss()
, estas métricas se realiza por 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>
Opcionalmente, puede habilitar la serialización en sus capas
Si necesita que sus capas personalizadas para ser serializable como parte de un modelo funcional , se puede aplicar opcionalmente un get_config()
método:
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}
Tenga en cuenta que el __init__()
método de la base de Layer
clase tiene algunos argumentos de palabras clave, en particular, un name
y una dtype
. Es una buena práctica para pasar estos argumentos a la clase padre en __init__()
e incluirlos en la configuración de la capa:
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 necesita más flexibilidad al deserializar la capa de su configuración, también puede anular la from_config()
método de clase. Esta es la implementación base de from_config()
:
def from_config(cls, config):
return cls(**config)
Para obtener más información sobre la serialización y el ahorro, consulte la completa guía para el ahorro y la serialización de los modelos .
Privilegiado training
argumento en la call()
método
Algunas capas, en particular la BatchNormalization
capa y la Dropout
capa, tienen diferentes comportamientos durante el entrenamiento y la inferencia. Para tales capas, es una práctica estándar para exponer una training
argumento (booleano) en el call()
método.
Al exponer este argumento en call()
, se habilita la (por ejemplo, una función de bucles de formación y evaluación fit()
) para utilizar correctamente la capa en la formación y la inferencia.
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
Privilegiada mask
argumento en la call()
método
El otro argumento privilegiada con el apoyo de call()
es la mask
argumento.
Lo encontrarás en todas las capas de Keras RNN. Una máscara es un tensor booleano (un valor booleano por período de tiempo en la entrada) que se utiliza para omitir ciertos períodos de tiempo de entrada al procesar datos de series temporales.
Keras pasará automáticamente la correcta mask
argumento para __call__()
para las capas que lo soportan, cuando una máscara se genera por una capa anterior. Las capas de máscara de generación son la Embedding
capa configurado con mask_zero=True
, y el Masking
capa.
Para obtener más información sobre el enmascaramiento y la forma de escribir habilitado capas de enmascaramiento, por favor echa un vistazo a la guía de "entender el relleno y el enmascaramiento" .
El Model
de clases
En general, se utiliza la Layer
de clase para definir bloques de cálculo internas, y usará el Model
de clase para definir el modelo exterior - el objeto que va a entrenar.
Por ejemplo, en un modelo ResNet50, que tendría varios bloques ResNet de subclases Layer
, y un solo Model
que abarca toda la red ResNet50.
El Model
clase tiene el mismo API Layer
, con las siguientes diferencias:
- Expone una función de bucles de formación, evaluación y predicción (
model.fit()
,model.evaluate()
,model.predict()
). - Se expone la lista de sus capas internas, a través de la
model.layers
propiedad. - Expone el ahorro y la API de serialización (
save()
,save_weights()
...)
Efectivamente, los Layer
de clase corresponde a lo que nos referimos en la literatura como una "capa" (como en "capa de convolución" o "capa recurrente") o como un "bloque" (como en "ResNet bloque" o "bloque de inicio" ).
Mientras tanto, los Model
corresponde a la clase lo que se conoce en la literatura como un "modelo" (como en "modelo de aprendizaje profundo") o como una "red" (como en "red neuronal") de profundidad.
Así que si usted se está preguntando, "debería utilizar la Layer
la clase o el Model
de clase?", Se pregunta: ¿necesitaré a la llamada de fit()
en él? Voy a tener que llamar save()
en él? Si es así, ir con Model
. Si no es así (ya sea debido a que su clase es sólo un bloque en un sistema más grande, o porque usted está escribiendo la formación y el ahorro de código usted mismo), el uso Layer
.
Por ejemplo, podríamos tener nuestro ejemplo mini-resnet arriba, y lo utilizan para construir un Model
que podríamos entrenar con fit()
, y que podríamos ahorrar con 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)
Poniendo todo junto: un ejemplo de extremo a extremo
Esto es lo que has aprendido hasta ahora:
- Una
Layer
encapsular un estado (creado en__init__()
o debuild()
) y algún cálculo (definido encall()
). - Las capas se pueden anidar recursivamente para crear nuevos bloques de cálculo más grandes.
- Capas pueden crear y pérdidas de pista (típicamente pérdidas de regularización), así como métricas, a través de
add_loss()
yadd_metric()
- El recipiente exterior, lo que desea entrenar, es un
Model
. UnModel
es como unaLayer
, pero con los servicios públicos de formación y de serialización añadido.
Pongamos todas estas cosas juntas en un ejemplo de extremo a extremo: vamos a implementar un codificador automático variacional (VAE). Lo entrenaremos en dígitos MNIST.
Nuestra VAE será una subclase de Model
, construido como una composición anidada de capas que subclase Layer
. Contará con una pérdida de regularización (divergencia 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
Escribamos un ciclo de entrenamiento simple en 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
Tenga en cuenta que, dado que el VAE se subclassing Model
, que cuenta con una función de bucles de formación. Entonces también podrías haberlo entrenado así:
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>
Más allá del desarrollo orientado a objetos: la API funcional
¿Fue este ejemplo demasiado desarrollo orientado a objetos para usted? También es posible construir modelos utilizando la API funcional . Es importante destacar que elegir un estilo u otro no le impide aprovechar los componentes escritos en el otro estilo: siempre puede mezclar y combinar.
Por ejemplo, el ejemplo API funcional continuación vuelve a utilizar el mismo Sampling
capa que se define en el ejemplo anterior:
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>
Para obtener más información, asegúrese de leer la guía API funcional .