AIGC实战------BicycleGAN详解与实现
-
- [0. 前言](#0. 前言)
- [1. BicycleGAN 原理](#1. BicycleGAN 原理)
- [2. cVAE-GAN](#2. cVAE-GAN)
- [3. cLR-GAN](#3. cLR-GAN)
- [4. 实现 BicycleGAN](#4. 实现 BicycleGAN)
-
- [4.1 将潜编码插入生成器](#4.1 将潜编码插入生成器)
- [4.2 cVAE-GAN](#4.2 cVAE-GAN)
- [4.3 cLR-GAN](#4.3 cLR-GAN)
- [4.4 训练步骤](#4.4 训练步骤)
- 相关链接
0. 前言
pix2pix和 CycleGAN 是非常的流行生成对抗网络 (Generative Adversarial Network, GAN),不仅在学术界有许多变体,同时也有许多基于此的应用。但是,它们都有一个缺点------图像的输出看起来几乎总是相同的。例如,如果我们要执行斑马到马的转换,被转换的同一马的照片将始终具有相同的外观和色调,这是由于它学会过滤了噪声的随机性。为了进行多样化图像转换,本文详解了 BicycleGAN
如何解决此问题以生成更丰富的图像,并利用 Tensorflow2
实现 BicycleGAN
。
1. BicycleGAN 原理
初看之下,可能会认为 BicycleGAN
是 CycleGAN 的升级,像是增加了另一个循环(从单循环到双循环)。但其实它与 CycleGAN
无关,而是对 pix2pix
的一种改进。
pix2pix
是一对一映射,其中给定输入的输出始终相同。BicycleGAN
作者试图将噪声添加到生成器输入中,但是网络会忽略噪声,并且没有在输出图像中产生变化。因此,他们试图寻找一种方法,其中生成器不忽略噪声,而是使用噪声来生成多样化的图像,因此是一对多映射。
在以下图中,我们可以看到与 BicycleGAN
相关的模型和配置。图(a)
是推理的配置,其中图像 A A A 与输入噪声相结合以生成图像 B ^ \hat B B^,可以看作是cGAN。在 cGAN
中,噪声起着主导作用,输入为具有大小为 100
的一维潜编码和作为条件的类别标签(大小为 10
的一维向量)。在 BicyleGAN
中,形状为 (256, 256, 3)
的图像 A
是条件,而从潜编码 z z z 采样的噪声为大小为 8
的一维向量。图(b)
是 pix2pix +噪声
的训练配置,图底部的两个配置由 BicycleGAN
使用:

简而言之,BicycleGAN
的主要概念是找到潜编码 z z z 与目标图像 B B B 之间的关系,因此生成器可以在给定不同的 z z z 时学会生成不同的图像 B ^ \hat B B^。如上图所示,BicycleGAN
通过组合 cVAE-GAN
和 cLR-GAN
这两种方法来做到这一点。
2. cVAE-GAN
VAE-GAN
的作者认为,L1
损失并不是衡量图像视觉质量的良好指标。例如,如果图像向右移动几个像素,则人眼看起来可能没有什么不同,但会导致较大的 L1
损失。因此使用 GAN
的判别器来学习目标函数,以判断伪造的图像是否真实,并使用VAE作为生成器,生成的图像更清晰。如果忽略上图(c)
中的图像 A A A,那就是 VAE-GAN
,由于以 A A A 为条件,其成为 cVAE-GAN
。训练步骤如下:
VAE
将真实图片 B B B 编码为多元高斯分布的潜编码,然后从它们中采样以创建噪声输入,此流程是标准的VAE
工作流程- 使用图像 A A A 作为条件及从潜编码 z z z 采样的噪声用于生成伪图像 B ^ \hat B B^
训练中的信息流为 B → z → B ^ B\rightarrow z\rightarrow \hat B B→z→B^ (图(c)
中的实线箭头),总的损失函数由三个损失组成:
- L G A N V A E \mathcal L_{GAN}^{VAE} LGANVAE:对抗损失
- L 1 V A E \mathcal L_1^{VAE} L1VAE:
L1
重建损失 - L K L \mathcal L_{KL} LKL:
KL
散度损失
3. cLR-GAN
在 cVAE-GAN
中,对真实图像 B B B 进行编码,以提供潜编码的真实样本并从中进行采样。但是, cLR-GAN
的处理方式有所不同,其首先使用生成器从随机噪声中生成虚假图像 B ^ \hat B B^ ,然后对伪图像 B ^ \hat B B^ 进行编码,最后计算其与输入随机噪声差异。
前向计算步骤如下:
- 首先,类似于
cGAN
,随机产生一些噪声,然后串联图像 A A A 以生成虚假图像 B ^ \hat B B^ - 之后,使用来自
VAE-GAN
的同一编码器将虚假图像 B ^ \hat B B^ 编码为潜编码 - 最后,从编码的潜编码中采样 z ^ \hat z z^ ,并用输入噪声 z z z 计算损失
数据流为 z − > B ^ − > z ^ z-> \hat B -> \hat z z−>B^−>z^ ( 图(d)
中的实线箭头),有两个损失:
- L G A N \mathcal L_{GAN} LGAN:对抗损失
- L 1 l a t e n t \mathcal L_1^{latent} L1latent:噪声 N ( z ) N(z) N(z) 与潜在编码之间的
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+λlatentL1latent+λ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
架构和损失函数,接下来使用 TensorFlow
实现 BicycleGAN
。
4. 实现 BicycleGAN
BicycleGAN
中有三种类型的网络-生成器,判别器和编码器。随着输入图像尺寸的增大,编码器将包含更多的卷积核和更深的网络层。原始的 BicycleGAN
使用两个 PatchGAN
,其有效感受野分别为 70x70
和 140x140
。
为简单起见,我们将使用一个 70x70
的 PatchGAN
。为 cVAE-GAN
和 cLR-GAN
使用单独的判别器可以提高图像质量,这意味着我们总共有四个网络-生成器,编码器和两个判别器。
4.1 将潜编码插入生成器
将潜编码插入到生成器中有两种方法,一种与输入图像进行拼接,另一种将其插入到生成器的下采样路径中的其他层中,如下图所示,实验表明前者效果更好。

有多种方法可以将不同形状的输入和条件结合起来。BicycleGAN
的方法是多次重复潜编码并与输入图像连接。
在 BicycleGAN
中,潜编码长度为 8
。我们从噪声分布中提取了 8
个样本,每个样本重复 H×W
次以形成形状为 (H, W, 8)
的张量。换句话说,在 8
个通道中,其 (H, W)
特征图都是相同的。以下是 build_generator()
的代码片段,显示了潜编码的拼接和连接:
python
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-GAN
和 cLR-GAN
,以合并网络并创建前向信息流。
4.2 cVAE-GAN
创建 cVAE-GAN
模型的代码,实现前向计算:
python
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])
我们在模型中使用了 KL
散度损失。由于可以直接根据均值和对数方差来计算 kl_loss
,而无需从训练步骤中传入外部标签,因此此方法更加简单有效。
4.3 cLR-GAN
接下来,实现 cLR-GAN
:
python
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])
完成模型定义后,实现训练步骤。
4.4 训练步骤
两种模型一起训练,但是具有不同的图像对。因此,在每个训练步骤中,我们两次获取数据,每个模型一次。一个方法是通过创建数据管道来完成的,该数据管道将两次加载数据:
python
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
将这两个模型的生成器和判别器组合为一个训练步骤。
-
执行前向传递并收集两个模型的输出:
pythondef 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])
-
接下来,我们反向传播并更新判别器:
pythonself.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))
-
然后,我们根据模型的输出计算损失。与
CycleGAN
相似,BicycleGAN
也使用LSGAN
损失函数,即均方误差:
python
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)
- 最后,还有生成器和编码器权重的更新。
L1
潜编码损失仅用于更新生成器,而不用于更新编码器。由于针对损失同时优化将导致它们隐藏与潜编码有关的信息,而不学习潜在编码中有意义的模式。因此,需要为生成器和编码器分别计算损失,并相应地更新权重:
python
encoder_loss = self.gan_1_loss + self.gan_2_loss + self.image_loss + self.kl_loss
generator_loss = encoder_loss + self.latent_loss
gradients_generator = tape_g.gradient(generator_loss, self.generator.trainable_variables)
self.optimizer_generator.apply_gradients(zip(gradients_generator, self.generator.trainable_variables))
gradients_encoder = tape_e.gradient(encoder_loss, self.encoder.trainable_variables)
self.optimizer_encoder.apply_gradients(zip(gradients_encoder, self.encoder.trainable_variables))
使用鞋子数据集训练 BicycleGAN
,以下图像是 BicycleGAN
训练结果的展示示例。第一张图片是线稿,第二个图片是线稿对应的真实图像,右边的四个图片是生成的:
可以看到,由同一线稿生成的不同图片间的差异主要是颜色。
相关链接
AIGC实战------生成模型简介
AIGC实战------深度学习 (Deep Learning, DL)
AIGC实战------卷积神经网络(Convolutional Neural Network, CNN)
AIGC实战------自编码器(Autoencoder)
AIGC实战------变分自编码器(Variational Autoencoder, VAE)
AIGC实战------使用变分自编码器生成面部图像
AIGC实战------生成对抗网络(Generative Adversarial Network, GAN)
AIGC实战------WGAN(Wasserstein GAN)
AIGC实战------条件生成对抗网络(Conditional Generative Adversarial Net, CGAN)
AIGC实战------CycleGAN详解与实现
AIGC实战------自回归模型(Autoregressive Model)
AIGC实战------改进循环神经网络
AIGC实战------像素卷积神经网络(PixelCNN)
AIGC实战------归一化流模型(Normalizing Flow Model)
AIGC实战------能量模型(Energy-Based Model)
AIGC实战------扩散模型(Diffusion Model)
AIGC实战------GPT(Generative Pre-trained Transformer)
AIGC实战------Transformer模型
AIGC实战------ProGAN(Progressive Growing Generative Adversarial Network)
AIGC实战------StyleGAN(Style-Based Generative Adversarial Network)
AIGC实战------VQ-GAN(Vector Quantized Generative Adversarial Network)
AIGC实战------基于Transformer实现音乐生成
AIGC实战------MuseGAN详解与实现
AIGC实战------多模态模型DALL.E 2
AIGC实战------多模态模型Flamingo
AIGC实战------世界模型(World Model)