Introducción a las GANs

Las Redes Generativas Antagónicas (GANs) son un tipo de modelo de aprendizaje profundo que consta de dos redes neuronales que compiten entre sí: una red generadora y una red discriminadora. Las GANs fueron introducidas por Ian Goodfellow y sus colegas en 2014 y han revolucionado el campo de la generación de datos sintéticos.

Conceptos Clave

  1. Red Generadora (G): Esta red toma un vector de ruido como entrada y genera datos sintéticos que intentan imitar los datos reales.
  2. Red Discriminadora (D): Esta red toma datos como entrada (tanto reales como generados) y trata de distinguir entre los datos reales y los generados por la red generadora.
  3. Competencia: La red generadora intenta engañar a la red discriminadora produciendo datos cada vez más realistas, mientras que la red discriminadora mejora en la identificación de datos falsos.

Arquitectura de una GAN

Arquitectura de una GAN

Implementación de una GAN en PyTorch

Paso 1: Importar Librerías

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision.datasets as dsets
import torchvision.transforms as transforms
from torch.utils.data import DataLoader

Paso 2: Definir la Red Generadora

class Generator(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(Generator, self).__init__()
        self.main = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.ReLU(True),
            nn.Linear(hidden_size, hidden_size),
            nn.ReLU(True),
            nn.Linear(hidden_size, output_size),
            nn.Tanh()
        )

    def forward(self, x):
        return self.main(x)

Paso 3: Definir la Red Discriminadora

class Discriminator(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(Discriminator, self).__init__()
        self.main = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Linear(hidden_size, hidden_size),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Linear(hidden_size, output_size),
            nn.Sigmoid()
        )

    def forward(self, x):
        return self.main(x)

Paso 4: Configuración del Entrenamiento

# Hiperparámetros
batch_size = 100
learning_rate = 0.0002
num_epochs = 200
input_size = 784  # Para MNIST
hidden_size = 256
latent_size = 64

# Cargar el dataset MNIST
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(mean=(0.5,), std=(0.5,))
])

mnist = dsets.MNIST(root='./data', train=True, transform=transform, download=True)
data_loader = DataLoader(dataset=mnist, batch_size=batch_size, shuffle=True)

# Crear las redes
G = Generator(latent_size, hidden_size, input_size)
D = Discriminator(input_size, hidden_size, 1)

# Definir los optimizadores
criterion = nn.BCELoss()
optimizerD = optim.Adam(D.parameters(), lr=learning_rate)
optimizerG = optim.Adam(G.parameters(), lr=learning_rate)

Paso 5: Bucle de Entrenamiento

for epoch in range(num_epochs):
    for i, (images, _) in enumerate(data_loader):
        # Preparar datos reales y falsos
        real_images = images.view(batch_size, -1)
        real_labels = torch.ones(batch_size, 1)
        fake_labels = torch.zeros(batch_size, 1)

        # Entrenar la red discriminadora
        outputs = D(real_images)
        d_loss_real = criterion(outputs, real_labels)
        real_score = outputs

        z = torch.randn(batch_size, latent_size)
        fake_images = G(z)
        outputs = D(fake_images)
        d_loss_fake = criterion(outputs, fake_labels)
        fake_score = outputs

        d_loss = d_loss_real + d_loss_fake
        optimizerD.zero_grad()
        d_loss.backward()
        optimizerD.step()

        # Entrenar la red generadora
        z = torch.randn(batch_size, latent_size)
        fake_images = G(z)
        outputs = D(fake_images)
        g_loss = criterion(outputs, real_labels)

        optimizerG.zero_grad()
        g_loss.backward()
        optimizerG.step()

    print(f'Epoch [{epoch}/{num_epochs}], d_loss: {d_loss.item():.4f}, g_loss: {g_loss.item():.4f}, '
          f'D(x): {real_score.mean().item():.2f}, D(G(z)): {fake_score.mean().item():.2f}')

Ejercicio Práctico

Ejercicio: Modifica la arquitectura de la red generadora y discriminadora para mejorar la calidad de las imágenes generadas. Experimenta con diferentes tamaños de capas ocultas y funciones de activación.

Solución Propuesta:

class Generator(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(Generator, self).__init__()
        self.main = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.BatchNorm1d(hidden_size),
            nn.ReLU(True),
            nn.Linear(hidden_size, hidden_size),
            nn.BatchNorm1d(hidden_size),
            nn.ReLU(True),
            nn.Linear(hidden_size, output_size),
            nn.Tanh()
        )

    def forward(self, x):
        return self.main(x)

class Discriminator(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(Discriminator, self).__init__()
        self.main = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Linear(hidden_size, hidden_size),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Linear(hidden_size, output_size),
            nn.Sigmoid()
        )

    def forward(self, x):
        return self.main(x)

Conclusión

En esta sección, hemos aprendido sobre las Redes Generativas Antagónicas (GANs) y cómo implementarlas en PyTorch. Hemos cubierto la arquitectura básica de una GAN, incluyendo la red generadora y la red discriminadora, y hemos visto cómo entrenar estas redes en un bucle de entrenamiento. Además, hemos proporcionado un ejercicio práctico para mejorar la arquitectura de las redes.

En el siguiente módulo, exploraremos el Aprendizaje por Refuerzo con PyTorch, donde aprenderemos a construir agentes que pueden tomar decisiones en entornos dinámicos.

© Copyright 2024. Todos los derechos reservados