AIGC实战——BicycleGAN详解与实现
AIGC实战——BicycleGAN详解与实现
- 0. 前言
- 1. BicycleGAN 原理
- 2. cVAE-GAN
- 3. cLR-GAN
- 4. 实现 BicycleGAN
- 4.1 将潜编码插入生成器
- 4.2 cVAE-GAN
- 4.3 cLR-GAN
- 4.4 训练步骤
- 相关链接
0. 前言
pix2pix 和 CycleGAN 是非常的流行生成对抗网络 (Generative Adversarial Network, GAN) ,不仅在学术界有许多变体,同时也有许多基于此的应用。但是,它们都有一个缺点——图像的输出看起来几乎总是相同的。例如,如果我们要执行斑马到马的转换,被转换的同一马的照片将始终具有相同的外观和色调,这是由于它学会过滤了噪声的随机性。为了进行多样化图像转换,本文详解了 BicycleGAN
如何解决此问题以生成更丰富的图像,并利用 Tensorflow2
实现 BicycleGAN
。
1. BicycleGAN 原理
初看之下,可能会认为 BicycleGAN
是 CycleGAN 的升级,像是增加了另一个循环(从单循环到双循环)。但其实它与 CycleGAN
无关,而是对 pix2pix
的一种改进。
pix2pix
是一对一映射,其中给定输入的输出始终相同。BicycleGAN
作者试图将噪声添加到生成器输入中,但是网络会忽略噪声,并且没有在输出图像中产生变化。因此,他们试图寻找一种方法,其中生成器不忽略噪声,而是使用噪声来生成多样化的图像,因此是一对多映射。
在以下图中,我们可以看到与 BicycleGAN
相关的模型和配置。图(a)
是推理的配置,其中图像 AAA 与输入噪声相结合以生成图像 B^\hat BB^,可以看作是 cGAN。在 cGAN
中,噪声起着主导作用,输入为具有大小为 100
的一维潜编码和作为条件的类别标签(大小为 10
的一维向量)。在 BicyleGAN
中,形状为 (256, 256, 3)
的图像 A
是条件,而从潜编码 zzz 采样的噪声为大小为 8
的一维向量。图(b)
是 pix2pix +噪声
的训练配置,图底部的两个配置由 BicycleGAN
使用:
简而言之,BicycleGAN
的主要概念是找到潜编码 zzz 与目标图像 BBB 之间的关系,因此生成器可以在给定不同的 zzz 时学会生成不同的图像 B^\hat BB^。如上图所示,BicycleGAN
通过组合 cVAE-GAN
和 cLR-GAN
这两种方法来做到这一点。
2. cVAE-GAN
VAE-GAN
的作者认为,L1
损失并不是衡量图像视觉质量的良好指标。例如,如果图像向右移动几个像素,则人眼看起来可能没有什么不同,但会导致较大的 L1
损失。因此使用 GAN
的判别器来学习目标函数,以判断伪造的图像是否真实,并使用 VAE 作为生成器,生成的图像更清晰。如果忽略上图(c)
中的图像 AAA,那就是 VAE-GAN
,由于以 AAA 为条件,其成为 cVAE-GAN
。训练步骤如下:
VAE
将真实图片 BBB 编码为多元高斯分布的潜编码,然后从它们中采样以创建噪声输入,此流程是标准的VAE
工作流程- 使用图像 AAA 作为条件及从潜编码 zzz 采样的噪声用于生成伪图像B^\hat BB^
训练中的信息流为 B→z→B^B\rightarrow z\rightarrow \hat BB→z→B^ (图(c)
中的实线箭头),总的损失函数由三个损失组成:
- LGANVAE\mathcal L_{GAN}^{VAE}LGANVAE:对抗损失
- L1VAE\mathcal L_1^{VAE}L1VAE:
L1
重建损失 - LKL\mathcal L_{KL}LKL:
KL
散度损失
3. cLR-GAN
在 cVAE-GAN
中,对真实图像 BBB 进行编码,以提供潜编码的真实样本并从中进行采样。但是, cLR-GAN
的处理方式有所不同,其首先使用生成器从随机噪声中生成虚假图像 B^\hat BB^ ,然后对伪图像 B^\hat BB^ 进行编码,最后计算其与输入随机噪声差异。
前向计算步骤如下:
- 首先,类似于
cGAN
,随机产生一些噪声,然后串联图像 AAA 以生成虚假图像 B^\hat BB^ - 之后,使用来自
VAE-GAN
的同一编码器将虚假图像 B^\hat BB^ 编码为潜编码 - 最后,从编码的潜编码中采样 z^\hat zz^ ,并用输入噪声 zzz 计算损失
数据流为 z−>B^−>z^z-> \hat B -> \hat zz−>B^−>z^ ( 图(d)
中的实线箭头),有两个损失:
- LGAN\mathcal L_{GAN}LGAN:对抗损失
- L1latent\mathcal L_1^{latent}L1latent:噪声 N(z)N(z)N(z) 与潜在编码之间的
L1
损失
通过组合这两个数据流,在输出和潜空间之间得到了一个双映射循环。BicycleGAN
中的 bi
来自双映射(双向单射),这是一个数学术语,简单来说其表示一对一映射,并且是可逆的。在这种情况下,BicycleGAN
将输出映射到潜空间,并且类似地从潜空间映射到输出。总损失如下:
lossBicycle=LGANVAE+LGAN+λL1VAE+λlatentL1latent+λKLloss_{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、λlatent=0.5λ_{latent} = 0.5λlatent=0.5、λlatent=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()
的代码片段,显示了潜编码的拼接和连接:
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
模型的代码,实现前向计算:
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
:
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 训练步骤
两种模型一起训练,但是具有不同的图像对。因此,在每个训练步骤中,我们两次获取数据,每个模型一次。一个方法是通过创建数据管道来完成的,该数据管道将两次加载数据:
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
将这两个模型的生成器和判别器组合为一个训练步骤。
- 执行前向传递并收集两个模型的输出:
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])
- 接下来,我们反向传播并更新判别器:
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))
- 然后,我们根据模型的输出计算损失。与
CycleGAN
相似,BicycleGAN
也使用LSGAN
损失函数,即均方误差:
self.LAMBDA_IMAGE = 10self.LAMBDA_LATENT = 0.5self.LAMBDA_KL = 0.01# Generator and Encoder lossself.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_lossself.latent_loss = self.LAMBDA_LATENT * self.mae(z, mean_random)
- 最后,还有生成器和编码器权重的更新。
L1
潜编码损失仅用于更新生成器,而不用于更新编码器。由于针对损失同时优化将导致它们隐藏与潜编码有关的信息,而不学习潜在编码中有意义的模式。因此,需要为生成器和编码器分别计算损失,并相应地更新权重:
encoder_loss = self.gan_1_loss + self.gan_2_loss + self.image_loss + self.kl_lossgenerator_loss = encoder_loss + self.latent_lossgradients_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)