""" DCGAN Generator and Discriminator for 64x64 texture image generation. """ import torch import torch.nn as nn # Latent vector size LATENT_DIM = 100 # Number of feature maps in generator NGF = 64 # Number of feature maps in discriminator NDF = 64 # Number of color channels NC = 3 class Generator(nn.Module): """ DCGAN Generator: maps a latent vector (100-dim) to a 64x64 RGB image. Architecture: Linear -> Reshape -> TransposedConv blocks -> Tanh """ def __init__(self, latent_dim=LATENT_DIM, ngf=NGF, nc=NC): super(Generator, self).__init__() self.main = nn.Sequential( # Input: latent_dim x 1 x 1 nn.ConvTranspose2d(latent_dim, ngf * 8, 4, 1, 0, bias=False), nn.BatchNorm2d(ngf * 8), nn.ReLU(True), # State: (ngf*8) x 4 x 4 nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, bias=False), nn.BatchNorm2d(ngf * 4), nn.ReLU(True), # State: (ngf*4) x 8 x 8 nn.ConvTranspose2d(ngf * 4, ngf * 2, 4, 2, 1, bias=False), nn.BatchNorm2d(ngf * 2), nn.ReLU(True), # State: (ngf*2) x 16 x 16 nn.ConvTranspose2d(ngf * 2, ngf, 4, 2, 1, bias=False), nn.BatchNorm2d(ngf), nn.ReLU(True), # State: (ngf) x 32 x 32 nn.ConvTranspose2d(ngf, nc, 4, 2, 1, bias=False), nn.Tanh() # Output: nc x 64 x 64 ) def forward(self, x): return self.main(x) class Discriminator(nn.Module): """ DCGAN Discriminator: classifies 64x64 RGB images as real or fake. Architecture: Conv blocks -> Sigmoid """ def __init__(self, ndf=NDF, nc=NC): super(Discriminator, self).__init__() self.main = nn.Sequential( # Input: nc x 64 x 64 nn.Conv2d(nc, ndf, 4, 2, 1, bias=False), nn.LeakyReLU(0.2, inplace=True), # State: ndf x 32 x 32 nn.Conv2d(ndf, ndf * 2, 4, 2, 1, bias=False), nn.BatchNorm2d(ndf * 2), nn.LeakyReLU(0.2, inplace=True), # State: (ndf*2) x 16 x 16 nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False), nn.BatchNorm2d(ndf * 4), nn.LeakyReLU(0.2, inplace=True), # State: (ndf*4) x 8 x 8 nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1, bias=False), nn.BatchNorm2d(ndf * 8), nn.LeakyReLU(0.2, inplace=True), # State: (ndf*8) x 4 x 4 nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=False), nn.Sigmoid() # Output: 1 x 1 x 1 ) def forward(self, x): return self.main(x).view(-1, 1).squeeze(1) def weights_init(m): """Initialize weights from a normal distribution (DCGAN paper recommendation).""" classname = m.__class__.__name__ if classname.find('Conv') != -1: nn.init.normal_(m.weight.data, 0.0, 0.02) elif classname.find('BatchNorm') != -1: nn.init.normal_(m.weight.data, 1.0, 0.02) nn.init.constant_(m.bias.data, 0)