![]() | ![]() | ![]() | ![]() |
Настраивать
import tensorflow as tf
from tensorflow import keras
Layer
класс: сочетание государственных (весы) и некоторые вычисления
Один из центральной абстракции Keras является Layer
класса. Слой инкапсулирует как состояние («веса» слоя), так и преобразование входных данных в выходные («вызов», прямой проход уровня).
Вот плотносвязный слой. Он имеет состояние: переменные w
и 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
Вы могли бы использовать слой, вызывая его на некоторых входных данных тензора, очень похоже на функцию 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)
Обратите внимание , что вес w
и b
автоматически отслеживаются слоем при задаются как атрибуты слоя:
assert linear_layer.weights == [linear_layer.w, linear_layer.b]
Обратите внимание , у вас также есть доступ к более быстрому ярлыка для добавления веса к слою: в 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.05790994 0.060931 -0.0402256 -0.09450993] [ 0.05790994 0.060931 -0.0402256 -0.09450993]], shape=(2, 4), dtype=float32)
Слои могут иметь необучаемые веса
Помимо обучаемых весов, вы также можете добавить к слою необучаемые веса. Такие веса не должны учитываться во время обратного распространения, когда вы тренируете слой.
Вот как добавить и использовать необучаемый вес:
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.]
Это часть layer.weights
, но он получает классифицируется как не обучаемый вес:
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: []
Лучшая практика: откладывание создания весов до тех пор, пока не станет известна форма входных данных.
Наши Linear
слой над принимал input_dim
аргумент , который был использован для вычисления формы весов w
и b
в __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
Во многих случаях вы можете не знать заранее размер ваших входных данных, и вы хотели бы лениво создавать веса, когда это значение становится известным, через некоторое время после создания экземпляра слоя.
В API Keras, мы рекомендуем создавать вес слоя в build(self, inputs_shape)
метода вашего слоя. Нравится:
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__()
метод вашего слоя будет автоматически запускаться построить первый раз , когда он называется. Теперь у вас есть ленивый слой, который проще в использовании:
# 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)
Реализация build()
отдельно , как показано выше , хорошо отделяет создание весов только один раз с использованием весов в каждом вызове. Однако для некоторых расширенных пользовательских слоев может оказаться непрактичным разделять создание состояния и вычисление. Исполнители слоя разрешены отложить создание веса до первого __call__()
, но нужно позаботиться о том , последующие вызовы используют одни и тот же вес. Кроме того, так как __call__()
, вероятно, будет выполнена в первый раз внутри tf.function
, любое создание переменной , которая имеет место в __call__()
должны быть завернуты в tf.init_scope
.
Слои рекурсивно компонуются
Если вы назначите экземпляр слоя как атрибут другого слоя, внешний слой начнет отслеживать веса, созданные внутренним слоем.
Мы рекомендуем создавать такие подуровни в __init__()
метод и оставить его на первый __call__()
для запуска здания их веса.
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()
метод
При написании call()
метода слоя, вы можете создать тензоры потери , которые вы хотите использовать в дальнейшем, при написании цикла обучения. Это выполнимо по телефону 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
Эти потери ( в том числе тех , которые созданы любой внутренний слой) могут быть получены с помощью layer.losses
. Это свойство сбрасывается в начале каждого __call__()
к слою верхнего уровня, так что layer.losses
всегда содержит значение потери , созданное в течение последнего прохода вперед.
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
Кроме того, loss
свойство также содержит потери регуляризации , созданные для весов любого внутреннего слоя:
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>]
Эти потери должны учитываться при написании тренировочных циклов, например:
# 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))
Для подробного руководства о написании учебных циклов, см руководство по написанию учебного цикла с нуля .
Эти потери также работать совместно с fit()
(они получают автоматически суммируются и добавляются к основной потери, если таковые имеются):
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()
метод
Аналогично add_loss()
, слои также имеют add_metric()
метод для отслеживания скользящего среднего значения некоторого количества во время тренировки.
Рассмотрим следующий уровень: уровень «логической конечной точки». Он принимает в качестве входных сигналов предсказания и цели, он вычисляет потери , которые он отслеживает с помощью add_loss()
, и он вычисляет точность скаляр, который она отслеживает с помощью 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)
Метрики отслеживаемые таким образом доступны через 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
Так же , как и для add_loss()
, эти показатели отслеживаются 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>
При желании вы можете включить сериализацию на своих слоях.
Если вам нужны пользовательские слои сериализовать в рамках функциональной модели можно дополнительно реализовать 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}
Обратите внимание , что __init__()
метод базового Layer
класса принимает некоторые именованные аргументы, в частности, name
и dtype
. Это хорошая практика , чтобы передать эти аргументы родительского класса в __init__()
и включить их в слой конфигурации:
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}
Если вам нужно больше гибкости при десериализации слоя от его конфигурации, вы можете также переопределить from_config()
метод класса. Это базовая реализация from_config()
:
def from_config(cls, config):
return cls(**config)
Чтобы узнать больше о сериализации и экономии, увидеть полное руководство по экономии и сериализации модели .
Льготное training
аргумент в call()
метод
Некоторые слои, в частности BatchNormalization
слой и Dropout
слой, имеют различные модели поведения во время тренировки и умозаключений. Для таких слоев, это является стандартной практикой подвергать training
(булево) аргумент в call()
метода.
Выставляя этот аргумент в call()
, включить встроенную в обучении и оценке контуров (например , fit()
) , чтобы правильно использовать слой в обучении и умозаключений.
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
Привилегированное mask
аргумент в call()
метод
Другой привилегированный аргумент поддерживается call()
является mask
аргумента.
Вы найдете его во всех слоях Keras RNN. Маска — это логический тензор (одно логическое значение на каждый временной шаг во входных данных), используемый для пропуска определенных входных временных шагов при обработке данных временных рядов.
Keras автоматически передаст правильные mask
аргумента __call__()
для слоев , которые поддерживают его, когда маска генерируются предыдущим слоем. Маска генерирующие слои являются Embedding
уровня , выполненный с mask_zero=True
, и Masking
слоя.
Чтобы узнать больше о маскирующих и как писать маскировки с поддержкой слоев, пожалуйста , проверьте руководство «понимание отступов и маскирующий» .
Model
класса
В общем, вы будете использовать Layer
класс для определения внутренних блоков вычислений, и будете использовать Model
класс для определения внешней модели - объект , который будет обучать.
Например, в модели ResNet50, вы бы несколько RESNET блоков подклассов Layer
, и один Model
, охватывающем всю сеть ResNet50.
Model
класс имеет тот же интерфейс , как Layer
, со следующими отличиями:
- Он выставляет встроенные циклы обучения, оценки и прогнозирования (
model.fit()
,model.evaluate()
,model.predict()
). - Он предоставляет список своих внутренних слоев, через
model.layers
свойства. - Он предоставляет сохранение и сериализации API (
save()
,save_weights()
...)
Эффективно, что Layer
класс соответствует тому , что мы называем в литературе как «слой» (как в «свертке слоя» или «рецидивирующий слой») , либо как «блок» (как в «RESNET блока» или «Начальный блок» ).
Между тем, Model
класса соответствует тому , что упоминается в литературе как «модель» (как в «глубокой модели обучения») или как «сеть» (как в «глубокой нейронной сети»).
Так что если вам интересно, «я должен использовать Layer
класс или Model
класса?», Спросите себя: мне нужно , чтобы позвонить fit()
на нем? Нужно ли мне позвонить save()
на нем? Если да, то идти с Model
. Если нет (либо потому , что ваш класс только блок в более крупной системе, или потому , что вы пишете обучение и сохранение коды самостоятельно), используйте Layer
.
Например, мы могли бы взять наш пример мини-RESNET выше, и использовать его , чтобы построить Model
, чтобы мы могли тренироваться с fit()
, и что мы могли бы спасти с 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)
Собираем все вместе: сквозной пример
Вот что вы узнали на данный момент:
-
Layer
инкапсулировать состояние (созданные в__init__()
илиbuild()
) и некоторые вычисления (определенные вcall()
). - Слои могут быть рекурсивно вложены друг в друга для создания новых, более крупных вычислительных блоков.
- Слои могут создавать и потерю дорожки ( как правило , потери регуляризирующих), а также показатели, с помощью
add_loss()
иadd_metric()
- Внешний контейнер, то , что вы хотите тренироваться, это
Model
.Model
точно так же какLayer
, но с дополнительными учебными и сериализации коммунальных услуг.
Давайте объединим все эти вещи в сквозной пример: мы собираемся реализовать вариационный автоэнкодер (VAE). Мы будем обучать его на цифрах MNIST.
Наш VAE будет подкласс Model
, построенной в виде вложенного состава слоев этого подкласса Layer
. Он будет иметь потерю регуляризации (расхождение 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
Напишем простой обучающий цикл на 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
Обратите внимание , что поскольку VAE является подклассов Model
, он имеет встроенный в учебных циклах. Таким образом, вы могли бы также обучить его следующим образом:
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>
Помимо объектно-ориентированной разработки: функциональный API
Был ли этот пример слишком много объектно-ориентированной разработки для вас? Кроме того, можно строить модели с использованием функционального API . Важно отметить, что выбор того или иного стиля не мешает вам использовать компоненты, написанные в другом стиле: вы всегда можете смешивать и сочетать.
Например, функциональный пример API , ниже кратное использования того же Sampling
слой мы определили в приведенном выше примере:
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>
Для получения дополнительной информации, не забудьте прочитать Functional руководство по API .