Halaman ini diterjemahkan oleh Cloud Translation API.
Switch to English

Membuat Layers baru & amp; Model melalui subclassing

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub Unduh buku catatan

Mempersiapkan

 import tensorflow as tf
from tensorflow import keras
 

Kelas Layer : kombinasi status (bobot) dan beberapa perhitungan

Salah satu abstraksi utama dalam Keras adalah kelas Layer . Sebuah layer merangkum status (bobot "layer") dan transformasi dari input ke output ("panggilan", pass forward layer).

Inilah lapisan yang terhubung erat. Ia memiliki status: variabel w dan 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

 

Anda akan menggunakan layer dengan memanggilnya pada beberapa input tensor, seperti fungsi Python.

 x = tf.ones((2, 2))
linear_layer = Linear(4, 2)
y = linear_layer(x)
print(y)
 
tf.Tensor(
[[-0.00892124  0.03003723  0.01141541 -0.13389507]
 [-0.00892124  0.03003723  0.01141541 -0.13389507]], shape=(2, 4), dtype=float32)

Perhatikan bahwa bobot w dan b secara otomatis dilacak oleh layer setelah ditetapkan sebagai atribut layer:

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

Perhatikan bahwa Anda juga memiliki akses ke pintasan yang lebih cepat untuk menambahkan bobot ke lapisan: metode 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.01266684  0.01941528 -0.09573359  0.03471692]
 [-0.01266684  0.01941528 -0.09573359  0.03471692]], shape=(2, 4), dtype=float32)

Lapisan dapat memiliki bobot yang tidak dapat dilatih

Selain bobot yang bisa dilatih, Anda juga bisa menambahkan bobot yang tidak dapat dilatih ke layer. Bobot seperti itu dimaksudkan untuk tidak diperhitungkan selama backpropagation, ketika Anda melatih layer.

Berikut cara menambah dan menggunakan berat yang tidak bisa dilatih:

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

Ini adalah bagian dari layer.weights , tetapi dikategorikan sebagai bobot yang tidak dapat dilatih:

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

Praktik terbaik: menunda penciptaan berat sampai bentuk input diketahui

Lapisan Linear kami di atas mengambil argumen input_dim yang digunakan untuk menghitung bentuk bobot w dan b dalam __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

 

Dalam banyak kasus, Anda mungkin tidak tahu sebelumnya ukuran input Anda, dan Anda ingin dengan malas membuat bobot ketika nilai itu diketahui, beberapa saat setelah instantiating layer.

Dalam API Keras, kami sarankan untuk membuat bobot lapisan dalam metode build(self, inputs_shape) dari layer Anda. Seperti ini:

 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

 

Metode __call__() pada layer Anda akan secara otomatis menjalankan build saat pertama kali dipanggil. Anda sekarang memiliki lapisan yang malas dan karenanya lebih mudah digunakan:

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

Lapisan secara komposif dapat disusun

Jika Anda menetapkan instance Layer sebagai atribut dari Layer lain, lapisan luar akan mulai melacak bobot lapisan dalam.

Kami menyarankan untuk membuat sublayers seperti itu dalam metode __init__() (karena sublayers biasanya memiliki metode build, mereka akan dibangun ketika layer terluar dibangun).

 # 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

Metode add_loss()

Saat menulis metode call() pada layer, Anda bisa membuat tensor kerugian yang ingin Anda gunakan nanti, saat menulis loop latihan Anda. Ini bisa dilakukan dengan memanggil 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

 

Kerugian ini (termasuk yang dibuat oleh lapisan bagian dalam) dapat diambil melalui layer.losses . Properti ini di-reset pada awal setiap __call__() ke lapisan tingkat atas, sehingga layer.losses selalu berisi nilai-nilai kerugian yang dibuat selama umpan maju terakhir.

 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
 

Selain itu, properti loss juga mengandung kerugian regularisasi yang dibuat untuk bobot setiap lapisan dalam:

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

Kerugian ini dimaksudkan untuk diperhitungkan saat menulis loop pelatihan, seperti ini:

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

Untuk panduan terperinci tentang menulis loop pelatihan, lihat panduan untuk menulis loop pelatihan dari awal .

Kerugian ini juga bekerja secara mulus dengan fit() (mereka secara otomatis dijumlahkan dan ditambahkan ke kerugian utama, jika ada):

 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`, thee 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 1ms/step - loss: 0.2169
1/1 [==============================] - 0s 875us/step - loss: 0.0396

<tensorflow.python.keras.callbacks.History at 0x7fa7639fd6a0>

Metode add_metric()

Demikian pula untuk add_loss() , layer juga memiliki metode add_metric() untuk melacak rata-rata bergerak dari suatu kuantitas selama pelatihan.

Pertimbangkan lapisan berikut: lapisan "titik akhir logistik". Dibutuhkan sebagai input prediksi & target, menghitung kerugian yang dilacak melalui add_loss() , dan menghitung skalar akurasi, yang dilacak melalui 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)

 

Metrik yang dilacak dengan cara ini dapat diakses melalui 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 0x7fa7f03601d0>]
current accuracy value: 1.0

Sama seperti untuk add_loss() , metrik ini dilacak oleh 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 1ms/step - loss: 0.9958 - binary_accuracy: 0.0000e+00

<tensorflow.python.keras.callbacks.History at 0x7fa7639ffeb8>

Anda dapat secara opsional mengaktifkan serialisasi pada lapisan Anda

Jika Anda perlu lapisan khusus Anda menjadi serializable sebagai bagian dari model Fungsional , Anda dapat menerapkan metode 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}

Perhatikan bahwa __init__() metode dasar Layer kelas mengambil beberapa argumen kata kunci, dalam khususnya name dan dtype . Ini praktik yang baik untuk meneruskan argumen ini ke kelas induk di __init__() dan memasukkannya ke dalam konfigurasi layer:

 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}

Jika Anda membutuhkan fleksibilitas lebih saat deserializing layer dari from_config() , Anda juga dapat mengganti metode from_config() . Ini adalah implementasi dasar from_config() :

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

Untuk mempelajari lebih lanjut tentang serialisasi dan penyimpanan, lihat panduan lengkap untuk menyimpan dan membuat serialisasi model .

Argumen training diistimewakan dalam metode call()

Beberapa lapisan, khususnya lapisan BatchNormalization dan lapisan Dropout , memiliki perilaku yang berbeda selama pelatihan dan inferensi. Untuk lapisan seperti itu, itu adalah praktik standar untuk mengekspos argumen training (boolean) dalam metode call() .

Dengan memaparkan argumen ini dalam call() , Anda mengaktifkan loop pelatihan dan evaluasi bawaan (mis. fit() ) untuk menggunakan layer dengan benar dalam pelatihan dan inferensi.

 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

 

Argumen mask istimewa dalam metode call()

Argumen istimewa lainnya yang didukung oleh call() adalah argumen mask .

Anda akan menemukannya di semua lapisan Keras RNN. Mask adalah tensor boolean (satu nilai boolean per timestep dalam input) yang digunakan untuk melewati timestep input tertentu saat memproses data rentang waktu.

Keras akan secara otomatis meneruskan argumen mask benar ke __call__() untuk lapisan yang mendukungnya, ketika topeng dihasilkan oleh lapisan sebelumnya. Lapisan penghasil topeng adalah lapisan Embedding dikonfigurasikan dengan mask_zero=True , dan lapisan Masking .

Untuk mempelajari lebih lanjut tentang masking dan cara menulis lapisan yang diaktifkan masking, silakan lihat panduan "memahami padding dan masking" .

Kelas Model

Secara umum, Anda akan menggunakan kelas Layer untuk mendefinisikan blok perhitungan bagian dalam, dan akan menggunakan kelas Model untuk mendefinisikan model luar - objek yang akan Anda latih.

Misalnya, dalam model ResNet50, Anda akan memiliki beberapa blok ResNet subkelas Layer , dan Model tunggal yang mencakup seluruh jaringan ResNet50.

Kelas Model memiliki API yang sama dengan Layer , dengan perbedaan berikut:

  • Ini memperlihatkan pelatihan, evaluasi, dan loop prediksi model.fit() , model.evaluate() , model.predict() ).
  • Itu memperlihatkan daftar lapisan dalamnya, melalui properti model.layers .
  • Itu memperlihatkan API penyimpanan dan serialisasi ( save() , save_weights() ...)

Secara efektif, kelas Layer sesuai dengan apa yang kita sebut dalam literatur sebagai "lapisan" (seperti dalam "lapisan konvolusi" atau "lapisan berulang") atau sebagai "blok" (seperti dalam "blok ResNet" atau "blok Inception" ).

Sementara itu, kelas Model sesuai dengan apa yang disebut dalam literatur sebagai "model" (seperti dalam "model pembelajaran mendalam") atau sebagai "jaringan" (seperti dalam "jaringan saraf dalam").

Jadi jika Anda bertanya-tanya, "haruskah saya menggunakan kelas Layer atau kelas Model ?", Tanyakan pada diri sendiri: apakah saya perlu memanggil fit() di atasnya? Apakah saya perlu memanggil save() di atasnya? Jika demikian, gunakan Model . Jika tidak (baik karena kelas Anda hanya blok di sistem yang lebih besar, atau karena Anda menulis sendiri pelatihan & menyimpan kode), gunakan Layer .

Misalnya, kita bisa mengambil contoh mini-resnet di atas, dan menggunakannya untuk membangun Model yang dapat kita latih dengan fit() , dan yang bisa kita simpan dengan save_weights() :

 class ResNet(tf.keras.Model):

    def __init__(self):
        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)
 

Menyatukan semuanya: contoh ujung ke ujung

Inilah yang telah Anda pelajari sejauh ini:

  • Layer merangkum keadaan (dibuat dalam __init__() atau build() ) dan beberapa perhitungan (didefinisikan dalam call() ).
  • Layers dapat diulang secara rekursif untuk membuat blok perhitungan baru yang lebih besar.
  • Lapisan dapat membuat dan melacak kerugian (biasanya kerugian regularisasi) serta metrik, melalui add_loss() dan add_metric()
  • Wadah luar, yang ingin Anda latih, adalah Model . Model adalah seperti Layer , tetapi dengan pelatihan tambahan dan utilitas serialisasi.

Mari kita semua hal ini bersama-sama menjadi contoh ujung ke ujung: kita akan mengimplementasikan Variational AutoEncoder (VAE). Kami akan melatihnya pada angka MNIST.

VAE kami akan menjadi subkelas Model , dibangun sebagai komposisi berlapis dari lapisan yang subkelas Layer . Ini akan menampilkan kerugian regularisasi (KL divergence).

 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

 

Mari kita menulis loop pelatihan sederhana di 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.3052
step 100: mean loss = 0.1252
step 200: mean loss = 0.0990
step 300: mean loss = 0.0890
step 400: mean loss = 0.0841
step 500: mean loss = 0.0808
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.0714
step 900: mean loss = 0.0712

Perhatikan bahwa karena VAE adalah subkelas Model , ini dilengkapi loop pelatihan bawaan. Jadi Anda juga bisa melatihnya seperti ini:

 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 [==============================] - 2s 2ms/step - loss: 0.0749
Epoch 2/2
938/938 [==============================] - 2s 2ms/step - loss: 0.0676

<tensorflow.python.keras.callbacks.History at 0x7fa7583261d0>

Di luar pengembangan berorientasi objek: API Fungsional

Apakah contoh ini terlalu banyak pengembangan berorientasi objek untuk Anda? Anda juga dapat membuat model menggunakan API Fungsional . Yang penting, memilih satu gaya atau yang lain tidak mencegah Anda meningkatkan komponen yang ditulis dalam gaya lain: Anda selalu dapat mencampur dan mencocokkan.

Sebagai contoh, contoh API Fungsional di bawah ini menggunakan kembali layer Sampling sama seperti yang kami definisikan dalam contoh di atas:

 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 [==============================] - 2s 2ms/step - loss: 0.0751
Epoch 2/3
938/938 [==============================] - 2s 2ms/step - loss: 0.0676
Epoch 3/3
938/938 [==============================] - 2s 2ms/step - loss: 0.0676

<tensorflow.python.keras.callbacks.History at 0x7fa7580f1668>

Untuk informasi lebih lanjut, pastikan untuk membaca panduan API Fungsional .