Trang này được dịch bởi Cloud Translation API.
Switch to English

Tạo các lớp mới & amp; Mô hình thông qua phân lớp

Xem trên TensorFlow.org Chạy trong Google Colab Xem nguồn trên GitHub Tải vở

Thiết lập

 import tensorflow as tf
from tensorflow import keras
 

Layer : sự kết hợp giữa trạng thái (trọng số) và một số tính toán

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

Đây là một lớp 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ố đầu vào tenor, 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.00892124  0.03003723  0.01141541 -0.13389507]
 [-0.00892124  0.03003723  0.01141541 -0.13389507]], shape=(2, 4), dtype=float32)

Lưu ý rằng các trọng số wb được lớp tự động theo dõi khi được đặt làm thuộc tính lớp:

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

Lưu ý rằng bạn cũng có quyền truy cập vào một phím tắt nhanh hơn để thêm trọng lượng vào một lớp: phương thức 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)

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

Bên cạnh trọng lượng có thể huấn luyện, bạn cũng có thể thêm trọng lượng không thể đào tạo 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 backpropagation, khi bạn đang đào tạo lớp.

Đây là cách thêm và sử dụng trọng lượng không thể luyện tập:

 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 là trọng lượng không thể đào tạo:

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

Thực hành tốt nhất: trì hoãn việc tạo trọng lượng cho đến khi hình dạng của đầu vào được biết đến

Lớp Linear của chúng tôi ở trên đã lấy một đối số input_dim được sử dụng để tính toán hình dạng của các trọng số 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 của đầu vào của mình và bạn muốn lười biếng tạo trọng số khi giá trị đó được biết đến, 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 số lớp trong phương thức build(self, inputs_shape) 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

 

Phương thức __call__() của lớp của bạn sẽ tự động chạy bản dựng lần đầu tiên khi 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)
 

Các lớp được tổng hợp đệ quy

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

Chúng tôi khuyên bạn nên tạo các lớp con như vậy trong phương thức __init__() (vì các lớp con thường sẽ có một phương thức xây dựng, chúng sẽ được xây dựng khi lớp ngoài được xây dựng).

 # 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

Phương thức add_loss()

Khi viết phương thức call() của một lớp, bạn có thể tạo các thang đo 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. Điều này có thể thực hiện được 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 mất mát này (bao gồm cả những tổn thất được tạo bởi bất kỳ lớp bên trong nào) có thể được truy xuất thông qua layer.losses . Thuộc tính này được đặt lại ở đầu mỗi __call__() thành lớp cấp cao nhất, để layer.losses luôn chứa các giá trị mất được tạo trong lần chuyển tiếp 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
 

Ngoài ra, thuộc tính loss cũng chứa các tổn thất chính quy được tạo cho các trọng số của bất kỳ lớp bên trong nào:

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

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

 # 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 lặp đào tạo, hãy xem hướng dẫn viết vòng lặp đào tạo từ đầu .

Những mất mát này cũng hoạt động trơn tru với fit() (chúng được tự động tính tổng và thêm vào tổn thấ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`, 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>

Phương thức add_metric()

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

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

 

Các số liệu được theo dõi theo cách này có thể truy cập 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: [<tensorflow.python.keras.metrics.BinaryAccuracy object at 0x7fa7f03601d0>]
current accuracy value: 1.0

Giống như đối với add_loss() , các số liệu này đượ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 1ms/step - loss: 0.9958 - binary_accuracy: 0.0000e+00

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

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 mình được tuần tự hóa như là một phần của mô hình Hàm , bạn có thể tùy ý triển khai phương thức 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}

Lưu ý rằng phương thức __init__() của Layer cơ sở có một số đối số từ khóa, đặc biệt là name và kiểu dtype . Đó là một cách thực hành tốt để truyền các đối số này cho lớp cha trong __init__() và đưa chúng vào 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 giải tuần tự hóa lớp từ cấu hình của nó, bạn cũng có thể ghi đè phương thức lớp from_config() . Đây là triển khai cơ sở của from_config() :

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

Để tìm hiểu thêm về tuần tự hóa và lưu, hãy xem hướng dẫn đầy đủ để lưu và tuần tự hóa các mô hình .

Đối số training đặc quyền trong phương thức call()

Một số lớp, đặc biệt là lớp BatchNormalization và lớp Dropout , có cá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, đó là cách thực hành tiêu chuẩn để đưa ra một đối số training (boolean) trong phương thức call() .

Bằng cách hiển thị đối số này trong call() , bạn kích hoạt các vòng lặp đào tạo và đánh giá tích hợp (ví dụ: fit() ) để sử dụng chính xác lớp trong đà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

 

Đối số mask đặc quyền trong phương thức call()

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

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

Máy ảnh sẽ tự động chuyển đối số mask chính xác cho __call__() cho các lớp hỗ trợ nó, khi mặt nạ được tạo bởi lớp trước. Các lớp tạo mặt nạ là lớp Embedding cấu hình với mask_zero=True và lớp Masking .

Để tìm hiểu thêm về mặt nạ và cách viết các lớp kích hoạt mặt nạ, vui lòng xem hướng dẫn "hiểu về đệm và mặt nạ" .

Lớp Model

Nói chung, bạn sẽ sử dụng Layer để xác định các khối tính toán bên trong và sẽ sử dụng lớp Model để 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.

Lớp Model có cùng API với Layer , với các khác biệt sau:

  • Nó hiển thị các vòng lặp đào tạo, đánh giá và dự đoán tích hợp ( model.fit() , model.evaluate() , model.predict() ).
  • Nó hiển thị danh sách các lớp bên trong của nó, thông qua thuộc tính model.layers .
  • Nó hiển thị các API lưu và tuần tự hóa ( save() , save_weights() ...)

Thực tế, Layer tương ứng với những gì chúng ta đề cập đến trong tài liệu là "lớp" (như trong "lớp chập" hoặc "lớp lặp lại") hoặc là "khối" (như trong "khối ResNet" hoặc "Khối khởi động" ).

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

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

Chẳng hạn, chúng ta có thể lấy ví dụ mini-resnet ở trên và sử dụng nó để xây dựng một Model mà chúng ta có thể đào tạo với fit() và chúng ta có thể lưu với 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)
 

Đặt 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 đóng gói một trạng thái (được tạo trong __init__() hoặc build() ) và một số tính toán (được xác định trong call() ).
  • Các lớp có thể được lồng theo cách đệ quy để tạo các khối tính toán mới, lớn hơn.
  • Các lớp có thể tạo và theo dõi tổn thất (thường là tổn thất thường hóa) cũng như số liệu, thông qua add_loss()add_metric()
  • Container bên ngoài, thứ bạn muốn đào tạo, là một Model . Một Model giống như một Layer , nhưng có thêm các tiện ích đào tạo và tuần tự hóa.

Chúng ta hãy kết hợp tất cả những điều này vào một ví dụ từ đầu đến cuối: chúng ta sẽ triển khai Bộ biến đổi 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 các lớp mà Layer con đó. Nó sẽ có một mất mát thường xuyên (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.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

Lưu ý rằng vì VAE là Model phân lớp, nó có các vòng lặp đào tạo tích hợp. 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 [==============================] - 2s 2ms/step - loss: 0.0749
Epoch 2/2
938/938 [==============================] - 2s 2ms/step - loss: 0.0676

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

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

Có phải ví dụ này là quá nhiều phát triển hướng đối tượng cho bạn? Bạn cũng có thể xây dựng các mô hình bằng API chức năng . Điều quan trọng, chọn kiểu này hay kiểu khác không ngă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ể trộn và kết hợp.

Chẳng hạn, ví dụ API chức năng bên dưới sử dụng lại cùng một lớp Sampling mà 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 [==============================] - 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>

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