diff --git a/codes/models/archs/ChainedEmbeddingGen.py b/codes/models/archs/ChainedEmbeddingGen.py index 4e454a27..fd1e76fc 100644 --- a/codes/models/archs/ChainedEmbeddingGen.py +++ b/codes/models/archs/ChainedEmbeddingGen.py @@ -1,4 +1,7 @@ +import os + import torch +import torchvision from torch import nn from models.archs.SPSR_arch import ImageGradientNoPadding @@ -46,7 +49,7 @@ class BasicEmbeddingPyramid(nn.Module): for i in range(2): p = self.expanders[i](p, identities[-(i+1)]) x = self.final_process(torch.cat([x, p], dim=1)) - return x + return x, p class ChainedEmbeddingGen(nn.Module): @@ -61,7 +64,7 @@ class ChainedEmbeddingGen(nn.Module): fea = self.initial_conv(x) emb = checkpoint(self.spine, fea) for block in self.blocks: - fea = fea + checkpoint(block, fea, *emb) + fea = fea + checkpoint(block, fea, *emb)[0] return checkpoint(self.upsample, fea), @@ -100,7 +103,7 @@ class ChainedEmbeddingGenWithStructure(nn.Module): emb = checkpoint(self.spine, fea) grad = fea for i, block in enumerate(self.blocks): - fea = fea + checkpoint(block, fea, *emb) + fea = fea + checkpoint(block, fea, *emb)[0] if i < 3: structure_br = checkpoint(self.structure_joins[i], grad, fea) grad = grad + checkpoint(self.structure_blocks[i], structure_br) @@ -109,3 +112,76 @@ class ChainedEmbeddingGenWithStructure(nn.Module): def get_debug_values(self, step, net_name): return { 'ref_join_std': self.ref_join_std } + + +# This is a structural block that learns to mute regions of a residual transformation given a signal. +class OptionalPassthroughBlock(nn.Module): + def __init__(self, nf, initial_bias=10): + super(OptionalPassthroughBlock, self).__init__() + self.switch_process = nn.Sequential(ConvGnLelu(nf, nf // 2, 1, activation=False, norm=False, bias=False), + ConvGnLelu(nf // 2, nf // 4, 1, activation=False, norm=False, bias=False), + ConvGnLelu(nf // 4, 1, 1, activation=False, norm=False, bias=False)) + self.bias = nn.Parameter(torch.tensor(initial_bias, dtype=torch.float), requires_grad=True) + self.activation = nn.Sigmoid() + + def forward(self, x, switch_signal): + switch = self.switch_process(switch_signal) + bypass_map = self.activation(self.bias + switch) + return x * bypass_map, bypass_map + + +class StructuredChainedEmbeddingGenWithBypass(nn.Module): + def __init__(self, depth=10, recurrent=False, recurrent_nf=3, recurrent_stride=2, bypass_bias=10): + super(StructuredChainedEmbeddingGenWithBypass, self).__init__() + self.recurrent = recurrent + self.initial_conv = ConvGnLelu(3, 64, kernel_size=7, bias=True, norm=False, activation=False) + if recurrent: + self.recurrent_nf = recurrent_nf + self.recurrent_stride = recurrent_stride + self.recurrent_process = ConvGnLelu(recurrent_nf, 64, kernel_size=3, stride=recurrent_stride, norm=False, bias=True, activation=False) + self.recurrent_join = ReferenceJoinBlock(64, residual_weight_init_factor=.01, final_norm=False, kernel_size=1, depth=3, join=False) + self.spine = SpineNet(arch='49', output_level=[3, 4], double_reduce_early=False) + self.blocks = nn.ModuleList([BasicEmbeddingPyramid() for i in range(depth)]) + self.bypasses = nn.ModuleList([OptionalPassthroughBlock(64, initial_bias=bypass_bias) for i in range(depth)]) + self.structure_joins = nn.ModuleList([ConjoinBlock(64) for i in range(3)]) + self.structure_blocks = nn.ModuleList([ConvGnLelu(64, 64, kernel_size=3, bias=False, norm=False, activation=False, weight_init_factor=.1) for i in range(3)]) + self.structure_upsample = FinalUpsampleBlock2x(64) + self.grad_extract = ImageGradientNoPadding() + self.upsample = FinalUpsampleBlock2x(64) + self.ref_join_std = 0 + self.bypass_maps = [] + + def forward(self, x, recurrent=None): + fea = self.initial_conv(x) + if self.recurrent: + if recurrent is None: + if self.recurrent_nf == 3: + recurrent = torch.zeros_like(x) + if self.recurrent_stride != 1: + recurrent = torch.nn.functional.interpolate(recurrent, scale_factor=self.recurrent_stride, mode='nearest') + else: + recurrent = torch.zeros_like(fea) + rec = self.recurrent_process(recurrent) + fea, recstd = self.recurrent_join(fea, rec) + self.ref_join_std = recstd.item() + emb = checkpoint(self.spine, fea) + grad = fea + self.bypass_maps = [] + for i, block in enumerate(self.blocks): + residual, context = checkpoint(block, fea, *emb) + residual, bypass_map = checkpoint(self.bypasses[i], residual, context) + fea = fea + residual + self.bypass_maps.append(bypass_map.detach()) + if i < 3: + structure_br = checkpoint(self.structure_joins[i], grad, fea) + grad = grad + checkpoint(self.structure_blocks[i], structure_br) + out = checkpoint(self.upsample, fea) + return out, self.grad_extract(checkpoint(self.structure_upsample, grad)), self.grad_extract(out), fea + + def visual_dbg(self, step, path): + for i, bm in enumerate(self.bypass_maps): + torchvision.utils.save_image(bm.cpu(), os.path.join(path, "%i_bypass_%i.png" % (step, i+1))) + + def get_debug_values(self, step, net_name): + biases = [b.bias.item() for b in self.bypasses] + return { 'ref_join_std': self.ref_join_std, 'bypass_biases': sum(biases) / len(biases) } diff --git a/codes/models/networks.py b/codes/models/networks.py index 50f0851f..729e3293 100644 --- a/codes/models/networks.py +++ b/codes/models/networks.py @@ -18,7 +18,8 @@ import models.archs.discriminator_vgg_arch as SRGAN_arch import models.archs.feature_arch as feature_arch import models.archs.panet.panet as panet import models.archs.rcan as rcan -from models.archs.ChainedEmbeddingGen import ChainedEmbeddingGen, ChainedEmbeddingGenWithStructure +from models.archs.ChainedEmbeddingGen import ChainedEmbeddingGen, ChainedEmbeddingGenWithStructure, \ + StructuredChainedEmbeddingGenWithBypass logger = logging.getLogger('base') @@ -130,6 +131,12 @@ def define_G(opt, net_key='network_G', scale=None): recnf = opt_net['recurrent_nf'] if 'recurrent_nf' in opt_net.keys() else 3 recstd = opt_net['recurrent_stride'] if 'recurrent_stride' in opt_net.keys() else 2 netG = ChainedEmbeddingGenWithStructure(depth=opt_net['depth'], recurrent=rec, recurrent_nf=recnf, recurrent_stride=recstd) + elif which_model == 'chained_gen_structured_with_bypass': + rec = opt_net['recurrent'] if 'recurrent' in opt_net.keys() else False + recnf = opt_net['recurrent_nf'] if 'recurrent_nf' in opt_net.keys() else 3 + recstd = opt_net['recurrent_stride'] if 'recurrent_stride' in opt_net.keys() else 2 + bypass_bias = opt_net['bypass_bias'] if 'bypass_bias' in opt_net.keys() else 0 + netG = StructuredChainedEmbeddingGenWithBypass(depth=opt_net['depth'], recurrent=rec, recurrent_nf=recnf, recurrent_stride=recstd, bypass_bias=bypass_bias) elif which_model == "flownet2": from models.flownet2.models import FlowNet2 ld = torch.load(opt_net['load_path'])