深度学习笔记(三)——GAN入门实现MNIST数据集

深度学习笔记(三)——GAN入门实现MNIST数据集

闲聊

这周的任务完成了哇,但是本着好好学习,天天向上的宏伟计划(PS:一直感觉GAN蛮好玩的,打算玩玩),所以打算尝试学习下GAN。

什么是GAN?

论文
生成式对抗网络(GAN)是近年来复杂分布上无监督学习最具前景的方法之一,GAN的主要灵感来源于博弈论中零和博弈的思想,应用到深度学习中来说是通过生成网络G(Generator)和判别网络D(Discriminator)不断博弈,进而使G学习到数据的分布 ,如果是在图片生成上进行应用,就是
GAN

  • G是一个生成式的网络,它接收一个随机的噪声z(随机数),通过这个噪声生成图像

  • D是一个判别网络,判别一张图片是不是“真实的”。它的输入参数是x,x代表一张图片,输出D(x)代表x为真实图片的概率,如果为1,就代表100%是真实的图片,而输出为0,就代表不可能是真实的图片

  • G的目的是生成让判别的模型无法判断真伪的输出

  • D的目的是判断这个是否真实

Generator

什么是生成(generation)?就是模型通过学习一些数据,然后生成类似的数据。让机器看一些图片,然后自己来产生的图片,这就是生成。

以前就有很多生成的技术,比如auto-encoder(自编码器):
Generator
训练一个encoder(编码器),把image input转换成code,然后训练一个decoder解码器,将code转换成一个Image,然后计算得到的image和image input之间的MSE(mean square error)

训练完模型后,取出后半部分的decoder,输入一个随机的code,就能通过Generator生成一个image
随机code生成图片

Discriminator

上面讲到了Generator可以根据一个随机的code生成图片,但是这里涉及到了一个问题,生成出来的图片,到底让我们看是怎么样的呢?这个时候就需要Discriminator对Generator生成的图片进行判别

如果非要简单地形容一下就是自己和自己下围棋?

GAN的特点

  • GAN是一种生成式模型,相比较其他生成模型(玻尔兹曼机和GSNs)只用到了反向传播,而不需要复杂的马尔科夫链
  • GAN采用的是一种无监督的学习方式训练,可以被广泛用在无监督学习和半监督学习领域
  • GAN不适合处理离散形式的数据,比如文本
  • GAN中的G的梯度更新信息来自于D,而不是样本数据
  • 它存在两个不同的网络,而不是传统的单一网络

为什么GAN不适合处理文本数据

对于文本来说,通常需要将一个词映射为一个高维向量,最终预测输出的是一个one-hot向量,假设softmax的输出(0.2,0.3,0.1,0.2,0.15,0.05)那么变为onehot是(0,1,0,0,0,0),如果softmax输出是(0.2,0.25,0.2,0.1,0.15,0.1),而onehot仍然是(0,1,0,0,0,0)。对于G来说,输出了不同的结果,但是D给出了相同的判别结果,并不能梯度更新信息很好的传递到G

GAN的优化器为什么不常用SGD

  • SGD容易震荡,容易使GAN训练不稳定
  • GAN的目的是在高维非凸的参数空间中找到纳什均衡点,GAN的均衡点是一个鞍点,但是SGD只会找到局部极小值,因为SGD解决的是求最小值问题,GAN是一个博弈问题

训练技巧

  • 输入规范化到(-1,1)之间,最后一层的激活函数使用tanh(BEGAN除外)
  • 使用wassertein GAN的损失函数,
  • 如果有标签数据的话,尽量使用标签,也有人提出使用反转标签效果很好,另外使用标签平滑,单边标签平滑或者双边标签平滑
  • 使用mini-batch norm, 如果不用batch norm可以使用instance norm或者weight norm
  • 避免使用RELUpooling层,减少稀疏梯度的可能性,可以使用leakrelu激活函数
  • 优化器尽量选择ADAM,学习率不要设置太大,初始1e-4可以参考,另外可以随着训练进行不断缩小学习率,
  • 给D的网络层增加高斯噪声,相当于是一种正则

具体实现

代码参考

Generator

生成网络的目标是输入一行正太分布的随机数,生成mnist手写体图片,输出一个28x28x1的图片

全连接层:256——>512——>1024——>784-变形->28x28

def build_generator(self):
    # --------------------------------- #
    #   生成器,输入一串随机数字
    # --------------------------------- #
    model = Sequential()

    model.add(Dense(256, input_dim=self.latent_dim))
    model.add(LeakyReLU(alpha=0.2))
    model.add(BatchNormalization(momentum=0.8))

    model.add(Dense(512))
    model.add(LeakyReLU(alpha=0.2))
    model.add(BatchNormalization(momentum=0.8))

    model.add(Dense(1024))
    model.add(LeakyReLU(alpha=0.2))
    model.add(BatchNormalization(momentum=0.8))

    model.add(Dense(np.prod(self.img_shape), activation='tanh'))
    model.add(Reshape(self.img_shape))

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

    return Model(noise, img)

Discriminator

判别模型的目的是根据输入的图片判断出真伪。因此它的输入一个28,28,1维的图片,输出是0到1之间的数,1代表判断这个图片是真的,0代表判断这个图片是假的。

全连接层:28x28-打平->784——>512——>256——>1(0或1真伪)

def build_discriminator(self):
    # ----------------------------------- #
    #   评价器,对输入进来的图片进行评价
    # ----------------------------------- #
    model = Sequential()
    # 输入一张图片
    model.add(Flatten(input_shape=self.img_shape))
    model.add(Dense(512))
    model.add(LeakyReLU(alpha=0.2))
    model.add(Dense(256))
    model.add(LeakyReLU(alpha=0.2))
    # 判断真伪
    model.add(Dense(1, activation='sigmoid'))

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

    return Model(img, validity)

训练

GAN的训练分为如下几个步骤:

  • 随机选取batch_size个真实的图片
  • 随机生成batch_size个N维向量,传入到Generator中生成batch_size个虚假图片
  • 真实图片的label为1,虚假图片的label为0,将真实图片和虚假图片当作训练集传入到Discriminator中进行训练
  • 将虚假图片的Discriminator预测结果与1的对比作为loss对Generator进行训练(与1对比的意思是,如果Discriminator将虚假图片判断为1,说明这个生成的图片很“真实”)

全部代码

from __future__ import print_function, division

from tensorflow.keras.datasets import mnist
from tensorflow.keras.layers import Input, Dense, Reshape, Flatten, Dropout
from tensorflow.keras.layers import BatchNormalization, Activation, ZeroPadding2D
# from tensorflow.keras.layers.advanced_activations import LeakyReLU
from tensorflow.keras.layers import LeakyReLU
# from tensorflow.keras.layers.convolutional import UpSampling2D, Conv2D
from tensorflow.keras.layers import UpSampling2D, Conv2D
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.optimizers import Adam
import matplotlib.pyplot as plt

import sys
import os
import numpy as np

class GAN():
    def __init__(self):
        # --------------------------------- #
        #   行28,列28,也就是mnist的shape
        # --------------------------------- #
        self.img_rows = 28
        self.img_cols = 28
        self.channels = 1
        # 28,28,1
        self.img_shape = (self.img_rows, self.img_cols, self.channels)
        self.latent_dim = 100
        # adam优化器
        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()
        gan_input = Input(shape=(self.latent_dim,))
        img = self.generator(gan_input)
        # 在训练generate的时候不训练discriminator
        self.discriminator.trainable = False
        # 对生成的假图片进行预测
        validity = self.discriminator(img)
        self.combined = Model(gan_input, validity)
        self.combined.compile(loss='binary_crossentropy', optimizer=optimizer)


    def build_generator(self):
        # --------------------------------- #
        #   生成器,输入一串随机数字
        # --------------------------------- #
        model = Sequential()

        model.add(Dense(256, input_dim=self.latent_dim))
        model.add(LeakyReLU(alpha=0.2))
        model.add(BatchNormalization(momentum=0.8))

        model.add(Dense(512))
        model.add(LeakyReLU(alpha=0.2))
        model.add(BatchNormalization(momentum=0.8))

        model.add(Dense(1024))
        model.add(LeakyReLU(alpha=0.2))
        model.add(BatchNormalization(momentum=0.8))

        model.add(Dense(np.prod(self.img_shape), activation='tanh'))
        model.add(Reshape(self.img_shape))

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

        return Model(noise, img)

    def build_discriminator(self):
        # ----------------------------------- #
        #   评价器,对输入进来的图片进行评价
        # ----------------------------------- #
        model = Sequential()
        # 输入一张图片
        model.add(Flatten(input_shape=self.img_shape))
        model.add(Dense(512))
        model.add(LeakyReLU(alpha=0.2))
        model.add(Dense(256))
        model.add(LeakyReLU(alpha=0.2))
        # 判断真伪
        model.add(Dense(1, activation='sigmoid'))

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

        return Model(img, validity)

    def train(self, epochs, batch_size=128, sample_interval=50):
        # 获得数据
        (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):

            # --------------------------- #
            #   随机选取batch_size个图片
            #   对discriminator进行训练
            # --------------------------- #
            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_real, d_loss_fake)

            # --------------------------- #
            #  训练generator
            # --------------------------- #
            noise = np.random.normal(0, 1, (batch_size, self.latent_dim))
            g_loss = self.combined.train_on_batch(noise, valid)
            print ("%d [D loss: %f, acc.: %.2f%%] [G loss: %f]" % (epoch, d_loss[0], 100*d_loss[1], g_loss))

            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, self.latent_dim))
        gen_imgs = self.generator.predict(noise)

        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()


if __name__ == '__main__':
    if not os.path.exists("./images"):
        os.makedirs("./images")
    gan = GAN()
    gan.train(epochs=30000, batch_size=256, sample_interval=200)

结语

终于跑了一次GAN了,还是蛮好玩的

参考:

深度学习----GAN(生成对抗神经网络)原理解析
好像还挺好玩的GAN1——Keras搭建简单GAN生成MNIST手写体

训练结果

0轮
5000轮
15000轮
25000轮
30000轮

猜你喜欢

转载自blog.csdn.net/InkBamboo920/article/details/106393091