生成对抗网络(二) ---------- DCGAN

一、DCGAN的介绍

GAN是无监督学习的一种方法,其训练样本是不需要标签的。DCGAN是在GAN的基础上,在2015年的《Unsupervised Representation Leanrning with Deep Convolutions Generative Adversarial Network》论文中提出。将GAN的网络结构由MLP换成卷积神经网络。论文中验证了DCGAN的生成器和判别器都可以学习到有层次的特征表示。该网络在训练过程中,状态稳定,并且可以有效的实现高质量图片生成及相关的生成模型应用。具有非常好的实用性。之后的GAN改良版本,大部分都是基于DCGAN的。DCGAN的主要贡献如下:

  • 我们提出了评估卷积GAN的拓扑架构的一组约束,使得它们在大多数训练集中都能稳定的训练。命名为DCGAN
  • 使用经过训练的判别器进行图像分类任务,展示出与其他无监督算法的性能差别
  • 可视化了GAN的过滤器,展示了特定过滤器学会了特定特征表示
  • 展示生成器具有有趣的向量算术特性,允许轻松操纵生成的样本的语义

二、DCGAN的设计原则

为了让GAN能够适应于卷积神经网络结构,DCGAN提出了四点架构设计规则,分别为:

  1. 使用卷积层替代池化层
  2. 去除全连接层
  3. 使用批量归一化
  4. 使用恰当的激活函数

下面我们逐条看一下DCGAN的设计原则:

第一点是把所有的池化层全部去除。使用卷积层替代。在判别器中使用步长卷积代替池化层。对于生成器使用分数步长卷积替代池化层。步长卷积相当于对判别器中的数据进行空间下采样。分数步长卷积相当于对数据进行空间上采样。使用卷积层替代池化层的目的是为了能够让网络自身去学习空间上采样和下采样。使判别器和生成器都具有相应的能力。

第二点是去除全连接层。常规的卷积神经网络都会在卷积层后添加全连接层用以输出最终向量。但是全连接层的缺点是参数过多,并且会造成过拟合。可以使用全局平均池化代替全连接层,可以使模型更稳定,但是收敛速度变慢。DCGAN的方法是将生成器的输入直接与卷积层特征输入进行连接,同样判别器的输出层也是与卷积层的输出特征连接。

第三点是使用批量归一化。神经网络的层数很多,经过每一层的运算,输出的数据的分布都会发生变化。随着层数的增加网络的整体偏差会越来越大。通过对每一层的输入进行归一化处理,能够有效使得数据服从某个固定的数据分布。

第四点是使用不同的激活函数。激活函数的作用是在神经网络中进行非线性变换。在生成器中所有层都使用ReLU激活,除了输出层使用tenh激活。在判别器中所有层都使用LeakyReLU激活。

下面我们分别看一下生成器和判别器的架构图:

图1:DCGAN生成器的架构图

图2:DCGAN判别器结构图

三、DCGAN的代码实现

1. 导包

from __future__ import print_function, division

from keras.datasets import mnist
from keras.layers import Input, Dense, Reshape, Flatten, Dropout
from keras.layers import BatchNormalization, Activation, ZeroPadding2D
from keras.layers.advanced_activations import LeakyReLU
from keras.layers.convolutional import UpSampling2D, Conv2D
from keras.models import Sequential, Model
from keras.optimizers import Adam
from skimage.io import imsave

import os
import numpy as np
import matplotlib.pyplot as plt

2. 初始化

class DCGAN():
    def __init__(self):
        # 输入形状
        self.img_rows = 28
        self.img_cols = 28
        self.channels = 1
        self.img_shape = (self.img_rows, self.img_cols, self.channels)
        self.latent_dim = 100

        optimizer = Adam(0.0002, 0.5)

        # 构建编译判别器
        self.discriminator = self.build_discriminator()
        self.discriminator.compile(loss='binary_crossentropy',
                                   optimizer=optimizer,
                                   metrics = ['accuracy'])

        # 构建生成器
        self.generator = self.build_generator()

        z = Input(shape=(100,))
        img = self.generator(z)


        self.discriminator.trainable = False
        valid = self.discriminator(img)

        # 编译生成器
        self.combined = Model(z, valid)
        self.combined.compile(loss='binary_crossentropy',
                              optimizer=optimizer)

3. 建造生成器

    # 构建生成器
    def build_generator(self):

        model = Sequential()

        model.add(Dense(128 * 7 * 7, activation="relu", input_dim=self.latent_dim))
        model.add(Reshape((7, 7, 128)))
        model.add(UpSampling2D())
        model.add(Conv2D(128, kernel_size=3, padding="same"))
        model.add(BatchNormalization(momentum=0.8))
        model.add(Activation("relu"))
        model.add(UpSampling2D())
        model.add(Conv2D(64, kernel_size=3, padding="same"))
        model.add(BatchNormalization(momentum=0.8))
        model.add(Activation("relu"))
        model.add(Conv2D(self.channels, kernel_size=3, padding="same"))
        model.add(Activation("tanh"))

        model.summary()

        noise = Input(shape=(self.latent_dim,))
        img = model(noise)

        return Model(noise, img)

4. 构建判别器

    # 构建判别器
    def build_discriminator(self):

        model = Sequential()

        model.add(Conv2D(32, kernel_size=3, strides=2, input_shape=self.img_shape, padding='same'))
        model.add(LeakyReLU(alpha=0.2))
        model.add(Dropout(0.25))
        model.add(Conv2D(64, kernel_size=3, strides=2, padding='same'))
        model.add(ZeroPadding2D(padding=((0, 1), (0, 1))))
        model.add(BatchNormalization(momentum=0.8))
        model.add(LeakyReLU(alpha=0.2))
        model.add(Dropout(0.25))
        model.add(Conv2D(128, kernel_size=3, strides=2, padding="same"))
        model.add(BatchNormalization(momentum=0.8))
        model.add(LeakyReLU(alpha=0.2))
        model.add(Dropout(0.25))
        model.add(Conv2D(256, kernel_size=3, strides=1, padding="same"))
        model.add(BatchNormalization(momentum=0.8))
        model.add(LeakyReLU(alpha=0.2))
        model.add(Dropout(0.25))
        model.add(Flatten())
        model.add(Dense(1, activation='sigmoid'))

        model.summary()

        img = Input(shape=self.img_shape)
        validity = model(img)

        return Model(img, validity)

5. 训练

    # 训练
    def train(self, epochs, batch_size=128, sample_interval=50):

        X_train, X_label = self.load_data('./MNIST_data/')
        #(X_train, _), (_, _) = mnist.load_data()

        # 归一化
        X_train = X_train / 127.5 - 1.
        X_train = np.expand_dims(X_train, axis=3)

        # 数据真实值
        valid = np.ones((batch_size, 1))
        fake = np.zeros((batch_size, 1))

        for epoch in range(epochs):
            idx = np.random.randint(0, X_train.shape[0], batch_size)
            imgs = X_train[idx]

            noise = np.random.normal(0, 1, (batch_size, self.latent_dim))
            gen_imgs = self.generator.predict(noise)

            d_loss_real = self.discriminator.train_on_batch(imgs, valid)
            d_loss_fake = self.discriminator.train_on_batch(gen_imgs, fake)
            d_loss = 0.5 * np.add(d_loss_fake, d_loss_real)

            g_loss = self.combined.train_on_batch(noise, valid)

            # Plot the progress
            print("%d [D loss: %f, acc.: %.2f%%] [G loss: %f]" % (epoch, d_loss[0], 100 * d_loss[1], g_loss))

            # If at save interval => save generated image samples
            if epoch % sample_interval == 0:
                self.sample_images(epoch)

    # 展示结果
    def sample_images(self, epoch):
        r, c = 5, 5
        noise = np.random.normal(0, 1, (r * c, 100))
        gen_imgs = self.generator.predict(noise)

        # Rescale images 0 - 1
        gen_imgs = 0.5 * gen_imgs + 0.5

        fig, axs = plt.subplots(r, c)
        cnt = 0
        for i in range(r):
            for j in range(c):
                axs[i, j].imshow(gen_imgs[cnt, :, :, 0], cmap='gray')
                axs[i, j].axis('off')
                cnt += 1
        fig.savefig("images/%d.png" % epoch)
        plt.close()

    # 加载数据
    def load_data(self, data_paths):
        '''
        函数功能:加载数据
        :param data_paths:数据存在的路径
        :return: 训练数据与标签
        '''
        f_data = open(os.path.join(data_paths, 'train-images.idx3-ubyte'))  # 定位数据
        loaded_data = np.fromfile(file=f_data, dtype=np.uint8)  # 读取数据
        train_data = loaded_data[16:].reshape((-1, 28, 28)).astype(np.float)  # 截取有用的数据并转换形状

        f_label = open(os.path.join(data_paths, 'train-labels.idx1-ubyte'))
        loaded_label = np.fromfile(file=f_label, dtype=np.uint8)
        train_label = loaded_label[8:].reshape((-1)).astype(np.float)

        return train_data, train_label

6. 运行程序

if __name__ == '__main__':
    gan= DCGAN()
    gan.train(epochs=30000)

猜你喜欢

转载自blog.csdn.net/gyt15663668337/article/details/90018378