CycleGAN 是什么?

CycleGAN,即循环生成对抗网络,是一种专门用于无监督图像转换的深度学习模型。该模型的主要目标是学习两个不同域间(比如马与斑马、夏天与冬天等)的映射函数,而无需任何成对的训练样本。这与传统的生成对抗网络(GAN)不同,因为传统的GAN通常需要源数据和目标数据一一对应的训练。

CycleGAN已被广泛应用于各种场景中,包括但不限于风格迁移(如将一幅画的风格转换为另一幅画的风格)、季节转换(将夏天的景色转换为冬天的景色)、动物转换(将马的图片转换为斑马的图片)等。更重要的是,CycleGAN提供了一种新的视角,可以用于各种创新的深度学习应用。


原理

以下是CycleGAN的主要步骤和原理:

生成器和判别器: CycleGAN使用两个生成器(G和F)和两个判别器(D_X和D_Y)。生成器G旨在将输入的X域图像转换为Y域图像。相反,生成器F旨在将输入的Y域图像转换为X域图像。判别器D_X旨在区分真实的X域图像和由F生成的X域图像。相应地,判别器D_Y旨在区分真实的Y域图像和由G生成的Y域图像。

对抗性损失: 这是GANs的基本组成部分,其中生成器试图欺骗判别器,判别器试图正确地区分真实图像和生成的图像。在CycleGAN中,有两种类型的对抗性损失:一种是关于生成器G和判别器D_Y,另一种是关于生成器F和判别器D_X。

循环一致性损失: 这是CycleGAN的核心,也是它的命名来源。循环一致性损失确保转换是一致的,即如果一个图像从X域转换到Y域,然后再转换回X域,那么得到的图像应与原始X域图像相同。同样,如果一个图像从Y域转换到X域,然后再转换回Y域,那么得到的图像应该与原始Y域图像相同。

在训练期间,对抗性损失和循环一致性损失联合最小化。这样就可以训练模型在保持输入图像的内容的同时改变其样式。

循环一致性损失

循环一致性损失是CycleGAN的关键组成部分。这种损失函数试图确保当图像从源领域转换到目标领域,然后再转换回源领域时,得到的图像与原始图像尽可能接近。这种损失的动机是确保转换过程不会丢失原始图像的重要内容。

具体来说,假设我们有两个领域:X和Y,以及两个生成器:G和F。生成器G负责将X领域的图像转换为Y领域的图像,生成器F负责将Y领域的图像转换为X领域的图像。

循环一致性损失包含两部分:

  1. 正向循环一致性损失:这是当我们将图像从X领域转换到Y领域,然后再转换回X领域时产生的损失。数学上,这可以表示为:||F(G(x)) - x||,其中x是从X领域采样的图像,G(x)x转换为Y领域的图像,F(G(x))再将G(x)转换回X领域的图像。这个损失项试图最小化F(G(x))x之间的差距。
  2. 反向循环一致性损失:这是当我们将图像从Y领域转换到X领域,然后再转换回Y领域时产生的损失。数学上,这可以表示为:||G(F(y)) - y||,其中y是从Y领域采样的图像,F(y)y转换为X领域的图像,G(F(y))再将F(y)转换回Y领域的图像。这个损失项试图最小化G(F(y))y之间的差距。

在实际应用中,循环一致性损失常常使用L1范数(绝对值之和)或者L2范数(平方和)来计算上述两个差距。

通过最小化这两部分的损失,循环一致性损失函数强迫网络学习到一种映射,使得转换后再逆转换回来,能够得到与原图像尽可能接近的图像。这样,转换过程可以保持输入图像的关键内容,同时改变它的样式。

训练过程

CycleGAN的训练流程包括以下几个步骤:

  1. 首先,生成器G和F分别将图像从域X转换到域Y,以及从域Y转换到域X。
  2. 接着,生成的假图像被判别器DX和DY分别判断其是否来自目标域。
  3. 然后,生成器G和F分别将假的域Y和域X图像转换回其原始域,形成一个循环。这一步生成的图像应与原始输入图像尽可能相似。
  4. 判别器和生成器的损失然后被计算出来。对于判别器,损失函数通常是二元交叉熵损失,用于判断其对真假图像的判断准确度。对于生成器,损失函数既包括对抗损失(使生成的图像更难被判别器识别),也包括循环一致性损失(使转换后再转换回来的图像与原图尽可能接近)。
  5. 最后,使用优化器(如Adam)根据损失函数的梯度来更新生成器和判别器的参数。

示例代码

import tensorflow as tf
from tensorflow.keras import layers, Model, applications, losses, optimizers

# 定义生成器, 基于ResNet50
def create_generator():
    model = applications.ResNet50(include_top=False, weights=None, input_shape=(64,64,3))
    model.add(layers.Conv2D(3, (1,1), activation='tanh'))
    return model

# 定义判别器, 基于ResNet50
def create_discriminator():
    model = applications.ResNet50(include_top=False, weights=None, input_shape=(64,64,3))
    model.add(layers.Flatten())
    model.add(layers.Dense(1, activation='sigmoid'))
    return model

# 创建生成器和判别器实例
generator_G = create_generator()
generator_F = create_generator()
discriminator_X = create_discriminator()
discriminator_Y = create_discriminator()

# 定义损失函数和优化器
adv_loss_fn = losses.BinaryCrossentropy()
cycle_loss_fn = losses.MeanAbsoluteError()
optimizer = optimizers.Adam(2e-4, beta_1=0.5)

# 定义训练步骤
@tf.function
def train_step(real_x, real_y):
    with tf.GradientTape(persistent=True) as tape:
        # 生成器G将real_x转换为fake_y,生成器F将real_y转换为fake_x
        fake_y = generator_G(real_x, training=True)
        fake_x = generator_F(real_y, training=True)

        # 将fake_y转换回cycled_x,将fake_x转换回cycled_y
        cycled_x = generator_F(fake_y, training=True)
        cycled_y = generator_G(fake_x, training=True)

        # 判别器对real和fake图像的预测
        real_x_disc_pred = discriminator_X(real_x, training=True)
        real_y_disc_pred = discriminator_Y(real_y, training=True)
        fake_x_disc_pred = discriminator_X(fake_x, training=True)
        fake_y_disc_pred = discriminator_Y(fake_y, training=True)

        # 计算生成器和判别器的对抗损失
        adv_loss_G = adv_loss_fn(real_y_disc_pred, fake_y_disc_pred)
        adv_loss_F = adv_loss_fn(real_x_disc_pred, fake_x_disc_pred)

        # 计算循环一致性损失
        cycle_loss_G = cycle_loss_fn(real_y, cycled_y)
        cycle_loss_F = cycle_loss_fn(real_x, cycled_x)

        # 生成器的总损失是对抗损失和循环一致性损失的和
        total_loss_G = adv_loss_G + cycle_loss_G
        total_loss_F = adv_loss_F + cycle_loss_F

        # 判别器的损失是对抗损失
        disc_loss_X = adv_loss_fn(real_x_disc_pred, fake_x_disc_pred)
        disc_loss_Y = adv_loss_fn(real_y_disc_pred, fake_y_disc_pred)

    # 计算梯度并应用到模型的参数上      
    grads_G = tape.gradient(total_loss_G, generator_G.trainable_variables)
    grads_F = tape.gradient(total_loss_F, generator_F.trainable_variables)
    grads_D_X = tape.gradient(disc_loss_X, discriminator_X.trainable_variables)
    grads_D_Y = tape.gradient(disc_loss_Y, discriminator_Y.trainable_variables)

    optimizer.apply_gradients(zip(grads_G, generator_G.trainable_variables))
    optimizer.apply_gradients(zip(grads_F, generator_F.trainable_variables))
    optimizer.apply_gradients(zip(grads_D_X, discriminator_X.trainable_variables))
    optimizer.apply_gradients(zip(grads_D_Y, discriminator_Y.trainable_variables))

# 数据预处理
def preprocess_image(image):
    # 将图像的像素值从[0, 255]缩放到[-1, 1]范围
    image = tf.cast(image, dtype=tf.float32)
    image = (image / 127.5) - 1
    return image

# 假设dataset_x_raw和dataset_y_raw是原始的图像数据集
# 我们应用预处理函数来创建新的数据集
dataset_x = dataset_x_raw.map(preprocess_image)
dataset_y = dataset_y_raw.map(preprocess_image)

# 训练
EPOCHS = 100  # 你可以根据需要设置epoch的数量
for epoch in range(EPOCHS):
    for image_x, image_y in tf.data.Dataset.zip((dataset_x, dataset_y)):
        train_step(image_x, image_y)
如果觉得我的文章对你有用,请随意赞赏