专栏首页程序生活GAN对抗网络入门教程

GAN对抗网络入门教程

译:A Beginner's Guide to Generative Adversarial Networks (GANs) https://skymind.ai/wiki/generative-adversarial-network-gan

1 GAN简介

生成对抗网络(英语:Generative Adversarial Network,简称GAN)是非监督式学习的一种方法,通过让两个神经网络相互博弈的方式进行学习。该方法由伊恩·古德费洛等人于2014年提出。生成对抗网络由一个生成网络与一个判别网络组成。生成网络从潜在空间(latent space)中随机取样作为输入,其输出结果需要尽量模仿训练集中的真实样本。判别网络的输入则为真实样本或生成网络的输出,其目的是将生成网络的输出从真实样本中尽可能分辨出来。而生成网络则要尽可能地欺骗判别网络。两个网络相互对抗、不断调整参数,最终目的是使判别网络无法判断生成网络的输出结果是否真实。

生成对抗网络常用于生成以假乱真的图片。此外,该方法还被用于生成影片、三维物体模型等。

虽然生成对抗网络原先是为了无监督学习提出的,它也被证明对半监督学习、完全监督学习 、强化学习是有用的。

image

2 生成与判别算法

要理解GAN,你应该知道生成算法是如何工作的,但是在理解生成算法之前,将它们与判别算法进行对比可以加深理解。我们先看下什么事判别算法?

判别算法试图对输入数据进行分类; 也就是说,给定数据实例的特征,它们预测该数据所属的标签或类别。

例如,给定电子邮件中的所有单词(数据实例),判别算法可以预测该消息是spam(垃圾邮件)还是not_spam(非垃圾邮件)。 其中spam是标签之一,从电子邮件收集的单词包是构成输入数据的特征。 当以数学方式表达此问题时,标签称为y,并且要素称为x。公式p(y|x)用于表示“给定x条件下y发生的概率”,在这种情况下,它将转换为“在给定邮件所包含的字词情况下,电子邮件是垃圾邮件的概率”。

因此,判别算法是将特征映射到标签,而生成算法恰恰在做相反的事情。生成算法试图预测给定某个标签下的特征,而不是预测给定某些特征的标签。

生成算法试图回答的问题是:假设这封电子邮件是垃圾邮件,特征的分布或者概率是怎么样的? 虽然判别模型关注y和x之间的关系,但是生成模型关心“你如何得到x。”生成算法是为了计算出(x | y),给出y条件下x发生的概率,或者说给出标签时,特征的概率。 (也就是说,生成算法也可以用作分类器。恰好它们不是对输入数据进行分类。)

下面两句话将判别与生成区分开来:

  • 判别模型学习了类之间的界限
  • 生成模型模拟各个类的分布

3 GANs原理

GAN的基本原理其实非常简单,这里以生成图片为例进行说明。假设我们有两个网络,G(Generator)和D(Discriminator)。正如它的名字所暗示的那样,它们的功能分别是:一个神经网络,称为生成器,生成新的数据实例,而另一个神经网络,判别器,评估它们的真实性; 即判别器决定它所评测的每个数据实例是否属于实际训练数据集。

G是一个生成图片的网络,它接收一个随机的噪声z,通过这个噪声生成图片,记做G(z)。 D是一个判别网络,判别一张图片是不是“真实的”。它的输入参数是x,x代表一张图片,输出D(x)代表x为真实图片的概率,如果为1,就代表100%是真实的图片,而输出为0,就代表不可能是真实的图片。 在训练过程中,生成网络G的目标就是尽量生成真实的图片去欺骗判别网络D。而D的目标就是尽量把G生成的图片和真实的图片分别开来。这样,G和D构成了一个动态的“博弈过程”。

最后博弈的结果是什么?在最理想的状态下,G可以生成足以“以假乱真”的图片G(z)。对于D来说,它难以判定G生成的图片究竟是不是真实的,因此D(G(z)) = 0.5。

reference:https://zhuanlan.zhihu.com/p/24767059

以下是GAN大致步骤:

  • 生成器接收随机数并返回图像。
  • 将生成的图像与从真实数据集中获取的图像流一起馈送到判别器中。
  • 判别器接收真实和假图像并返回概率,0到1之间的数字,1表示真实性的预测,0表示假。

image

您可以将GAN视为诈骗者和警察在猫与老鼠游戏中的反对,其中诈骗者正在学习传递虚假信息,并且警察正在学习如何检测它们。 两者都是动态的; 也就是说,警察也在接受培训,每一方都在不断升级中学习对方的方法。

对于MNIST数据集,判别器网络是标准卷积网络,可以对馈送给它的图像进行分类,二项分类器将图像标记为真实或伪造。 在某种意义上,生成器是反卷积网络:当标准卷积分类器采用图像并对其进行下采样以产生概率时,生成器采用随机噪声矢量并将其上采样到图像。 第一个通过下采样技术(如maxpooling)丢弃数据,第二个生成新数据。

image

4 GANs, Autoencoders and VAEs

下面对生成性对抗网络与其他神经网络(例如自动编码器和变分自动编码器)进行比较。

自动编码器将输入数据编码为矢量。它们创建原始数据的隐藏或压缩表示,在减少维数方面很有用; 也就是说,用作隐藏表示的向量将原始数据压缩为较少数量的突出维度。 自动编码器可以与所谓的解码器配对,允许您根据其隐藏的表示重建输入数据,就像使用受限制的Boltzmann机器一样。

image

变分自动编码器是生成算法,其为编码输入数据添加额外约束,即隐藏表示被标准化。 变分自动编码器能够像自动编码器一样压缩数据并像GAN一样合成数据。 然而GAN可以更精细、细粒度的生成数据,VAE生成的图像往往更加模糊。 Deeplearning4j的例子包括自动编码器和变分自动编码器。(https://github.com/deeplearning4j/dl4j-examples/tree/master/dl4j-examples/src/main/java/org/deeplearning4j/examples/unsupervised

5 Keras 实现GAN

https://github.com/eriklindernoren/Keras-GAN

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

import matplotlib.pyplot as plt

import sys

import numpy as np
Using TensorFlow backend.
class GAN():
    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)

        optimizer = Adam(0.0002, 0.5)

        # Build and compile the discriminator
        self.discriminator = self.build_discriminator()
        self.discriminator.compile(loss='binary_crossentropy',
            optimizer=optimizer,
            metrics=['accuracy'])

        # Build and compile the generator
        self.generator = self.build_generator()
        self.generator.compile(loss='binary_crossentropy', optimizer=optimizer)

        # The generator takes noise as input and generated imgs
        z = Input(shape=(100,))
        img = self.generator(z)

        # For the combined model we will only train the generator
        self.discriminator.trainable = False

        # The valid takes generated images as input and determines validity
        valid = self.discriminator(img)

        # The combined model  (stacked generator and discriminator) takes
        # noise as input => generates images => determines validity
        self.combined = Model(z, valid)
        self.combined.compile(loss='binary_crossentropy', optimizer=optimizer)

    def build_generator(self):

        noise_shape = (100,)

        model = Sequential()

        model.add(Dense(256, input_shape=noise_shape))
        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))

        model.summary()

        noise = Input(shape=noise_shape)
        img = model(noise)

        return Model(noise, img)

    def build_discriminator(self):

        img_shape = (self.img_rows, self.img_cols, self.channels)

        model = Sequential()

        model.add(Flatten(input_shape=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'))
        model.summary()

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

        return Model(img, validity)

    def train(self, epochs, batch_size=128, save_interval=50):

        # Load the dataset
        (X_train, _), (_, _) = mnist.load_data()

        # Rescale -1 to 1
        X_train = (X_train.astype(np.float32) - 127.5) / 127.5
        X_train = np.expand_dims(X_train, axis=3)

        half_batch = int(batch_size / 2)

        for epoch in range(epochs):

            # ---------------------
            #  Train Discriminator
            # ---------------------

            # Select a random half batch of images
            idx = np.random.randint(0, X_train.shape[0], half_batch)
            imgs = X_train[idx]

            noise = np.random.normal(0, 1, (half_batch, 100))

            # Generate a half batch of new images
            gen_imgs = self.generator.predict(noise)

            # Train the discriminator
            d_loss_real = self.discriminator.train_on_batch(imgs, np.ones((half_batch, 1)))
            d_loss_fake = self.discriminator.train_on_batch(gen_imgs, np.zeros((half_batch, 1)))
            d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)


            # ---------------------
            #  Train Generator
            # ---------------------

            noise = np.random.normal(0, 1, (batch_size, 100))

            # The generator wants the discriminator to label the generated samples
            # as valid (ones)
            valid_y = np.array([1] * batch_size)

            # Train the generator
            g_loss = self.combined.train_on_batch(noise, valid_y)

            # Plot the progress
            if epoch%1000==0:
                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 % save_interval == 0:
                self.save_imgs(epoch)

    def save_imgs(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("data/gan/images/mnist_%d.png" % epoch)
        plt.close()


if __name__ == '__main__':
    gan = GAN()
    gan.train(epochs=30000, batch_size=32, save_interval=200)
WARNING:tensorflow:From D:\ProgramData\Anaconda3\lib\site-packages\keras\backend\tensorflow_backend.py:66: The name tf.get_default_graph is deprecated. Please use tf.compat.v1.get_default_graph instead.

WARNING:tensorflow:From D:\ProgramData\Anaconda3\lib\site-packages\keras\backend\tensorflow_backend.py:541: The name tf.placeholder is deprecated. Please use tf.compat.v1.placeholder instead.

WARNING:tensorflow:From D:\ProgramData\Anaconda3\lib\site-packages\keras\backend\tensorflow_backend.py:4432: The name tf.random_uniform is deprecated. Please use tf.random.uniform instead.

Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
flatten_1 (Flatten)          (None, 784)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 512)               401920    
_________________________________________________________________
leaky_re_lu_1 (LeakyReLU)    (None, 512)               0         
_________________________________________________________________
dense_2 (Dense)              (None, 256)               131328    
_________________________________________________________________
leaky_re_lu_2 (LeakyReLU)    (None, 256)               0         
_________________________________________________________________
dense_3 (Dense)              (None, 1)                 257       
=================================================================
Total params: 533,505
Trainable params: 533,505
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:From D:\ProgramData\Anaconda3\lib\site-packages\keras\optimizers.py:793: The name tf.train.Optimizer is deprecated. Please use tf.compat.v1.train.Optimizer instead.

WARNING:tensorflow:From D:\ProgramData\Anaconda3\lib\site-packages\keras\backend\tensorflow_backend.py:3657: The name tf.log is deprecated. Please use tf.math.log instead.

WARNING:tensorflow:From D:\ProgramData\Anaconda3\lib\site-packages\tensorflow\python\ops\nn_impl.py:180: add_dispatch_support.<locals>.wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where
WARNING:tensorflow:From D:\ProgramData\Anaconda3\lib\site-packages\keras\backend\tensorflow_backend.py:148: The name tf.placeholder_with_default is deprecated. Please use tf.compat.v1.placeholder_with_default instead.

Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_4 (Dense)              (None, 256)               25856     
_________________________________________________________________
leaky_re_lu_3 (LeakyReLU)    (None, 256)               0         
_________________________________________________________________
batch_normalization_1 (Batch (None, 256)               1024      
_________________________________________________________________
dense_5 (Dense)              (None, 512)               131584    
_________________________________________________________________
leaky_re_lu_4 (LeakyReLU)    (None, 512)               0         
_________________________________________________________________
batch_normalization_2 (Batch (None, 512)               2048      
_________________________________________________________________
dense_6 (Dense)              (None, 1024)              525312    
_________________________________________________________________
leaky_re_lu_5 (LeakyReLU)    (None, 1024)              0         
_________________________________________________________________
batch_normalization_3 (Batch (None, 1024)              4096      
_________________________________________________________________
dense_7 (Dense)              (None, 784)               803600    
_________________________________________________________________
reshape_1 (Reshape)          (None, 28, 28, 1)         0         
=================================================================
Total params: 1,493,520
Trainable params: 1,489,936
Non-trainable params: 3,584
_________________________________________________________________


D:\ProgramData\Anaconda3\lib\site-packages\keras\engine\training.py:493: UserWarning: Discrepancy between trainable weights and collected trainable weights, did you set `model.trainable` without calling `model.compile` after ?
  'Discrepancy between trainable weights and collected trainable'


0 [D loss: 0.735185, acc.: 46.88%] [G loss: 0.829077]


D:\ProgramData\Anaconda3\lib\site-packages\keras\engine\training.py:493: UserWarning: Discrepancy between trainable weights and collected trainable weights, did you set `model.trainable` without calling `model.compile` after ?
  'Discrepancy between trainable weights and collected trainable'


1000 [D loss: 0.590758, acc.: 71.88%] [G loss: 0.793450]
2000 [D loss: 0.587990, acc.: 62.50%] [G loss: 0.956186]
3000 [D loss: 0.644352, acc.: 59.38%] [G loss: 0.914777]
4000 [D loss: 0.673936, acc.: 62.50%] [G loss: 0.971460]
5000 [D loss: 0.759974, acc.: 53.12%] [G loss: 0.904706]
6000 [D loss: 0.555306, acc.: 81.25%] [G loss: 0.835633]
7000 [D loss: 0.674409, acc.: 62.50%] [G loss: 0.823623]
8000 [D loss: 0.672854, acc.: 53.12%] [G loss: 0.863680]
9000 [D loss: 0.743683, acc.: 46.88%] [G loss: 0.868321]
10000 [D loss: 0.635190, acc.: 59.38%] [G loss: 0.854181]
11000 [D loss: 0.700397, acc.: 56.25%] [G loss: 0.778778]
12000 [D loss: 0.741978, acc.: 46.88%] [G loss: 0.813542]
13000 [D loss: 0.760614, acc.: 46.88%] [G loss: 0.833507]
14000 [D loss: 0.671199, acc.: 68.75%] [G loss: 0.853395]
15000 [D loss: 0.676217, acc.: 62.50%] [G loss: 0.920993]
16000 [D loss: 0.593898, acc.: 68.75%] [G loss: 0.889001]
17000 [D loss: 0.724363, acc.: 50.00%] [G loss: 0.893431]
18000 [D loss: 0.779740, acc.: 43.75%] [G loss: 0.853765]
19000 [D loss: 0.642237, acc.: 59.38%] [G loss: 0.830348]
20000 [D loss: 0.587237, acc.: 62.50%] [G loss: 0.876839]
21000 [D loss: 0.645381, acc.: 62.50%] [G loss: 0.827465]
22000 [D loss: 0.723597, acc.: 46.88%] [G loss: 0.862281]
23000 [D loss: 0.671319, acc.: 65.62%] [G loss: 0.903444]
24000 [D loss: 0.684801, acc.: 62.50%] [G loss: 0.807403]
25000 [D loss: 0.737355, acc.: 43.75%] [G loss: 0.813877]
26000 [D loss: 0.606201, acc.: 68.75%] [G loss: 0.802509]
27000 [D loss: 0.711020, acc.: 56.25%] [G loss: 0.894887]
28000 [D loss: 0.641023, acc.: 56.25%] [G loss: 0.856079]
29000 [D loss: 0.696889, acc.: 46.88%] [G loss: 0.728626]

可以看到D的判别准确率最终在46%-56%之间,也就是说G网络生成的图片已经真假难分

6 参考资料

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 求一个数n次方后的末尾数(数论/快速幂)问题描述解题思路代码实现运行结果参考

    问题描述 hdu1061-Rightmost Digit hdu1097-A hard puzzle 这两个oj题目思路几乎一样,都是为了快速求出一个数n次...

    致Great
  • 大数据入门与实战-SQL基础教程

    致Great
  • 卡特兰数简介原理性质应用参考:

    简介 卡特兰数又称卡塔兰数,卡特兰数是组合数学中一个常出现在各种计数问题中的数列。 卡塔兰数的一般项公式为: ? 卡特兰公式 其前20项为:1, 1, 2, ...

    致Great
  • 深度学习中常用的损失函数loss有哪些?

    这是专栏《AI初识境》的第11篇文章。所谓初识,就是对相关技术有基本了解,掌握了基本的使用方法。

    小草AI
  • 【AI初识境】深度学习中常用的损失函数有哪些?

    这是专栏《AI初识境》的第11篇文章。所谓初识,就是对相关技术有基本了解,掌握了基本的使用方法。

    用户1508658
  • 目标检测损失函数Loss大盘点

    在one-stage检测算法中,会出现正负样本数量不平衡以及难易样本数量不平衡的情况,为了解决则以问题提出了focal loss。

    Amusi
  • CenterNet之loss计算代码解析

    本文主要讲解CenterNet的loss,由偏置部分(reg loss)、热图部分(heatmap loss)、宽高(wh loss)部分三部分loss组成,附...

    BBuf
  • 二分类语义分割损失函数

    1 - softmax 交叉熵损失函数(softmax loss,softmax with cross entroy loss)

    AIHGF
  • pytorch进行CIFAR-10分类(4)训练

    经过前面的数据加载和网络定义后,就可以开始训练了,这里会看到前面遇到的一些东西究竟在后面会有什么用,所以这一步希望各位也能仔细研究一下

    TeeyoHuang
  • 样本混进了噪声怎么办?通过Loss分布把它们揪出来!

    当训练样本中混有噪音,就很容易让模型过拟合,学习到错误的信息,因此必须加以干涉,来控制噪音带来的影响。这方面的研究,主要集中于“损失修正”方法,即loss co...

    beyondGuo

扫码关注云+社区

领取腾讯云代金券