50、用于合成新数据的生成对抗网络

生成对抗网络关键技术解析

用于合成新数据的生成对抗网络

1. 简单GAN模型的局限性与改进方向

在之前的实验中,生成器网络随着训练的进行生成了越来越逼真的图像。然而,即使经过100个训练周期,生成的图像与MNIST数据集中的手写数字仍有很大差异。此前设计的简单GAN模型,生成器和判别器都只有一个全连接隐藏层。在MNIST数据集上训练该模型后,虽然取得了一些有希望的结果,但仍不尽如人意。

由于在图像分类方面,具有卷积层的神经网络架构比全连接层有诸多优势,因此可以尝试在GAN模型中添加卷积层来处理图像数据,以改善结果。接下来将实现深度卷积生成对抗网络(DCGAN),其生成器和判别器网络都使用卷积层。

2. 改进合成图像质量的关键技术

为了提高之前GAN示例的性能,将实现DCGAN,并采用一些额外的关键技术,同时实现Wasserstein GAN(WGAN)。这些技术包括:
- 转置卷积
- 批量归一化(BatchNorm)
- WGAN
- 梯度惩罚

2.1 转置卷积

转置卷积通常用于对特征空间进行上采样,而普通卷积操作通常用于对特征空间进行下采样。为了理解转置卷积操作,可通过一个简单的思想实验:假设有一个大小为 𝑛×𝑛 的输入特征图,对其应用具有特定填充和步幅参数的二维卷积操作,得到大小为 𝑚×𝑚 的输出特征图。现在的问题是,如何对这个 𝑚×𝑚 的输出特征图应用另一个卷积操作,以获得与初始维度 𝑛×𝑛 相同的特征图,同时保持输入和输出之间的连接模式。需要注意的是,这里只是恢复了 𝑛×𝑛 输入矩阵的形状,而不是实际的矩阵值。

转置卷积也称为分数步长卷积,在深度学习文献中,另一个常用于指代转置卷积的术语是反卷积。但反卷积最初被定义为对特征图 𝑥 应用卷积操作 𝑓 并使用权重参数 𝑤 得到特征图 𝒙′(即 𝑓𝑤(𝒙) = 𝒙′)的逆操作,反卷积函数 𝑓−1 可定义为 𝑓𝑤−1(𝑓(𝒙)) = 𝒙 。而转置卷积仅关注恢复特征空间的维度,而不是实际值。

使用转置卷积对特征图进行上采样是通过在输入特征图的元素之间插入0来实现的。以下是一个对大小为 4×4 的输入应用转置卷积的示例,步长为 2×2,核大小为 2×2 。中心大小为 9×9 的矩阵显示了在输入特征图中插入0后的结果。然后,使用 2×2 的核以步长为 1 进行普通卷积,得到大小为 8×8 的输出。可以通过对输出以步长为 2 进行常规卷积来验证反向过程,结果得到大小为 4×4 的输出特征图,与原始输入大小相同。

如果想了解更多关于输入大小、核大小、步长和填充变化如何影响输出的不同情况,可参考Vincent Dumoulin和Francesco Visin的教程。

2.2 批量归一化(BatchNorm)

BatchNorm由Sergey Ioffe和Christian Szegedy在2015年提出,其主要思想之一是对层输入进行归一化,并防止在训练过程中其分布发生变化,从而实现更快更好的收敛。

BatchNorm基于计算得到的统计信息对一小批量特征进行转换。假设在卷积层之后得到的网络预激活特征图存储在一个四维张量 𝑍 中,形状为 [𝑚×ℎ×𝑤×𝑐] ,其中 𝑚 是批量中的样本数(即批量大小), ℎ×𝑤 是特征图的空间维度, 𝑐 是通道数。BatchNorm可以概括为以下三个步骤:
1. 计算每个小批量网络输入的均值和标准差
- $\mu_{B}=\frac{1}{m\times h\times w}\sum_{i,j,k}Z[i,j,k,.]$
- $\sigma_{B}^{2}=\frac{1}{m\times h\times w}\sum_{i,j,k}(Z[i,j,k,.] - \mu_{B})^{2}$
其中 $\mu_{B}$ 和 $\sigma_{B}^{2}$ 的大小均为 𝑐 。
2. 对批量中所有样本的网络输入进行标准化
- $Z_{std}[i] = \frac{Z[i] - \mu_{B}}{\sigma_{B} + \epsilon}$
其中 $\epsilon$ 是一个用于数值稳定性的小数(即避免除以零)。
3. 使用两个可学习的参数向量 $\gamma$ 和 $\beta$ 对归一化后的网络输入进行缩放和平移
- $A_{pre}[i] = \gamma Z_{std}[i] + \beta$

在BatchNorm的第一步中,计算小批量的均值 $\mu_{B}$ 和标准差 $\sigma_{B}$ ,它们都是大小为 𝑐 的向量。然后,在第二步中使用这些统计信息通过z分数归一化(标准化)对每个小批量中的样本进行缩放,得到标准化的网络输入 $Z_{std}[i]$ 。因此,这些网络输入以均值为中心且具有单位方差,这通常是基于梯度下降的优化的一个有用属性。但如果总是将网络输入归一化,使得它们在不同的小批量中具有相同的属性(这些小批量可能是多样的),则会严重影响神经网络的表示能力。

最初,BatchNorm的开发是为了减少所谓的内部协变量偏移,即由于训练过程中网络参数的更新,导致层的激活分布发生变化。但在2018年,有研究人员进一步调查发现,BatchNorm对内部协变量偏移的影响很小,他们假设BatchNorm的有效性实际上是基于损失函数更平滑的表面,这使得非凸优化更稳健。

TensorFlow Keras API提供了一个类 tf.keras.layers.BatchNormalization() ,可在定义模型时将其用作一个层,它将执行上述BatchNorm的所有步骤。需要注意的是,更新可学习参数 $\gamma$ 和 $\beta$ 的行为取决于 training=False 还是 training=True ,这可确保这些参数仅在训练期间学习。

3. 实现生成器和判别器

3.1 生成器架构

生成器以大小为20的向量 𝑧 作为输入,通过一个全连接(密集)层将其大小增加到6272,然后将其重塑为形状为 7×7×128 的三维张量(空间维度为 7×7 ,通道数为128)。接着,使用 tf.keras.layers.Conv2DTransposed() 进行一系列转置卷积操作,对特征图进行上采样,直到得到的特征图的空间维度达到 28×28 。除最后一层外,每个转置卷积层后特征图的通道数减半,最后一层使用单个输出滤波器生成灰度图像。除最后一层使用tanh激活函数(不使用BatchNorm)外,每个转置卷积层后都跟随BatchNorm和Leaky ReLU激活函数。

生成器的代码如下:

import tensorflow as tf
import numpy as np

def make_dcgan_generator(
        z_size=20,
        output_size=(28, 28, 1),
        n_filters=128,
        n_blocks=2):
    size_factor = 2**n_blocks
    hidden_size = (
        output_size[0]//size_factor,
        output_size[1]//size_factor)

    model = tf.keras.Sequential([
        tf.keras.layers.Input(shape=(z_size,)),

        tf.keras.layers.Dense(
            units=n_filters*np.prod(hidden_size),
            use_bias=False),
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.LeakyReLU(),
        tf.keras.layers.Reshape(
            (hidden_size[0], hidden_size[1], n_filters)),

        tf.keras.layers.Conv2DTranspose(
            filters=n_filters, kernel_size=(5, 5),
            strides=(1, 1), padding='same', use_bias=False),
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.LeakyReLU()
    ])

    nf = n_filters
    for i in range(n_blocks):
        nf = nf // 2
        model.add(
            tf.keras.layers.Conv2DTranspose(
                filters=nf, kernel_size=(5, 5),
                strides=(2, 2), padding='same',
                use_bias=False))
        model.add(tf.keras.layers.BatchNormalization())
        model.add(tf.keras.layers.LeakyReLU())

    model.add(
        tf.keras.layers.Conv2DTranspose(
            filters=output_size[2], kernel_size=(5, 5),
            strides=(1, 1), padding='same', use_bias=False,
            activation='tanh'))

    return model

3.2 判别器架构

判别器接收大小为 28×28×1 的图像,通过四个卷积层进行处理。前三个卷积层在增加特征图通道数的同时,将空间维度缩小为原来的四分之一。每个卷积层后都跟随BatchNorm、Leaky ReLU激活函数和一个丢弃率为0.3的丢弃层。最后一个卷积层使用大小为 7×7 的核和单个滤波器,将输出的空间维度缩小到 1×1×1 。

判别器的代码如下:

def make_dcgan_discriminator(
        input_size=(28, 28, 1),
        n_filters=64,
        n_blocks=2):
    model = tf.keras.Sequential([
        tf.keras.layers.Input(shape=input_size),
        tf.keras.layers.Conv2D(
            filters=n_filters, kernel_size=5,
            strides=(1, 1), padding='same'),
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.LeakyReLU()
    ])

    nf = n_filters
    for i in range(n_blocks):
        nf = nf*2
        model.add(
            tf.keras.layers.Conv2D(
                filters=nf, kernel_size=(5, 5),
                strides=(2, 2),padding='same'))
        model.add(tf.keras.layers.BatchNormalization())
        model.add(tf.keras.layers.LeakyReLU())
        model.add(tf.keras.layers.Dropout(0.3))

    model.add(
        tf.keras.layers.Conv2D(
                filters=1, kernel_size=(7, 7),
                padding='valid'))

    model.add(tf.keras.layers.Reshape((1,)))

    return model

3.3 架构设计考虑

在设计卷积GAN时,需要注意生成器和判别器中特征图数量的变化趋势不同。在生成器中,从大量的特征图开始,随着向最后一层推进逐渐减少;而在判别器中,从较少的通道数开始,向最后一层推进时逐渐增加。这是设计CNN时的一个重要点,即特征图数量和特征图空间大小呈相反的变化趋势。此外,通常不建议在BatchNorm层之后的层中使用偏置单元,因为BatchNorm已经有一个偏移参数 $\beta$ ,使用偏置单元会造成冗余。可以通过在 tf.keras.layers.Dense tf.keras.layers.Conv2D 中设置 use_bias=False 来省略给定层的偏置单元。

3.4 创建模型并查看架构

可以使用上述辅助函数创建生成器和判别器网络,并查看它们的架构:

mnist_bldr = tfds.builder('mnist')
mnist_bldr.download_and_prepare()
mnist = mnist_bldr.as_dataset(shuffle_files=False)

def preprocess(ex, mode='uniform'):
    image = ex['image']
    image = tf.image.convert_image_dtype(image, tf.float32)

    image = image*2 - 1.0
    if mode == 'uniform':
        input_z = tf.random.uniform(
            shape=(z_size,), minval=-1.0, maxval=1.0)
    elif mode == 'normal':
        input_z = tf.random.normal(shape=(z_size,))
    return input_z, image

gen_model = make_dcgan_generator()
gen_model.summary()

disc_model = make_dcgan_discriminator()
disc_model.summary()

需要注意的是,这种特定的架构在使用交叉熵作为损失函数时表现不佳。接下来将介绍WGAN,它使用基于真实图像和虚假图像分布之间所谓的Wasserstein - 1(或推土机)距离的修改后的损失函数来提高训练性能。

4. 两种分布之间的差异度量

4.1 差异度量方法

生成模型的目标是学习如何合成与训练数据集分布相同的新样本。为了衡量两个分布 𝑃(𝑥) 和 𝑄(𝑥) 之间的差异,可以使用以下几种方法:
| 差异度量方法 | 描述 |
| ---- | ---- |
| 总变差(TV)距离 | 测量两个分布在每个点上的最大差异。使用上确界函数 sup(S) ,它指的是大于集合S中所有元素的最小的值,即S的最小上界。 |
| 推土机距离(EM) | 可以解释为将一个分布转换为另一个分布所需的最小工作量。下确界函数 inf(S) 用于该距离度量,它指的是小于集合S中所有元素的最大的值,即S的最大下界。该距离的下确界函数是在所有边缘分布为 𝑃 或 𝑄 的联合分布集合 Π(𝑃,𝑄) 上取值, 𝛾(𝑢,𝑣) 是一个转移计划,指示如何将“土”从位置 𝑢 重新分配到位置 𝑣 ,同时要满足一些约束条件以确保转移后分布的有效性。计算EM距离本身是一个优化问题,即找到最优的转移计划 𝛾(𝑢,𝑣) 。 |
| 库尔贝克 - 莱布勒(KL)散度 | 来自信息论领域,衡量分布 𝑃 相对于参考分布 𝑄 的相对熵。需要注意的是,KL散度是非对称的,即 𝐾𝐿(𝑃‖𝑄) ≠ 𝐾𝐿(𝑄‖𝑃) 。 |
| 詹森 - 香农(JS)散度 | 同样来自信息论领域,是对称的。 |

这些差异度量方程最初是针对连续分布给出的,但也可以扩展到离散情况。以下是一个使用两个简单离散分布计算这些不同差异度量的示例。

4.2 KL散度与交叉熵的关系

KL散度 𝐾𝐿(𝑃‖𝑄) 可以扩展为:
- 对于连续分布:$KL(P||Q) = -\int P(x) \log(Q(x)) dx - (-\int P(x) \log(P(x)) dx)$
- 对于离散分布:$KL(P||Q) = -\sum_{i} P(x_{i}) \frac{P(x_{i})}{Q(x_{i})} = -\sum_{i} P(x_{i}) \log(Q(x_{i})) - (-\sum_{i} P(x_{i}) \log(P(x_{i})))$

基于上述扩展公式(无论是离散还是连续情况),KL散度可以看作是 𝑃 和 𝑄 之间的交叉熵(前一个公式中的第一项)减去 𝑃 的(自)熵(第二项),即 𝐾𝐿(𝑃‖𝑄) = 𝐻(𝑃,𝑄) − 𝐻(𝑃) 。

4.3 不同差异度量在GAN中的应用及EM距离的优势

数学上可以证明,原始GAN中的损失函数实际上是最小化真实样本和虚假样本分布之间的JS散度。但JS散度在训练GAN模型时存在问题,因此为了改进训练,研究人员提出使用EM距离作为真实图像和虚假图像分布之间的差异度量。

以两个平行直线分布 𝑃 和 𝑄 为例,其中一条直线固定在 𝑥 = 0 ,另一条直线可以在 𝑥 轴上移动,初始位置为 𝑥 = 𝜃 ( 𝜃 > 0 )。可以证明,KL、TV和JS差异度量分别为 𝐾𝐿(𝑃‖𝑄) = +∞ 、 𝑇𝑉(𝑃,𝑄) = 1 和 𝐽𝑆(𝑃,𝑄) = $\frac{1}{2} \log 2$ ,这些差异度量都不是参数 𝜃 的函数,因此无法对 𝜃 求导以使得分布 𝑃 和 𝑄 变得相似。而EM距离为 𝐸𝑀(𝑃,𝑄) = |𝜃| ,其关于 𝜃 的梯度存在,可以推动 𝑄 向 𝑃 靠近。

下面是一个简单的mermaid流程图,展示了从简单GAN到DCGAN再到WGAN的改进过程:

graph LR
    A[简单GAN模型] --> B[存在局限性]
    B --> C[改进方向:添加卷积层]
    C --> D[DCGAN]
    D --> E[采用额外技术]
    E --> F[WGAN]

5. WGAN的原理与实现

5.1 WGAN的核心思想

传统GAN使用交叉熵损失函数,其本质是最小化真实样本和虚假样本分布之间的JS散度,但JS散度在训练GAN模型时存在问题,例如可能导致梯度消失等。而WGAN使用基于Wasserstein - 1(或推土机)距离的修改后的损失函数。推土机距离可以解释为将一个分布转换为另一个分布所需的最小工作量,它能够提供更平滑的梯度,使得训练过程更加稳定。

5.2 WGAN对GAN训练的改进

在前面提到的例子中,当两个分布如两条平行直线时,KL、TV和JS差异度量无法对参数求导以使得分布变得相似,而EM距离(即Wasserstein - 1距离)关于参数的梯度存在,可以推动虚假分布向真实分布靠近。这意味着在GAN训练中使用Wasserstein - 1距离作为损失函数的度量,能够避免传统GAN训练中的一些问题,如梯度消失,从而使生成器和判别器的训练更加有效。

5.3 WGAN的实现步骤

虽然文中未详细给出WGAN的完整代码实现,但我们可以总结其大致的实现步骤:
1. 定义生成器和判别器网络 :可以使用前面实现的DCGAN的生成器和判别器架构,如 make_dcgan_generator() make_dcgan_discriminator() 函数。
2. 修改损失函数 :将传统GAN的交叉熵损失函数替换为基于Wasserstein - 1距离的损失函数。
3. 训练过程调整 :在训练过程中,需要对判别器的权重进行裁剪,以满足Lipschitz连续性条件,保证Wasserstein - 1距离的有效性。同时,通常需要多次更新判别器,然后再更新一次生成器。

以下是一个简单的伪代码示例,展示了WGAN训练的大致流程:

# 定义生成器和判别器
generator = make_dcgan_generator()
discriminator = make_dcgan_discriminator()

# 定义优化器
generator_optimizer = tf.keras.optimizers.Adam()
discriminator_optimizer = tf.keras.optimizers.Adam()

# 训练参数
epochs = 100
batch_size = 64
n_critic = 5  # 判别器更新次数

for epoch in range(epochs):
    for _ in range(n_critic):
        # 训练判别器
        with tf.GradientTape() as disc_tape:
            # 生成虚假样本
            noise = tf.random.normal([batch_size, z_size])
            fake_images = generator(noise)
            # 获取真实样本
            real_images = get_real_images()
            # 计算判别器对真实和虚假样本的输出
            real_output = discriminator(real_images)
            fake_output = discriminator(fake_images)
            # 计算Wasserstein损失
            disc_loss = -(tf.reduce_mean(real_output) - tf.reduce_mean(fake_output))
        # 计算判别器梯度
        gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)
        # 更新判别器权重
        discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))
        # 裁剪判别器权重
        for variable in discriminator.trainable_variables:
            variable.assign(tf.clip_by_value(variable, -0.01, 0.01))

    # 训练生成器
    with tf.GradientTape() as gen_tape:
        noise = tf.random.normal([batch_size, z_size])
        fake_images = generator(noise)
        fake_output = discriminator(fake_images)
        gen_loss = -tf.reduce_mean(fake_output)
    # 计算生成器梯度
    gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
    # 更新生成器权重
    generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))

6. 总结与应用展望

6.1 技术总结

本文围绕用于合成新数据的生成对抗网络展开,首先介绍了简单GAN模型在生成手写数字图像时的局限性,并提出通过添加卷积层构建DCGAN来改进性能。详细讲解了DCGAN中使用的关键技术,包括转置卷积、批量归一化(BatchNorm),并给出了生成器和判别器的架构设计与代码实现。接着探讨了两种分布之间的差异度量方法,分析了传统GAN中使用的JS散度的问题,从而引出了使用Wasserstein - 1距离的WGAN,说明了其对GAN训练的改进原理和大致实现步骤。

6.2 应用场景

生成对抗网络在许多领域都有广泛的应用,例如:
- 图像生成 :可以生成逼真的人脸、风景等图像,应用于游戏、影视制作等领域。
- 数据增强 :在训练数据有限的情况下,通过生成新的数据来扩充训练集,提高模型的泛化能力。
- 风格迁移 :将一种图像的风格迁移到另一种图像上,如将油画风格应用到普通照片上。

6.3 未来发展方向

虽然GAN已经取得了很大的进展,但仍然存在一些挑战,如训练不稳定、模式崩溃等问题。未来的研究可能会集中在以下几个方面:
- 改进损失函数 :寻找更合适的损失函数,进一步提高GAN的训练稳定性和生成质量。
- 架构创新 :设计更高效、更强大的网络架构,以更好地处理复杂的数据分布。
- 多模态融合 :将GAN应用于多模态数据,如结合图像和文本信息进行生成。

下面是一个mermaid流程图,展示了GAN技术的应用与发展方向:

graph LR
    A[GAN技术] --> B[图像生成]
    A --> C[数据增强]
    A --> D[风格迁移]
    A --> E[面临挑战]
    E --> F[改进损失函数]
    E --> G[架构创新]
    E --> H[多模态融合]

通过不断的研究和改进,生成对抗网络有望在更多领域发挥重要作用,为人工智能的发展带来新的突破。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值