BicycleGAN详解与实现

cLR-GAN(Conditional Latent Regressor GAN)

cVAE-GAN 中,对真实图像B进行编码,以提供潜在矢量的真实样本并从中进行采样。但是, cLR-GAN 的处理方式有所不同,其首先使用生成器从随机噪声中生成伪图像 B ^ \hat B B^ ,然后对伪图像 B ^ \hat B B^ 进行编码,最后计算其与输入随机噪声差异。

前向计算步骤如下:

  1. 首先,类似于 cGAN ,随机产生一些噪声,然后串联图像A以生成伪图像 B ^ \hat B B^。

  2. 之后,使用来自 VAE-GAN 的同一编码器将伪图像 B ^ \hat B B^ 编码为潜矢量。

  3. 最后,从编码的潜矢量中采样 z ^ \hat z z^ ,并用输入噪声 z z z 计算损失。

数据流为 z − > B ^ − > z ^ z-> \hat B -> \hat z z−>B−>z ( 图(d) 中的实线箭头),有两个损失:

  1. L G A N \mathcal L_{GAN} LGAN​:对抗损失

  2. L 1 l a t e n t \mathcal L_1^{latent} L1latent​:噪声 N(z) 与潜在编码之间的 L 1 L_1 L1​ 损失

通过组合这两个数据流,在输出和潜在空间之间得到了一个双映射循环。 BicycleGAN 中的 bi 来自双映射(双向单射),这是一个数学术语,简单来说其表示一对一映射,并且是可逆的。在这种情况下,BicycleGAN 将输出映射到潜在空间,并且类似地从潜在空间映射到输出。总损失如下:

l o s s B i c y c l e = L G A N V A E + L G A N + λ L 1 V A E + λ l a t e n t L 1 l a t e n t + λ K L loss_{Bicycle}=\mathcal L_{GAN}^{VAE}+\mathcal L_{GAN}+λ\mathcal L_1^{VAE}+λ_{latent}\mathcal L_1^{latent}+λ_{KL} lossBicycle​=LGANVAE​+LGAN​+λL1VAE​+λlatent​L1latent​+λKL​

在默认配置中, λ = 10 λ = 10 λ=10、 λ l a t e n t = 0.5 λ_{latent} = 0.5 λlatent​=0.5、 λ l a t e n t = 0.01 λ_{latent} = 0.01 λlatent​=0.01。

BicycleGAN实现


BicycleGAN 中有三种类型的网络——生成器,鉴别器和编码器。为 cVAE-GANcLR-GAN 使用单独的鉴别器可以提高图像质量,因此我们将使用四个网络-生成器,编码器和两个鉴别器。

在生成器中插入潜在编码

将潜在编码插入到生成器中有两种方法,如下图所示:

  1. 与输入图像进行级联;

  2. 将其插入到生成器的下采样路径中的其他层中。

在生成器中插入潜在编码  实验发现前者效果很好。

有多种方法可以将不同形状的输入和条件结合起来。 BicycleGAN 使用的方法是多次重复潜在编码然后与输入图像连接。

BicycleGAN 中,潜在编码长度为8,我们从噪声分布中提取了8个样本,每个样本重复H×W次以形成形状为 (H, W, 8) 的张量。换句话说,在8个通道中,其 (H,W) 特征图都是相同的。以下代码显示了潜在编码的拼接和连接:

input_image = layers.Input(shape=image_shape, name=‘input_image’)

input_z = layers.Input(shape=(self.z_dim,), name=‘z’)

z = layers.Reshape((1,1, self.z_dim))(input_z)

z_tiles = tf.tile(z, [self.batch_size, self.input_shape[0], self.input_shape[1], self.z_dim])

x = layers.Concatenate()([input_image, z_tiles])

下一步是创建两个模型,即 cVAE-GANcLR-GAN,以合并网络并创建前向信息流。

cVAE-GAN

下面创建 cVAE-GAN 模型的代码,前向计算的实现:

images_A_1 = layers.Input(shape=input_shape, name=‘ImageA_1’)

images_B_1 = layers.Input(shape=input_shape, name=‘ImageB_1’)

z_encode, self.mean_encode, self.logvar_encode = self.encoder(images_B_1)

fake_B_encode = self.generator([images_A_1, z_encode])

encode_fake = self.discriminator_1(fake_B_encode)

encode_real = self.discriminator_1(images_B_1)

kl_loss = - 0.5 * tf.reduce_sum(1 + self.logvar_encode - tf.square(self.mean_encode) - tf.exp(self.logvar_encode))

self.cvae_gan = Model(inputs=[images_A_1, images_B_1], outputs=[encode_real, encode_fake, fake_B_encode, kl_loss])

我们在模型中使用了 K L KL KL 散度损失。由于可以直接根据均值和对数方差来计算 kl_loss ,而无需在训练步骤中传入外部标签,因此更加简单有效。

cLR-GAN

下面是 cLR-GAN 的实现,前向计算的实现:

images_A_2 = layers.Input(shape=input_shape, name=‘ImageA_2’)

images_B_2 = layers.Input(shape=input_shape, name=‘ImageB_2’)

z_random = layers.Input(shape=(self.z_dim,), name=‘z’)

fake_B_random = self.generator([images_A_2, z_random])

_, mean_random, _ = self.encoder(fake_B_random)

random_fake = self.discriminator_2(fake_B_random)

random_real = self.discriminator_2(images_B_2)

self.clr_gan = Model(inputs=[images_A_2, images_B_2, z_random],

outputs=[random_real, random_fake, mean_random])

现在,我们现在已经定义了模型,下一步是实现训练步骤。

训练步骤

两种模型一起进行训练,但是具有不同的图像对。因此,在每个训练步骤中,我们两次获取数据,每个模型一次,这是通过创建数据管道来完成的,该数据管道将调用两次以加载数据:

images_A_1, images_B_1 = next(data_generator)

images_A_2, images_B_2 = next(data_generator)

self.train_step(images_A_1, images_B_1, images_A_2, images_B_2)

我们可以使用两种不同的方法来执行训练。一种是使用优化器和损失函数定义和编译Keras模型,然后调用 train_on_batch() 来执行训练步骤,这种方法在定义明确的模型上效果很好。此外,我们也可以使用 tf.GradientTape 来更好地控制梯度更新。BicycleGAN 有两个模型,它们共享一个生成器和一个编码器,但是我们需要使用损失函数的不同组合来更新它们的参数,这使 train_on_batch 方法在不修改原始设置的情况下不可行。因此,我们使用 tf.GradientTape 将这两个模型的生成器和鉴别器组合为一个训练步骤,如下所示:

  1. 第一步是执行前向传递并收集两个模型的输出:

def train_step(self, images_A_1, images_B_1, images_A_2, images_B_2):

z = tf.random.normal((self.batch_size, self.z_dim))

real_labels = tf.ones((self.batch_size, self.patch_size, self.patch_size, 1))

fake_labels = tf.zeros((self.batch_size, self.patch_size, self.patch_size, 1))

with tf.GradientTape() as tape_e, tf.GradientTape() as tape_g, tf.GradientTape() as tape_d1, tf.GradientTape() as tape_d2:

encode_real, encode_fake, fake_B_encode, kl_loss = self.cvae_gan([images_A_1, images_B_1])

random_real, random_fake, mean_random = self.clr_gan([images_A_2, images_B_2, z])

  1. 接下来,我们通过反向梯度传播更新鉴别器:

discriminator loss

self.d1_loss = self.mse(real_labels, encode_real) + self.mse(fake_labels, encode_fake)

gradients_d1 = tape_d1.gradient(self.d1_loss, self.discriminator_1.trainable_variables)

self.optimizer_d1.apply_gradients(zip(gradients_d1, self.discriminator_1.trainable_variables))

self.d2_loss = self.mse(real_labels, random_real) + self.mse(fake_labels, random_fake)

gradients_d2 = tape_d2.gradient(self.d2_loss,self.discriminator_2.trainable_variables)

self.optimizer_d2.apply_gradients(zip(gradients_d2, self.discriminator_2.trainable_variables))

  1. 然后,我们根据模型的输出计算损失。与 CycleGAN 相似,BicycleGAN 也使用 LSGAN 损失函数,即均方误差:

self.LAMBDA_IMAGE = 10

self.LAMBDA_LATENT = 0.5

self.LAMBDA_KL = 0.01

Generator and Encoder loss

self.gan_1_loss = self.mse(real_labels, encode_fake)

self.gan_2_loss = self.mse(real_labels, random_fake)

self.image_loss = self.LAMBDA_IMAGE * self.mae(images_B_1, fake_B_encode)

self.kl_loss = self.LAMBDA_KL * kl_loss

self.latent_loss = self.LAMBDA_LATENT * self.mae(z, mean_random)

  1. 最后,还有生成器和编码器权重的更新。 L 1 L_1 L1​潜在编码损失仅用于更新生成器,而不用于更新编码器。由于针对损失同时优化将导致它们隐藏与潜在编码有关的信息,而不学习潜在编码中有意义的模式。因此,需要为生成器和编码器分别计算损失,并相应地更新权重:

encoder_loss = self.gan_1_loss + self.gan_2_loss + self.image_loss + self.kl_loss

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值