Alt sınıflama yoluyla yeni Katmanlar ve Modeller oluşturma

TensorFlow.org'da görüntüleyin Google Colab'da çalıştırın Kaynağı GitHub'da görüntüleyin Not defterini indir

Kurmak

import tensorflow as tf
from tensorflow import keras

Layer sınıfı: durumu (ağırlıklar) kombinasyonu ve bir hesaplama

Keras merkezi soyutlama biri Layer sınıfı. Bir katman, hem bir durumu (katmanın "ağırlıkları") hem de girdilerden çıktılara bir dönüşümü ("çağrı", katmanın ileri geçişi) kapsar.

İşte yoğun bağlantılı bir katman. Değişkenler: Bu durumu vardır w ve 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

Bir Python işlevi gibi, bazı tensör girişlerinde onu çağırarak bir katman kullanırsınız.

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)

Ağırlıklar bu Not w ve b otomatik tabaka özellikler olarak ayarlandıktan sonra tabaka ile takip edilir:

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

: Ayrıca bir katmana ağırlık eklemek için hızlı bir kısayol erişimi Not add_weight() yöntemi:

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)

Katmanlar eğitilemez ağırlıklara sahip olabilir

Eğitilebilir ağırlıkların yanı sıra, bir katmana eğitilemez ağırlıklar da ekleyebilirsiniz. Bu tür ağırlıklar, katmanı eğitirken geri yayılım sırasında dikkate alınmamalıdır.

Eğitilemez bir ağırlığı nasıl ekleyeceğiniz ve kullanacağınız aşağıda açıklanmıştır:

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

Bu bir parçası layer.weights ama olmayan bir eğitilebilir ağırlık olarak sınıflandırılan:

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

En iyi uygulama: girdilerin şekli bilinene kadar ağırlık oluşturmayı ertelemek

Bizim Linear tabaka üzerinde bir aldı input_dim ağırlıkları şeklini hesaplamak için kullanılmıştır bağımsız değişken w ve b olarak __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

Çoğu durumda, girdilerinizin boyutunu önceden bilemeyebilirsiniz ve bu değer bilindiğinde, katmanı somutlaştırdıktan bir süre sonra tembelce ağırlıklar oluşturmak istersiniz.

Keras API, biz katman ağırlıkları oluşturmaya tavsiye build(self, inputs_shape) katmanınıza yöntemi. Bunun gibi:

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

__call__() katmanınıza yöntemi otomatik build denir ilk kez çalışır. Artık tembel ve dolayısıyla kullanımı daha kolay bir katmanınız var:

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

Uygulama build() ayrı ayrı yukarıdaki gibi güzel bir şekilde gösterilen her çağrı ağırlıkları kullanılarak sadece bir kez ağırlıkları oluşturmak ayırır. Ancak, bazı gelişmiş özel katmanlar için durum oluşturma ve hesaplamayı ayırmak pratik olmayabilir. Katman uygulayıcılar birinci ağırlık oluşturma ertelemek için izin verilir __call__() , ancak daha sonra aramaları aynı ağırlıkları kullandıkları ihtiyaç dikkat çekmek için. Buna ilaveten, __call__() muhtemelen içinde ilk defa yürütülecek olan tf.function , içinde yer alan herhangi bir değişken oluşturulması __call__() bir sarılmış olmalıdır tf.init_scope .

Katmanlar özyinelemeli olarak birleştirilebilir

Bir Katman örneğini başka bir Katmanın özniteliği olarak atarsanız, dış katman, iç katman tarafından oluşturulan ağırlıkları izlemeye başlar.

Biz bu tür alt katmanlar oluşturarak tavsiye __init__() yöntemi ve ilk bırakın __call__() kendi ağırlıkları bina tetiğe.

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

add_loss() metodu

Yazarken call() bir katmanın yöntemini, size eğitim döngü yazarken daha sonra kullanmak istediğiniz edeceği kaybı tensörleri oluşturabilir. Bu arayarak yapılabilir olduğunu 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

(Herhangi bir iç tabaka ile oluşturulanlar dahil olmak üzere), bu kayıplar ile alınabilir layer.losses . Bu özellik her başlangıcında sıfırlanır __call__() böylece, üst düzey katmana layer.losses daima son ileriye geçiş sırasında oluşturulan kayıp değerleri içerir.

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

Buna ek olarak, loss özelliği de bir iç katmanın ağırlıkları için oluşturulan düzenlilestirme kayıplarını içerir:

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

Bu kayıplar, aşağıdaki gibi eğitim döngüleri yazılırken dikkate alınmalıdır:

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

Eğitim döngüler yazma konusunda detaylı bir kılavuz için, bkz sıfırdan bir eğitim döngü yazma kılavuzuna .

Bu kayıplar da ile sorunsuz çalışacak fit() (varsa bunlar otomatik ana kaybına toplanır ve ekledi olsun):

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>

add_metric() metodu

Benzer için add_loss() , tabakalar da bir bilgisi add_metric() eğitimi sırasında bir miktar hareketli ortalama izleme için bir yöntem.

Şu katmanı göz önünde bulundurun: bir "lojistik uç nokta" katmanı. Girişler tahminler & hedefler, bunun aracılığıyla izleyen bir kayıp hesaplar gibi sürer add_loss() ve bunun aracılığıyla izleyen bir doğruluk sayıl, hesaplar 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)

Bu şekilde takip Metrik yoluyla erişilebilir 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

Sadece için benzeri add_loss() bu ölçümler tarafından izlenir 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>

Katmanlarınızda isteğe bağlı olarak serileştirmeyi etkinleştirebilirsiniz

Eğer bir parçası olarak seri hale getirilebilir olması için özel katmanlar gerekiyorsa Fonksiyonel modelin , isteğe bağlı olarak bir uygulayabilirsiniz get_config() yöntemini:

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}

O Not __init__() baz yöntemi Layer sınıfında özellikle bir de, bazı anahtar kelime argüman alır name ve dtype . Bu içinde üst sınıfa bu argümanları geçmek iyi bir uygulamadır __init__() ve katman config dahil etmek:

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}

Onun yapılandırmasındaki tabakasını deserializing yaparken daha fazla esneklik gerekiyorsa, ayrıca geçersiz kılabilir from_config() sınıfı yöntemini. Bu taban uygulamasıdır from_config() :

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

Serileştirme ve kaydetme hakkında daha fazla bilgi edinmek için komple bkz tasarrufu ve modelleri seri için rehber .

İmtiyazlı training içinde argüman call() yöntemiyle

Bazı katmanlar, özellikle BatchNormalization katmanı ve Dropout tabakası, eğitim ve çıkarsama sırasında farklı davranışlara sahip. Örneğin katman için, bir açığa standart bir uygulamadır training içinde (boolean) argüman call() yöntemiyle.

Bu argüman teşhir ederek call() , yerleşik eğitim ve değerlendirme döngüler (örneğin etkinleştirmek fit() doğru bir şekilde çalışma ve çıkarım katmanı kullanmak için).

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

İmtiyazlı mask içinde argüman call() yöntemiyle

Tarafından desteklenen diğer ayrıcalıklı argüman call() olan mask argüman.

Bunu tüm Keras RNN katmanlarında bulacaksınız. Maske, zaman serisi verilerini işlerken belirli giriş zaman adımlarını atlamak için kullanılan bir boole tensörüdür (girişteki zaman adımı başına bir boole değeri).

Keras otomatik olarak doğru geçecek mask için bağımsız değişkeni __call__() bir maske önceki tabaka ile üretildiğinde, bu destek katmanları. Maske üreten tabakalardır Embedding ile yapılandırılmış tabaka mask_zero=True ve Masking tabakası.

Daha maskeleme ve nasıl yazmaya özellikli maskeleme katmanları hakkında bilgi edinmek için rehber kontrol edin "dolgu anlamak ve maskeleme" .

Model sınıfı

Genel olarak, kullanmak olacaktır Layer iç hesaplama blokları tanımlama sınıf ve kullanır Model dış modeli tanımlamak için sınıf - antrenman nesneyi.

Örneğin, bir ResNet50 modelinde, sen sınıflara birkaç ResNet blokları olurdu Layer ve tek Model tüm ResNet50 ağını kapsayan.

Model sınıfı ile aynı API vardır Layer , aşağıda farklılıklar:

  • Bu yerleşik açığa eğitim, değerlendirme ve öngörü döngüler ( model.fit() , model.evaluate() , model.predict() ).
  • Bu yoluyla, iç katman listesi ortaya model.layers özelliği.
  • Bu tasarruf ve seri hale API'leri ortaya ( save() , save_weights() ...)

Etkin bir şekilde, Layer ne "blok ResNet" de olduğu gibi (( "kıvrım tabaka" ya da "tekrarlayan tabaka" olduğu gibi) ya da bir "blok" olarak bir "kat" olarak literatürde bakın veya "Başlangıç blok" sınıf karşılık ).

Bu arada, Model ( "derin sinir ağı" gibi) ( "derin öğrenme modelinin" de olduğu gibi) ya da bir "ağ" olarak, bir "model" olarak literatürde adlandırılan sınıf denk gelmektedir.

Eğer merak ediyorsanız eğer öyleyse, "kullanmalıyım Layer sınıf veya Model sınıfı?" Diye sorun: çağrı gerekir fit() Üzerinde? Aramaya gerekecek mi save() üzerinde? Eğer öyleyse, ile gitmek Model . Değilse (ya da sınıf daha büyük sisteminde sadece bir blok ya da çünkü eğitim yazma ve kodu kendiniz tasarruf çünkü), kullanım Layer .

Mesela, bizim mini resnet örneği yukarıda almak ve bir inşa için kullanabilir Model biz eğitebilmeyi fit() ve biz birlikte kurtarabilecek 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)

Hepsini bir araya getirmek: uçtan uca bir örnek

İşte şimdiye kadar öğrendikleriniz:

  • Bir Layer bir durum (oluşturulan kapsül __init__() ya da build() ) ve bazı hesaplama (tanımlanmış call() ).
  • Katmanlar, yeni, daha büyük hesaplama blokları oluşturmak için özyinelemeli olarak iç içe yerleştirilebilir.
  • Oluşturabilir Katmanlar ve parça kayıpları (genellikle düzenlileştirme kayıpları) yanı sıra ölçümlerini aracılığıyla add_loss() ve add_metric()
  • Dış kap, alıştırma yapmak istediğiniz şey, bir olduğunu Model . Bir Model sadece gibidir Layer , ancak ilave eğitim ve seri ekipmana sahip.

Tüm bunları uçtan uca bir örnekte bir araya getirelim: Variational AutoEncoder (VAE) uygulayacağız. MNIST rakamlarıyla eğiteceğiz.

Bizim VAE bir alt sınıfı olacaktır Model alt sınıf olduğunu tabakaların iç içe geçmiş bir kompozisyon olarak inşa Layer . Bir düzenleme kaybı (KL sapması) içerecektir.

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

MNIST üzerinde basit bir eğitim döngüsü yazalım:

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

VAE sınıflara beri unutmayın Model , bu yerleşik özellikleri eğitim döngüler. Yani onu şu şekilde de eğitebilirdin:

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>

Nesneye yönelik geliştirmenin ötesinde: İşlevsel API

Bu örnek sizin için çok fazla nesne yönelimli geliştirme miydi? Ayrıca kullanarak model oluşturabilir Fonksiyonel API . Daha da önemlisi, bir stil veya diğerini seçmek, diğer stilde yazılmış bileşenlerden yararlanmanızı engellemez: her zaman karıştırıp eşleştirebilirsiniz.

Örneğin, tekrar kullanım altında fonksiyonel API örnek, aynı Sampling tabakası biz, örneğin, yukarıda tanımlanan:

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>

Daha fazla bilgi için, okumak için emin olun Fonksiyonel API kılavuzu .