【生成对抗网络】GAN入门与代码实现(二)

文章目录

    • 1 导包
    • 2 数据准备
    • 3 生成器模型
    • 4 判别器模型
    • 5 编写损失函数,定义优化器
    • 6 获取模型&定义训练批次函数
    • 7 定义可视化方法
    • 8 主训练方法
    • 9 开始训练
    • 10 训练结果

生成对抗网络系列
【生成对抗网络】GAN入门与代码实现(一)
【生成对抗网络】GAN入门与代码实现(二)
【生成对抗网络】基于DCGAN的二次元人物头像生成(TensorFlow2)
【生成对抗网络】ACGAN的代码实现

上篇博客:【生成对抗网络】GAN入门与代码实现(一)
本篇主要介绍简单GAN的另一种实现方法(不使用卷积),依然使用TensorFlow2进行搭建,主要运用了TensorFlow2中的求导机制进行自定义训练,自由度更高。对比上篇博客中的实现方法可加深对GAN的编写理解。

1 导包

import tensorflow as tf # 2.3版本
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np
import os
import matplotlib.pyplot as plt
%matplotlib inline

2 数据准备

我们使用MNIST手写数据集作为训练生成的数据。

# 加载数据
(train_images,train_labels),(_,_) = tf.keras.datasets.mnist.load_data() # train_images的shape为(60000,28,28)
# 把数据改为float类型,然后归一化,目的是使数据落在0的周围,因为激活函数在0的周围能发挥好的作用
train_images = train_images.reshape(train_images.shape[0],28,28,1).astype('float32') # numpy方法,重塑shape为(60000,28,28,1)
train_images = (train_images - 127.5)/127.5 # 把0-255的数据范围变为-1到1之间

定义相关参数。

BATCH_SIZE = 300    # batch大小
BUFFER_SIZE = 60000 # 训练集有6w张图片

EPOCHS = 300    # 批次数量
noise_dim = 100 # 随机数的维度

将原数据创建为Dataset数据,便于训练。

datasets = tf.data.Dataset.from_tensor_slices(train_images)
# 
datasets = datasets.shuffle(BUFFER_SIZE).batch(BATCH_SIZE) # 在全部范围内做一个乱序,设置一个batch为256
# 
# 测试datasets:
for item in datasets:
    print(item.shape)
    print(type(item))
    break
# (300, 28, 28, 1) # batch为300 图片为28*28的单通道照片
#     
    

3 生成器模型

输入100维的随机向量,输出一张(28,28,1)维的图片。

def generator_model():
    
    generator = keras.models.Sequential([
        keras.layers.Input(shape=(100,)), # 输入为长度100点随机向量
        keras.layers.Dense(256),
        keras.layers.LeakyReLU(alpha = 0.2),
        keras.layers.BatchNormalization(momentum = 0.8),
        keras.layers.Dense(512),
        keras.layers.LeakyReLU(alpha = 0.2),
        keras.layers.BatchNormalization(momentum = 0.8),
        keras.layers.Dense(1024),
        keras.layers.LeakyReLU(alpha = 0.2),
        keras.layers.BatchNormalization(momentum = 0.8),
        keras.layers.Dense(np.prod((28,28,1)),activation='tanh'),
        keras.layers.Reshape((28,28,1)) #  将向量重塑shape为(28,28,1),输出图片
    ])
    
    return generator

4 判别器模型

输入图片,输出1维的判定结果(最后没有使用激活函数)。

def discriminator_model():

    discriminator = keras.models.Sequential([
        keras.layers.Flatten(), # 将输入的多维数据展平为一维
        keras.layers.Dense(512),
        keras.layers.LeakyReLU(alpha = 0.2),
        keras.layers.Dense(256),
        keras.layers.LeakyReLU(alpha = 0.2),
        keras.layers.Dense(1)
    ])
    
    return discriminator
    

5 编写损失函数,定义优化器

cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True) 
# 因为判别器最后没有使用激活 所以我们添加from_logits=True

判别器损失:

判别器的目标是让真实图片判别为1,生成的图片判别为0,因此损失函数中真实图片与1比较,生成图片与0比较,从而计算损失。

# 求判别器损失的函数
def discriminator_loss(real_out,fake_out):
    real_loss = cross_entropy(tf.ones_like(real_out),real_out) # 真实图片的输出与1比较
    fake_loss = cross_entropy(tf.zeros_like(fake_out),fake_out) # 生成图片的输出与0比较
    return real_loss + fake_loss

生成器损失:

生成器的目标是使得自己生成的图片在判别器中判别为1(真实),因此损失函数中需要与1对比。

# 求生成器损失的函数
def generator_loss(fake_out):
    fake_loss = cross_entropy(tf.ones_like(fake_out),fake_out)
    return fake_loss

定义优化器:

learning_rate:0.0002

beta_1:0.5

# 优化器
generator_opt = tf.keras.optimizers.Adam(2e-4,0.5)
discriminator_opt = tf.keras.optimizers.Adam(2e-4,0.5)

参数简介:

learning_rate:一个张量,浮点值,或者是一个tf.keras.optimizer .schedules时间表。LearningRateSchedule,或者一个不带参数并返回要使用的实际值的可调用对象,即学习速率。默认为0.001。

beta_1:一个浮点值或一个常量浮点张量,或者一个不带参数并返回实际值的可调用对象。一阶矩的指数衰减率估计。默认为0.9

6 获取模型&定义训练批次函数

generator = generator_model() # 获取生成器模型
discriminator = discriminator_model() # 获取判别器模型

定义训练函数,使用Tensorflow中的自动求导与根据梯度更新参数的方法来训练生成器与判别器。

@tf.function
# 接收一个批次的图片,对其进行训练
def train_step(images):
    noise = tf.random.normal([BATCH_SIZE,noise_dim]) # 生成BATCH_SIZE个长度为100的随机向量
    # images 是真实图片的输入
    # noise 是噪声输入
    
    with tf.GradientTape() as gen_tape,tf.GradientTape() as disc_tape:
        real_out = discriminator(images,training = True) # 输出真实图片在判别器中的判别结果
        gen_image = generator(noise,training = True)    # 用随机向量在生成器中生成图片
        fake_out = discriminator(gen_image,training = True) # 输出生成图片在判别器中的判别结果
        # 调用步骤5中的方法,计算损失
        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))

7 定义可视化方法

为了在训练的过程中查看生成器输出图片的效果,我们定义6个100维度的随机数来检测训练过程中的生成器模型,使用matlibplot中的方法绘制图片。

num_example_to_generate = 6 # 用于绘图过程中生成图片的数量

seed = np.random.normal(0,1,(num_example_to_generate,noise_dim)) # 生成6个长度为100的随机向量
# 画图函数
def generate_plot_image(test_noise):

    pre_image = generator(test_noise,training = False) # 用生成器,生成手写图片
    # print(pre_image.shape) # (6,28,28,1)
    fig = plt.figure(figsize=(16,3)) # figsize:指定figure的宽和高,单位为英寸
    for i in range(pre_image.shape[0]):   # pre_image的shape的第一个维度就是个数,这里是6
        plt.subplot(1,6,i+1) # 几行几列的 第i+1个图片(从1开始)
        plt.imshow((pre_image[i,:,:,:] + 1)/2) # 加1除2: 将生成的-1~1的图片弄到0-1之间,
        plt.axis('off') # 不要坐标
    plt.show()

8 主训练方法

训练epochs次,每次epoch中从dataset依次取出batch个数据调用步骤6中的方法进行训练,每次epoch结束后调用步骤7中的方法绘制几张图片查看生成器的生成效果。

# 训练(主方法)
def train(dataset,epochs):
    for epoch in range(1,epochs+1): # 总过训练epochs次
        print("epoch:",epoch)
        for image_batch in dataset: # 从数据集中遍历所有batch
            train_step(image_batch)# 训练一个batch
            print(".",end="")
        generate_plot_image(seed) # 绘图,使用前面定义的随机数seed在生成器中生成图片并展示

9 开始训练

train(datasets,EPOCHS) 

10 训练结果

epoch 1:
在这里插入图片描述
epoch 10:
在这里插入图片描述
epoch 20:
在这里插入图片描述
epoch 50:
在这里插入图片描述
epoch 100:
在这里插入图片描述
epoch 290:
在这里插入图片描述

你可能感兴趣的:(TensorFlow2,Python,机器学习与深度学习,生成对抗网络,tensorflow,深度学习)