代码地址
首先定义一个trainer。
trainer = Pix2PixTrainer(opt)
在Pix2PixTrainer内部,首先定义Pix2PixModel模型。
self.pix2pix_model = Pix2PixModel(opt)
在Pix2PixModel内部定义生成器,判别器。
self.netG, self.netD, self.netE = self.initialize_networks(opt)
在initialize_networks内部定义功能。
netG = networks.define_G(opt)
netD = networks.define_D(opt) if opt.isTrain else None
netE = networks.define_E(opt) if opt.use_vae else None
首先看生成器:
def define_G(opt):netG_cls = find_network_using_name(opt.netG, 'generator')#netG=spadereturn create_network(netG_cls, opt)
输入的参数是opt.netG,在option中对应的是spade。在find_network_using_name中:
def find_network_using_name(target_network_name, filename):#spade,generatortarget_class_name = target_network_name + filename#spadegeneratormodule_name = 'models.networks.' + filename#models.networks.generatornetwork = util.find_class_in_module(target_class_name, module_name)#<class 'models.networks.generator.SPADEGenerator'>assert issubclass(network, BaseNetwork), \"Class %s should be a subclass of BaseNetwork" % networkreturn network
根据target_network_name和对应的filename输入到find_class_in_module中:
def find_class_in_module(target_cls_name, module):target_cls_name = target_cls_name.replace('_', '').lower()#spadegeneratorclslib = importlib.import_module(module)#import_module()返回指定的包或模块cls = Nonefor name, clsobj in clslib.__dict__.items():if name.lower() == target_cls_name:cls = clsobjif cls is None:print("In %s, there should be a class whose name matches %s in lowercase without underscore(_)" % (module, target_cls_name))exit(0)return cls
我们通过import_module函数载入module这个模块,module对应的是models.networks.generator。即clslib 就是generator文件中的类。我们遍历clslib的字典,如果name等于spadegenerator,令cls = clsobj。
即network等于cls。
network = util.find_class_in_module(target_class_name, module_name)
这里有两个语法问题:
①:导入importlib,调用import_module()方法,根据输入的字符串可以获得模块clslib ,clslib 可以调用models.networks.generator文件下所有的属性和方法。
在generator内部是:
可以通过clslib.SPADEGenerator来实例化SPADEGenerator,然后再调用SPADEGenerator内部的方法。
举个例子:新建三个文件。
train:
用不到test,在tt文件内部中导入train中的类s。
因为是同级目录,直接导入字符串train即可,如果不在同级目录,需要导入前一个目录。
接着a就会变成一个module,即train。然后实例化train文件夹下的类s。最后调用类s的方法kill和qqq。
输出:
②: dict,该属性可以用类名或者类的实例对象来调用,用**类名直接调用 dict,会输出该由类中所有类属性组成的字典;**而使用类的实例对象调用 dict,会输出由类中所有实例属性组成的字典。
参考
这里SPADEGenerator继承了BaseNetwork,对于具有继承关系的父类和子类来说,父类有自己的 dict,同样子类也有自己的 dict,它不会包含父类的 dict。
例子:按上面的例子,a是一个module,查看a的__dict__:
输出:
回到代码中:我们输出的network就是类<class ‘models.networks.generator.SPADEGenerator’>。
下一步我们创建网络:
cls对应的是SPADEGenerator网络。
在SPADE中:
"""
Copyright (C) 2019 NVIDIA Corporation. All rights reserved.
Licensed under the CC BY-NC-SA 4.0 license (https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode).
"""import torch
import torch.nn as nn
import torch.nn.functional as F
from models.networks.base_network import BaseNetwork
from models.networks.normalization import get_nonspade_norm_layer
from models.networks.architecture import ResnetBlock as ResnetBlock
from models.networks.architecture import SPADEResnetBlock as SPADEResnetBlock
from models.networks.architecture import Zencoderclass SPADEGenerator(BaseNetwork):@staticmethoddef modify_commandline_options(parser, is_train):parser.set_defaults(norm_G='spectralspadesyncbatch3x3')parser.add_argument('--num_upsampling_layers',choices=('normal', 'more', 'most'), default='normal',help="If 'more', adds upsampling layer between the two middle resnet blocks. If 'most', also add one more upsampling + resnet layer at the end of the generator")return parserdef __init__(self, opt):super().__init__()self.opt = optnf = opt.ngfself.sw, self.sh = self.compute_latent_vector_size(opt)self.Zencoder = Zencoder(3, 512)self.fc = nn.Conv2d(self.opt.semantic_nc, 16 * nf, 3, padding=1)self.head_0 = SPADEResnetBlock(16 * nf, 16 * nf, opt, Block_Name='head_0')self.G_middle_0 = SPADEResnetBlock(16 * nf, 16 * nf, opt, Block_Name='G_middle_0')self.G_middle_1 = SPADEResnetBlock(16 * nf, 16 * nf, opt, Block_Name='G_middle_1')self.up_0 = SPADEResnetBlock(16 * nf, 8 * nf, opt, Block_Name='up_0')self.up_1 = SPADEResnetBlock(8 * nf, 4 * nf, opt, Block_Name='up_1')self.up_2 = SPADEResnetBlock(4 * nf, 2 * nf, opt, Block_Name='up_2')self.up_3 = SPADEResnetBlock(2 * nf, 1 * nf, opt, Block_Name='up_3', use_rgb=False)final_nc = nfif opt.num_upsampling_layers == 'most':self.up_4 = SPADEResnetBlock(1 * nf, nf // 2, opt, Block_Name='up_4')final_nc = nf // 2self.conv_img = nn.Conv2d(final_nc, 3, 3, padding=1)self.up = nn.Upsample(scale_factor=2)#self.up = nn.Upsample(scale_factor=2, mode='bilinear')def compute_latent_vector_size(self, opt):if opt.num_upsampling_layers == 'normal':#默认num_up_layers = 5elif opt.num_upsampling_layers == 'more':num_up_layers = 6elif opt.num_upsampling_layers == 'most':num_up_layers = 7else:raise ValueError('opt.num_upsampling_layers [%s] not recognized' %opt.num_upsampling_layers)sw = opt.crop_size // (2**num_up_layers)#256//32=16sh = round(sw / opt.aspect_ratio)#8return sw, shdef forward(self, input, rgb_img, obj_dic=None):seg = inputx = F.interpolate(seg, size=(self.sh, self.sw))#(16,16)x = self.fc(x)#(b,1024,16,16)style_codes = self.Zencoder(input=rgb_img, segmap=seg)x = self.head_0(x, seg, style_codes, obj_dic=obj_dic)x = self.up(x)x = self.G_middle_0(x, seg, style_codes, obj_dic=obj_dic)if self.opt.num_upsampling_layers == 'more' or \self.opt.num_upsampling_layers == 'most':x = self.up(x)x = self.G_middle_1(x, seg, style_codes, obj_dic=obj_dic)x = self.up(x)x = self.up_0(x, seg, style_codes, obj_dic=obj_dic)x = self.up(x)x = self.up_1(x, seg, style_codes, obj_dic=obj_dic)x = self.up(x)x = self.up_2(x, seg, style_codes, obj_dic=obj_dic)x = self.up(x)x = self.up_3(x, seg, style_codes, obj_dic=obj_dic)# if self.opt.num_upsampling_layers == 'most':# x = self.up(x)# x= self.up_4(x, seg, style_codes, obj_dic=obj_dic)x = self.conv_img(F.leaky_relu(x, 2e-1))x = F.tanh(x)return x
首先计算潜在空间向量的大小:
接着计算style matrixST。对应文章的 :
在代码中:通过卷积,下采样,下采样,上采样,卷积。输出一个通道为512的向量。
接着是连续的四个上采样模块:
对应于:
在SPADEResnetBlock内部:使用ACE类定义了SEAN块。
在ACE内部定义了归一化的参数和噪声等。
下面设计python正则表达式,没学过,下去补。只能先用debug获得结果。
这里使用SynchronizedBatchNorm2d进行归一化:
γ和β通过卷积获得:
执行完上采样的四个SEAN块之后,最后进过一个卷积输出合成图像。这就是整个network的流程。
生成器打印参数:
接着是判别器:
按照生成器的逻辑,target_class_name=multiscalediscriminator,module_name=models.networks.discriminator。
然后我们导入判别器模块。
在多尺度判别器内部:创建两个single_discriminator。
在单个判别器内部定义参数:
定义判别器的输入:将label通道和RGB图片拼接后输入。
接着经过一个4x4大小步长为2的卷积,再经过两个步长为2的卷积,最后再经过输出通道为1,步长为1的卷积。将每一个卷积都注册到模型中。
即判别器由五个卷积组成。
将单个判别器注册到判别器中。注册两次,这样盘比起由10个卷积组成,且都有对应的吗名称。
MultiscaleDiscriminator((discriminator_0): NLayerDiscriminator((model0): Sequential((0): Conv2d(16, 64, kernel_size=(4, 4), stride=(2, 2), padding=(2, 2))(1): LeakyReLU(negative_slope=0.2))(model1): Sequential((0): Sequential((0): Conv2d(64, 128, kernel_size=(4, 4), stride=(2, 2), padding=(2, 2), bias=False)(1): InstanceNorm2d(128, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False))(1): LeakyReLU(negative_slope=0.2))(model2): Sequential((0): Sequential((0): Conv2d(128, 256, kernel_size=(4, 4), stride=(2, 2), padding=(2, 2), bias=False)(1): InstanceNorm2d(256, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False))(1): LeakyReLU(negative_slope=0.2))(model3): Sequential((0): Conv2d(256, 1, kernel_size=(4, 4), stride=(1, 1), padding=(2, 2))))(discriminator_1): NLayerDiscriminator((model0): Sequential((0): Conv2d(16, 64, kernel_size=(4, 4), stride=(2, 2), padding=(2, 2))(1): LeakyReLU(negative_slope=0.2))(model1): Sequential((0): Sequential((0): Conv2d(64, 128, kernel_size=(4, 4), stride=(2, 2), padding=(2, 2), bias=False)(1): InstanceNorm2d(128, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False))(1): LeakyReLU(negative_slope=0.2))(model2): Sequential((0): Sequential((0): Conv2d(128, 256, kernel_size=(4, 4), stride=(2, 2), padding=(2, 2), bias=False)(1): InstanceNorm2d(256, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False))(1): LeakyReLU(negative_slope=0.2))(model3): Sequential((0): Conv2d(256, 1, kernel_size=(4, 4), stride=(1, 1), padding=(2, 2))))
)
这样生成器判别器都狗仔完毕,netE为空。