博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
DCGAN(深度卷积对抗网络)案例
阅读量:7048 次
发布时间:2019-06-28

本文共 8619 字,大约阅读时间需要 28 分钟。

介绍

图片描述

如图所示,GAN网络会同时训练两个模型。生成器:负责生成数据(比如:照片);判别器:判别所生成照片的真假。训练过程中,生成器生成的照片会越来越接近真实照片,直到判别器无法区分照片真假。

图片描述

DCGAN(深度卷积对抗生成网络)是GAN的变体,是一种将卷积引入模型的网络。特点是:

  • 判别器使用strided convolutions来替代空间池化,生成器使用反卷积
  • 使用BN稳定学习,有助于处理初始化不良导致的训练问题
  • 生成器输出层使用Tanh激活函数,其它层使用Relu激活函数。判别器上使用Leaky Relu激活函数。

本次案例我们将使用mnist作为数据集训练DCGAN网络,程序最后将使用GIF的方式展示训练效果。

数据导入

import tensorflow as tfimport globimport imageioimport matplotlib.pyplot as pltimport numpy as npimport osimport tensorflow.contrib as tconimport PILimport timefrom IPython import display# shape:(60000,28,28)(train_images,train_labels),(_,_)=tf.keras.datasets.mnist.load_data()# shape:[batch_size,height,width,channel]train_images_reshape=tf.reshape(train_images,shape=(train_images.shape[0],28,28,1)).astype(tf.float32)# 缩放图片[-1,1]train_images_nor=(train_images-127.5)/127.5

dataset加载数据

BUFFER_SIZE=60000BATCH_SIZE=256# 优化输入管道需要从:读取,转换,加载三方面考虑。train_dataset=tf.data.Dataset.from_tensor_slices(train_images).shuffle(buffer_size=BUFFER_SIZE).batch(BATCH_SIZE)

生成模型

该生成模型将使用反卷积层,我们首先创建全连接层然后通过两次上采样将图片分辨率扩充至28x28x1。我们将逐步提升分辨率降低depth,除最后一层使用tanh激活函数,其它层都使用Leaky Relu激活函数。

def make_generator_model():    # 反卷积,从后往前    model=tf.keras.Sequential()    model.add(        tf.keras.layers.Dense(            input_dim=7*7*256,                        # 不使用bias的原因是我们使用了BN,BN会抵消掉bias的作用。            # bias的作用:            # 提升网络拟合能力,而且计算简单(只要一次加法)。            # 能力的提升源于调整输出的整体分布            use_bias=False,            # noise dim            input_shape=(100,)        )    )    """    随着神经网络的训练,网络层的输入分布会发生变动,逐渐向激活函数取值两端靠拢,如:sigmoid激活函数,    此时会进入饱和状态,梯度更新缓慢,对输入变动不敏感,甚至梯度消失导致模型难以训练。    BN,在网络层输入激活函数输入值之前加入,可以将分布拉到均值为0,标准差为1的正态分布,从而    使激活函数处于对输入值敏感的区域,从而加快模型训练。此外,BN还能起到类似dropout的正则化作用,由于我们会有    ‘强拉’操作,所以对初始化要求没有那么高,可以使用较大的学习率。    """    model.add(tf.keras.layers.BatchNormalization())    """    relu 激活函数在输入为负值的时候,激活值为0,此时神经元无法学习    leakyrelu 激活函数在输入为负值的时候,激活值不为0(但值很小),神经元可以继续学习    """    model.add(tf.keras.layers.LeakyReLU())    model.add(tf.keras.layers.Reshape(input_shape=(7,7,256)))    assert model.output_shape == (None,7,7,256)    model.add(tf.keras.layers.Conv2DTranspose(        filters=128,        kernel_size=5,        strides=1,        padding='same',        use_bias='False'    ))    assert model.output_shape == (None,7,7,128)    model.add(tf.keras.layers.BatchNormalization())    model.add(tf.keras.layers.LeakyReLU())    # 卷积核为奇数:图像两边可以对称padding 00xxxx00    model.add(tf.keras.layers.Conv2DTranspose(        filters=64,        kernel_size=5,        strides=2,        padding='same',        use_bias='False'    ))    assert model.output_shape == (None,14,14,64)    model.add(tf.keras.layers.BatchNormalization())    model.add(tf.keras.layers.LeakyReLU())    model.add(tf.keras.layers.Conv2DTranspose(        filters=1,        kernel_size=5,        strides=2,        padding='same',        use_bias='False',                # tanh激活函数值区间[-1,1],均值为0关于原点中心对称。、        # sigmoid激活函数梯度在反向传播过程中会出全正数或全负数,导致权重更新出现Z型下降。        activation='tanh'    ))    assert model.output_shape == (None,28,28,1)    return model

判别模型

判别器使用strided convolutions来替代空间池化,比如这里strided=2。卷积层使用LeakyReLU替代Relu,并使用Dropout为全连接层提供加噪声的输入。

def make_discriminator_model():    # 常规卷积操作    model = tf.keras.Sequential()    model.add(tf.keras.layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same'))    model.add(tf.keras.layers.LeakyReLU())        # dropout常见于全连接层,其实卷积层也是可以使用的。    # 这里简单翻译下dropout论文观点:    """    可能很多人认为因为卷积层参数较少,过拟合发生概率较低,所以dropout作用并不大。    但是,dropout在前面几层依然有帮助,因为它为后面的全连接层提供了加噪声的输入,从而防止过拟合。    """    model.add(tf.keras.layers.Dropout(0.3))          model.add(tf.keras.layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same'))    model.add(tf.keras.layers.LeakyReLU())    model.add(tf.keras.layers.Dropout(0.3))           model.add(tf.keras.layers.Flatten())    model.add(tf.keras.layers.Dense(1))         return model

损失函数

获取模型:

generator = make_generator_model()discriminator = make_discriminator_model()

生成器损失函数:

损失函数使用sigmoid cross entropylabels使用值全为1的数组。

def generator_loss(generator_output):    return tf.losses.sigmoid_cross_entropy(        multi_class_labels=tf.ones_like(generator_output),        logits=generator_output    )

判别器损失函数

判别器损失函数接受两种输入,生成器生成的图像和数据集中的真实图像,损失函数计算方法如下:

  • 使用sigmoid cross entropy损失函数计算数据集中真实图像的损失,labels使用值全为1的数组。
  • 使用sigmoid cross entropy损失函数计算生成器图像的损失,labels使用值全为0的数组。
  • 将以上损失相加得到判别器损失。
def discriminator_loss(real_output, generated_output):    # real:[1,1,...,1]     real_loss = tf.losses.sigmoid_cross_entropy(multi_class_labels=tf.ones_like(real_output), logits=real_output)    #:generated:[0,0,...,0]     generated_loss = tf.losses.sigmoid_cross_entropy(multi_class_labels=tf.zeros_like(generated_output), logits=generated_output)        # 总损失为两者相加    total_loss = real_loss + generated_loss    return total_loss

模型保存:

# 两种模型同时训练,自然需要使用两种优化器,学习率为:0.0001generator_optimizer = tf.train.AdamOptimizer(1e-4)discriminator_optimizer = tf.train.AdamOptimizer(1e-4)
checkpoint_dir = './training_checkpoints'checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")# checkpoint配置checkpoint = tf.train.Checkpoint(generator_optimizer=generator_optimizer,                                 discriminator_optimizer=discriminator_optimizer,                                 generator=generator,                                 discriminator=discriminator)

模型训练

训练参数配置:

# 数据集迭代次数EPOCHS = 50# 生成器噪声维度noise_dim = 100# 可视化效果数量设置num_examples_to_generate = 16random_vector_for_generation = tf.random_normal([num_examples_to_generate,                                                 noise_dim])

生成器将我们设定的正态分布的噪声向量作为输入,用来生成图像。判别器将同时显示数据集真实图像和生成器生成的图像用于判别。随后,我们计算生成器和判断器损失函数对参数的梯度,然后使用梯度下降进行更新。

def train_step(images):      # 正态分布噪声作为生成器输入      noise = tf.random_normal([BATCH_SIZE, noise_dim])            # tf.GradientTape进行记录      with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:        generated_images = generator(noise, training=True)                # 判别器中真实图像和生成器的假图像        real_output = discriminator(images, training=True)        generated_output = discriminator(generated_images, training=True)                gen_loss = generator_loss(generated_output)        disc_loss = discriminator_loss(real_output, generated_output)              gradients_of_generator = gen_tape.gradient(gen_loss, generator.variables)      gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.variables)            generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.variables))      discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.variables))

开始训练:

加速计算节约内存,但是不可以使用'pdb','print'。

train_step = tf.contrib.eager.defun(train_step)
def train(dataset, epochs):    for epoch in range(epochs):    start = time.time()        # 迭代数据集    for images in dataset:      train_step(images)    display.clear_output(wait=True)        # 保存图像用于后面的可视化    generate_and_save_images(generator,                               epoch + 1,                               random_vector_for_generation)        # 每迭代15次数据集保存一次模型    # 如需部署至tensorflow serving需要使用savemodel    if (epoch + 1) % 15 == 0:      checkpoint.save(file_prefix = checkpoint_prefix)        print ('Time taken for epoch {} is {} sec'.format(epoch + 1,                                                      time.time()-start))  display.clear_output(wait=True)  generate_and_save_images(generator,                           epochs,                           random_vector_for_generation)

可视化生成器图像:

def generate_and_save_images(model, epoch, test_input):  # training:False 不训练BN  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()train(train_dataset, EPOCHS)

可视化模型训练结果

展示照片:

def display_image(epoch_no):  return PIL.Image.open('image_at_epoch_{:04d}.png'.format(epoch_no))

动画展示训练结果:

with imageio.get_writer('dcgan.gif', mode='I') as writer:  filenames = glob.glob('image*.png')  filenames = sorted(filenames)  last = -1  for i,filename in enumerate(filenames):    frame = 2*(i**0.5)    if round(frame) > round(last):      last = frame    else:      continue    image = imageio.imread(filename)    writer.append_data(image)  image = imageio.imread(filename)  writer.append_data(image)    os.system('cp dcgan.gif dcgan.gif.png')display.Image(filename="dcgan.gif.png")

总结

DCGAN中生成器判别器都使用卷积网络来提升生成和判别能力,其中生成器利用反卷积,判别器利用常规卷积。生成器用随机噪声向量作为输入来生成假图像,判别器通过对真实样本的学习判断生成器图像真伪,如果判断为假,生成器重新调校训练,直到判别器无法区分真实样本图像和生成器的图像。

本文代码部分参考,在此表示感谢。

转载地址:http://jckol.baihongyu.com/

你可能感兴趣的文章
汇编指令-MRS(读)和MSR(写)指令操作CPSR寄存器和SPSR寄存器使用(1)
查看>>
Instagram的Material Design概念设计文章分享
查看>>
Jersey VS Django-Rest
查看>>
安装 openCV 2.4.10
查看>>
去哪网实习总结:用到的easyui组件总结(JavaWeb)
查看>>
spring-oauth-server实践:使用授权方式四:client_credentials 模式下access_token做业务!!!...
查看>>
jquery miniui 学习笔记
查看>>
dubbo AdaptiveExtension
查看>>
Scrapy系列教程(1)------命令行工具
查看>>
Using Autorelease Pool Blocks
查看>>
spring-struts-mybatis整合错误集锦
查看>>
Maven 通过maven对项目进行拆分、聚合(重点)
查看>>
TWaver版3D化学元素周期表
查看>>
Java 中最常见的 5 个错误
查看>>
[AWS vs Azure] 云计算里AWS和Azure的探究(2)
查看>>
查看是否安装.NET Framework、.NET Framework的版本号、CLR版本号
查看>>
数据结构基础温故-5.图(下):最短路径
查看>>
调试Release发布版程序的Crash错误(转)
查看>>
深入浅出话VC++(2)——MFC的本质
查看>>
跟我一起学WCF(5)——深入解析服务契约[上篇]
查看>>