Tạo các Lớp và Mô hình mới thông qua phân lớp

Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.

Xem trên TensorFlow.org Chạy trong Google Colab Xem nguồn trên GitHub Tải xuống sổ ghi chép

Thành lập

import tensorflow as tf
from tensorflow import keras

Các Layer lớp: sự kết hợp của nhà nước (trọng lượng) và một số tính toán

Một trong những khái niệm trừu tượng trung tâm trong Keras là Layer lớp. Một lớp đóng gói cả trạng thái ("trọng số" của lớp) và sự chuyển đổi từ đầu vào thành đầu ra ("lệnh gọi", chuyển tiếp của lớp).

Đây là một lớp được kết nối dày đặc. Nó có một trạng thái: các biến wb .

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

Bạn sẽ sử dụng một lớp bằng cách gọi nó trên một số (các) đầu vào tensor, giống như một hàm 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)

Lưu ý rằng các trọng wb sẽ tự động được theo dõi bởi các lớp trên được thiết lập như là thuộc tính lớp:

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

Lưu ý: Bạn cũng có thể sử dụng một phím tắt nhanh hơn để thêm trọng lượng cho một lớp: các add_weight() phương pháp:

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)

Các lớp có thể có trọng lượng không thể đào tạo

Bên cạnh các trọng lượng có thể huấn luyện, bạn cũng có thể thêm các trọng lượng không thể huấn luyện vào một lớp. Trọng lượng như vậy có nghĩa là không được tính đến trong quá trình nhân giống ngược, khi bạn đang đào tạo lớp.

Dưới đây là cách thêm và sử dụng trọng lượng không thể đào tạo:

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

Đó là một phần của layer.weights , nhưng nó được phân loại như một trọng lượng không khả năng huấn luyện:

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

Phương pháp hay nhất: trì hoãn việc tạo trọng lượng cho đến khi biết hình dạng của các đầu vào

Chúng tôi Linear lớp trên mất một input_dim lập luận được sử dụng để tính toán hình dạng của khối lượng wb trong __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

Trong nhiều trường hợp, bạn có thể không biết trước kích thước đầu vào của mình và bạn muốn tạo trọng số một cách lười biếng khi biết giá trị đó, một thời gian sau khi khởi tạo lớp.

Trong API Keras, chúng tôi khuyên bạn nên tạo trọng lượng lớp trong build(self, inputs_shape) phương pháp của lớp của bạn. Như thế này:

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

Các __call__() phương pháp của lớp của bạn sẽ tự động chạy xây dựng lần đầu tiên nó được gọi. Bây giờ bạn có một lớp lười biếng và do đó dễ sử dụng hơn:

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

Thực hiện build() riêng biệt như trình bày ở trên độc đáo tách tạo trọng lượng chỉ một lần từ việc sử dụng trọng lượng trong mỗi cuộc gọi. Tuy nhiên, đối với một số lớp tùy chỉnh nâng cao, việc tách biệt giữa việc tạo và tính toán trạng thái có thể trở nên không thực tế. Lớp người thực hiện được phép trì hoãn việc tạo cân để là người đầu tiên __call__() , nhưng cần phải chăm sóc mà các cuộc gọi sau đó sử dụng các trọng số tương tự. Bên cạnh đó, kể từ __call__() có thể được thực hiện lần đầu tiên trong một tf.function , bất kỳ sự sáng tạo biến diễn ra trong __call__() nên được bọc trong một tf.init_scope .

Các lớp có thể kết hợp đệ quy

Nếu bạn chỉ định một cá thể Lớp làm thuộc tính của một Lớp khác, thì lớp bên ngoài sẽ bắt đầu theo dõi trọng số được tạo bởi lớp bên trong.

Chúng tôi khuyên bạn nên tạo lớp con như vậy trong __init__() phương pháp và rời khỏi nó để là người đầu tiên __call__() để kích hoạt việc xây dựng trọng lượng của họ.

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

Các add_loss() phương pháp

Khi viết các call() phương pháp của một lớp, bạn có thể tạo tensors mất mát mà bạn sẽ muốn sử dụng sau này, khi viết vòng lặp đào tạo của bạn. Đây là doable bằng cách gọi 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

Những thiệt hại (bao gồm cả những người tạo ra bởi bất kỳ lớp bên trong) có thể được truy xuất thông qua layer.losses . Khách sạn này được thiết lập lại khi bắt đầu mỗi __call__() vào lớp cấp cao nhất, do đó layer.losses luôn chứa các giá trị thiệt hại tạo ra trong quá về phía trước vượt qua cuối cùng.

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

Bên cạnh đó, loss tài sản cũng chứa lỗ theo quy tắc tạo ra cho các trọng của bất kỳ lớp bên trong:

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

Những tổn thất này có nghĩa là được tính đến khi viết các vòng lặp đào tạo, như sau:

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

Để có hướng dẫn chi tiết về cách viết vòng đào tạo, vui lòng xem hướng dẫn để viết một vòng lặp đào tạo từ đầu .

Những tổn thất này cũng làm việc liên tục với fit() (họ nhận được tự động tổng hợp và bổ sung vào sự mất mát chính, nếu có):

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>

Các add_metric() phương pháp

Tương tự như vậy để add_loss() , lớp cũng có một add_metric() phương pháp để theo dõi di chuyển trung bình của một số lượng quá trình đào tạo.

Hãy xem xét lớp sau: lớp "điểm cuối logistic". Phải mất như đầu vào dự đoán & mục tiêu, nó tính toán một sự mất mát mà nó theo dõi qua add_loss() , và nó sẽ tính toán một vô hướng chính xác, mà nó theo dõi qua 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 theo dõi theo cách này có thể truy cập thông qua 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

Cũng giống như đối với add_loss() , các chỉ số được theo dõi bởi 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>

Bạn có thể tùy chọn kích hoạt tuần tự hóa trên các lớp của mình

Nếu bạn cần các lớp tùy chỉnh của bạn sẽ được serializable như một phần của một mô hình chức năng , bạn có thể tùy chọn thực hiện một get_config() phương pháp:

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}

Lưu ý rằng __init__() phương pháp của các cơ sở Layer lớp mất một đối số từ khóa, trong một đặc biệt name và một dtype . Đó là thực hành tốt để vượt qua những đối số cho các tầng lớp phụ huynh trong __init__() và bao gồm chúng trong cấu hình lớp:

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}

Nếu bạn cần linh hoạt hơn khi deserializing lớp từ cấu hình của nó, bạn cũng có thể ghi đè lên from_config() phương pháp lớp. Đây là việc thực hiện cơ sở của from_config() :

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

Để tìm hiểu thêm về serialization và tiết kiệm, xem hoàn chỉnh dẫn đến tiết kiệm và serializing mô hình .

Ưu tiên training lý luận trong call() phương pháp

Một số lớp, đặc biệt là BatchNormalization lớp và Dropout lớp, có hành vi khác nhau trong quá trình đào tạo và suy luận. Đối với các lớp như vậy, nó là tiêu chuẩn thực hiện để lộ một training (boolean) lập luận trong call() phương pháp.

Bằng cách phơi bày lập luận này trong call() , bạn kích hoạt được xây dựng trong đào tạo và đánh giá vòng (ví dụ fit() ) để sử dụng một cách chính xác các lớp đào tạo và suy luận.

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

Privileged mask lập luận trong call() phương pháp

Đối số đặc quyền khác được hỗ trợ bởi call()mask cuộc tranh cãi.

Bạn sẽ tìm thấy nó trong tất cả các lớp Keras RNN. Mặt nạ là một tensor boolean (một giá trị boolean cho mỗi bước thời gian trong đầu vào) được sử dụng để bỏ qua các bước thời gian đầu vào nhất định khi xử lý dữ liệu thời gian.

Keras sẽ tự động chuyển đúng mask lập luận để __call__() cho các lớp có hỗ trợ nó, khi một mặt nạ được tạo ra bởi một lớp trước. Một layer mask-tạo là Embedding lớp cấu hình với mask_zero=True , và các Masking lớp.

Để tìm hiểu thêm về mặt nạ và làm thế nào để viết Masking-enabled lớp, xin vui lòng kiểm tra hướng dẫn "hiểu đệm và mặt nạ" .

Các Model lớp

Nói chung, bạn sẽ sử dụng Layer lớp để xác định khối tính toán bên trong, và sẽ sử dụng các Model lớp để xác định mô hình bên ngoài - đối tượng bạn sẽ đào tạo.

Ví dụ, trong một mô hình ResNet50, bạn sẽ có một vài khối ResNet subclassing Layer , và một đơn Model bao gồm toàn bộ mạng ResNet50.

Các Model lớp có API giống như Layer , với những khác biệt sau:

  • Nó cho thấy built-in vòng đào tạo, đánh giá và dự đoán ( model.fit() , model.evaluate() , model.predict() ).
  • Nó cho thấy danh sách các lớp bên trong của nó, thông qua model.layers tài sản.
  • Nó cho thấy tiết kiệm và API serialization ( save() , save_weights() ...)

Một cách hiệu quả, các Layer lớp tương ứng với những gì chúng tôi đề cập đến trong các tài liệu như là một "lớp" (như trong "lớp chập" hoặc "lớp tái phát") hoặc là một "khối" (như trong "ResNet khối" hoặc "chặn Inception" ).

Trong khi đó, Model lớp tương ứng với những gì được đề cập đến trong các tài liệu như là một "mô hình" (như trong "mô hình học tập sâu") hoặc là một "mạng" (như trong "mạng nơron sâu").

Vì vậy, nếu bạn đang tự hỏi: "Tôi nên sử dụng Layer lớp hoặc các Model lớp học?", Hãy tự hỏi: Tôi sẽ cần phải gọi fit() vào nó? Tôi sẽ cần phải gọi save() vào nó? Nếu vậy, đi với Model . Nếu không được (hoặc vì lớp học của bạn chỉ là một khối trong một hệ thống lớn hơn, hoặc bởi vì bạn đang viết đào tạo & tiết kiệm đang cho mình), sử dụng Layer .

Ví dụ, chúng ta có thể lấy một ví dụ nhỏ resnet chúng tôi ở trên, và sử dụng nó để xây dựng một Model mà chúng ta có thể tập luyện cùng fit() , và rằng chúng ta có thể tiết kiệm với 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)

Tổng hợp tất cả lại với nhau: một ví dụ từ đầu đến cuối

Đây là những gì bạn đã học được cho đến nay:

  • Một Layer gói gọn một trạng thái (tạo ra trong __init__() hoặc build() ) và một số tính toán (được định nghĩa trong call() ).
  • Các lớp có thể được lồng vào nhau một cách đệ quy để tạo ra các khối tính toán mới, lớn hơn.
  • Lớp có thể tạo và tổn thất theo dõi (thường thiệt hại theo quy tắc) cũng như các số liệu, thông qua add_loss()add_metric()
  • Container bên ngoài, những điều bạn muốn đào tạo, là một Model . Một Model là giống như một Layer , nhưng với tiện ích đào tạo và serialization thêm.

Hãy tập hợp tất cả những điều này lại với nhau thành một ví dụ từ đầu đến cuối: chúng ta sẽ triển khai Bộ mã hóa tự động biến đổi (VAE). Chúng tôi sẽ đào tạo nó trên các chữ số MNIST.

VAE của chúng tôi sẽ là một lớp con của Model , được xây dựng như một thành phần lồng nhau của lớp mà lớp con Layer . Nó sẽ có đặc điểm là mất điều hòa (phân kỳ 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

Hãy viết một vòng lặp đào tạo đơn giản trên 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

Lưu ý rằng kể từ khi VAE được subclassing Model , nó tính năng built-in vòng đào tạo. Vì vậy, bạn cũng có thể đã đào tạo nó như thế này:

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>

Ngoài phát triển hướng đối tượng: API chức năng

Ví dụ này có quá nhiều phát triển hướng đối tượng đối với bạn không? Bạn cũng có thể xây dựng mô hình sử dụng API chức năng . Quan trọng là, việc chọn kiểu này hay kiểu khác không ngăn cản bạn tận dụng các thành phần được viết theo kiểu khác: bạn luôn có thể mix-and-match.

Ví dụ, ví dụ API chức năng dưới đây lần sử dụng cùng một Sampling lớp chúng ta định nghĩa trong ví dụ trên:

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>

Để biết thêm thông tin, hãy chắc chắn để đọc hướng dẫn API chức năng .