The Art of Digital Mimicry: Understanding and Implementing GANs
The Duel: Generator vs. Discriminator
The genius of GANs lies in their game-theoretic structure. The architecture consists of two distinct components:
- The Generator (G): Acting like a digital art forger, the Generator takes random noise as input and attempts to transform it into data that mimics a real dataset. It learns exclusively through the feedback it receives from its opponent.
- The Discriminator (D): Functioning as a seasoned art critic, the Discriminator receives both real samples from a dataset and fake samples from the Generator. Its job is to output a probability indicating whether the input is authentic.
Implementation: A Simple GAN in PyTorch
The following code implements a basic GAN designed to learn a 1D Gaussian distribution. We include
matplotlib to visualize how well the Generator mimics the target mean.
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
import numpy as np
# 1. Define the Generator
class Generator(nn.Module):
def __init__(self, input_dim, output_dim):
super(Generator, self).__init__()
self.net = nn.Sequential(
nn.Linear(input_dim, 128),
nn.LeakyReLU(0.2),
nn.Linear(128, output_dim)
)
def forward(self, x):
return self.net(x)
# 2. Define the Discriminator
class Discriminator(nn.Module):
def __init__(self, input_dim):
super(Discriminator, self).__init__()
self.net = nn.Sequential(
nn.Linear(input_dim, 128),
nn.LeakyReLU(0.2),
nn.Linear(128, 1),
nn.Sigmoid()
)
def forward(self, x):
return self.net(x)
# 3. Hyperparameters & Initialization
latent_dim = 10
data_dim = 1
lr = 0.0002
TARGET_MEAN = 5.0
G = Generator(latent_dim, data_dim)
D = Discriminator(data_dim)
g_optimizer = optim.Adam(G.parameters(), lr=lr)
d_optimizer = optim.Adam(D.parameters(), lr=lr)
criterion = nn.BCELoss()
# 4. Training Loop
for epoch in range(10001):
# Train Discriminator
real_data = torch.randn(64, data_dim) + TARGET_MEAN
real_labels = torch.ones(64, 1)
noise = torch.randn(64, latent_dim)
fake_data = G(noise)
fake_labels = torch.zeros(64, 1)
d_optimizer.zero_grad()
loss_real = criterion(D(real_data), real_labels)
loss_fake = criterion(D(fake_data.detach()), fake_labels)
d_loss = loss_real + loss_fake
d_loss.backward()
d_optimizer.step()
# Train Generator
g_optimizer.zero_grad()
g_loss = criterion(D(fake_data), real_labels)
g_loss.backward()
g_optimizer.step()
if epoch % 2000 == 0:
print(f"Epoch {epoch} | D Loss: {d_loss.item():.4f} | G Loss: {g_loss.item():.4f}")
# 5. Visualization
with torch.no_grad():
test_noise = torch.randn(1000, latent_dim)
generated = G(test_noise).numpy()
plt.hist(generated, bins=50, alpha=0.6, color='blue', label='Generated Data')
plt.axvline(TARGET_MEAN, color='red', linestyle='dashed', linewidth=2, label='Target Mean')
plt.title('GAN Learning a 1D Gaussian Distribution')
plt.legend()
plt.show()
Conclusion
GANs represent a shift from machines that merely "know" to machines that can "imagine." While they present significant engineering challenges like mode collapse, their ability to model complex data distributions makes them a cornerstone of modern generative AI.
Comments
Post a Comment