ساخت لایه ها و مدل های جدید از طریق طبقه بندی فرعی

با مجموعه‌ها، منظم بمانید ذخیره و دسته‌بندی محتوا براساس اولویت‌های شما.

مشاهده در TensorFlow.org در Google Colab اجرا شود مشاهده منبع در GitHub دانلود دفترچه یادداشت

برپایی

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

شما می توانید یک لایه را با فراخوانی آن بر روی برخی ورودی(های) تانسور، بسیار شبیه به یک تابع پایتون، استفاده کنید.

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>

شما به صورت اختیاری می توانید سریال سازی را در لایه های خود فعال کنید

اگر شما نیاز به لایه های سفارشی خود را به serializable عنوان بخشی از یک مدل کاربردی ، شما به صورت اختیاری می تواند پیاده سازی 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}

اگر شما نیاز به انعطاف پذیری بیشتر در هنگام deserializing لایه از پیکربندی آن، شما همچنین می توانید نادیده گرفتن from_config() روش کلاس. این اجرای پایه است from_config() :

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

برای کسب اطلاعات بیشتر در مورد ترتیب و صرفه جویی، کامل را ببینید راهنمای صرفه جویی در و serialize کردن مدل .

ممتاز 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 لایه.

برای کسب اطلاعات بیشتر در مورد پوشش و نحوه ارسال لایه پوشش را فعال کنید، لطفا به راهنمای "درک padding و پوشش" .

Model کلاس

به طور کلی، شما را به استفاده از Layer کلاس برای تعریف بلوکهای محاسبات داخلی، خواهد شد و استفاده از Model کلاس به تعریف مدل بیرونی - شی به شما آموزش خواهد داد.

به عنوان مثال، در یک مدل ResNet50، شما می توانید چندین بلوک RESNET subclassing دارند Layer ، و یک Model جامع و فراگیر در کل شبکه ResNet50.

Model کلاس است API همان 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 است subclassing 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>

فراتر از توسعه شی گرا: Functional 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>

برای کسب اطلاعات بیشتر، مطمئن شوید که به خواندن راهنمای API کاربردی .