Spaces:
Runtime error
Runtime error
| """Model definitions for the CycleGAN-style architecture.""" | |
| from torch import Tensor | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| class ResidualBlock(nn.Module): | |
| """Simple residual block with two conv layers.""" | |
| def __init__(self, in_features: int) -> None: | |
| super().__init__() | |
| conv_block = [ | |
| nn.ReflectionPad2d(1), # (B, C, H+2, W+2) | |
| nn.Conv2d(in_features, in_features, 3), # (B, C, H, W) | |
| nn.BatchNorm2d(in_features), # (B, C, H, W) | |
| nn.ReLU(), # (B, C, H, W) | |
| nn.ReflectionPad2d(1), # (B, C, H+2, W+2) | |
| nn.Conv2d(in_features, in_features, 3), # (B, C, H, W) | |
| nn.BatchNorm2d(in_features), | |
| ] # (B, C, H, W) | |
| self.conv_block = nn.Sequential(*conv_block) | |
| def forward(self, x: Tensor) -> Tensor: | |
| """Apply the residual block.""" | |
| return x + self.conv_block(x) | |
| class Generator(nn.Module): | |
| """U-Net style generator used for domain translation.""" | |
| def __init__(self, ngf: int, n_residual_blocks: int = 9) -> None: | |
| super().__init__() | |
| # Initial convlution block | |
| model = [ | |
| nn.ReflectionPad2d( | |
| 3 | |
| ), # (B, 3, H+6, W+6), applies 2D "reflection" padding of 3 pixels on all four sides of image | |
| nn.Conv2d( | |
| 3, ngf, 7 | |
| ), # (B, ngf, H, W), 3 in_channels, ngf out_channels, kernel size 7 (keeps same image size) | |
| nn.BatchNorm2d( | |
| ngf | |
| ), # (B, ngf, H, W), normalized for each ngf across all B, H, W | |
| nn.ReLU(), | |
| ] # (B, ngf, H, W) | |
| # Downsampling | |
| in_features = ngf # number of generator filters | |
| out_features = in_features * 2 | |
| for _ in range(2): | |
| model += [ | |
| nn.Conv2d( | |
| in_features, out_features, 3, stride=2, padding=1 | |
| ), # (B, in_features*2, H//2, W//2), doubles number of channels and reduces H, W by half | |
| nn.BatchNorm2d(out_features), # (B, in_features*2, H//2, W//2) | |
| nn.ReLU(), | |
| ] # (B, in_features*2, H//2, W//2) | |
| in_features = out_features | |
| out_features = in_features * 2 | |
| # Residual blocks | |
| for _ in range(n_residual_blocks): | |
| model += [ | |
| ResidualBlock(in_features) | |
| ] # (B, in_features, H, W), returns same size as input | |
| # Upsampling | |
| out_features = in_features // 2 | |
| for _ in range(2): | |
| model += [ | |
| nn.ConvTranspose2d( | |
| in_features, out_features, 3, stride=2, padding=1, output_padding=1 | |
| ), # (B, in_features//2, H*2, W*2), upsamples to twice the H, W with half the channels | |
| nn.BatchNorm2d(out_features), # (B, in_features//2, H*2, W*2) | |
| nn.ReLU(), | |
| ] # (B, in_features//2, H*2, W*2) | |
| in_features = out_features | |
| out_features = in_features // 2 | |
| # Output layer | |
| model += [ | |
| nn.ReflectionPad2d(3), # (B, in_features, H+6, W+6) | |
| nn.Conv2d(ngf, 3, 7), # (B, 3, H, W) | |
| nn.Tanh(), | |
| ] # (B, 3, H, W), passed tanh activation | |
| self.model = nn.Sequential(*model) | |
| def forward(self, x: Tensor) -> Tensor: | |
| """Generate an image from ``x``.""" | |
| return self.model(x) | |
| class Discriminator(nn.Module): | |
| """PatchGAN discriminator.""" | |
| def __init__(self, ndf: int) -> None: | |
| super().__init__() | |
| model = [ | |
| nn.Conv2d( | |
| 3, ndf, 4, stride=2, padding=1 | |
| ), # (B, ndf, H//2, W//2), channel from 3 -> ndf | |
| nn.LeakyReLU(0.2, inplace=True), | |
| ] # (B, ndf, H//2, W//2) | |
| model += [ | |
| nn.Conv2d(ndf, ndf * 2, 4, stride=2, padding=1), # (B, ndf * 2, H//4, W//4) | |
| nn.BatchNorm2d(ndf * 2), | |
| nn.LeakyReLU(0.2, inplace=True), | |
| ] | |
| model += [ | |
| nn.Conv2d( | |
| ndf * 2, ndf * 4, 4, stride=2, padding=1 | |
| ), # (B, ndf * 4, H//8, W//8) | |
| nn.InstanceNorm2d(ndf * 4), | |
| nn.LeakyReLU(0.2, inplace=True), | |
| ] | |
| model += [ | |
| nn.Conv2d(ndf * 4, ndf * 8, 4, padding=1), # (B, ndf * 8, H//8-1, W//8-1) | |
| nn.InstanceNorm2d(ndf * 8), | |
| nn.LeakyReLU(0.2, inplace=True), | |
| ] | |
| # FCN classification layer | |
| model += [nn.Conv2d(ndf * 8, 1, 4, padding=1)] # (B, 1, H//8-2, W//8-2) | |
| self.model = nn.Sequential(*model) | |
| def forward(self, x: Tensor) -> Tensor: | |
| """Return discriminator logits for input ``x``.""" | |
| # x: (B, 3, H, W) | |
| x = self.model(x) # (B, 1, H//8-2, W//8-2) | |
| # Average pooling and flatten | |
| return F.avg_pool2d(x, x.size()[2:]).view( | |
| x.size()[0], -1 | |
| ) # global average -> (B, 1, 1, 1) -> flatten to (B, 1) | |
| # Initialize and return the generators and discriminators used for training | |
| def initialize_models( | |
| ngf: int = 32, | |
| ndf: int = 32, | |
| n_blocks: int = 9, | |
| ) -> tuple[Generator, Generator, Discriminator, Discriminator]: | |
| """Instantiate generators and discriminators with default sizes.""" | |
| # initialize the generators and discriminators | |
| G = Generator(ngf, n_blocks) | |
| F = Generator(ngf, n_blocks) | |
| DX = Discriminator(ndf) | |
| DY = Discriminator(ndf) | |
| return G, F, DX, DY | |