生成对抗网络(GAN)与其变种(DCGAN)的实现——基于tensorflow

生成对抗网络(GAN)与其变种(DCGAN)的实现——基于tensorflow

前言

要理解什么是生成对抗网络,先解释一下有监督学习以及无监督学习:
有监督学习:基于大量带有标签的训练集与测试集的机器学习过程,比如图片分类器需要一系列图片和对应的标签(“猫”,“狗”…)。MNIST手写数字集就是一堆带有标签的训练集与测试集的数据集。
无监督学习:根据类别未知(没有被标记)的训练样本解决模式识别中的各种问题。这就是无监督学习与有监督学习的最大的差别。无监督学习不需要耗费大量的人力去标注训练集,它可以自己从训练集中学习到特征。K-means聚类算法就是其中比较出名的算法之一。

概念——GAN

Ian J. Goodfellow等人于2014年10月在Generative Adversarial Networks中提出了一个通过对抗过程估计生成模型的新框架。框架中同时训练两个模型:捕获数据分布的生成模型G,和估计样本来自训练数据的概率的判别模型D。G的训练程序是将D错误的概率最大化。这个框架对应一个最大值集下限的双方对抗游戏。可以证明在任意函数G和D的空间中,存在唯一的解决方案,使得G重现训练数据分布,而D=0.5。在G和D由多层感知器定义的情况下,整个系统可以用反向传播进行训练。在训练或生成样本期间,不需要任何马尔可夫链或展开的近似推理网络。
在这里插入图片描述

该框架就是生成对抗网络(Generative Adversarial Networks,GAN),简单理解就是生成对抗网络中有两个模型——生成器、鉴别器,在训练过程中两个模型相互博弈训练,最后训练成一个能够生成接近真实图片的生成器与一个能够识别真实图片与虚拟图片的鉴别器。

GAN变种——DCGAN

DCGAN是对GAN比较好的改进,其主要的改进是在网络结构上,到目前为,DCGAN极大的提升了GAN训练的稳定性以及生成结果质量。
原始的GAN使用的是多层感知机作为生成器和鉴别器的基本模型(当然,从理论上来说,使用其他模型也可以,只要能完成对应任务),而DCGAN使用卷积神经网络作为生成器和鉴别器的基本模型。当然,这里使用的卷积神经网络不同于平常的卷积神经网络。主要有以下几点改变:
在这里插入图片描述
1、取消所有的池化层,G中用转置卷积完成上采样,D中使用跨步卷积代替;
2、生成器和判决器都使用批归一化;
3、去除全连接隐藏层;
4、G中使用ReLU作为激活函数,但输出采用Tanh;
5、鉴别器采用LeakyReLU作为激活函数。

训练过程

在这里插入图片描述
如图所示,在训练鉴别器的时候,直接使用D模型进行训练,并为真实数据和生成数据设置不同标签。训练生成器的时候,直接通过整个生成对抗网络来训练,但此时D模型是不可训练的,同时应该将标签设置为1,因为我们希望最终的输出结果是鉴别器将生成的图片识别为真实的图片。
两个模型并不是分别训练的即等D完全训练好后再训练G,他们是同时训练的即D训练完一个batch,G开始训练,G训练完一个batch,D又开始训练,如此往复。

这里只对训练过程做一个简单的概述,详细的训练过程可以参考详解生成对抗网络(GAN)

代码实现

GAN

首先导入相应的库

import tensorflow as tf
import keras
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential,Model
from tensorflow.keras.layers import Input,Dense,Dropout,Activation,Flatten
from tensorflow.keras.optimizers import Adam,RMSprop
import numpy as np
import matplotlib.pyplot as plt
import random

然后,导入数据并预处理,这里直接使用MNIST数据集

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(60000,784)
x_test = x_test.reshape(10000,784)
x_train = x_train.astype('float32')/255
x_test = x_test.astype('float32')/255

设置一下噪声的大小

z_dim = 100

设置一下优化器,这里参考的DCGAN论文里所设置的参数

adam = Adam(lr=0.0002,beta_1=0.5)

搭建生成器模型

g = Sequential()
g.add(Dense(256,input_dim=z_dim,activation='relu'))
g.add(Dense(512,activation='relu'))
g.add(Dense(1024,activation='relu'))
g.add(Dense(2048,activation='relu'))
g.add(Dense(784,activation='sigmoid'))
g.compile(loss='binary_crossentropy',optimizer=adam,metrics=['accuracy'])

搭建鉴别器模型,这里先把D模型设置为不可训练,因为我们需要先训练G

d = Sequential()
d.add(Dense(1024,input_dim=784,activation='relu'))
d.add(Dropout(0.3))
d.add(Dense(512,activation='relu'))
d.add(Dropout(0.3))
d.add(Dense(256,activation='relu'))
d.add(Dropout(0.3))
d.add(Dense(64,activation='relu'))
d.add(Dropout(0.3))
d.add(Dense(1,activation='sigmoid'))
d.compile(loss='binary_crossentropy',optimizer=adam,metrics=['accuracy'])
d.trainable = False

把两个模型连接起来组成生成对抗网络

inputs = Input(shape=(z_dim,))
hidden = g(inputs)
output = d(hidden)
gan = Model(inputs,output)
gan.compile(loss='binary_crossentropy',optimizer=adam,metrics=['accuracy'])

写两个函数用于最后输出损失值和生成器生成的图片

def plot_loss(losses):
    d_loss = [v[0] for v in losses["D"]]
    g_loss = [v[0] for v in losses["G"]]

    plt.figure(figsize=(10,8))
    plt.plot(d_loss,label="Discriminator_loss")
    plt.plot(g_loss,label="Generator_loss")
    plt.legend()
    plt.show()

def plot_generatored(n_ex=10,dim=(1,10),figsize=(12,2)):
    #####
    #np.random.seed(time.time())
    noise = np.random.normal(0,1,size=(n_ex,z_dim))
    generatored_images = g.predict(noise)
    generatored_images = generatored_images.reshape(n_ex,28,28)

    plt.figure(figsize = figsize)
    for i in range(generatored_images.shape[0]):
        plt.subplot(dim[0],dim[1],i+1)
        plt.imshow(generatored_images[i],interpolation='nearest',cmap='gray_r')
        plt.axis('off')
    plt.tight_layout()
    plt.show()

设置一个字典用于保存损失值

losses = {"D":[],"G":[]}

下面是train函数

def train(epochs=1,plt_frq=1,BATCH_SIZE=128):
    batchCount = int(x_train.shape[0]/BATCH_SIZE)
    print("Epochs:",epochs)
    print("Batch size:",BATCH_SIZE)
    print("Batches per epoch:",batchCount)

    for e in range(1,epochs+1):
        if e == 1 or e%plt_frq == 0:
            print('-'*15,'Epoch %d' %e,'-'*15)
        for _ in range(batchCount):
            image_batch = x_train[np.random.randint(0,x_train.shape[0],size=BATCH_SIZE)]
            ########
            #np.random.seed(time.time())
            noise = np.random.normal(0,1,size=(BATCH_SIZE,z_dim))
            generatored_images = g.predict(noise)
            #train d
            #set data set which is composed of 2 parts
            x = np.concatenate((image_batch, generatored_images))
            #y are labels
            y = np.zeros(2*BATCH_SIZE)
            y[:BATCH_SIZE] = 0.9

            d.trainable = True
            d_loss = d.train_on_batch(x,y)

            #train g
            #set up data set
            noise = np.random.normal(0,1,size=(BATCH_SIZE,z_dim))
            y2 = np.ones(BATCH_SIZE)
            d.trainable = False
            g_loss = gan.train_on_batch(noise,y2)
        losses["D"].append(d_loss)
        losses["G"].append(g_loss)
        if e==1 or e%plt_frq==0:
            plot_generatored()
    plot_loss(losses)

然后就可以开始训练了

train(200,20,128)

由于我之前的训练数据丢失了,所以这里就不展示GAN的结果了。

DCGAN

我这里没办法完全复制论文的结果,仅通过论文中提到的几点然后改造自己之前使用的卷积神经网络来替换上面代码里面使用的神经网络。
首先依旧是导入相关库,但这里相对于上面有些层不需要了

import tensorflow as tf
import keras
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential,Model
from tensorflow.keras.layers import Input,Dense,Dropout,Activation,Flatten,Conv2D,Conv2DTranspose,BatchNormalization,LeakyReLU
from tensorflow.keras.optimizers import Adam,RMSprop
import numpy as np
import matplotlib.pyplot as plt

然后同样是导入数据,并预处理

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(60000,784)
x_test = x_test.reshape(10000,784)
x_train = x_train.astype('float32')/255
x_test = x_test.astype('float32')/255

设置噪声维度,这里使用的噪声维度与上面不同,我在之前学习自编码器的时候发现数据集中使用4*4*8的维度对手写数字图片的还原程度挺好的,所以在这里也设置成这个维度。

z_dim = (4,4,8)

设置优化器,同上

adam = Adam(lr=0.0002,beta_1=0.5)

搭建生成器模型

g = Sequential()
g.add(Conv2DTranspose(8,(5,5),activation='relu',input_shape=z_dim))
g.add(BatchNormalization())
g.add(Conv2DTranspose(8,(5,5),activation='relu',strides=2,padding='same'))
g.add(BatchNormalization())
g.add(Conv2DTranspose(32,(5,5),activation='relu'))
g.add(BatchNormalization())
g.add(Conv2DTranspose(64,(5,5),activation='relu'))
g.add(Conv2DTranspose(1,(5,5),activation='tanh'))
g.compile(loss='binary_crossentropy',optimizer=adam,metrics=['accuracy'])
g.summary()

:这是G模型的结构
在这里插入图片描述
搭建鉴别器

d = Sequential()
d.add(Conv2D(32, (5,5),input_shape=(28, 28, 1)))
d.add(LeakyReLU(alpha=0.2))
d.add(BatchNormalization())

d.add(Conv2D(64, (5,5)))
d.add(LeakyReLU(alpha=0.2))

d.add(Flatten())

d.add(Dense(128))
d.add(LeakyReLU(alpha=0.2))

d.add(BatchNormalization())
d.add(Dense(1, activation='softmax'))
d.compile(loss='categorical_crossentropy', optimizer=adam, metrics = ['accuracy'])#定义损失值、优化器
d.trainable=False
d.summary()

:这是鉴别器的结构
在这里插入图片描述
同样,组合两模型成生成对抗网络

inputs = Input(shape=(z_dim[0],z_dim[1],z_dim[2],))
hidden = g(inputs)
output = d(hidden)
gan = Model(inputs,output)
gan.compile(loss='binary_crossentropy',optimizer=adam,metrics=['accuracy'])
gan.summary()

两个函数,绘制损失值和生成器图像

def plot_loss(losses):
    d_loss = [v[0] for v in losses["D"]]
    g_loss = [v[0] for v in losses["G"]]

    plt.figure(figsize=(10,8))
    plt.plot(d_loss,label="Discriminator_loss")
    plt.plot(g_loss,label="Generator_loss")
    plt.legend()
    plt.show()

def plot_generatored(n_ex=10,dim=(1,10),figsize=(12,2)):

    noise = np.random.normal(0,1,size=(n_ex,z_dim[0],z_dim[1],z_dim[2]))
    generatored_images = g.predict(noise)
    generatored_images = generatored_images.reshape(n_ex,28,28)

    plt.figure(figsize = figsize)
    for i in range(generatored_images.shape[0]):
        plt.subplot(dim[0],dim[1],i+1)
        plt.imshow(generatored_images[i],interpolation='nearest',cmap='gray_r')
        plt.axis('off')
    plt.tight_layout()
    plt.show()

设置一个字典,同上

losses = {"D":[],"G":[]}

train函数,同上

def train(epochs=1,plt_frq=1,BATCH_SIZE=128):
    batchCount = int(x_train.shape[0]/BATCH_SIZE)
    print("Epochs:",epochs)
    print("Batch size:",BATCH_SIZE)
    print("Batches per epoch:",batchCount)

    for e in range(1,epochs+1):
        if e == 1 or e%plt_frq == 0:
            print('-'*15,'Epoch %d' %e,'-'*15)
        for _ in range(batchCount):
            image_batch = x_train[np.random.randint(0,x_train.shape[0],size=BATCH_SIZE)]
            noise = np.random.normal(0,1,size=(BATCH_SIZE,z_dim[0],z_dim[1],z_dim[2]))
            generatored_images = g.predict(noise)
            #train d
            #set data set which is composed of 2 parts
            x = np.concatenate((np.reshape(image_batch,(-1,28,28,1)), generatored_images))
            #y are labels
            y = np.zeros(2*BATCH_SIZE)
            y[:BATCH_SIZE] = 0.9

            d.trainable = True
            d_loss = d.train_on_batch(x,y)

            #train g
            #set up data set
            noise = np.random.normal(0,1,size=(BATCH_SIZE,z_dim[0],z_dim[1],z_dim[2]))
            y2 = np.ones(BATCH_SIZE)
            d.trainable = False
            g_loss = gan.train_on_batch(noise,y2)
        losses["D"].append(d_loss)
        losses["G"].append(g_loss)
        if e==1 or e%plt_frq==0:
            plot_generatored()
    plot_loss(losses)

然后就可以开始训练了

train(100,10,128)

结果展示

这是第一轮生成的图像:
在这里插入图片描述
挺乱的,看不出啥,可能是我的网络模型设置的不好。
10轮之后
在这里插入图片描述
现在稍微能看出点啥了,但还是很乱

100轮
在这里插入图片描述
这下基本能够看出点东西了,9、7、6、5都有比较强的辨识度了。

总结

我最开始训练DCGAN的时候,直接使用了平常常用的卷积神经网络,包括卷积池化等等,生成器也是用的上采样而不是转置卷积。那时候电脑不仅训练时间非常长容易崩溃,而且结果非常的不理想。
所有代码已经发布在和鲸社区点击直达,可以在上面直接运行。
写博客的时候参考了很多大佬的博客以及GAN和DCGAN的原始论文部分贴在下面:
博客里的图片也来自于此。
详解生成对抗网络(GAN)
GAN生成对抗网络
论文:
GAN
DCGAN

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
像素循环神经网络 Pixel recurrent neural networks (2016) 作者 A. Oord et al. 训练GANs的改善性技巧 Improved techniques for training GANs (2016) 作者T. Salimans et al. 摘要:近年来,利用卷积网络(CNN)的监督学习已经在计算机视觉应用中被广泛采用。 相比之下,使用CNN的无监督学习得到的关注较少。 在这项工作中,我们希望帮助弥合CNN的监督学习和无监督学习的成功之间的差距。 我们引入一类称为深层卷积生成对抗网络DCGAN)的CNN,它们具有某些架构约束,已显示出它们是无监督学习的强有力的候选者。 对各种图像数据集的训练,我们展示了令人信服的证据,表明我们的深层卷积对抗组件从发生器和鉴别器中的对象到场景里面都学习了表征层次。此外,我们使用学习到的特性去完成新任务 – 这显示了它们像一般图像表征一样具有适用性。 使用深度卷积生成对抗网络进行无监督表征学习 Unsupervised representation learning with deep convolutional generative adversarial networks (2015) 作者A. Radford et al. DRAW:一个用于图像生成的循环神经网络 DRAW: A recurrent neural network for image generation (2015) 作者K. Gregor et al. 生成对抗网络 Generative adversarial nets (2014) 作者I. Goodfellow et al. 自编码变量贝叶斯 Auto-encoding variational Bayes (2013) 作者D. Kingma and M. Welling 用大规模无监督学习构建高水平特征 Building high-level features using large scale unsupervised learning (2013) 作者Q. Le et al.

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值