عمل طبقات ونماذج جديدة عن طريق التصنيف الفرعي

تنظيم صفحاتك في مجموعات يمكنك حفظ المحتوى وتصنيفه حسب إعداداتك المفضّلة.

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

يثبت

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)

يمكن أن تحتوي الطبقات على أوزان غير قابلة للتدريب

إلى جانب الأوزان القابلة للتدريب ، يمكنك إضافة أوزان غير قابلة للتدريب إلى طبقة أيضًا. لا يُقصد بهذه الأوزان أن تؤخذ في الاعتبار أثناء backpropagation ، عندما تقوم بتدريب الطبقة.

إليك كيفية إضافة واستخدام وزن غير قابل للتدريب:

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}

اذا كنت بحاجة الى مزيد من المرونة عند deserializing طبقة من التكوين بها، يمكنك أيضا تجاوز 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 إن شاء subclasses ترث Layer ، واحد Model يشمل شبكة ResNet50 بأكملها.

و Model فئة لديها نفس API كما Layer ، مع الاختلافات التالية:

  • يعرض بنيت في حلقات التدريب والتقييم والتنبؤ ( model.fit() ، model.evaluate() ، model.predict() ).
  • وإنما يعرض قائمة من طبقات الداخلية، عن طريق model.layers الممتلكات.
  • وإنما يعرض توفير واجهات برمجة التطبيقات التسلسل ( 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)

وضع كل ذلك معًا: مثال شامل

هذا ما تعلمته حتى الآن:

  • A Layer تلخص حالة (تم إنشاؤه في __init__() أو build() ) وبعض الحساب (المحددة في call() ).
  • يمكن أن تتداخل الطبقات بشكل متكرر لإنشاء كتل حساب جديدة أكبر.
  • طبقات يمكن أن تخلق وخسائر المسار (عادة خسائر التنظيم)، وكذلك المقاييس، عبر add_loss() و add_metric()
  • الحاوية الخارجية، الشيء الذي تريد لتدريب، هو Model . A Model هو تماما مثل Layer ، ولكن مع واضاف التدريب والتسلسل المرافق.

دعنا نضع كل هذه الأشياء معًا في مثال شامل: سنقوم بتطبيق Variational AutoEncoder (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 وإن شاء subclasses ترث 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 الوظيفي أدناه الاستخدامات الجديدة نفس 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 الوظيفي .