$$ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Self-defined math definitions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Math symbol commands \newcommand{\intd}{\,{\rm d}} % Symbol 'd' used in integration, such as 'dx' \newcommand{\diff}{{\rm d}} % Symbol 'd' used in differentiation ... $$

那些说不完的 GAN - Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks

  Deep Convolutional Generative Adversarial Networks( DCGAN ),中文名称深度卷积生成对抗网络,于 2016 年被 Alec Radford 等人提出。目的是为了减小 CNN 在监督学习和无监督学习上的差距。DCGAN 通过一定的架构约束,在大量的数据集上证明了 DCGAN 对 CNN 在无监督学习中的发展发挥了重要作用。

  在此之前,使用 CNN + GAN 的尝试也有过,但都不是很理想。但 DCGAN 使用了一种全新的架构,不仅提高了训练的稳定性,也允许更高分辨率的图片和更深的网络。CNN 架构的变化主要有三个:

  1. 使用全卷积神经网络( all convolutional net )。通过使用跨步卷积( strided convolution )替代确定性的池化操作,从而让网络自己学习下采样。在生成器和判别器中均使用了这种方式。

   2. 取消全连接层。常见的做法有使用全局平均池( global average pooling )化代替全连接层。全局平均池化提升了模型的稳定性,但是却降低了收敛速度。GAN的输入采用均匀分布初始化,可能会使用全连接层(矩阵相乘),然后得到的结果可以转换成一个 4 dimension 的 tensor,然后后面堆叠卷积层即可;对于鉴别器,最后的卷积层可以先 flatten,然后送入一个 sigmoid 分类器。

  3. 批归一化( Batch Normalization )。 将每一层的输入分布变换为0均值和单位方差,BN 被证明是深度学习中非常重要的加速收敛和减缓过拟合的手段。但是将所有层都进行 Batch Normalization,会导致样本震荡和模型不稳定,因此只对生成器的输出层和鉴别器的输入层使用 BN。

  4. 激活函数的使用。生成器的输出层使用 tanh 激活函数,其余层使用 relu 激活函数。鉴别器采用 leaky relu 函数。

  文章提出了一套更稳定的架构来训练生成式对抗网络,并通过大量的数据集测试表明,对抗网络为监督学习和生成建模学习了良好的图像表示。但任然存在某些情况下的模型不稳定性。

  使用 mnist 手写数据集来写一个简单的 DCGAN,使得生成器网络可以对任意输出入的噪声,产生相应的数字图片。

#代码参考来源:https://tensorflow.google.cn/tutorials/generative/dcgan?hl=zh-cn
#tensorflow 2.10.0, Python 3.9

import tensorflow as tf
import keras
from keras import layers, datasets
import matplotlib.pyplot as plt
import time
print(tf.__version__)
#超参数的定义
Batch_Size = 128
Epoches = 100
Noise_Dim = 100 #噪音的数量
Learning_Rate = 0.0001
Buffer_Size = 60000
seed = tf.random.normal([16, Noise_Dim])  # 16组随机数组,每组含100个随机数,用来生成16张图片。


#获取mnist数据集并对数据进行处理
(train_images, train_labels), (_, _) = datasets.mnist.load_data()
train_images = train_images / 255.0     #数据归一化
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1)
#在第一个维度进行切分,生成 train_images.shape[0] 多的 tensord dataset
datasets = tf.data.Dataset.from_tensor_slices(train_images)
#从Buffer_Size大小的数据中随机选取填充缓冲区实现选取,合并成 Batch_Size大小
datasets = datasets.shuffle(Buffer_Size).batch(Batch_Size)

#建立生成器
def Generator():
    model = keras.Sequential([
        layers.Dense(7 * 7 * 256, use_bias=False, input_shape=(100,)),
        layers.BatchNormalization(),
        layers.ReLU(),
        layers.Reshape((7, 7, 256)),

        layers.Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False),
        layers.BatchNormalization(),
        layers.ReLU(),

        layers.Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False),
        layers.BatchNormalization(),
        layers.ReLU(),

        layers.Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh')
    ])

    return model

#定义判别器
def Discriminator():
    model = keras.Sequential([
        layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same', input_shape=[28, 28, 1]),
        layers.LeakyReLU(),
        layers.Dropout(0.3),

        layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same'),
        layers.LeakyReLU(),
        layers.Dropout(0.3),

        layers.Flatten(),
        layers.Dense(1)
    ])
    return model

#定义各自的优化函数
generator_opt = keras.optimizers.Adam(Learning_Rate)
discriminator_opt = keras.optimizers.Adam(Learning_Rate)

#定义损失函数,二进制交叉熵适用于二分类问题
cross_entropy = keras.losses.BinaryCrossentropy(from_logits=True)

#判别器对真实图片的预测值与值全为 1 的数组进行对比
#将判别器对伪造(生成的)图片的预测值与值全为 0 的数组进行对比
def discriminator_loss(real_out, fake_out):
    #real_out是判别器对数据集图片的输出,fake_out是判别器对生成图片的输出
    real_loss = cross_entropy(tf.ones_like(real_out), real_out)
    fake_loss = cross_entropy(tf.zeros_like(fake_out), fake_out)
    return real_loss + fake_loss

def generator_loss(fake_out):
    return cross_entropy(tf.ones_like(fake_out), fake_out)

#实例化生成器和判别器
generator = Generator()

discriminator = Discriminator()

#定义模型训练过程
def train():
    for epoch in range(Epoches):
        start = time.time()
        for image_batch in datasets:
            #生成随机数来生成图片
            noise = tf.random.normal([Batch_Size, Noise_Dim])

            with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
            #等价于:gen_tape = tf.GradientTape(), disc_tape = tf.GradientTape()
                real_out = discriminator(image_batch, training=True)

                gen_image = generator(noise, training=True)
                fake_out = discriminator(gen_image, training=True)

                gen_loss = generator_loss(fake_out)
                disc_loss = discriminator_loss(real_out, fake_out)
            #进行梯度的计算与更新
            gradient_gen = gen_tape.gradient(gen_loss, generator.trainable_variables)
            gradient_disc = disc_tape.gradient(disc_loss, discriminator.trainable_variables)
            generator_opt.apply_gradients(zip(gradient_gen, generator.trainable_variables))
            discriminator_opt.apply_gradients(zip(gradient_disc, discriminator.trainable_variables))
        print('Time for epoch {} is {} sec'.format(epoch + 1, time.time() - start))
        if(epoch%10==0):
            generate_plot_image(generator, seed)

def generate_plot_image(gen_model, test_noise):
    pre_images = gen_model(test_noise, training=False)
    fig = plt.figure(figsize=(4, 4))
    for i in range(pre_images.shape[0]):
        plt.subplot(4, 4, i+1)
        plt.imshow((pre_images[i, :, :, 0] + 1)/2, cmap='gray')
        plt.axis('off')
    plt.show()

if __name__ == '__main__':
    train()

DCGAN 训练结果:

 

 

 

posted @ 2023-09-15 16:52  素衣叹风尘  阅读(53)  评论(0)    收藏  举报