1、引言

2、对抗生成网络(GAN)

3、深度卷积对抗生成网络(DCGAN)

4、总结


1、引言

生成对抗网络 (GAN) 是一种算法架构,它使用两个神经网络,其中一个对另一个(因此称为“对抗性”),以便生成新的合成数据实例,这些实例可以通过真实数据。它们广泛用于图像生成、视频生成和语音生成。

虽然大多数深度生成模型是通过最大化对数似然或对数似然的下限来训练的,但GANs采取了一种完全不同的方法,不需要推理或明确计算数据的似然。相反,两个模型被用来解决一个最小化任务:一个对数据进行采样的生成器,以及一个将数据分类为真实或生成的判别器。

本文以最直观的方式向初学者介绍生成对抗网络 (GAN) 的基本概念。目的是描述这种网络的工作,而无需深入研究相关的复杂数学,并以更实用的方式,以便读者可以开发自己的简单GAN。

首先构建一个简单的GAN,只有隐藏的密集层,并尝试输出有意义的图像。分析输出以发现在简单 GAN 中常见的一个特定问题。这个问题将在下一部分使用一种称为深度卷积生成对抗网络(DCGAN)的特定类型的GAN来解决。

本期内容『数据+代码』已上传百度网盘。有需要的朋友可以关注公众号【小Z的科研日常】,后台回复关键词[GAN]获取

2、对抗生成网络(GAN)

GAN 由两个组件组成 -

  1. 生成器 - 生成新的数据实例

  2. 鉴别器 - 尝试将生成的或虚假的数据与真实数据集区分开来。

判别算法试图对输入数据进行分类;也就是说,给定数据实例的特征,它们预测该数据所属的标签或类别。因此,判别算法将特征映射到标签。他们只关心这种相关性。另一种方式,生成算法做相反的事情。他们不是预测给定某些特征的标签,而是尝试预测给定特定标签的特征。

在训练时,它们都从头开始,生成器通过训练时期学习塑造随机分布。

工作原理

Generative生成网络馈送噪声的形式可以是随机分布并从噪声中生成假数据。来自生成器的假数据被输入到鉴别器。训练完成后,生成器应该能够从噪声中生成真实的类似数据。

这里有趣的事实是,生成器学会产生有意义的图像,甚至没有实际查看图像。

鉴别器 或对抗网络充当生成器的对手。它基本上是一个分类器或鉴别器,其功能是区分两类不同的数据。这里的类是真实数据(标记为 1)和生成器生成的假数据(标记为 0)。

训练网络

训练 GAN 的重要一点是,这两个组件永远不应该一起训练。相反,网络分两个不同的阶段进行训练,第一阶段是训练鉴别器并适当地更新权重,在下一步中,在禁用鉴别器训练的同时训练生成器。

第 1 阶段 在训练的第一阶段,生成器被馈送随机数据(以分布的形式)作为噪声。生成器创建一些随机图像,这些图像被提供给鉴别器。鉴别器还从真实图像数据集中获取输入。鉴别器通过从其输入中学习或评估特征来学习区分真实数据和虚假数据。判别器输出一些概率,预测结果之间的差异与实际结果通过网络反向传播,并更新判别器的权重。请记住,在此阶段,反向传播在鉴别器结束时停止,并且生成器未训练或更新。

第 2 阶段 在此阶段,生成器生成的一批图像直接作为判别器的输入给出。这次没有将真实图像提供给鉴别器。生成器通过诱骗鉴别器进行学习,从而输出误报。鉴别器输出概率,根据实际结果进行评估,生成器的权重通过反向传播更新。在反向传播期间,判别器权重不应更新并保持原样。

简单GAN的损失函数

本实验中,生成器试图最小化以下函数,而鉴别器试图最大化它:

在此函数中:

  • D(x)是鉴别器对真实数据实例 x 为真实概率的估计。

  • Ex 是所有实际数据实例的预期值。

  • G(z) 是给定噪声 z 时发生器的输出。

  • D(G(z)) 是鉴别器对假实例真实概率的估计。

  • Ez 是生成器所有随机输入的期望值(实际上是所有生成的假实例 G(z) 的期望值)。

生成器不能直接影响函数中的log(D(x))项,因此,对于生成器来说,最小化损失等同于最小化log(1 - D(G(z)))。

代码实现

导入有用的包:

import numpy as np
import pandas as pd 
import matplotlib.pyplot as plt
from keras import preprocessing
from keras.models import Sequential
from keras.layers import Conv2D,Dropout,Dense,Flatten,Conv2DTranspose,BatchNormalization,LeakyReLU,Reshape
import tensorflow as tf

加载和预处理数据:

train_data = pd.read_csv('fasion_mnist/fashion-mnist_train.csv')
X_train = train_data.drop('label',axis=1)
X_train=X_train.values
print(f"X_train形状:    {X_train.shape}")
X_train=X_train.reshape(-1,28,28,1)
print(f"X_train形状:    {X_train.shape}")

数据可视化:

fig,axe=plt.subplots(4,4)
idx = 0
for i in range(4):
    for j in range(4):
        axe[i,j].imshow(X_train[idx].reshape(28,28),cmap='gray')
        idx+=1

像素数据的范围是0到255,因此将每个像素除以255,也就是将数据归一化,使其范围在0到1之间。

注意:在归一化之后,它被乘以2并从1中减去,这样它的范围是(-1,1),因为在DCGANs的最后一层生成模型激活是tanh,其范围是(-1,1),不像sigmoid范围(0,1)。

X_train =  X_train.astype('float32')
X_train = X_train/255
X_train = X_train*2 - 1.
print(X_train.max(),X_train.min())

简单的GAN模型:

generator = Sequential()
generator.add(Dense(512,input_shape=[100]))
generator.add(LeakyReLU(alpha=0.2))
generator.add(BatchNormalization(momentum=0.8))
generator.add(Dense(256))
generator.add(LeakyReLU(alpha=0.2))
generator.add(BatchNormalization(momentum=0.8))
generator.add(Dense(128))
generator.add(LeakyReLU(alpha=0.2))
generator.add(BatchNormalization(momentum=0.8))
generator.add(Dense(784))
generator.add(Reshape([28,28,1]))

discriminator = Sequential()
discriminator.add(Dense(1,input_shape=[28,28,1]))
discriminator.add(Flatten())
discriminator.add(Dense(256))
discriminator.add(LeakyReLU(alpha=0.2))
discriminator.add(Dropout(0.5))
discriminator.add(Dense(128))
discriminator.add(LeakyReLU(alpha=0.2))
discriminator.add(Dropout(0.5))
discriminator.add(Dense(64))
discriminator.add(LeakyReLU(alpha=0.2))
discriminator.add(Dropout(0.5))
discriminator.add(Dense(1,activation='sigmoid'))

注意:生成器层不是单独编译的,因为它是作为组合模型的一部分训练的,但训练鉴别器是必要的,因为它是在组合模型之前训练的。

GAN =Sequential([generator,discriminator])
discriminator.compile(optimizer='adam',loss='binary_crossentropy')
discriminator.trainable = False
GAN.compile(optimizer='adam',loss='binary_crossentropy')

epochs = 30
batch_size = 100
noise_shape=100

with tf.device('/gpu:0'):
 for epoch in range(epochs):
    print(f"Currently on Epoch {epoch+1}")
    
    
    for i in range(X_train.shape[0]//batch_size):
        
        if (i+1)%50 == 0:
            print(f"\tCurrently on batch number {i+1} of {X_train.shape[0]//batch_size}")
            
        noise=np.random.normal(size=[batch_size,noise_shape])
       
        gen_image = generator.predict_on_batch(noise)
        
        train_dataset = X_train[i*batch_size:(i+1)*batch_size]

        train_label=np.ones(shape=(batch_size,1))
        discriminator.trainable = True
        d_loss_real=discriminator.train_on_batch(train_dataset,train_label)

        train_label=np.zeros(shape=(batch_size,1))
        d_loss_fake=discriminator.train_on_batch(gen_image,train_label)
        
        noise=np.random.normal(size=[batch_size,noise_shape])
        train_label=np.ones(shape=(batch_size,1))
        discriminator.trainable = False
        
        d_g_loss_batch =GAN.train_on_batch(noise, train_label)

    if epoch % 10 == 0:
        samples = 10
        x_fake = generator.predict(np.random.normal(loc=0, scale=1, size=(samples, 100)))

        for k in range(samples):
            plt.subplot(2, 5, k+1)
            plt.imshow(x_fake[k].reshape(28, 28), cmap='gray')
            plt.xticks([])
            plt.yticks([])

        plt.tight_layout()
        plt.show()
 
print('Training is complete')

使用np.random.normal生成的噪声被作为输入给发生器。在接下来的步骤中,生成器从随机分布中产生一批有意义的相似图像。

noise=np.random.normal(size=[10,noise_shape])
gen_image = generator.predict(noise)
plt.imshow(noise)
plt.title('Noise')

噪声图像:

展示生成的图像:

fig,axe=plt.subplots(2,5)
fig.suptitle('Generated Images from Noise using GANs')
idx=0
for i in range(2):
    for j in range(5):
         axe[i,j].imshow(gen_image[idx].reshape(28,28),cmap='gray')
         idx+=1

在迭代了几十个epoch之后,生成器学会了只用一个特定类别的输出图像来欺骗鉴别器,因此在该点之后停止学习。因此,完成训练后的输出只是特征几乎相似的单一类型的图像,即在这种特殊情况下的衬衫,并且生成的图像特征没有变化。

上述问题可以使用 DCGAN 解决,如下所示。

3、深度卷积对抗生成网络(DCGAN)

在这个模型中,我们使用转置卷积层而不是简单的密集层来构建生成器,这有助于更好地捕获特征并防止前面描述的问题。类似地,在构建鉴别层时,它使用卷积层来提高分类效率,而不是简单的密集单元。

代码实现

DCGAN模型:

generator = Sequential()
generator.add(Dense(7 * 7 * 128, input_shape=[100]))
generator.add(Reshape([7, 7, 128]))
generator.add(BatchNormalization())
generator.add(Conv2DTranspose(64, kernel_size=5, strides=2, padding="same",
                                 activation="relu"))
generator.add(BatchNormalization())
generator.add(Conv2DTranspose(1, kernel_size=5, strides=2, padding="same",
                                 activation="tanh"))

discriminator = Sequential()
discriminator.add(Conv2D(64, kernel_size=5, strides=2, padding="same",
                        activation=LeakyReLU(0.3),
                        input_shape=[28, 28, 1]))
discriminator.add(Dropout(0.5))
discriminator.add(Conv2D(128, kernel_size=5, strides=2, padding="same",
                        activation=LeakyReLU(0.3)))
discriminator.add(Dropout(0.5))
discriminator.add(Flatten())
discriminator.add(Dense(1, activation="sigmoid"))

模型训练:

GAN =Sequential([generator,discriminator])
discriminator.compile(optimizer='adam',loss='binary_crossentropy')
discriminator.trainable = False

GAN.compile(optimizer='adam',loss='binary_crossentropy')

epochs = 150 
batch_size = 100
noise_shape=100

with tf.device('/gpu:0'):
 for epoch in range(epochs):
    print(f"Currently on Epoch {epoch+1}")
    
    for i in range(X_train.shape[0]//batch_size):
        
        if (i+1)%50 == 0:
            print(f"\tCurrently on batch number {i+1} of {X_train.shape[0]//batch_size}")
            
        noise=np.random.normal(size=[batch_size,noise_shape])
       
        gen_image = generator.predict_on_batch(noise)
        
        train_dataset = X_train[i*batch_size:(i+1)*batch_size]
       
        train_label=np.ones(shape=(batch_size,1))
        discriminator.trainable = True
        d_loss_real=discriminator.train_on_batch(train_dataset,train_label)
        
        train_label=np.zeros(shape=(batch_size,1))
        d_loss_fake=discriminator.train_on_batch(gen_image,train_label)
        
        noise=np.random.normal(size=[batch_size,noise_shape])
        train_label=np.ones(shape=(batch_size,1))
        discriminator.trainable = False #while training the generator as combined model,discriminator training should be turned off
        
        d_g_loss_batch =GAN.train_on_batch(noise, train_label)
        
    if epoch % 10 == 0:
        samples = 10
        x_fake = generator.predict(np.random.normal(loc=0, scale=1, size=(samples, 100)))

        for k in range(samples):
            plt.subplot(2, 5, k+1)
            plt.imshow(x_fake[k].reshape(28, 28), cmap='gray')
            plt.xticks([])
            plt.yticks([])

        plt.tight_layout()
        plt.show()
        
print('Training is complete')

使用np.random.normal生成的噪声被作为输入给发生器。在接下来的步骤中,生成器从随机分布中产生一批有意义的相似图像。

noise=np.random.normal(loc=0, scale=1, size=(100,noise_shape))
gen_image = generator.predict(noise)
plt.imshow(noise)
plt.title('DCGAN Noise')

噪声图像:

展示生成的图像:

fig,axe=plt.subplots(2,5)
fig.suptitle('Generated Images from Noise using DCGANs')
idx=0
for i in range(2):
     for j in range(5):
         axe[i,j].imshow(gen_image[idx].reshape(28,28),cmap='gray')
         idx+=3

DCGAN生成图像图下:

4、总结

GAN的优点是可以生成与真实数据相似的新数据,具有广泛的应用前景;缺点则包括训练不稳定、容易陷入局部最优等问题。DCGAN相对于普通GAN的优点是能够生成更加逼真、清晰的图像,但需要更多的计算资源和时间来训练。

此次实验中,我们对比了使用DCGAN和普通GAN在生成Fashion-MNIST图像上的效果差异。结果显示,使用DCGAN生成的图像更加逼真、清晰,而普通GAN生成的图像则较为模糊。

 

Logo

科技之力与好奇之心,共建有温度的智能世界

更多推荐