基于TensorFlow2.x框架实现的DCGAN模型

目录

开发环境

引言:超参数定义

第一部分:生成器模型

第二部分:判别器模型

第三部分:训练过程

第四部分:加载数据集正式开始训练

第五部分:通过TensorBoard可视化生成器和判别器损失 


开发环境

作者:嘟粥yyds
时间:2023年3月8日
集成开发工具:jupyter notebook 6.5.2
集成开发环境:Python 3.10.6
第三方库:tensorflow-gpu 2.9.3

这段代码是实现了一个基于DCGAN(Deep Convolutional Generative Adversarial Network)模型的图像生成器。主要分为三个部分:生成器模型、判别器模型和训练过程。具体讲解如下:

引言:超参数定义

# 图像大小
IMAGE_SIZE = 64
# 图像通道数
IMAGE_CHANNELS = 3
# 噪声向量维度
NOISE_DIM = 100
# 训练批次大小,若GPU内存不足则需调小
BATCH_SIZE = 256
# 训练轮数
EPOCHS = 100
# 学习率
LEARNING_RATE = 5e-4
# beta_1参数
BETA_1 = 0.5
# 保存模型的目录
CHECKPOINT_DIR = "./checkpoints"
# 生成图像的数量
NUM_GENERATE_IMAGES = 25

第一部分:生成器模型

生成器模型是一个基于TensorFlow和Keras框架的神经网络模型,包括以下几层:

  • 全连接层:输入为噪声向量(100维),输出为(IMAGE_SIZE // 16) * (IMAGE_SIZE // 16) * 256维。
  • BatchNormalization层:对全连接层的输出进行标准化。
  • LeakyReLU层:对标准化后的结果进行激活,以避免神经元饱和问题。
  • Reshape层:将全连接层的输出重塑为(IMAGE_SIZE // 16, IMAGE_SIZE // 16, 256)的三维张量。
  • Conv2DTranspose层:反卷积层,用于将三维张量升采样为更高分辨率的图像。
  • 最后一层使用tanh激活函数输出生成的RGB图像。
def make_generator_model():
    model = tf.keras.Sequential()
    model.add(layers.Dense((IMAGE_SIZE // 16) * (IMAGE_SIZE // 16) * 256, use_bias=False, input_shape=(100,)))
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())

    model.add(layers.Reshape((IMAGE_SIZE // 16, IMAGE_SIZE // 16, 256)))
    assert model.output_shape == (None, IMAGE_SIZE // 16, IMAGE_SIZE // 16, 256)

    model.add(layers.Conv2DTranspose(128, (5, 5), strides=(2, 2), padding='same', use_bias=False))
    assert model.output_shape == (None, IMAGE_SIZE // 8, IMAGE_SIZE // 8, 128)
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())

    model.add(layers.Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False))
    assert model.output_shape == (None, IMAGE_SIZE // 4, IMAGE_SIZE // 4, 64)
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())

    model.add(layers.Conv2DTranspose(32, (5, 5), strides=(2, 2), padding='same', use_bias=False))
    assert model.output_shape == (None, IMAGE_SIZE // 2, IMAGE_SIZE // 2, 32)
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())

    model.add(layers.Conv2DTranspose(3, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh'))
    assert model.output_shape == (None, IMAGE_SIZE, IMAGE_SIZE, 3)

    return model

生成器模型架构 

Model: "Generator"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 dense (Dense)               (None, 4096)              409600    
                                                                 
 batch_normalization (BatchN  (None, 4096)             16384     
 ormalization)                                                   
                                                                 
 leaky_re_lu (LeakyReLU)     (None, 4096)              0         
                                                                 
 reshape (Reshape)           (None, 4, 4, 256)         0         
                                                                 
 conv2d_transpose (Conv2DTra  (None, 8, 8, 128)        819200    
 nspose)                                                         
                                                                 
 batch_normalization_1 (Batc  (None, 8, 8, 128)        512       
 hNormalization)                                                 
                                                                 
 leaky_re_lu_1 (LeakyReLU)   (None, 8, 8, 128)         0         
                                                                 
 conv2d_transpose_1 (Conv2DT  (None, 16, 16, 64)       204800    
 ranspose)                                                       
                                                                 
 batch_normalization_2 (Batc  (None, 16, 16, 64)       256       
 hNormalization)                                                 
                                                                 
 leaky_re_lu_2 (LeakyReLU)   (None, 16, 16, 64)        0         
                                                                 
 conv2d_transpose_2 (Conv2DT  (None, 32, 32, 32)       51200     
 ranspose)                                                       
                                                                 
 batch_normalization_3 (Batc  (None, 32, 32, 32)       128       
 hNormalization)                                                 
                                                                 
 leaky_re_lu_3 (LeakyReLU)   (None, 32, 32, 32)        0         
                                                                 
 conv2d_transpose_3 (Conv2DT  (None, 64, 64, 3)        2400      
 ranspose)                                                       
                                                                 
=================================================================
Total params: 1,504,480
Trainable params: 1,495,840
Non-trainable params: 8,640

第二部分:判别器模型

判别器模型同样是一个基于TensorFlow和Keras框架的神经网络模型,包括以下几层:

  • 卷积层1:输入为(IMAGE_SIZE, IMAGE_SIZE, 3)的图像,输出为64维特征图。
  • LeakyReLU层:对卷积层1的输出进行激活。
  • Dropout层:为了防止过拟合,对激活后的特征图进行随机失活。
  • 卷积层2:输出为128维特征图。
  • Flatten层:将二维特征图展平成一维向量。
  • 全连接层:输出为1维的实数,用于二分类,判断图像是真实的还是生成的。
def make_discriminator_model():
    model = tf.keras.Sequential()
    # 1. 卷积层1
    model.add(layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same', input_shape=[IMAGE_SIZE, IMAGE_SIZE, 3]))
    model.add(layers.LeakyReLU())
    model.add(layers.Dropout(0.3))

    # 2. 卷积层2
    model.add(layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same'))
    model.add(layers.LeakyReLU())
    model.add(layers.Dropout(0.3))

    # 3. 将二维图像展平成一维向量
    model.add(layers.Flatten())

    # 4. 全连接层
    model.add(layers.Dense(1, activation=None))

    return model

判别器模型架构

Model: "Discriminator"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d (Conv2D)             (None, 32, 32, 64)        4864      
                                                                 
 leaky_re_lu_4 (LeakyReLU)   (None, 32, 32, 64)        0         
                                                                 
 dropout (Dropout)           (None, 32, 32, 64)        0         
                                                                 
 conv2d_1 (Conv2D)           (None, 16, 16, 128)       204928    
                                                                 
 leaky_re_lu_5 (LeakyReLU)   (None, 16, 16, 128)       0         
                                                                 
 dropout_1 (Dropout)         (None, 16, 16, 128)       0         
                                                                 
 flatten (Flatten)           (None, 32768)             0         
                                                                 
 dense_1 (Dense)             (None, 1)                 32769     
                                                                 
=================================================================
Total params: 242,561
Trainable params: 242,561
Non-trainable params: 0

第三部分:训练过程

训练过程包括以下几个步骤:  

  • 定义优化器:生成器和判别器都使用Adam优化器,采用不同的学习率和beta_1参数。
  • 定义损失函数:生成器和判别器的损失函数都使用二元交叉熵函数。
  • 定义记录器:使用TensorFlow的Summary API记录训练过程中的损失和准确率。
  • 训练:对于每一个训练批次,先通过生成器生成一批假图像,并将它们喂给判别器进行判断,记录判别器的输出结果和损失。然后再将一批真实的图像喂给判别器进行判断,同样记录判别器的输出结果和损失。接下来,计算生成器的损失函数和准确率。
# 优化器
generator_optimizer = tf.keras.optimizers.Adam(learning_rate=LEARNING_RATE, beta_1=BETA_1)
discriminator_optimizer = tf.keras.optimizers.Adam(learning_rate=LEARNING_RATE, beta_1=BETA_1)

# 损失函数
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)
# 定义记录器
train_summary_writer = tf.summary.create_file_writer('./logs')


# 生成器损失函数
def generator_loss(fake_output):
    gen_loss = cross_entropy(tf.ones_like(fake_output), fake_output)
    with train_summary_writer.as_default():
        tf.summary.scalar('generator_loss', gen_loss, step=generator_optimizer.iterations)
    return gen_loss


# 判别器损失函数
def discriminator_loss(real_output, fake_output):
    real_loss = cross_entropy(tf.ones_like(real_output), real_output)
    fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)
    total_loss = real_loss + fake_loss
    with train_summary_writer.as_default():
        tf.summary.scalar('discriminator_loss', total_loss, step=discriminator_optimizer.iterations)
    return total_loss


# 记录损失函数和准确率的指标
generator_loss_metrics = tf.metrics.Mean('generator_loss', dtype=tf.float32)
generator_accuracy_metrics = tf.metrics.BinaryAccuracy('generator_accuracy', dtype=tf.float32)
discriminator_loss_metrics = tf.metrics.Mean('discriminator_loss', dtype=tf.float32)
discriminator_accuracy_metrics = tf.metrics.BinaryAccuracy('discriminator_accuracy')


# 加载保存的模型结构,若是第一次训练则此代码块需注释掉
checkpoint_prefix = os.path.join(CHECKPOINT_DIR, "ckpt")
checkpoint = tf.train.Checkpoint(
    generator_optimizer=generator_optimizer,
    discriminator_optimizer=discriminator_optimizer,
    generator=generator,
    discriminator=discriminator
)
checkpoint.restore(tf.train.latest_checkpoint(CHECKPOINT_DIR))
# 打印生成器和判别器模型结构
generator.summary()
discriminator.summary()
# 定义训练函数
@tf.function
def train_step(images):
    (images_x, images_y) = images
    # 1. 生成噪声
    noise = tf.random.normal([BATCH_SIZE, NOISE_DIM])
    # 对图像进行类型转换,将数据类型转换为 uint8,并将像素值缩放到 [0, 255] 范围内
    images_x = tf.cast(images_x, tf.uint8)
    # 将图像的像素值缩放到 [0, 1] 范围内
    images_x = tf.image.convert_image_dtype(images_x, dtype=tf.float32)
    images = (images_x, images_y)
    # 2. 训练判别器
    with tf.GradientTape() as disc_tape:
        # 2.1 真实图像的判别结果
        real_output = discriminator(images_x, training=True)

        # 2.2 生成器生成的图像的判别结果
        fake_images = generator(noise, training=True)
        fake_output = discriminator(fake_images, training=True)

        # 2.3 计算判别器损失
        disc_loss = discriminator_loss(real_output, fake_output)

    # 记录判别器损失和准确率的指标
    discriminator_loss_metrics.update_state(disc_loss)
    discriminator_accuracy_metrics.update_state(tf.ones_like(real_output), real_output)
    discriminator_accuracy_metrics.update_state(tf.zeros_like(fake_output), fake_output)

    # 计算判别器梯度
    grads_disc = disc_tape.gradient(disc_loss, discriminator.trainable_variables)

    # 3. 更新判别器权重
    discriminator_optimizer.apply_gradients(zip(grads_disc, discriminator.trainable_variables))

    # 4. 训练生成器
    with tf.GradientTape() as gen_tape:
        # 4.1 生成器生成图像的判别结果
        fake_images = generator(noise, training=True)
        fake_output = discriminator(fake_images, training=True)

        # 4.2 计算生成器损失
        gen_loss = generator_loss(fake_output)

    # 记录生成器损失和准确率的指标
    generator_loss_metrics.update_state(gen_loss)
    generator_accuracy_metrics.update_state(tf.ones_like(fake_output), fake_output)

    # 计算生成器梯度
    grads_gen = gen_tape.gradient(gen_loss, generator.trainable_variables)

    # 5. 更新生成器权重
    generator_optimizer.apply_gradients(zip(grads_gen, generator.trainable_variables))

第四部分:加载数据集正式开始训练

本文使用的是LFW(Labeled Faces in the Wild)数据集,是一个用于人脸识别的公共数据集。该数据集由美国马里兰大学的Gary B. Huang、Manu Ramesh和Tamara Berg三位教授共同创建,于2007年发布。该数据集包含了13,233张人脸图片,其中包括了5749个人的1680个身份。这些图片来自互联网上的各种场景,包括户外、室内、不同的光照条件和角度等,因此具有一定的复杂性和多样性。

  • 通过 tf.keras.preprocessing.image_dataset_from_directory 函数加载数据集,并指定训练集比例为 0.8,图像大小为 IMAGE_SIZE x IMAGE_SIZE,批量大小为 BATCH_SIZE,并存储在变量 dataset 中。
  • 使用一个双重循环进行模型的训练。外层循环遍历每个 epoch,内层循环遍历训练集中的每个 batch。对于每个 batch,调用 train_step 函数进行一次训练,该函数会对生成器和判别器进行一次前向传播和反向传播,并根据反向传播的结果更新生成器和判别器的参数。
  • 在每个 epoch 结束时,判断是否要生成并保存一组生成图像。如果当前 epoch 是 3 的倍数,就生成一组 25 张图像,并保存在本地。具体地,首先使用随机噪声向量 noise 作为生成器的输入,生成器会将其转换为一组图像。然后,将这组图像绘制在一个 5x5 的网格中,并将其保存到本地。
  • 在每个 10 个 epoch 结束时,保存模型参数。通过 tf.train.Checkpoint 函数可以保存训练过程中生成器和判别器的参数,便于后续重新加载和使用。同时,该函数也可以保存优化器的状态,以便在重新加载模型时恢复优化器状态。模型参数会保存在指定路径下的 checkpoint 文件中。
# 训练模型
dataset = tf.keras.preprocessing.image_dataset_from_directory(
    "lfw",  # LFW数据集存放路径
    validation_split=0.2,
    subset='training',
    seed=123,
    image_size=(IMAGE_SIZE, IMAGE_SIZE),
    batch_size=BATCH_SIZE,
)
for epoch in range(EPOCHS):
    print(f'正处于第{epoch + 1}个epoch')
    for image_batch in dataset:
        train_step(image_batch)
    print(f"已完成第{epoch + 1}个epoch")
    # 每3个epoch结束后生成并保存一组生成图像
    if (epoch + 1) % 3 == 0:
        noise = tf.random.normal([NUM_GENERATE_IMAGES, NOISE_DIM])
        generated_images = generator(noise, training=False)
        fig = plt.figure(figsize=(12, 12))
        for i in range(generated_images.shape[0]):
            plt.subplot(5, 5, i + 1)
            img = generated_images[i, :, :, :] * 0.5 + 0.5
            plt.imshow(img)
            plt.axis('off')

    plt.savefig("generated_images\\{(epoch + 1)}.png")  # 图像保存路径
    plt.close()
    if (epoch + 1) % 10 == 0:
        # 保存模型
        checkpoint_prefix = os.path.join(CHECKPOINT_DIR, "ckpt")
        checkpoint = tf.train.Checkpoint(
            generator_optimizer=generator_optimizer,
            discriminator_optimizer=discriminator_optimizer,
            generator=generator,
            discriminator=discriminator
        )
        checkpoint.save(file_prefix=checkpoint_prefix)

第五部分:通过TensorBoard可视化生成器和判别器损失 

 在终端输入tensorboard --logdir='logs'('logs'换成你记录器的保存目录)

 

 

猜你喜欢

转载自blog.csdn.net/zzp20031120/article/details/130151193
今日推荐