तिथि को रक्षित करें! Google I / O 18-20 मई को पंजीकृत करता है
इस पेज का अनुवाद Cloud Translation API से किया गया है.
Switch to English

स्क्रैच से एक ट्रेनिंग लूप लिखना

TensorFlow.org पर देखें Google Colab में चलाएं GitHub पर स्रोत देखें नोटबुक डाउनलोड करें

सेट अप

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np

परिचय

केरस डिफ़ॉल्ट प्रशिक्षण और मूल्यांकन लूप, fit() और evaluate() । उनका उपयोग अंतर्निहित प्रशिक्षण के साथ गाइड प्रशिक्षण और मूल्यांकन में शामिल है

यदि आप अभी भी fit() (उदाहरण के लिए, fit() का उपयोग करके GAN को प्रशिक्षित करने के लिए fit() अपने मॉडल के सीखने के एल्गोरिदम को अनुकूलित करना चाहते हैं, तो आप Model वर्ग को उप-वर्ग कर सकते हैं और अपनी स्वयं की train_step() विधि को लागू कर सकते हैं, जो fit() दौरान बार-बार कहा जाता fit() । यह गाइड में कवर किया गया है कि क्या fit() होता fit() में कस्टमाइज़ करना

अब, यदि आप प्रशिक्षण और मूल्यांकन पर बहुत निम्न-स्तरीय नियंत्रण चाहते हैं, तो आपको अपने स्वयं के प्रशिक्षण और मूल्यांकन छोरों को खरोंच से लिखना चाहिए। यह इस गाइड के बारे में है।

GradientTape का उपयोग करना: पहला एंड-टू-एंड उदाहरण

GradientTape स्कोप के अंदर एक मॉडल को कॉल करने से आप हानि मान के संबंध में परत के ट्रेन वेट के ग्रेडिएंट्स को पुनः प्राप्त कर सकते हैं। एक ऑप्टिमाइज़र उदाहरण का उपयोग करके, आप इन वैरिएबल को अपडेट करने के लिए इन ग्रेडिएंट का उपयोग कर सकते हैं (जिसे आप model.trainable_weights का उपयोग करके पुनः प्राप्त कर सकते हैं।

आइए एक साधारण MNIST मॉडल पर विचार करें:

inputs = keras.Input(shape=(784,), name="digits")
x1 = layers.Dense(64, activation="relu")(inputs)
x2 = layers.Dense(64, activation="relu")(x1)
outputs = layers.Dense(10, name="predictions")(x2)
model = keras.Model(inputs=inputs, outputs=outputs)

चलो एक कस्टम प्रशिक्षण पाश के साथ मिनी-बैच ढाल का उपयोग करके इसे प्रशिक्षित करें।

सबसे पहले, हमें एक ऑप्टिमाइज़र, एक हानि फ़ंक्शन और एक डेटासेट की आवश्यकता होने वाली है:

# Instantiate an optimizer.
optimizer = keras.optimizers.SGD(learning_rate=1e-3)
# Instantiate a loss function.
loss_fn = keras.losses.SparseCategoricalCrossentropy(from_logits=True)

# Prepare the training dataset.
batch_size = 64
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = np.reshape(x_train, (-1, 784))
x_test = np.reshape(x_test, (-1, 784))

# Reserve 10,000 samples for validation.
x_val = x_train[-10000:]
y_val = y_train[-10000:]
x_train = x_train[:-10000]
y_train = y_train[:-10000]

# Prepare the training dataset.
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size)

# Prepare the validation dataset.
val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))
val_dataset = val_dataset.batch(batch_size)

यहाँ हमारे प्रशिक्षण पाश है:

  • हम एक खोलने for पाश कि अवधियों को अधिक दोहराता
  • प्रत्येक युग के लिए, हम लूप के for खोलते हैं, जो डेटासेट पर, बैचों में पुनरावृत्त करता है
  • प्रत्येक बैच के लिए, हम एक GradientTape() स्कोप खोलते हैं
  • इस दायरे के अंदर, हम मॉडल (फॉरवर्ड पास) को कॉल करते हैं और नुकसान की गणना करते हैं
  • दायरे के बाहर, हम नुकसान के संबंध में मॉडल के वजन के ग्रेडिएंट्स को पुनः प्राप्त करते हैं
  • अंत में, हम ग्रेडिएंट्स के आधार पर मॉडल के वज़न को अपडेट करने के लिए ऑप्टिमाइज़र का उपयोग करते हैं
epochs = 2
for epoch in range(epochs):
    print("\nStart of epoch %d" % (epoch,))

    # Iterate over the batches of the dataset.
    for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):

        # Open a GradientTape to record the operations run
        # during the forward pass, which enables auto-differentiation.
        with tf.GradientTape() as tape:

            # Run the forward pass of the layer.
            # The operations that the layer applies
            # to its inputs are going to be recorded
            # on the GradientTape.
            logits = model(x_batch_train, training=True)  # Logits for this minibatch

            # Compute the loss value for this minibatch.
            loss_value = loss_fn(y_batch_train, logits)

        # Use the gradient tape to automatically retrieve
        # the gradients of the trainable variables with respect to the loss.
        grads = tape.gradient(loss_value, model.trainable_weights)

        # Run one step of gradient descent by updating
        # the value of the variables to minimize the loss.
        optimizer.apply_gradients(zip(grads, model.trainable_weights))

        # Log every 200 batches.
        if step % 200 == 0:
            print(
                "Training loss (for one batch) at step %d: %.4f"
                % (step, float(loss_value))
            )
            print("Seen so far: %s samples" % ((step + 1) * batch_size))
Start of epoch 0
Training loss (for one batch) at step 0: 153.8545
Seen so far: 64 samples
Training loss (for one batch) at step 200: 1.4767
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 1.4645
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 0.7049
Seen so far: 38464 samples

Start of epoch 1
Training loss (for one batch) at step 0: 0.9202
Seen so far: 64 samples
Training loss (for one batch) at step 200: 0.8473
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 0.6632
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 0.8758
Seen so far: 38464 samples

मेट्रिक्स की निम्न-स्तरीय हैंडलिंग

आइए इस बुनियादी लूप में मीट्रिक निगरानी जोड़ें।

आप स्क्रैच से लिखे गए ऐसे प्रशिक्षण लूप में अंतर्निहित मेट्रिक्स (या आपके द्वारा लिखे गए कस्टम) का पुन: उपयोग कर सकते हैं। यहाँ प्रवाह है:

  • लूप की शुरुआत में मीट्रिक को तुरंत
  • प्रत्येक बैच के बाद metric.update_state() कॉल करें
  • जब आपको मीट्रिक के वर्तमान मूल्य को प्रदर्शित करने की आवश्यकता हो, तो metric.result() कॉल करें
  • जब आपको मीट्रिक की स्थिति (आमतौर पर एक युग के अंत में) को साफ़ करने की आवश्यकता हो, तो metric.reset_states() कॉल करें

चलो प्रत्येक युग के अंत में सत्यापन डेटा पर SparseCategoricalAccuracy गणना करने के लिए इस ज्ञान का उपयोग करें:

# Get model
inputs = keras.Input(shape=(784,), name="digits")
x = layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = layers.Dense(10, name="predictions")(x)
model = keras.Model(inputs=inputs, outputs=outputs)

# Instantiate an optimizer to train the model.
optimizer = keras.optimizers.SGD(learning_rate=1e-3)
# Instantiate a loss function.
loss_fn = keras.losses.SparseCategoricalCrossentropy(from_logits=True)

# Prepare the metrics.
train_acc_metric = keras.metrics.SparseCategoricalAccuracy()
val_acc_metric = keras.metrics.SparseCategoricalAccuracy()

यहां हमारा प्रशिक्षण और मूल्यांकन लूप है:

import time

epochs = 2
for epoch in range(epochs):
    print("\nStart of epoch %d" % (epoch,))
    start_time = time.time()

    # Iterate over the batches of the dataset.
    for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):
        with tf.GradientTape() as tape:
            logits = model(x_batch_train, training=True)
            loss_value = loss_fn(y_batch_train, logits)
        grads = tape.gradient(loss_value, model.trainable_weights)
        optimizer.apply_gradients(zip(grads, model.trainable_weights))

        # Update training metric.
        train_acc_metric.update_state(y_batch_train, logits)

        # Log every 200 batches.
        if step % 200 == 0:
            print(
                "Training loss (for one batch) at step %d: %.4f"
                % (step, float(loss_value))
            )
            print("Seen so far: %d samples" % ((step + 1) * batch_size))

    # Display metrics at the end of each epoch.
    train_acc = train_acc_metric.result()
    print("Training acc over epoch: %.4f" % (float(train_acc),))

    # Reset training metrics at the end of each epoch
    train_acc_metric.reset_states()

    # Run a validation loop at the end of each epoch.
    for x_batch_val, y_batch_val in val_dataset:
        val_logits = model(x_batch_val, training=False)
        # Update val metrics
        val_acc_metric.update_state(y_batch_val, val_logits)
    val_acc = val_acc_metric.result()
    val_acc_metric.reset_states()
    print("Validation acc: %.4f" % (float(val_acc),))
    print("Time taken: %.2fs" % (time.time() - start_time))
Start of epoch 0
Training loss (for one batch) at step 0: 114.3453
Seen so far: 64 samples
Training loss (for one batch) at step 200: 2.2635
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 0.5206
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 1.0906
Seen so far: 38464 samples
Training acc over epoch: 0.7022
Validation acc: 0.7853
Time taken: 5.38s

Start of epoch 1
Training loss (for one batch) at step 0: 0.5879
Seen so far: 64 samples
Training loss (for one batch) at step 200: 0.9477
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 0.4649
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 0.6874
Seen so far: 38464 samples
Training acc over epoch: 0.8114
Validation acc: 0.8291
Time taken: 5.46s

tf.function साथ अपने प्रशिक्षण कदम को tf.function

TensorFlow 2.0 में डिफ़ॉल्ट रनटाइम उत्सुक निष्पादन है । जैसे, ऊपर हमारे प्रशिक्षण पाश उत्सुकता से निष्पादित करता है।

यह डीबगिंग के लिए बहुत अच्छा है, लेकिन ग्राफ़ संकलन का एक निश्चित प्रदर्शन लाभ है। अपनी गणना को एक स्थिर ग्राफ़ के रूप में वर्णन करने से वैश्विक प्रदर्शन अनुकूलन को लागू करने में रूपरेखा सक्षम होती है। यह असंभव है जब रूपरेखा एक के बाद एक ऑपरेशन को लालच से निष्पादित करने के लिए विवश होती है, जिसके बारे में कोई जानकारी नहीं होती है कि आगे क्या आता है।

आप एक स्टैटिक ग्राफ को किसी भी फंक्शन में संकलित कर सकते हैं जो इनपुट के रूप में टेंसर्स लेता है। बस इस पर एक @tf.function डेकोरेटर जोड़ें, जैसे:

@tf.function
def train_step(x, y):
    with tf.GradientTape() as tape:
        logits = model(x, training=True)
        loss_value = loss_fn(y, logits)
    grads = tape.gradient(loss_value, model.trainable_weights)
    optimizer.apply_gradients(zip(grads, model.trainable_weights))
    train_acc_metric.update_state(y, logits)
    return loss_value

आइए मूल्यांकन चरण के साथ ऐसा ही करें:

@tf.function
def test_step(x, y):
    val_logits = model(x, training=False)
    val_acc_metric.update_state(y, val_logits)

अब, इस संकलित प्रशिक्षण कदम के साथ अपने प्रशिक्षण पाश को फिर से चलाएं:

import time

epochs = 2
for epoch in range(epochs):
    print("\nStart of epoch %d" % (epoch,))
    start_time = time.time()

    # Iterate over the batches of the dataset.
    for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):
        loss_value = train_step(x_batch_train, y_batch_train)

        # Log every 200 batches.
        if step % 200 == 0:
            print(
                "Training loss (for one batch) at step %d: %.4f"
                % (step, float(loss_value))
            )
            print("Seen so far: %d samples" % ((step + 1) * batch_size))

    # Display metrics at the end of each epoch.
    train_acc = train_acc_metric.result()
    print("Training acc over epoch: %.4f" % (float(train_acc),))

    # Reset training metrics at the end of each epoch
    train_acc_metric.reset_states()

    # Run a validation loop at the end of each epoch.
    for x_batch_val, y_batch_val in val_dataset:
        test_step(x_batch_val, y_batch_val)

    val_acc = val_acc_metric.result()
    val_acc_metric.reset_states()
    print("Validation acc: %.4f" % (float(val_acc),))
    print("Time taken: %.2fs" % (time.time() - start_time))
Start of epoch 0
Training loss (for one batch) at step 0: 0.4854
Seen so far: 64 samples
Training loss (for one batch) at step 200: 0.5259
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 0.5035
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 0.2240
Seen so far: 38464 samples
Training acc over epoch: 0.8502
Validation acc: 0.8616
Time taken: 1.32s

Start of epoch 1
Training loss (for one batch) at step 0: 0.6278
Seen so far: 64 samples
Training loss (for one batch) at step 200: 0.3667
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 0.3374
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 0.5318
Seen so far: 38464 samples
Training acc over epoch: 0.8709
Validation acc: 0.8720
Time taken: 1.02s

बहुत तेज, है ना?

मॉडल द्वारा ट्रैक किए गए नुकसान की कम-स्तरीय हैंडलिंग

परतें और मॉडल आगे ले जाने के दौरान बनाए गए किसी भी नुकसान को परतों से ट्रैक करते हैं जो self.add_loss(value) कॉल self.add_loss(value) । स्केलर हानि मानों की परिणामी सूची संपत्ति model.losses माध्यम से उपलब्ध हैं। फॉरवर्ड पास के अंत में स्थित हैं।

यदि आप इन नुकसान घटकों का उपयोग करना चाहते हैं, तो आपको उन्हें योग करना चाहिए और उन्हें अपने प्रशिक्षण चरण में मुख्य नुकसान में जोड़ना चाहिए।

इस परत पर विचार करें, जिससे एक गतिविधि नियमितीकरण हानि होती है:

class ActivityRegularizationLayer(layers.Layer):
    def call(self, inputs):
        self.add_loss(1e-2 * tf.reduce_sum(inputs))
        return inputs

चलो एक बहुत ही सरल मॉडल बनाते हैं जो इसका उपयोग करता है:

inputs = keras.Input(shape=(784,), name="digits")
x = layers.Dense(64, activation="relu")(inputs)
# Insert activity regularization as a layer
x = ActivityRegularizationLayer()(x)
x = layers.Dense(64, activation="relu")(x)
outputs = layers.Dense(10, name="predictions")(x)

model = keras.Model(inputs=inputs, outputs=outputs)

यहां बताया गया है कि हमारे प्रशिक्षण कदम को अब क्या देखना चाहिए:

@tf.function
def train_step(x, y):
    with tf.GradientTape() as tape:
        logits = model(x, training=True)
        loss_value = loss_fn(y, logits)
        # Add any extra losses created during the forward pass.
        loss_value += sum(model.losses)
    grads = tape.gradient(loss_value, model.trainable_weights)
    optimizer.apply_gradients(zip(grads, model.trainable_weights))
    train_acc_metric.update_state(y, logits)
    return loss_value

सारांश

अब आप सब कुछ जानते हैं कि अंतर्निहित प्रशिक्षण छोरों का उपयोग करने और खरोंच से अपना खुद का लिखने के बारे में जानना है।

निष्कर्ष निकालने के लिए, यहां एक सरल एंड-टू-एंड उदाहरण है जो इस गाइड में आपके द्वारा सीखी गई सभी चीजों को एक साथ जोड़ता है: एक डीसीजीएन जो एमएनआईएसटी अंकों पर प्रशिक्षित है।

एंड-टू-एंड उदाहरण: खरोंच से एक गण प्रशिक्षण पाश

आप जनरेटिव एडवरसरी नेटवर्क्स (GANs) से परिचित हो सकते हैं। GANs छवियों के एक प्रशिक्षण डेटासेट के अव्यक्त वितरण (छवियों के "अव्यक्त स्थान") को सीखकर, लगभग वास्तविक दिखने वाली नई छवियां उत्पन्न कर सकते हैं।

GAN दो भागों से बना होता है: एक "जनरेटर" मॉडल जो छवि स्थान में बिंदुओं के लिए अव्यक्त स्थान में मैप करता है, एक "विभेदक" मॉडल, एक क्लासिफायरियर जो वास्तविक छवियों (प्रशिक्षण डेटासेट से) और नकली के बीच अंतर बता सकता है छवियाँ (जनरेटर नेटवर्क का उत्पादन)।

एक GAN ट्रेनिंग लूप इस तरह दिखता है:

1) विवेचक को प्रशिक्षित करें। - अव्यक्त स्थान में यादृच्छिक बिंदुओं के एक बैच का नमूना। - "जनरेटर" मॉडल के माध्यम से नकली छवियों में बिंदुओं को मोड़ें। - वास्तविक छवियों का एक बैच प्राप्त करें और उन्हें उत्पन्न छवियों के साथ संयोजित करें। - उत्पन्न बनाम वास्तविक छवियों को वर्गीकृत करने के लिए "विभेदक" मॉडल को प्रशिक्षित करें।

2) जनरेटर को प्रशिक्षित करें। - अव्यक्त स्थान में यादृच्छिक अंक का नमूना। - "जनरेटर" नेटवर्क के माध्यम से नकली छवियों में बिंदुओं को चालू करें। - वास्तविक छवियों का एक बैच प्राप्त करें और उन्हें उत्पन्न छवियों के साथ संयोजित करें। - "जनरेटर" मॉडल को भेदभावकर्ता को "मूर्ख" करने के लिए प्रशिक्षित करें और नकली छवियों को वास्तविक के रूप में वर्गीकृत करें।

GANs कैसे काम करता है, इसके अधिक विस्तृत अवलोकन के लिए, पायथन के साथ डीप लर्निंग देखें।

आइए इस प्रशिक्षण लूप को लागू करें। सबसे पहले, नकली बनाम वास्तविक अंकों को वर्गीकृत करने के लिए विवेचक बनाएं:

discriminator = keras.Sequential(
    [
        keras.Input(shape=(28, 28, 1)),
        layers.Conv2D(64, (3, 3), strides=(2, 2), padding="same"),
        layers.LeakyReLU(alpha=0.2),
        layers.Conv2D(128, (3, 3), strides=(2, 2), padding="same"),
        layers.LeakyReLU(alpha=0.2),
        layers.GlobalMaxPooling2D(),
        layers.Dense(1),
    ],
    name="discriminator",
)
discriminator.summary()
Model: "discriminator"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 14, 14, 64)        640       
_________________________________________________________________
leaky_re_lu (LeakyReLU)      (None, 14, 14, 64)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 7, 7, 128)         73856     
_________________________________________________________________
leaky_re_lu_1 (LeakyReLU)    (None, 7, 7, 128)         0         
_________________________________________________________________
global_max_pooling2d (Global (None, 128)               0         
_________________________________________________________________
dense_4 (Dense)              (None, 1)                 129       
=================================================================
Total params: 74,625
Trainable params: 74,625
Non-trainable params: 0
_________________________________________________________________

फिर एक जनरेटर नेटवर्क बनाते हैं, जो अव्यक्त वैक्टर को आकार के आउटपुट (28, 28, 1) में बदलता है (28, 28, 1) MNIST अंकों का प्रतिनिधित्व करता है):

latent_dim = 128

generator = keras.Sequential(
    [
        keras.Input(shape=(latent_dim,)),
        # We want to generate 128 coefficients to reshape into a 7x7x128 map
        layers.Dense(7 * 7 * 128),
        layers.LeakyReLU(alpha=0.2),
        layers.Reshape((7, 7, 128)),
        layers.Conv2DTranspose(128, (4, 4), strides=(2, 2), padding="same"),
        layers.LeakyReLU(alpha=0.2),
        layers.Conv2DTranspose(128, (4, 4), strides=(2, 2), padding="same"),
        layers.LeakyReLU(alpha=0.2),
        layers.Conv2D(1, (7, 7), padding="same", activation="sigmoid"),
    ],
    name="generator",
)

यहाँ कुंजी बिट है: प्रशिक्षण लूप। जैसा कि आप देख सकते हैं यह काफी सीधा है। प्रशिक्षण चरण फ़ंक्शन केवल 17 लाइनें लेता है।

# Instantiate one optimizer for the discriminator and another for the generator.
d_optimizer = keras.optimizers.Adam(learning_rate=0.0003)
g_optimizer = keras.optimizers.Adam(learning_rate=0.0004)

# Instantiate a loss function.
loss_fn = keras.losses.BinaryCrossentropy(from_logits=True)


@tf.function
def train_step(real_images):
    # Sample random points in the latent space
    random_latent_vectors = tf.random.normal(shape=(batch_size, latent_dim))
    # Decode them to fake images
    generated_images = generator(random_latent_vectors)
    # Combine them with real images
    combined_images = tf.concat([generated_images, real_images], axis=0)

    # Assemble labels discriminating real from fake images
    labels = tf.concat(
        [tf.ones((batch_size, 1)), tf.zeros((real_images.shape[0], 1))], axis=0
    )
    # Add random noise to the labels - important trick!
    labels += 0.05 * tf.random.uniform(labels.shape)

    # Train the discriminator
    with tf.GradientTape() as tape:
        predictions = discriminator(combined_images)
        d_loss = loss_fn(labels, predictions)
    grads = tape.gradient(d_loss, discriminator.trainable_weights)
    d_optimizer.apply_gradients(zip(grads, discriminator.trainable_weights))

    # Sample random points in the latent space
    random_latent_vectors = tf.random.normal(shape=(batch_size, latent_dim))
    # Assemble labels that say "all real images"
    misleading_labels = tf.zeros((batch_size, 1))

    # Train the generator (note that we should *not* update the weights
    # of the discriminator)!
    with tf.GradientTape() as tape:
        predictions = discriminator(generator(random_latent_vectors))
        g_loss = loss_fn(misleading_labels, predictions)
    grads = tape.gradient(g_loss, generator.trainable_weights)
    g_optimizer.apply_gradients(zip(grads, generator.trainable_weights))
    return d_loss, g_loss, generated_images

चलो छवियों के बैचों पर बार-बार train_step कॉल करके, हमारे GAN को प्रशिक्षित करें।

चूँकि हमारे विवेचक और जनरेटर काफिले हैं, आप इस कोड को GPU पर चलाना चाहते हैं।

import os

# Prepare the dataset. We use both the training & test MNIST digits.
batch_size = 64
(x_train, _), (x_test, _) = keras.datasets.mnist.load_data()
all_digits = np.concatenate([x_train, x_test])
all_digits = all_digits.astype("float32") / 255.0
all_digits = np.reshape(all_digits, (-1, 28, 28, 1))
dataset = tf.data.Dataset.from_tensor_slices(all_digits)
dataset = dataset.shuffle(buffer_size=1024).batch(batch_size)

epochs = 1  # In practice you need at least 20 epochs to generate nice digits.
save_dir = "./"

for epoch in range(epochs):
    print("\nStart epoch", epoch)

    for step, real_images in enumerate(dataset):
        # Train the discriminator & generator on one batch of real images.
        d_loss, g_loss, generated_images = train_step(real_images)

        # Logging.
        if step % 200 == 0:
            # Print metrics
            print("discriminator loss at step %d: %.2f" % (step, d_loss))
            print("adversarial loss at step %d: %.2f" % (step, g_loss))

            # Save one generated image
            img = tf.keras.preprocessing.image.array_to_img(
                generated_images[0] * 255.0, scale=False
            )
            img.save(os.path.join(save_dir, "generated_img" + str(step) + ".png"))

        # To limit execution time we stop after 10 steps.
        # Remove the lines below to actually train the model!
        if step > 10:
            break
Start epoch 0
discriminator loss at step 0: 0.68
adversarial loss at step 0: 0.67

इतना ही! Colab GPU पर प्रशिक्षण के ~ 30s के बाद आपको अच्छे दिखने वाले नकली MNIST अंक मिलेंगे।