דף זה תורגם על ידי Cloud Translation API.
Switch to English

יצירת שכבות ודגמים חדשים באמצעות תת-סיווג

צפה ב- TensorFlow.org הפעל בגוגל קולאב צפה במקור ב- GitHub הורד מחברת

להכין

import tensorflow as tf
from tensorflow import keras

מעמד Layer : שילוב של מצב (משקולות) וחישוב כלשהו

אחת ההפשטות המרכזיות בקרס היא מעמד 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.03354992  0.00427916 -0.15999871 -0.00302821]
 [-0.03354992  0.00427916 -0.15999871 -0.00302821]], 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.04145671 0.06744058 0.05072812 0.0403844 ]
 [0.04145671 0.06744058 0.05072812 0.0403844 ]], 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)

שכבות ניתנות להרכבה רקורסיבית

אם תקצה מופע שכבה כתכונה של שכבה אחרת, השכבה החיצונית תתחיל לעקוב אחר משקולות השכבה הפנימית.

אנו ממליצים ליצור __init__() משנה כאלה בשיטת __init__() (מכיוון __init__() המשנה תהיה בדרך כלל שיטת בנייה, הן ייבנו כאשר השכבה החיצונית תיבנה).

# 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

שיטת 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.0021053506>]

הפסדים אלה נועדו להילקח בחשבון בעת ​​כתיבת לולאות אימונים, כך:

# 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`, 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 122ms/step - loss: 0.1508
1/1 [==============================] - 0s 49ms/step - loss: 0.0365

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

שיטת 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.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 0x7f91d06ef0b8>]
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 254ms/step - loss: 0.9207 - binary_accuracy: 0.0000e+00

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

באפשרותך להפעיל אפשרות סידור בשכבות שלך

אם אתה זקוק get_config() השכבות המותאמות אישית שלך כחלק ממודל פונקציונלי , באפשרותך ליישם שיטת 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__() ולכלול אותם __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}

אם אתה זקוק לגמישות רבה יותר בעת ביטול from_config() של from_config() שלה, תוכל גם לעקוף את שיטת המחלקה 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 .

תוכלו למצוא אותו בכל שכבות RNN של Keras. מסכה היא טנסור בוליאני (ערך בוליאני לכל שלב בזמן בקלט) המשמש לדילוג על צעדים מסוימים של קלט בעת עיבוד נתוני סדרות זמן.

Keras יעביר אוטומטית את טיעון mask הנכון ל- __call__() עבור שכבות התומכות בו, כאשר מסכה נוצרת על ידי שכבה קודמת. שכבות מייצרות מסכות הן שכבת Embedding המוגדרת עם mask_zero=True ושכבת ה- Masking .

למידע נוסף על מיסוך וכיצד לכתוב שכבות המותאמות למסכה, עיין במדריך "הבנת ריפוד ומסכות" .

כיתת Model

באופן כללי, תשתמשו במחלקה Layer להגדרת בלוקי חישוב פנימיים, Model במחלקת Model להגדרת המודל החיצוני - האובייקט שתאמנו.

לדוגמא, במודל ResNet50, יהיו לך מספר בלוקים של ResNet המשנים Layer משנה, Model יחיד המקיף את כל רשת ResNet50.

Model אותו API כמו Layer , עם ההבדלים הבאים:

  • הוא חושף לולאות אימון, הערכה וחיזוי model.fit() , model.evaluate() , model.predict() ).
  • היא חושפת את רשימת השכבות הפנימיות שלה, באמצעות המאפיין model.layers .
  • הוא חושף ממשקי API של save() save_weights() save() , save_weights() ...)

למעשה, מחלקת Layer תואמת את מה שאנו מכנים בספרות כ"שכבה "(כמו" שכבת פיתול "או" שכבה חוזרת ") או כ"בלוק" (כמו "בלוק ResNet" או "בלוק התחלה"). ).

בינתיים, מחלקת Model תואמת את מה שמכונה בספרות "מודל" (כמו "מודל למידה עמוקה") או כ"רשת "(כמו" רשת עצבית עמוקה ").

אז אם אתה תוהה, "האם עלי להשתמש בכיתת Layer או בכיתת Model ?", שאל את עצמך: האם אצטרך לקרוא fit() עליו? האם אצטרך להתקשר אליו save() ? אם כן, עבור עם Model . אם לא (כיוון שהשיעור שלך הוא רק חסם במערכת גדולה יותר, או בגלל שאתה כותב אימונים ושומר קוד בעצמך), השתמש Layer .

למשל, נוכל לקחת את הדוגמה המיני-רשתית שלנו לעיל ולהשתמש בה לבניית Model שנוכל להתאמן fit() save_weights() לחסוך בעזרת 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)

לחבר את הכל ביחד: דוגמא מקצה לקצה

הנה מה שלמדת עד כה:

  • Layer עוטפת מצב (נוצר ב __init__() או build() ) וחישוב כלשהו (מוגדר call() ).
  • ניתן לקנן שכבות רקורסיבית כדי ליצור חסימות חישוב חדשות וגדולות יותר.
  • שכבות יכולות ליצור ולעקוב אחר הפסדים (בדרך כלל הפסדי add_loss() וכן מדדים, באמצעות add_loss() ו- add_metric()
  • המכולה החיצונית, הדבר שאתה רוצה לאמן, היא Model . Model הוא בדיוק כמו Layer , אך עם תוספות הדרכה וסידור נוסף.

בואו נרכיב את כל הדברים הללו כדוגמה מקצה לקצה: אנו הולכים ליישם 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.3442
step 100: mean loss = 0.1247
step 200: mean loss = 0.0988
step 300: mean loss = 0.0888
step 400: mean loss = 0.0841
step 500: mean loss = 0.0807
step 600: mean loss = 0.0786
step 700: mean loss = 0.0770
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.0739
step 200: mean loss = 0.0734
step 300: mean loss = 0.0730
step 400: mean loss = 0.0726
step 500: mean loss = 0.0722
step 600: mean loss = 0.0720
step 700: mean loss = 0.0717
step 800: mean loss = 0.0714
step 900: mean loss = 0.0712

שים לב שמכיוון ש- VAE משנה את 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.0944
Epoch 2/2
938/938 [==============================] - 2s 3ms/step - loss: 0.0676

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

מעבר לפיתוח מונחה עצמים: ה- 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.0961
Epoch 2/3
938/938 [==============================] - 2s 3ms/step - loss: 0.0677
Epoch 3/3
938/938 [==============================] - 2s 3ms/step - loss: 0.0676

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

למידע נוסף, הקפד לקרוא את המדריך לממשק ה- API הפונקציונלי .