Erstellen neuer Ebenen und Modelle durch Unterklassenbildung

Auf TensorFlow.org ansehen In Google Colab ausführen Quelle auf GitHub anzeigen Notizbuch herunterladen

Aufstellen

import tensorflow as tf
from tensorflow import keras

Die Layer - Klasse: Die Kombination von Zustand (Gewichte) und einige Rechen

Einer der zentralen Abstraktion in Keras ist die Layer - Klasse. Ein Layer kapselt sowohl einen Zustand (die "Gewichte" des Layers) als auch eine Transformation von Eingaben zu Ausgaben (ein "Aufruf", der Vorwärtsdurchgang der Schicht).

Hier ist eine dicht verbundene Schicht. Es hat einen Zustand: die Variablen w und 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

Sie würden einen Layer verwenden, indem Sie ihn für eine oder mehrere Tensoreingaben aufrufen, ähnlich einer Python-Funktion.

x = tf.ones((2, 2))
linear_layer = Linear(4, 2)
y = linear_layer(x)
print(y)
tf.Tensor(
[[ 0.03029768 -0.05972501  0.00586849 -0.1109921 ]
 [ 0.03029768 -0.05972501  0.00586849 -0.1109921 ]], shape=(2, 4), dtype=float32)

Man beachte , dass die Gewichte w und b werden automatisch durch die Schicht auf nachgeführt wird Als Schichtattribute:

assert linear_layer.weights == [linear_layer.w, linear_layer.b]

Beachten Sie auch Zugriff auf eine schnellere Verknüpfung haben für das Hinzufügen von Gewicht zu einer Schicht: die add_weight() Methode:

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.05004499  0.01081884 -0.12212791  0.01023131]
 [ 0.05004499  0.01081884 -0.12212791  0.01023131]], shape=(2, 4), dtype=float32)

Schichten können nicht trainierbare Gewichte haben

Neben trainierbaren Gewichten können Sie einer Ebene auch nicht trainierbare Gewichte hinzufügen. Solche Gewichtungen sollen bei der Backpropagation nicht berücksichtigt werden, wenn Sie den Layer trainieren.

So fügen Sie ein nicht trainierbares Gewicht hinzu und verwenden es:

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 ist ein Teil von layer.weights , aber es wird als ein nicht trainierbar Gewicht kategorisiert:

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: []

Best Practice: Gewichtserstellung aufschieben, bis die Form der Eingaben bekannt ist

Unser Linear Schicht oberhalb nahm ein input_dim Argument , das verwendet wurde , um die Form der Gewichte zu berechnen , w und b in __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

In vielen Fällen wissen Sie möglicherweise nicht im Voraus, wie groß Ihre Eingaben sind, und möchten träge Gewichtungen erstellen, wenn dieser Wert einige Zeit nach der Instanziierung des Layers bekannt wird.

In der Keras API empfehlen wir Schichtgewichte in der Schaffung von build(self, inputs_shape) Methode Ihrer Schicht. So was:

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

Die __call__() Methode der Schicht baut das erste Mal automatisch ausgeführt wird sie aufgerufen wird. Sie haben jetzt eine Ebene, die faul und daher einfacher zu verwenden ist:

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

Ebenen sind rekursiv zusammensetzbar

Wenn Sie eine Layer-Instanz als Attribut eines anderen Layers zuweisen, beginnt der äußere Layer, die Gewichte des inneren Layers zu verfolgen.

Wir empfehlen die Schaffung einer solchen Unterschichten in der __init__() Methode (da die Unterschichten typischerweise eine Build - Methode haben, werden sie gebaut werden , wenn die äußere Schicht aufgebaut wird).

# 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

Die add_loss() Methode

Wenn das Schreiben call() Methode einer Schicht, können Sie Verlust Tensoren schaffen , dass Sie später nutzen wollen , wenn Ihre Trainingsschleife zu schreiben. Dies ist machbar durch den Aufruf 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

Diese Verluste (einschließlich der durch jede Innenschicht erzeugt) über abgerufen werden layer.losses . Diese Eigenschaft wird zurückgesetzt , zu Beginn eines jeden __call__() auf der obersten Ebene Schicht, so dass layer.losses die Verlustwerte enthält immer während der letzten Vorwärtsdurchlauf erzeugt.

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

Darüber hinaus ist die loss enthält Eigenschaft auch Regularisierung Verluste für die Gewichte jeder inneren Schicht erstellt:

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.0016696099>]

Diese Verluste sollen beim Schreiben von Trainingsschleifen wie folgt berücksichtigt werden:

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

Eine ausführliche Anleitung über die Ausbildung Schleifen schreiben, finden Sie in der Anleitung zu einer Trainingsschleife von Grund auf neu zu schreiben .

Diese Verluste wirken sich auch nahtlos mit fit() (sie an den Hauptverlust automatisch summiert und noch hinzukommen, falls vorhanden):

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 103ms/step - loss: 0.3750
1/1 [==============================] - 0s 44ms/step - loss: 0.0230
<keras.callbacks.History at 0x7fd0f80b3290>

Die add_metric() Methode

Ähnlich add_loss() , Schichten haben auch eine add_metric() Methode für den gleitenden Durchschnitt einer Menge während des Trainings zu verfolgen.

Betrachten Sie die folgende Schicht: eine "Logistikendpunkt"-Schicht. Es nimmt als Eingänge Vorhersagen & Ziele, es einen Verlust berechnet , die es über Spuren add_loss() , und berechnet sie eine Genauigkeit Skalar, die es über Spuren 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 auf diese Weise verfolgt sind über 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 0x7fd1b0214810>]
current accuracy value: 1.0

Genau wie für add_loss() werden diese Metriken durch verfolgt 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 242ms/step - loss: 0.9954 - binary_accuracy: 0.0000e+00
<keras.callbacks.History at 0x7fd0f80b3110>

Sie können optional die Serialisierung für Ihre Ebenen aktivieren

Wenn Sie Ihre benutzerdefinierten Schichten müssen als Teil eines serializable sein Funktionsmodell , können Sie optional eine Umsetzung get_config() Methode:

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}

Beachten Sie, dass die __init__() Methode der Layer Klasse einige Keyword Argumente annimmt, insbesondere einen name und ein dtype . Es ist gute Praxis , diese Argumente zu der übergeordneten Klasse in passieren __init__() und sie in der Schicht config zu umfassen:

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}

Wenn Sie mehr Flexibilität benötigen , wenn die Schicht aus seiner Konfigurations Deserialisieren, können Sie auch die außer Kraft setzen from_config() Klassenmethode. Dies ist die Basisimplementierung von from_config() :

def from_config(cls, config):
  return cls(**config)

Um mehr über die Serialisierung zu lernen und zu speichern, siehe die vollständige Anleitung zur Speicherung und Serialisierung Modelle .

Privilegierte training Argument in der call() Methode

Einige Schichten, insbesondere die BatchNormalization Schicht und die Dropout - Schicht, haben unterschiedliche Verhaltensweisen während des Trainings und Inferenz. Für solche Schichten, ist es üblich , ein zu belichten training (boolean) Argument in der call() Methode.

Durch Aussetzen dieses Arguments in call() , können Sie die eingebaute in der Ausbildung und Bewertungskreisen (zB fit() ) , um richtig die Schicht in der Ausbildung und Inferenz verwenden.

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

Privilegierte mask Argument in dem call() Methode

Das andere privilegierte Argument unterstützt call() ist der mask Argument.

Sie finden es in allen Keras RNN-Schichten. Eine Maske ist ein boolescher Tensor (ein boolescher Wert pro Zeitschritt in der Eingabe), der verwendet wird, um bestimmte Eingabezeitschritte bei der Verarbeitung von Zeitreihendaten zu überspringen.

Keras wird automatisch die richtige passieren mask Argument __call__() für die Schichten , die sie unterstützen, wenn eine Maske durch eine vorherige Schicht erzeugt wird. Maskenerzeugenden Schichten sind die Embedding konfiguriert , um mit mask_zero=True und die Masking Schicht.

Um mehr zu erfahren über Maskierung und wie man schreibt Maskierung fähige Schichten, bitte im Leitfaden „Verständnis Polsterung und Maskierung“ .

Die Model

In der Regel werden Sie die Verwendung Layer - Klasse innere Rechenblocks zu definieren, und die Verwendung Model Klasse das Außenmodell definieren - das Objekt , das Sie trainieren.

Zum Beispiel in einem ResNet50 Modell, würden Sie mehr RESNET Blöcke Subklassen haben Layer , und ein einziges Model das gesamte ResNet50 Netzwerk umfasst.

Die Model hat die gleiche API wie Layer , mit den folgenden Unterschieden:

  • Sie entlarvt Einbau-Training, Auswertung und Vorhersage Schleifen ( model.fit() , model.evaluate() , model.predict() ).
  • Es macht die Liste seiner inneren Schichten, über die model.layers Eigenschaft.
  • Es macht Speichern und Serialisierung APIs ( save() , save_weights() ...)

Effektiv wird die Layer Klasse entspricht , was verweisen wir auf dem in der Literatur als „Schicht“ (wie in „Faltungsschicht“ oder „wiederkehrende Schicht“ bezeichnet ) oder als „Block“ (wie in „RESNET Block“ oder „Auflageblock“ ).

Inzwischen hat das Model der Klasse entspricht , was in der Literatur bezeichnet wird als „Modell“ (wie in „deep Lernmodell“) oder als „Netzwerk“ (wie in „deep neuronales Netz“).

Also , wenn Sie sich fragen, „sollte ich die Layer - Klasse oder die Model - Klasse?“, Fragen Sie sich: werde ich brauchen , um Anruf fit() auf sie? Muss ich anrufen save() auf sie? Wenn ja, geht mit Model . Wenn nicht (entweder weil Ihre Klasse ist nur ein Block in einem größeren System, oder weil Sie schreiben , Training & Spar Code selbst), verwendet Layer .

Zum Beispiel könnten wir unser Mini-RESNET Beispiel oben nehmen und es verwenden , eine bauen Model , dass wir mit trainieren konnten fit() , und dass wir mit retten könnten 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)

Alles zusammenfügen: ein End-to-End-Beispiel

Folgendes haben Sie bisher gelernt:

  • Eine Layer einkapseln einen Zustand (erstellt in __init__() oder build() ) und einige Berechnung (in definierten call() ).
  • Ebenen können rekursiv verschachtelt werden, um neue, größere Rechenblöcke zu erstellen.
  • Schichten können erstellen und Spurverluste (typisch Regularisierung Verluste) sowie Metriken über add_loss() und add_metric()
  • Der Außenbehälter, das , was Sie trainieren wollen, ist ein Model . Ein Model ist wie eine Layer , jedoch mit zusätzlicher Ausbildung und Serialisierung Dienstprogramme.

Lassen Sie uns all diese Dinge in einem End-to-End-Beispiel zusammenfassen: Wir implementieren einen Variational AutoEncoder (VAE). Wir trainieren es mit MNIST-Ziffern.

Unsere VAE wird eine Unterklasse von seinem Model als verschachtelte Zusammensetzung der Schichten , der Unterklasse, integrierte Layer . Es weist einen Regularisierungsverlust (KL Divergenz) auf.

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

Schreiben wir eine einfache Trainingsschleife auf 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.3729
step 100: mean loss = 0.1265
step 200: mean loss = 0.0996
step 300: mean loss = 0.0895
step 400: mean loss = 0.0844
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.0751
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

Beachten Sie, dass seit der VAE ist Subklassen Model , es verfügt über integrierte in Trainingsschlaufen. Du hättest es also auch so trainieren können:

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.0747
Epoch 2/2
938/938 [==============================] - 2s 3ms/step - loss: 0.0676
<keras.callbacks.History at 0x7fcd2ad6bed0>

Jenseits der objektorientierten Entwicklung: die Functional API

War Ihnen dieses Beispiel zu viel objektorientierte Entwicklung? Sie können auch Modelle bauen , die mit Functional API . Wichtig ist, dass die Wahl des einen oder anderen Stils Sie nicht daran hindert, Komponenten zu nutzen, die im anderen Stil geschrieben sind: Sie können immer mischen und anpassen.

Zum Beispiel kann die Funktions API Beispiel unter Umnutzungen die gleiche Sampling definierte Schicht wir im Beispiel oben:

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.0749
Epoch 2/3
938/938 [==============================] - 2s 3ms/step - loss: 0.0676
Epoch 3/3
938/938 [==============================] - 2s 3ms/step - loss: 0.0675
<keras.callbacks.History at 0x7fcd2b3a37d0>

Für weitere Informationen, stellen Sie sicher , das lesen Functional API - Leitfaden .