![]() | ![]() | ![]() | ![]() |
يثبت
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
. AModel
هو تماما مثل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 الوظيفي .