CycleGAN网络结构 您所在的位置:网站首页 cyclegan模型 CycleGAN网络结构

CycleGAN网络结构

2023-03-19 08:27| 来源: 网络整理| 查看: 265

生成器:三个组件构成(编码器、转换器、解码器) 编码器由三层卷积层构成。假定输入图片大小(256,256,3)。第一步是通过卷积层从图像中提取特征。从卷积层中提取特征的数量也可看做是用于提取不同特征的不同滤波器的数量。卷积层依次逐渐提取更高级的特征,通过编码器后,输入图像由(256,256,3)变成了输出(64,64,256)。 转换器由6个残差块组成。由编码器输出图像的不同通道组合了图像的不同特征,根据这些特征来将图像的特征向量即编码从源域转换到目标域。 残差块由两个卷积层组成,并且将输入残差添加到输出中,这是为了确保先前图层的输入属性也可以用于后面的图层,使得它们的输出与原始输入不会有太大偏差,否则原始图像的特征不会保留在输出中。 解码器的作用是从特征向量重新构建低级特征,可以通过去卷积(转置卷积)层完成。最后将低级功能转换为目标域中的图像。 判别器:将图像作为输入,并预测图像是原始图像还是生成器的输出。判别器由多个卷积层构成,从图像中提取特征后,判断这些特征是否属于特定类别,判别器网络的最后一层是用于产生一维输出的卷积层。

如果不要完整的cycle,只做一半的话则会缺少的对偶的部分,即缺少了重构误差,而对偶重构误差能在引导模型迁移时保留图像的固有属性,所以是很重要的。

总结 Pix to Pix方法的关键是提供这两个域中有相同数据的训练样本,CycleGAN的创新在于能在源域和目标域之间,无需建立训练数据间的一对一映射,就可以实现这种迁移。 这种方法是通过对源域图像进行两步变换:首先尝试将其映射到目标域,然后返回源域得到二次生成图像,从而消除在目标域中图像配对的要求。 从理论上讲,对抗训练可以学习和产生与目标域Y和X相同分布的输出,即映射G和F。然而,在足够大的样本容量下,网络可以将相同的输入图像集合映射到目标域中图像的任何随机排列,其中任何学习的映射可以归纳出与目标分布匹配的输出分布。因此单独的对抗损失Loss不能保证学习函数可以将单个输入Xi映射到期望的输出Yi。 为了规范模型,提出循环一致性约束条件:如果从源分布转换为目标分布,然后再次转换为源分布,那么应该可以从源分布中获取样本。 在一个配对数据集中,每张图像,如imgA,认为映射到目标域中的某个图像,如imgB,以便两者共享各种特征。 从imgA到imgB的特征可以用于其相对应的映射过程中,即从imgB到imgA的特征,配对一般是为了使输入和输出共享一些共同特征。 CycleGAN的网络结构 生成器结构 生成器由三部分组成:编码器、转换器和解码器。 生成器定义的超参数如下,包括卷积核个数、批数量、池化大小和输入图像格式 1234567ngf = 32#number of filters in first layer of generator ndf = 64#number of filters in first layer of discriminator batch_size = 1 pool_size = 50 img_width = 256 img_height = 256 img_depth = 3#RGB format 编码器 为了简单,这里把输入大小固定在(256,256,3),第一步是利用卷积网络从输入图像中提取特征。卷积网络将图像作为输入,不同大小的卷积核在输入图像上移动并且提取特征,步幅大小能决定在图像中卷积核窗口的数量。 编码器第一层定义如下: 123456o_c1 = general_conv2d(input_gen,#input_gen是生成器的输入图像     num_features=ngf,#是在卷积层中得到的特征图谱数量,可以看做是提取不同特征的滤波器数量     window_width=7,#表示在输入图像上滑动来提取特征的滤波器窗口大小     window_height=7,     stride_width=1,#每次迭代后滤波器的移位方式     stride_height=1) 编码器第一层输出o_c1是尺寸为(256,256,64)的张量,即通过编码器第一层的卷积层后输入图片由(256,256,3)变成了(256,256,64),继续传输给下个卷积层。 编码器第一层的滤波器个数为64,当然也还可以添加其他层,如ReLU层或者是批归一化层 123def general_conv2d(inputconv,o_d=64,f_h=7,f_w=7,s_h=1,s_w=1):     with tf.variable_scope(name):         conv=tf.contrib.layers.conv2d(inputconv,num_features,[window_width,windo_height],[stride_width,stride_height],padding,activation_fn=None,weights_initializer=tf.truncated_normal_initializer=stddev),biases_initializer=tf.constant_initializer(0.0)) 接下来:通过编码器第二层的卷积层,由(256,256,64)变为(128,128,128)。图片尺寸减半,通道数加倍。最终A域中的生成器的输出由(128,128,128)变为(64,64,256),即尺寸再减半,通道数加倍。即编码器将输入图像压缩成256个尺寸为64*64的特征向量后,将A域中的特征向量转换为B域中的特征向量。 1234o_c2=general_conv2d(o_c1,num_features=64*2,window_width=3,window_height=3,stride_width=2,stride_height=2) #o_c2.shape=(128,128,128) o_enc_A=general_conv2d(o_c2,num_features=64*4,window_width=3,window_height=3,stride_width=2,stride_height=2) #o_enc_A.shape=(64,64,256) 卷积层越往上,需要增加高层特征的数量,将图像压缩成256个尺寸大小为64*64的特征向量,接着将A域中的特征向量转换为B域中的特征向量。 将A域中的一个尺寸为(256,256,3)的图像,输入到编码器中,得到尺寸为(64,64,256)的输出o_enc_A。 转换器 转换器模块中网络层的作用是组合图像的不同相近特征,然后基于这些特征,确定如何将图像的特征向量o_enc_A(编码器的输出,即编码器从输入图像中提取到的特征)从A域转换为B域的特征向量。作者使用了6层子Resnet模块。 12345678910#第一个残差块的输出,以编码器的输出为输入,num_features值即编码器输出图像的通道数256 o_r1=build_resnet_block(o_enc_A,num_features=64*4) #第二个残差块的输出,以第一个残差块输出为输入,特征数与第一个残差块特征数相同。 o_r2=build_resnet_block(o_r1,num_features=64*4) o_r3=build_resnet_block(o_r2,num_features=64*4) o_r4=build_resnet_block(o_r3,num_features=64*4) o_r5=build_resnet_block(o_r4,num_features=64*4) o_r6=build_resnet_block(o_r5,num_features=64*4) #总共有6个残差块构成了转换器模块,经过转换器模块后,由编码器得到的A域的输入图像的特征向量转换为了B域中的对应特征向量。尺寸与编码器输出的尺寸一致,即与A域中得到的特征向量尺寸一致。 #o_enc_B.shape=(64,64,256)

o_enc_B是转换器的最终输出,可以看做是将A域中的特征向量转换得到的对应的B域中图像的特征向量。 build_resnet_block即每一个子残差块都是一个由两个卷积层组成的神经网络层,其中部分数据直接添加到输出。这样做是为了确保先前网络层的输入信息直接作用于后面的网络层,使得相应输出与原始输入的偏差缩小,否则原始图像的特征将不会保留在输出中且输出结果会偏离目标轮廓。这个任务的一个主要目标是保留原始图像的特征,例如原始图像的大小和形状,因此残差网络非常适合完成这些转换。 resnet模块结构如下图 resnet即子残差块代码如下: 123456def resnet_blocks(input_res,num_features):     #由两层卷积层构成     out_res_1=general_conv2d(input_res,num_features,window_width=3,window_height=3,stride_width=1,strid_height=1)     out_res_2=general_conv2d(out_res_1,num_features,window_width=3,window_height=3,stride_width=1,strid_height=1)     #最后的输出是第二层卷积层的输出加上输入     return(out_res_2+input_res) 解码器 上面已经将特征向量o_enc_A传递到了转换层,得到了另一个同样大小也为(64,64,256)的特征向量o_enc_B。 解码过程与编码方式完全相反,从特征向量中还原出低级特征,这是利用了反卷积层deconvolutio来完成的。 对比编码器的代码,c1表示编码器coder的第一层,o即output,o_c1表示编码器第一层的输出。 1234#编码器代码 o_c1=general_conv2d(input_gen,num_features=ngf,window_width=7,window_height=7,stride_width=1,stride_height=1) o_c2=general_conv2d(o_c1,num_features=64*2,window_width=3,window_height=3,stride_width=2,stride_height=2) #o_c2.shape=(128,128,128) 123#编码器代码 o_enc_A=general_conv2d(o_c2,num_features=64*4,window_width=3,window_height=3,stride_width=2,stride_height=2) #o_enc_A.shape=(64,64,256)

对比上面编码器的代码,d1表示解码器decoder的第一层,o_d1表示解码器第一层的输出。解码器用的函数是反卷积,编码器用的函数是卷积。解码器第一层的输入是转换器的输出,即由A域转换得到的B域的对应特征向量。编码器第一层卷积的特征数目是ngf,第二层卷积的特征数目是2倍ngf。解码器第一层特征数目是2倍ngf,第二层特征数目是ngf。区别在于窗口大小值和移动步幅值。

123#解码器代码 o_d1=general_deconv2d(o_enc_B,num_features=ngf*2,window_width=3,window_height=3,stride_width=2,stride_height=2) o_d2=general_deconv2d(o_d1,num_features=ngf,window_width=3,window_height=3,stride_width=2,stride_height=2)

最后将这些低级特征转换得到一张在B域的图像,代码如下: 1gen_B=general_conv2d(o_d2,num_features=3,window_width=7,window_height=7,stride_width=1,stride_height=1) 最后得到了一个大小为(256,256,3)的生成图像genB(由A域转换到B域的生成图像),构建生成器的代码用如下函数实现: 123456789101112131415161718192021222324252627#生成器的构建代码 #input_gen是输入图像,先通过编码器,编码器由三层卷积层构成 def build_generator(input_gen):     #输入图像通过编码器第一层卷积层     o_c1=general_conv2d(input_gen,num_features=ngf,windows_width=7,window_height=7,stride_width=1,stride_height=1)     #通过编码器第二层卷积层     o_c2=general_conv2d(o_c1,num_features=ngf*2,windows_width=3,window_height=3,stride_width=2,stride_height=2)     #通过编码器第三层卷积层得到编码器的输出结果 ,即由输入图片提取得到的特征向量     o_enc_A=general_conv2d(o_c2,num_features=ngf*4,windows_width=3,window_height=3,stride_width=2,stride_height=2)         #编码器的输出通过转换器,转换器由6个子残差块构成     #Transformation     o_r1=build_resnet_block(o_enc_A,num_features=64*4)     o_r2=build_resnet_block(o_r1,num_features=64*4)     o_r3=build_resnet_block(o_r2,num_features=64*4)     o_r4=build_resnet_block(o_r3,num_features=64*4)     o_r5=build_resnet_block(o_r4,num_features=64*4)     o_enc_B=build_resnet_block(o_r5,num_features=64*4)         #转化器后通过解码器,解码器由三层反卷积层构成     #Decoding     o_d1=gereral_deconv2d(o_enc_B,num_features=nfg*2,window_width=3,window_height=3,stride_width=2,stride_height=2)     o_d2=gereral_deconv2d(o_d1,num_features=nfg,window_width=3,window_height=3,stride_width=2,stride_height=2)     gen_B=gereral_deconv2d(o_d2,num_features=3,window_width=7,window_height=7,stride_width=1,stride_height=1)           #最后得到由A域转换生成的B域的对应图像。     return gen_B 构建判别器 判别器的结构如下图: 判别器本身就属于卷积网络,由结构图可知,由5个卷积层构成。前4个卷积层完成提取特征,最后一个卷积层实现判断图像真假。(全连接层之前的卷积层的作用是提取特征,全连接层的作用是分类,但是全连接层参数太多,一些性能优越的网络模型如Resnet和GoogLeNet等均用全局平均池化GAP来取代全连接层融合学到的深度特征。全连接层后一般接激活函数。全连接层就是全连接的卷积层。一个神经元里面滤波器的个数是输入的特征图的个数。全连接层有两种理解方式:一种是卷积实现,一种是展平。)需要从图像中提取特征。 1234o_c1=general_conv2d(input_disc,ndf,f,f,2,2)#ndf表示判别器初始层的特征个数,可以调整这个值来获得最佳效果 o_c2=general_conv2d(o_c1,ndf*2,f,f,2,2) o_enc_A=general_conv2d(o_c2,ndf*4,f,f,2,2) o_c4=general_conv2d(o_enc_A,ndf*8,f,f,2,2) 下一步就是确定这些特征是否属于该特定类别,添加一个产生1维输出的卷积层来完成这个任务。可以通过调整ndf这个参数值来获得最佳效果 1decision=general_conv2d(o_c4,1,f,f,1,1,0.02) 要使这个模型可以从A到B域和B到A域两个方向工作,设置了两个生成器,A到B生成器和B到A生成器,以及两个判别器,判别器A和判别器B。 建立模型 在定义损失函数前,先定义基础输入变量来构建模型 12input_A=tf.placeholder(tf.float32,[batch_size,img_width,img_height,img_layer],name="input_A") input_B=tf.placeholder(tf.float32,[batch_size,img_width,img_height,img_layer],name="input_B") 这些占位符作为输入,同时定义模型如下: 12345678gen_B=build_generator(input_A,name="generator_AtoB") gen_A=build_generator(input_B,name="generator_BtoA") dec_A=build_discriminator(input_A,name="discriminator_A") dec_B=build_discriminator(input_B,name="discrimination_B") dec_gen_A=build_discrimination(gen_A,"discriminator_A") dec_gen_B=build_discriminator(gen_B,"discriminator_B") cyc_A=build_generator(gen_B,"generator_BtoA") cyc_B=build_generator(gen_A,"generator_AtoB") gen表示使用相应的生成器生成的图像,dec表示在将相应输入传递到判别器后作出的判断。 损失函数 损失函数应该包括如下四个部分: (1)判别器必须允许所有相应类别的原始图像,即对应输出置1 (2)判别器必须拒绝所有想要愚弄过关的生成图像,即对应输出置0 (3)生成器必须使判别器允许通过所有的生成图像,来实现愚弄操作。 (4)所生成的图像必须保留有原始图像的特性,所以如果使用A域到B域生成器生成一张假图像,那么要能够使用另一个B域到A域生成器来努力恢复成原始图像。此过程必须满足循环一致性。 判别器损失 第一部分 通过训练判别器A,使其对A类图像的输出接近于1,判别器B也是如此。判别器A的训练目标是最小化[DiscriminatorA(a)-1]^2的值,判别器B也是这样,代码如下: 12D_A_loss_1=tf.reduce_mean(tf.squared_difference(dec_A,1)) D_A_loss_1=tf.reduce_mean(tf.squared_difference(dec_B,1)) 第二部分 判别器要能够区分生成图像和原始图像,所以在处理生成图像时期输出为0,即判别器A要最小化(DiscriminatorA(GeneratorB到A(b)))^2。代码如下: 12345D_A_loss_2=tf.reduce_mean(tf.square(dec_gen_A)) D_B_loss_2=tf.reduce_mean(tf.reduce_mean(tf.square(dec_gen_B)) D_A_loss=(D_A_loss_1+D_A_loss_2)/2 D_B_loss=(D_B_loss_1+D_B_loss_2)/2

生成器损失 最终生成器应该能提高判别器对生成图像的输出值,如果判别器对生成图像的输出值尽可能接近1,则生成器的作用达到,故生成器想要最小化(DiscriminatorB(GeneratorA到B(a))-1)^2,因此损失为: 12g_loss_B_1=tf.reduce_mean(tf.squared_difference(dec_gen_A,1)) g_loss_A_1=tf.reduce_mean(tf.squared_difference(dec_gen_A,1)) 循环损失 判断另一个生成器得到的生成器得到的生成图像与原始图像的差别,因此原始图像和循环图像间的差异应该尽可能小。 1cyc_loss=tf.reduce_mean(tf.abs(input_A-cyc_A))+tf.reduce_mean(tf.abs(input_B-cyc_B)) 所以完整的生成器损失为: 12g_loss_A=g_loss_A_1+10*cyc_loss g_loss_B=g_loss_B_1+10*cyc_loss cyc_loss的乘法因子设置为10,说明循环损失比判别损失更重要。 混合参数 定义好损失函数,接下来只需要训练模型来最小化损失函数 1234d_A_trainer=optimizer.minimize(d_loss_A,var_list=d_A_vars) d_B_trainer=optimizer.minimize(d_loss_B,var_list=d_B_vars) g_A_trainer=optimizer.minimize(g_loss_A,var_list=d_A_vars) d_B_trainer=optimizer.minimize(d_loss_B,var_list=d_B_vars) 训练模型 123456789101112131415for epoch in range(0,100):     #Define the learning rate schedule.The learning rate is kept constant up to 100 epochs and then slowly decayed     if (epoch


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有