GAN concept (generator and discriminator) in TensorFlow - ML Experiment: Train & Evaluate
import tensorflow as tf from tensorflow.keras import layers import numpy as np import matplotlib.pyplot as plt # Load MNIST data (train_images, _), (_, _) = tf.keras.datasets.mnist.load_data() train_images = train_images.reshape(-1, 28, 28, 1).astype('float32') train_images = (train_images - 127.5) / 127.5 # Normalize to [-1, 1] BUFFER_SIZE = 60000 BATCH_SIZE = 256 train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(BUFFER_SIZE).batch(BATCH_SIZE) # Generator model def make_generator_model(): model = tf.keras.Sequential([ layers.Dense(7*7*256, use_bias=False, input_shape=(100,)), layers.BatchNormalization(), layers.LeakyReLU(), layers.Reshape((7, 7, 256)), layers.Conv2DTranspose(128, (5,5), strides=(1,1), padding='same', use_bias=False), layers.BatchNormalization(), layers.LeakyReLU(), layers.Conv2DTranspose(64, (5,5), strides=(2,2), padding='same', use_bias=False), layers.BatchNormalization(), layers.LeakyReLU(), layers.Conv2DTranspose(1, (5,5), strides=(2,2), padding='same', use_bias=False, activation='tanh') ]) return model # Discriminator model with dropout and label smoothing def make_discriminator_model(): model = tf.keras.Sequential([ layers.Conv2D(64, (5,5), strides=(2,2), padding='same', input_shape=[28,28,1]), layers.LeakyReLU(), layers.Dropout(0.3), layers.Conv2D(128, (5,5), strides=(2,2), padding='same'), layers.LeakyReLU(), layers.Dropout(0.3), layers.Flatten(), layers.Dense(1) ]) return model # Loss functions cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True) def discriminator_loss(real_output, fake_output): real_labels = tf.ones_like(real_output) * 0.9 # label smoothing fake_labels = tf.zeros_like(fake_output) real_loss = cross_entropy(real_labels, real_output) fake_loss = cross_entropy(fake_labels, fake_output) total_loss = real_loss + fake_loss return total_loss def generator_loss(fake_output): fake_labels = tf.ones_like(fake_output) # want generator to fool discriminator return cross_entropy(fake_labels, fake_output) # Create models generator = make_generator_model() discriminator = make_discriminator_model() # Optimizers with different learning rates generator_optimizer = tf.keras.optimizers.Adam(1e-4) discriminator_optimizer = tf.keras.optimizers.Adam(5e-5) # slower discriminator # Training loop EPOCHS = 50 noise_dim = 100 num_examples_to_generate = 16 seed = tf.random.normal([num_examples_to_generate, noise_dim]) @tf.function def train_step(images): noise = tf.random.normal([BATCH_SIZE, noise_dim]) with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape: generated_images = generator(noise, training=True) real_output = discriminator(images, training=True) fake_output = discriminator(generated_images, training=True) gen_loss = generator_loss(fake_output) disc_loss = discriminator_loss(real_output, fake_output) gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables) gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables) generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables)) discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables)) return gen_loss, disc_loss # Training function import time def train(dataset, epochs): for epoch in range(epochs): start = time.time() gen_loss_avg = 0 disc_loss_avg = 0 batch_count = 0 for image_batch in dataset: gen_loss, disc_loss = train_step(image_batch) gen_loss_avg += gen_loss disc_loss_avg += disc_loss batch_count += 1 gen_loss_avg /= batch_count disc_loss_avg /= batch_count print(f'Epoch {epoch+1}, Generator loss: {gen_loss_avg:.4f}, Discriminator loss: {disc_loss_avg:.4f}, Time: {time.time()-start:.2f} sec') train(train_dataset, EPOCHS) # Generate and show images after training import matplotlib.pyplot as plt def generate_and_show_images(model, test_input): predictions = model(test_input, training=False) fig = plt.figure(figsize=(4,4)) for i in range(predictions.shape[0]): plt.subplot(4, 4, i+1) plt.imshow((predictions[i, :, :, 0] * 127.5 + 127.5).numpy().astype('uint8'), cmap='gray') plt.axis('off') plt.show() generate_and_show_images(generator, seed)
Before: Generator loss: 2.5, Discriminator loss: 0.1, Discriminator accuracy: 98%, Generated images noisy and poor quality.
After: Generator loss: ~1.0, Discriminator loss: ~0.7, Balanced discriminator accuracy, Generated images clearly resemble digits.
Practice
What is the main role of the generator in a GAN?
Solution
Step 1: Understand the generator's purpose
The generator's job is to produce new data that mimics real data.Step 2: Differentiate from discriminator
The discriminator checks data authenticity, but the generator creates fake data.Final Answer:
To create fake data that looks real -> Option BQuick Check:
Generator = create fake data [OK]
- Confusing generator with discriminator
- Thinking generator labels data
- Assuming generator reduces data size
Which of the following is the correct way to define a simple generator model in TensorFlow Keras?
def make_generator_model():
model = tf.keras.Sequential([
tf.keras.layers.Dense(7*7*256, use_bias=False, input_shape=(100,)),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.LeakyReLU(),
tf.keras.layers.Reshape((7, 7, 256)),
tf.keras.layers.Conv2DTranspose(128, 5, strides=1, padding='same', use_bias=False),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.LeakyReLU(),
tf.keras.layers.Conv2DTranspose(1, 5, strides=2, padding='same', activation='tanh')
])
return model
Solution
Step 1: Check model layers and input
The model starts with a Dense layer with input_shape=(100,), which is correct for noise input.Step 2: Verify output activation and structure
The last Conv2DTranspose uses 'tanh' activation, common for generator output scaled between -1 and 1.Final Answer:
The code correctly defines a generator model -> Option CQuick Check:
Generator model code is correct [OK]
- Forgetting input_shape in first layer
- Using wrong activation for output layer
- Enabling bias unnecessarily in Dense layer
Given the following code snippet for a discriminator model, what will be the output shape after the last Conv2D layer?
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(64, 5, strides=2, padding='same', input_shape=(28, 28, 1)),
tf.keras.layers.LeakyReLU(),
tf.keras.layers.Dropout(0.3),
tf.keras.layers.Conv2D(128, 5, strides=2, padding='same'),
tf.keras.layers.LeakyReLU(),
tf.keras.layers.Dropout(0.3),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(1)
])
Solution
Step 1: Calculate output after first Conv2D
Input is (28,28,1), stride=2, padding='same' halves spatial dims: (14,14,64).Step 2: Calculate output after second Conv2D
Again stride=2, padding='same' halves spatial dims: (7,7,128).Final Answer:
(7, 7, 128) -> Option AQuick Check:
Conv2D output shape = (7,7,128) [OK]
- Forgetting padding='same' keeps output size rounded up
- Misapplying stride effect on dimensions
- Confusing number of filters with spatial size
Identify the error in this GAN training step code snippet:
noise = tf.random.normal([batch_size, noise_dim])
generated_images = generator(noise, training=True)
real_output = discriminator(real_images, training=True)
fake_output = discriminator(generated_images, training=False)
loss = generator_loss(fake_output) + discriminator_loss(real_output, fake_output)
Solution
Step 1: Check discriminator training mode
Discriminator must be in training mode for both real and fake images to update weights properly.Step 2: Identify incorrect training flag
Here, discriminator(fake images) uses training=False, which disables training behavior like dropout.Final Answer:
Discriminator should be in training mode for fake images -> Option AQuick Check:
Discriminator training mode = True for all inputs [OK]
- Setting discriminator training=False on fake data
- Confusing generator training flag
- Incorrect noise shape assumptions
You want to improve a GAN to generate clearer images. Which combined approach is best?
- Increase generator depth and add batch normalization
- Use a weaker discriminator to avoid overpowering generator
- Train discriminator more times per generator update
- Remove dropout layers from discriminator
Solution
Step 1: Evaluate option 1
Increasing generator depth and adding batch normalization helps create clearer, stable images.Step 2: Evaluate option 3
Training discriminator more per generator update helps discriminator learn better, pushing generator to improve.Step 3: Assess options 2 and 4
Weaker discriminator or removing dropout can reduce training quality and cause poor results.Final Answer:
Options 1 and 3 together are best -> Option DQuick Check:
Improve generator + stronger discriminator training = better images [OK]
- Weakening discriminator hurts GAN balance
- Removing dropout may cause overfitting
- Ignoring batch normalization benefits
Ready to prove it?
Pass the test and this pattern moves unknown
