不服就GAN:GAN网络生成 cifar10 的图片实例(keras 详细实现步骤),GAN 的训练的各种技巧总结,GAN的注意事项和大坑汇总

GAN 的调参技巧总结

  • 生成器的最后一层不使用 sigmoid,使用 tanh 代替

  • 使用噪声作为生成器的输入时,生成噪声的步骤使用 正态分布 的采样来产生,而不使用均匀分布

  • 训练 discriminator 的时候,将 fake img 的标签设为 1real img 的标签设成 0,这样更有利于其训练

  • 在 generator 和 discriminator 设计的时候都使用 dropout 层来增加随机性;或者在 discriminator 的标签中 添加噪声 来提高随机性;因为随机性对于 GAN 的训练有帮助

  • 在 discriminator 和 generator 中都使用 LeakyRelu 来作为激活函数而不用传统的 Relu

  • Conv2DTranspose,stride=2 来代替上采样操作;用 Conv2D,stride=2 来代替下采样操作(maxpooling)

  • 在生成的图像中,经常会见到棋盘状伪影,这是由于生成器中像素空间的不均匀覆盖造成的(如下图),为了解决这个问题,每当生成器和判别器中都使用步进的Conv2DTranspose或Conv2D时,使用的 内核大小要能够被步幅大小整除。例如 stride=2,kernel=(4,4)

  • 在这里插入图片描述

  • discriminator 的容量和能力一定要小于 generator,因为判别远比生成容易,如果 discriminator 太强了,反而不利于 generator 的学习,就像一个太过于严厉的老师是不利于学生大踏步地进行创新和进步的,老师一定要温和。比例大约控制在 generator 的容量是 discriminator 的 10 倍左右。

  • 在训练的时候,如果不想调整网络的参数,那么可以尝试在训练的时候让一个网络训练好几次,然后另外一个网络训练一次,例如,如果 generator 很强,那么久让 discriminator 训练 3 次,generator 更新一次参数,加个 for 循环即可,亲测有效。

  • generator 在训练的过程中,前期大概率会被 discriminator 压制;因为刚开始生成的东西还很简单,因此要保证在训练的时候 generator 在前几个 epochs 不能 loss 很快地上升到 1,这样的话不利于后面的训练 ,正确的引导方式应该是设计 generator 的 loss 先上升到 0.7,0.8 左右,然后再慢慢降下来,这样就很有利于训练 GAN 网络

  • 一个被良好训练的 GAN 网络应该具有下面的 loss 走向:鉴别器和生成器都在波动,而不是一方的 loss 很快上升到 1,而另一方很快降到 0;
    在这里插入图片描述

  • 定义网络的顺序是:

    • 定义 discriminator 网络,
    • 创建 discriminator,
    • compile discriminator,
    • 定义 generator,
    • 创建 generator;
    • 然后锁定 discriminator 的参数(dis.trainable=False)这时候千万不要再次 compile discriminator!!
    • 定义 GAN 的联合网络;
    • 创建 GAN 网络,
    • 然后 compile GAN 网络;因为 GAN 网络创建的时候 鉴别器被锁住,因此 GAN 网络 compile 之后能够记住这种状态,因此在 GAN 网络调用的时候 discriminator 永远都是锁住的,但是训练的其他时候,discriminator 都是没有被锁住的。这就是上面的 compile 顺序的巧妙!!!
  • 如果这里你没看懂,一定要在下面的代码中仔细留意关于 discriminator 的compile 部分,因为如果这里出问题,你最终即使网络训练的时候通过调整参数而达到了上图中演示的那种良好的交替波动情况,最后的输出也大概率属于下图中的情况,如果你遇到了下图的情况,请仔细考虑你的 compile 步骤:
    在这里插入图片描述

  • 在训练 GAN 之前,尽量对数据进行标准化,图片数据除以 255. 即可,下面代码中也有演示,对数据规范化绝对是一个好习惯

代码

1. 导包

import keras,os
from keras.models import *
from keras.layers import *
from keras.optimizers import *
from keras.losses import *
from keras import backend as K
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from PIL import Image
from keras.preprocessing import image


from keras.datasets import fashion_mnist,cifar10,cifar100,mnist
from keras.utils import to_categorical

os.environ["CUDA_VISIBLE_DEVICES"] = " 2"

2. 定义 generator

def generator(input_shape):
    inputs = Input(input_shape)
    x = Dense(128 * 16 * 16)(inputs)
    x = LeakyReLU()(x)
    x = Reshape((16, 16, 128))(x)

    x = Conv2D(256, 5, padding = 'same')(x)
    x = LeakyReLU()(x)

    x = Conv2DTranspose(256, 4, strides = 2, padding = 'same')(x)
    x = LeakyReLU()(x)

    x = Conv2D(256, 5, padding = 'same')(x)
    x = LeakyReLU()(x)
    x = Conv2D(256, 5, padding = 'same')(x)
    x = LeakyReLU()(x)

    x = Conv2D(3, 7, activation='tanh', padding = 'same')(x)
    return Model(inputs,x)
                  

3. 创建 generator

gen = generator((100,))

4. 定义 discriminator

def discriminator(input_shape):
                  
    inputs = Input(input_shape)
    
    x = Conv2D(128, 3)(inputs)
    x = LeakyReLU()(x)
    x = Conv2D(128, 4, strides=2)(x)
    x = LeakyReLU()(x)
    x = Conv2D(128, 4, strides=2)(x)
    x = LeakyReLU()(x)
    x = Conv2D(128, 4, strides=2)(x)
    x = LeakyReLU()(x)
    x = Flatten()(x)

    x = Dropout(0.4)(x)

    x = Dense(1, activation='sigmoid')(x) #分类层

    return Model(inputs,x)

5. 创建 discrminator 并 compile

dis = discriminator((32,32,3))
dis.compile(loss=keras.losses.binary_crossentropy,optimizer= keras.optimizers.RMSprop(lr = 0.0008,clipvalue = 1.0,decay=1e-8))

6. 定义 GAN 网络(先锁定 discrminator 但不 compile)


def GAN():
    dis.trainable=False
    gan_input = Input((100,))
    fake_image = gen(gan_input)
    score = dis(fake_image)
    return Model(gan_input,score)
    

7. 创建并 compile GAN 网络

gan = GAN()
gan.compile(loss=keras.losses.binary_crossentropy,optimizer=keras.optimizers.RMSprop(lr=0.0004, clipvalue=1.0, decay=1e-8))

8. 导入数据并规范化

(x_train,y_train),(x_test,y_test)= cifar10.load_data()

y_train_label = y_train
y_test_label = y_test

x_train = x_train[y_train.flatten() == 7]  #选择马类数据即可
x_train = x_train.reshape(5000,32,32,3).astype('float32')/255.

9. 训练过程

epochs = 4000
batch_size = 64
valid = np.ones((batch_size,1))
fake = np.zeros((batch_size,1))

generated_img = []
discriminator_loss = []
generator_loss = []
save_dir = './A-GAN-PHOTO'

for epoch in range(epochs):

    noise = np.random.normal(0,1,size=(batch_size,100))
    img_index = np.random.randint(0,5000,batch_size)
    fake_img = gen.predict(noise)
    real_img = x_train[img_index]
    data = np.concatenate([fake_img, real_img])
    label = np.concatenate([fake,valid])
    label += 0.05 * np.random.random(label.shape)

    
    d_loss = dis.train_on_batch(data,label)

    # ---------------------
    #  训练生成模型
    # ---------------------

    noise_ = np.random.normal(0,1,size=(batch_size,100))
    g_loss = gan.train_on_batch(noise_, valid)
    
    if epoch%100 == 0:

        im = fake_img[0]
        generated_img.append(im)
        img = image.array_to_img(fake_img[0] * 255, scale=False)
        img.save(os.path.join(save_dir, 'generated_horse' + str(epoch) + '.png'))	#保存一张生成图像

        img = image.array_to_img(real_img[0] * 255, scale=False)
        img.save(os.path.join(save_dir, 'real_horse' + str(epoch) +'.png'))   #保存一张真实图像用于对比

        
        print('discriminator_loss:',d_loss)
        print('adversal_loss:',g_loss)
        discriminator_loss.append(d_loss)
        generator_loss.append(g_loss)
        
        # discriminator_loss.append(d_loss[-1])
        # generator_loss.append(g_loss[-1])
        # print("d_loss:%f"%d_loss[-1])
        # print("g_loss:%f"%g_loss[-1])
        print("epoch:%d" % epoch + "========")

在这里插入图片描述

10. 展示 generated_img 里的所有图片

fig, axes = plt.subplots(nrows=2, ncols=20, sharex=True, sharey=True, figsize=(80,12))
imgs = generated_img

for image, row in zip([imgs[:20], imgs[20:40]], axes):
    for img, ax in zip(image, row):
        ax.imshow(img)
        ax.get_xaxis().set_visible(False)
        ax.get_yaxis().set_visible(False)

fig.tight_layout(pad=0.1)

在这里插入图片描述

  • 可以看到生成效果还是不错的。

11. 画出训练过程的 GAN 的 loss 曲线

plt.plot(discriminator_loss,label='discriminator_loss')
plt.plot(generator_loss,label='generator_loss')
plt.legend()

在这里插入图片描述

大坑汇总

  • 虽然很多人提倡在训练 GAN 的时候使用 Batchnormalization 层,但是我在实操中发现使用这个层很多时候会导致一方的 loss 很快地偏向 1 或者 0,另一方也是,这种情况导致的大波动非常不利于 GAN 总体的训练
  • 还是上面提到的,注意 compile,签完不要在定义 GAN 网络的时候再 compile discriminator,那样的话 discriminator 就永远地锁定了。如果不在 GAN 中 compile discriminator,那么 discriminator 具有两种状态,那就是在别的位置都是不被锁定的,唯独在 GAN 中是被锁定的。

写在后面

最后,祝大家的 GAN 网络都能训练有素,耗子尾汁,别那么不讲武德。

  • 13
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
GAN(Generative Adversarial Network)是一种生成式模型,常用于生成图片。以下是使用TensorFlow2.0和Keras实现的简单GAN生成图片的代码示例: ```python import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers # 定义生成器模型 def make_generator_model(): model = keras.Sequential() model.add(layers.Dense(7*7*256, use_bias=False, input_shape=(100,))) model.add(layers.BatchNormalization()) model.add(layers.LeakyReLU()) model.add(layers.Reshape((7, 7, 256))) assert model.output_shape == (None, 7, 7, 256) # 注意:batch size 没有限制 model.add(layers.Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False)) assert model.output_shape == (None, 7, 7, 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, 14, 14, 64) model.add(layers.BatchNormalization()) model.add(layers.LeakyReLU()) model.add(layers.Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh')) assert model.output_shape == (None, 28, 28, 1) return model # 定义判别器模型 def make_discriminator_model(): model = keras.Sequential() model.add(layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same', input_shape=[28, 28, 1])) model.add(layers.LeakyReLU()) model.add(layers.Dropout(0.3)) model.add(layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same')) model.add(layers.LeakyReLU()) model.add(layers.Dropout(0.3)) model.add(layers.Flatten()) model.add(layers.Dense(1)) return model # 定义损失函数 cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True) # 定义判别器损失函数 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 return total_loss # 定义生成器损失函数 def generator_loss(fake_output): return cross_entropy(tf.ones_like(fake_output), fake_output) # 定义优化器 generator_optimizer = tf.keras.optimizers.Adam(1e-4) discriminator_optimizer = tf.keras.optimizers.Adam(1e-4) # 定义训练函数 @tf.function def train_step(images): noise = tf.random.normal([BATCH_SIZE, 100]) with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape: generated_images = generator(noise, training=True) real_output = discriminator(images, training=True) fake_output = discriminator(generated_images, training=True) gen_loss = generator_loss(fake_output) disc_loss = discriminator_loss(real_output, fake_output) gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables) gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables) generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables)) discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables)) # 加载MNIST数据集 (train_images, train_labels), (_, _) = keras.datasets.mnist.load_data() train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32') train_images = (train_images - 127.5) / 127.5 # 将像素值归一化到[-1, 1]范围内 BUFFER_SIZE = 60000 BATCH_SIZE = 256 train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(BUFFER_SIZE).batch(BATCH_SIZE) # 创建生成器和判别器实例 generator = make_generator_model() discriminator = make_discriminator_model() # 训练模型 EPOCHS = 100 noise_dim = 100 num_examples_to_generate = 16 # 生成器固定输入噪声,用于每个epoch生成样本 seed = tf.random.normal([num_examples_to_generate, noise_dim]) for epoch in range(EPOCHS): for image_batch in train_dataset: train_step(image_batch) # 每个epoch结束后生成样本并保存 generate_and_save_images(generator, epoch + 1, seed) # 生成样本并保存 def generate_and_save_images(model, epoch, test_input): # 生成图片 predictions = model(test_input, training=False) fig = plt.figure(figsize=(4,4)) for i in range(predictions.shape[0]): plt.subplot(4, 4, i+1) plt.imshow(predictions[i, :, :, 0] * 127.5 + 127.5, cmap='gray') plt.axis('off') # 保存图片 plt.savefig('image_at_epoch_{:04d}.png'.format(epoch)) plt.show() ``` 在训练过程中,每个epoch结束后,会生成16张图片并保存到本地。可以通过调整超参数和网络结构来改进GAN生成图片质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暖仔会飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值