"""GAN implementation for financial data generation.""" import torch import torch.nn as nn import torch.optim as optim from typing import Dict, Tuple class Generator(nn.Module): def __init__(self, latent_dim: int, feature_dim: int, hidden_dims: List[int]): super().__init__() layers = [] prev_dim = latent_dim for hidden_dim in hidden_dims: layers.extend([ nn.Linear(prev_dim, hidden_dim), nn.BatchNorm1d(hidden_dim), nn.LeakyReLU(0.2), nn.Dropout(0.3) ]) prev_dim = hidden_dim layers.append(nn.Linear(prev_dim, feature_dim)) layers.append(nn.Tanh()) self.model = nn.Sequential(*layers) def forward(self, z: torch.Tensor) -> torch.Tensor: return self.model(z) class Discriminator(nn.Module): def __init__(self, feature_dim: int, hidden_dims: List[int]): super().__init__() layers = [] prev_dim = feature_dim for hidden_dim in hidden_dims: layers.extend([ nn.Linear(prev_dim, hidden_dim), nn.LeakyReLU(0.2), nn.Dropout(0.3) ]) prev_dim = hidden_dim layers.append(nn.Linear(prev_dim, 1)) layers.append(nn.Sigmoid()) self.model = nn.Sequential(*layers) def forward(self, x: torch.Tensor) -> torch.Tensor: return self.model(x) class FinancialGAN: def __init__(self, config: Dict): """Initialize the GAN.""" self.latent_dim = config['model']['latent_dim'] self.feature_dim = config['model']['feature_dim'] self.hidden_dims = config['model']['hidden_dims'] self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') self.generator = Generator( self.latent_dim, self.feature_dim, self.hidden_dims ).to(self.device) self.discriminator = Discriminator( self.feature_dim, self.hidden_dims[::-1] ).to(self.device) self.g_optimizer = optim.Adam( self.generator.parameters(), lr=config['model']['learning_rate'] ) self.d_optimizer = optim.Adam( self.discriminator.parameters(), lr=config['model']['learning_rate'] ) self.criterion = nn.BCELoss() def train_step(self, real_data: torch.Tensor) -> Tuple[float, float]: """Perform one training step.""" batch_size = real_data.size(0) real_label = torch.ones(batch_size, 1).to(self.device) fake_label = torch.zeros(batch_size, 1).to(self.device) # Train Discriminator self.d_optimizer.zero_grad() d_real_output = self.discriminator(real_data) d_real_loss = self.criterion(d_real_output, real_label) z = torch.randn(batch_size, self.latent_dim).to(self.device) fake_data = self.generator(z) d_fake_output = self.discriminator(fake_data.detach()) d_fake_loss = self.criterion(d_fake_output, fake_label) d_loss = d_real_loss + d_fake_loss d_loss.backward() self.d_optimizer.step() # Train Generator self.g_optimizer.zero_grad() g_output = self.discriminator(fake_data) g_loss = self.criterion(g_output, real_label) g_loss.backward() self.g_optimizer.step() return g_loss.item(), d_loss.item() def generate_samples(self, num_samples: int) -> torch.Tensor: """Generate synthetic financial data.""" self.generator.eval() with torch.no_grad(): z = torch.randn(num_samples, self.latent_dim).to(self.device) samples = self.generator(z) self.generator.train() return samples