| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| class TFBatchNorm2d(nn.Module): | |
| """BatchNorm compatible with the original TF graph's training=True usage.""" | |
| def __init__(self, channels, eps=1e-5, momentum=0.99): | |
| super().__init__() | |
| self.eps = eps | |
| self.momentum = momentum | |
| self.weight = nn.Parameter(torch.ones(channels)) | |
| self.bias = nn.Parameter(torch.zeros(channels)) | |
| self.register_buffer("running_mean", torch.zeros(channels)) | |
| self.register_buffer("running_var", torch.ones(channels)) | |
| def forward(self, x): | |
| mean = x.mean(dim=(0, 2, 3), keepdim=True) | |
| var = (x - mean).pow(2).mean(dim=(0, 2, 3), keepdim=True) | |
| if self.training: | |
| with torch.no_grad(): | |
| self.running_mean.mul_(self.momentum).add_( | |
| mean.reshape(-1), alpha=1.0 - self.momentum | |
| ) | |
| self.running_var.mul_(self.momentum).add_( | |
| var.reshape(-1), alpha=1.0 - self.momentum | |
| ) | |
| weight = self.weight.view(1, -1, 1, 1) | |
| bias = self.bias.view(1, -1, 1, 1) | |
| return (x - mean) * torch.rsqrt(var + self.eps) * weight + bias | |
| class Generator(nn.Module): | |
| def __init__(self, input_channels=3, batch_norm_eps=1e-5): | |
| super().__init__() | |
| filters = [64, 128, 256, 512, 512, 512, 512, 512, 512, 512, 512, 512, 256, 128, 64] | |
| self.down_convs = nn.ModuleList() | |
| in_channels = input_channels | |
| for out_channels in filters[:8]: | |
| self.down_convs.append( | |
| nn.Conv2d(in_channels, out_channels, kernel_size=4, stride=2, padding=1) | |
| ) | |
| in_channels = out_channels | |
| self.down_bns = nn.ModuleList( | |
| [TFBatchNorm2d(channels, eps=batch_norm_eps) for channels in filters[1:8]] | |
| ) | |
| self.up_convs = nn.ModuleList() | |
| up_in_channels = [ | |
| filters[7], | |
| filters[8] + filters[6], | |
| filters[9] + filters[5], | |
| filters[10] + filters[4], | |
| filters[11] + filters[3], | |
| filters[12] + filters[2], | |
| filters[13] + filters[1], | |
| filters[14] + filters[0], | |
| ] | |
| up_out_channels = filters[8:] + [input_channels] | |
| for in_channels, out_channels in zip(up_in_channels, up_out_channels): | |
| self.up_convs.append( | |
| nn.ConvTranspose2d(in_channels, out_channels, kernel_size=4, stride=2, padding=1) | |
| ) | |
| self.up_bns = nn.ModuleList( | |
| [TFBatchNorm2d(channels, eps=batch_norm_eps) for channels in filters[8:15]] | |
| ) | |
| self.apply(_initialize_layer) | |
| def forward(self, x): | |
| original = x | |
| layers = [] | |
| y = self.down_convs[0](x) | |
| layers.append(y) | |
| for i in range(1, 8): | |
| y = F.leaky_relu(layers[-1], negative_slope=0.2) | |
| y = self.down_convs[i](y) | |
| y = self.down_bns[i - 1](y) | |
| layers.append(y) | |
| y = F.relu(layers[-1]) | |
| y = self.up_convs[0](y) | |
| y = self.up_bns[0](y) | |
| layers.append(y) | |
| for up_index, skip_index in enumerate([6, 5, 4, 3, 2, 1], start=1): | |
| y = torch.cat([layers[-1], layers[skip_index]], dim=1) | |
| y = F.relu(y) | |
| y = self.up_convs[up_index](y) | |
| y = self.up_bns[up_index](y) | |
| layers.append(y) | |
| y = torch.cat([layers[-1], layers[0]], dim=1) | |
| y = F.relu(y) | |
| y = self.up_convs[7](y) | |
| y = F.relu(y) | |
| return original - y | |
| class Discriminator(nn.Module): | |
| def __init__(self, input_channels=3, batch_norm_eps=1e-3): | |
| super().__init__() | |
| filters = [32, 64, 64, 128, 128, 256, 256, 256, 8] | |
| strides = [1, 2, 1, 2, 1, 2, 1, 2, 2] | |
| self.convs = nn.ModuleList() | |
| in_channels = input_channels | |
| for index, (out_channels, stride) in enumerate(zip(filters, strides)): | |
| padding = 1 if stride == 1 else 0 | |
| self.convs.append( | |
| nn.Conv2d( | |
| in_channels, | |
| out_channels, | |
| kernel_size=3, | |
| stride=stride, | |
| padding=padding, | |
| ) | |
| ) | |
| in_channels = out_channels | |
| self.batch_norms = nn.ModuleList( | |
| [TFBatchNorm2d(channels, eps=batch_norm_eps) for channels in filters[1:]] | |
| ) | |
| self.dense = nn.Conv2d(filters[-1], 1, kernel_size=1) | |
| self.apply(_initialize_layer) | |
| def forward(self, x): | |
| features = [] | |
| y = F.leaky_relu(self.convs[0](x), negative_slope=0.2) | |
| features.append(y) | |
| for index in range(1, len(self.convs)): | |
| y = self.convs[index](features[-1]) | |
| y = self.batch_norms[index - 1](y) | |
| y = F.leaky_relu(y, negative_slope=0.2) | |
| features.append(y) | |
| prediction = torch.sigmoid(self.dense(features[-1])) | |
| return (*features[:8], prediction) | |
| def _initialize_layer(module): | |
| if isinstance(module, (nn.Conv2d, nn.ConvTranspose2d)): | |
| nn.init.xavier_uniform_(module.weight) | |
| if module.bias is not None: | |
| nn.init.zeros_(module.bias) | |
| def create_generator( | |
| weights_path=None, | |
| device=None, | |
| input_channels=3, | |
| batch_norm_eps=1e-5, | |
| ): | |
| device = device or torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
| net = Generator( | |
| input_channels=input_channels, | |
| batch_norm_eps=batch_norm_eps, | |
| ).to(device) | |
| if weights_path: | |
| checkpoint = torch.load(weights_path, map_location=device) | |
| state_dict = checkpoint.get("state_dict", checkpoint) | |
| net.load_state_dict(state_dict) | |
| net.eval() | |
| return net | |
| def create_discriminator( | |
| weights_path=None, | |
| device=None, | |
| input_channels=3, | |
| batch_norm_eps=1e-3, | |
| ): | |
| device = device or torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
| net = Discriminator( | |
| input_channels=input_channels, | |
| batch_norm_eps=batch_norm_eps, | |
| ).to(device) | |
| if weights_path: | |
| checkpoint = torch.load(weights_path, map_location=device) | |
| state_dict = checkpoint.get("state_dict", checkpoint) | |
| net.load_state_dict(state_dict) | |
| net.eval() | |
| return net | |