A GAN training loop teaches two models to work together: one creates fake data, the other learns to spot fakes. This helps computers make new, realistic data like images or sounds.
GAN training loop in PyTorch
for epoch in range(num_epochs): for real_data in dataloader: batch_size = real_data.size(0) noise = torch.randn(batch_size, noise_dim) # Train Discriminator optimizer_d.zero_grad() real_output = discriminator(real_data) fake_data = generator(noise) fake_output = discriminator(fake_data.detach()) loss_d_real = loss_function(real_output, torch.ones_like(real_output)) loss_d_fake = loss_function(fake_output, torch.zeros_like(fake_output)) loss_d = loss_d_real + loss_d_fake loss_d.backward() optimizer_d.step() # Train Generator optimizer_g.zero_grad() fake_output = discriminator(fake_data) loss_g = loss_function(fake_output, torch.ones_like(fake_output)) loss_g.backward() optimizer_g.step()
The loop trains the discriminator first, then the generator.
Detach is used to avoid updating the generator when training the discriminator.
for epoch in range(5): for real_images in dataloader: batch_size = real_images.size(0) noise = torch.randn(batch_size, noise_dim) fake_images = generator(noise) # Train discriminator and generator here
optimizer_d.zero_grad() real_loss = criterion(discriminator(real_data), real_labels) fake_loss = criterion(discriminator(fake_data.detach()), fake_labels) loss_d = real_loss + fake_loss loss_d.backward() optimizer_d.step()
This code trains a simple GAN with a tiny generator and discriminator on fake 2D points. It prints losses for both models each epoch.
import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader, TensorDataset # Simple Generator class Generator(nn.Module): def __init__(self): super().__init__() self.net = nn.Sequential( nn.Linear(10, 16), nn.ReLU(), nn.Linear(16, 2), nn.Tanh() ) def forward(self, x): return self.net(x) # Simple Discriminator class Discriminator(nn.Module): def __init__(self): super().__init__() self.net = nn.Sequential( nn.Linear(2, 16), nn.ReLU(), nn.Linear(16, 1), nn.Sigmoid() ) def forward(self, x): return self.net(x) # Create fake real data: points near (1,1) real_data = torch.randn(100, 2) * 0.1 + 1 real_labels = torch.ones(100, 1) fake_labels = torch.zeros(100, 1) # DataLoader dataset = TensorDataset(real_data, real_labels) dataloader = DataLoader(dataset, batch_size=10, shuffle=True) # Initialize models generator = Generator() discriminator = Discriminator() # Loss and optimizers criterion = nn.BCELoss() optimizer_d = optim.Adam(discriminator.parameters(), lr=0.01) optimizer_g = optim.Adam(generator.parameters(), lr=0.01) num_epochs = 3 noise_dim = 10 for epoch in range(num_epochs): for real_batch, _ in dataloader: batch_size = real_batch.size(0) # Train Discriminator optimizer_d.zero_grad() real_output = discriminator(real_batch) real_loss = criterion(real_output, torch.ones_like(real_output)) noise = torch.randn(batch_size, noise_dim) fake_data = generator(noise) fake_output = discriminator(fake_data.detach()) fake_loss = criterion(fake_output, torch.zeros_like(fake_output)) loss_d = real_loss + fake_loss loss_d.backward() optimizer_d.step() # Train Generator optimizer_g.zero_grad() fake_output = discriminator(fake_data) loss_g = criterion(fake_output, torch.ones_like(fake_output)) loss_g.backward() optimizer_g.step() print(f"Epoch {epoch+1}/{num_epochs} - Loss D: {loss_d.item():.4f}, Loss G: {loss_g.item():.4f}")
Use .detach() on fake data when training the discriminator to avoid updating the generator.
Generator tries to fool the discriminator by making fake data look real.
Discriminator tries to correctly tell real from fake data.
A GAN training loop alternates training two models: discriminator and generator.
Discriminator learns to spot fake data, generator learns to create better fakes.
Loss values show how well each model is doing during training.
Practice
Solution
Step 1: Understand the discriminator's purpose
The discriminator's job is to tell apart real data from fake data created by the generator.Step 2: Differentiate roles in GAN
The generator creates fake data, while the discriminator judges it. It does not generate data or adjust learning rates.Final Answer:
To distinguish between real and fake data -> Option CQuick Check:
Discriminator = spot fake data [OK]
- Confusing discriminator with generator
- Thinking discriminator generates data
- Assuming discriminator sets learning rate
Solution
Step 1: Recall PyTorch optimizer method
In PyTorch, the method to clear gradients before backpropagation is zero_grad().Step 2: Check each option's validity
Only optimizer_G.zero_grad(); optimizer_D.zero_grad() uses zero_grad(), which is the correct method. Other options use non-existent methods.Final Answer:
optimizer_G.zero_grad(); optimizer_D.zero_grad() -> Option BQuick Check:
Clear gradients = zero_grad() [OK]
- Using non-existent optimizer methods
- Forgetting to zero gradients before backward
- Confusing zero_grad() with optimizer step()
print(loss_D.item(), loss_G.item()) after one iteration?real_data = torch.ones(4) fake_data = torch.zeros(4) criterion = torch.nn.BCELoss() # Discriminator output output_real = torch.tensor([0.9, 0.8, 0.95, 0.85]) output_fake = torch.tensor([0.1, 0.2, 0.05, 0.15]) loss_real = criterion(output_real, real_data) loss_fake = criterion(output_fake, fake_data) loss_D = (loss_real + loss_fake) / 2 # Generator tries to fool discriminator output_fake_for_G = torch.tensor([0.4, 0.3, 0.5, 0.6]) loss_G = criterion(output_fake_for_G, real_data) print(loss_D.item(), loss_G.item())
Solution
Step 1: Calculate discriminator loss
Loss_real = BCELoss([0.9,0.8,0.95,0.85], [1,1,1,1]) ≈ 0.136; Loss_fake = BCELoss([0.1,0.2,0.05,0.15], [0,0,0,0]) ≈ 0.136; Average loss_D ≈ 0.136.Step 2: Calculate generator loss
Generator tries to fool discriminator, so loss_G = BCELoss([0.4,0.3,0.5,0.6], [1,1,1,1]) ≈ 0.831.Final Answer:
Approximately 0.136 and 0.831 -> Option DQuick Check:
Discriminator loss < Generator loss [OK]
- Mixing up real and fake labels in loss
- Confusing which loss belongs to generator
- Ignoring averaging of discriminator losses
for real_data in dataloader:
optimizer_D.zero_grad()
output_real = discriminator(real_data)
loss_real = criterion(output_real, torch.ones_like(output_real))
fake_data = generator(torch.randn(batch_size, noise_dim))
output_fake = discriminator(fake_data.detach())
loss_fake = criterion(output_fake, torch.zeros_like(output_fake))
loss_D = loss_real + loss_fake
loss_D.backward()
optimizer_D.step()
optimizer_G.zero_grad()
output_fake_for_G = discriminator(fake_data)
loss_G = criterion(output_fake_for_G, torch.ones_like(output_fake_for_G))
loss_G.backward()
optimizer_D.step()Solution
Step 1: Analyze optimizer steps
After computing generator loss and backward, the generator's optimizer should update weights, not discriminator's.Step 2: Identify incorrect optimizer call
The code calls optimizer_D.step() twice; the second should be optimizer_G.step() to update generator.Final Answer:
Replace last optimizer_D.step() with optimizer_G.step() -> Option AQuick Check:
Generator update uses optimizer_G.step() [OK]
- Calling discriminator optimizer twice
- Forgetting to update generator weights
- Misplacing zero_grad calls
Solution
Step 1: Understand discriminator overpowering
If the discriminator learns too fast, the generator receives poor feedback and training stalls.Step 2: Evaluate options to balance training
Training discriminator less often than generator slows its learning, giving generator a chance to improve.Final Answer:
Train the discriminator less frequently than the generator -> Option AQuick Check:
Balance training frequency to stabilize GAN [OK]
- Increasing discriminator learning rate worsens imbalance
- Removing generator loss stops generator learning
- Sharing optimizer causes parameter update conflicts
Ready to prove it?
Pass the test and this pattern moves unknown
