GAN实现(TensorFlow,MNIST数据集)

Generative Adversarial Nets in TensorFlow

生成对抗网络(简称GAN)是一种非常流行的神经网络。它由Ian Goodfellow等人在2014年NIPS论文中首次引入。这篇论文引发了对神经网络对抗训练的研究热潮。突然之间,GAN的许多改进都出现了:DCGAN,Sequence-GAN,LSTM-GAN等等。在2016年NIPS会议上,甚至有一整个专门针对GAN的研讨会!

请注意,该代码可在https://github.com/wiseodd/generative-models中找到。

首先,让我们回顾一下这篇论文的要点。之后,我们将一如既往地尝试使用TensorFlow和MNIST数据来实现GAN。

Generative Adversarial Nets

让我们举一个“造假币的罪犯”和“警察”之间的玫瑰关系的例子。在假币方面,罪犯的目标是什么?警察的目标是什么?我们列举一下:

  • 为了成为一个成功的伪造犯,罪犯想欺骗警察,这样警察就不能分辨出假币与真钱的区别
  • 为了成为正义的典范,警察希望尽可能地发现假币

在那里,我们看到产生了冲突。博弈论中的这种情况可以模拟为一个极小化极大游戏(minimax game)。这个过程被称为对抗过程。

生成对抗网(GAN)是对抗过程的一个特例,其组成部分(警察和罪犯)是神经网络。第一个网络生成数据,第二个网络试图分辨真实数据和第一个网络生成的假数据之间的差异。第二个网络将输出[0, 1],表示实际数据概率的标量。

在GAN中,第一个网络被称为发生器网络G(Z)而第二个网络叫做辨别器网络D(X)。

在这里插入图片描述

在最小极大值的平衡点上,第一个网络将产生近似真实的数据,第二个网络将输出0.5的概率,作为第一个网络产生的数据=真实数据的输出。

“为什么我们对GAN的感兴趣?这是因为数据Pdata的概率分布可能是一个非常复杂的分布,非常难以推断。因此,有一个“生成器”可以直接生成样本Pd而不必处理讨厌的概率分布是非常好的。如果我们有这个,那么我们就可以将它用于另一个需要从Pdata中抽样的过程,因为我们可以使用训练有素的“生成器”相对便宜地获取样本。

##GAN Implementation##

根据GAN的定义,我们需要两个网络。(不管是像convnet一样复杂的网络还是只是一个两层的神经网络),先让我们变得简单,使用两层的网络。我们将使用TensorFlow来达到这个目的。

# Discriminator Net
X = tf.placeholder(tf.float32, shape=[None, 784], name='X')

D_W1 = tf.Variable(xavier_init([784, 128]), name='D_W1')
D_b1 = tf.Variable(tf.zeros(shape=[128]), name='D_b1')

D_W2 = tf.Variable(xavier_init([128, 1]), name='D_W2')
D_b2 = tf.Variable(tf.zeros(shape=[1]), name='D_b2')

theta_D = [D_W1, D_W2, D_b1, D_b2]

# Generator Net
Z = tf.placeholder(tf.float32, shape=[None, 100], name='Z')

G_W1 = tf.Variable(xavier_init([100, 128]), name='G_W1')
G_b1 = tf.Variable(tf.zeros(shape=[128]), name='G_b1')

G_W2 = tf.Variable(xavier_init([128, 784]), name='G_W2')
G_b2 = tf.Variable(tf.zeros(shape=[784]), name='G_b2')

theta_G = [G_W1, G_W2, G_b1, G_b2]


def generator(z):
    G_h1 = tf.nn.relu(tf.matmul(z, G_W1) + G_b1)
    G_log_prob = tf.matmul(G_h1, G_W2) + G_b2
    G_prob = tf.nn.sigmoid(G_log_prob)

    return G_prob


def discriminator(x):
    D_h1 = tf.nn.relu(tf.matmul(x, D_W1) + D_b1)
    D_logit = tf.matmul(D_h1, D_W2) + D_b2
    D_prob = tf.nn.sigmoid(D_logit)

    return D_prob, D_logit

在上面的代码中,generator(z)输入100维矢量并返回786维矢量,即MNIST图像(28x28)。z表示G(Z)。从某种意义上来说,它学习了先验空间与Pdata之间的映射关系。

discriminator(x)输入MNIST图像,并返回表示真实MNIST图像的概率的标量。

现在,我们定义训练这个GAN的“对抗进程”。以下是论文中的训练算法:

在这里插入图片描述

G_sample = generator(Z)
D_real, D_logit_real = discriminator(X)
D_fake, D_logit_fake = discriminator(G_sample)

D_loss = -tf.reduce_mean(tf.log(D_real) + tf.log(1. - D_fake))
G_loss = -tf.reduce_mean(tf.log(D_fake))

在上面,我们加了负号作为损失函数。因为它们需要最大化,而TensorFlow的优化器只能做到最小化。

另外,根据论文的建议,在上述算法中,最好是最大化tf.reduce_mean(tf.log(D_fake))而不是最小化tf.reduce_mean(1 - tf.log(D_fake))。

然后,我们用以上那些损失函数来一个接一个地训练网络。

# Only update D(X)'s parameters, so var_list = theta_D
D_solver = tf.train.AdamOptimizer().minimize(D_loss, var_list=theta_D)
# Only update G(X)'s parameters, so var_list = theta_G
G_solver = tf.train.AdamOptimizer().minimize(G_loss, var_list=theta_G)

def sample_Z(m, n):
    '''Uniform prior for G(Z)'''
    return np.random.uniform(-1., 1., size=[m, n])

for it in range(1000000):
    X_mb, _ = mnist.train.next_batch(mb_size)

    _, D_loss_curr = sess.run([D_solver, D_loss], feed_dict={X: X_mb, Z: sample_Z(mb_size, Z_dim)})
    _, G_loss_curr = sess.run([G_solver, G_loss], feed_dict={Z: sample_Z(mb_size, Z_dim)})

我们完成了!我们可以通过每隔一段时间地抽样G(Z)来看训练过程

我们从随机噪声开始,随着训练的进行,G(Z)开始越来越接近Pdata,这表明G(Z)生成的样本与MNIST数据越来越相似。

Alternative Loss Formulation

我们还可以使用另一种方法定义损失函数D_loss和G_loss。

这是受到Brandon Amos博客中关于图像补全的文章的启发。
http://bamos.github.io/2016/08/09/deep-completion/

对于鉴别器discriminator():
根据定义,希望discriminator(X)使所有输出为1,因为我们希望输入实际数据后,最大化模型输出的概率。
根据定义,希望discriminator(G_sample)使所有的输出为0,因为我们希望输入虚假数据后,最小化模型输出的概率。
那么generator(Z)呢?它想要最大化假数据的概率!这是恰恰是D(G(Z))的相反目标!

因此,我们可以按照以下方式来制定损失。

# Alternative losses:
# -------------------
D_loss_real = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(D_logit_real, tf.ones_like(D_logit_real)))
D_loss_fake = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(D_logit_fake, tf.zeros_like(D_logit_fake)))
D_loss = D_loss_real + D_loss_fake
G_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(D_logit_fake, tf.ones_like(D_logit_fake)))

根据上述的想法,我们使用Logistic Loss。改变损失函数不会影响我们训练GAN,因为这只是思考问题的另一种方式。

Conclusion

在这篇文章中,我们研究了由Ian Goodfellow等人发表的“生成对抗网络”(GAN)。我们探讨了对抗过程的表述以及背后的直觉。

接下来,我们为生成器和鉴别器实现了GAN的2层神经网络。然后,我们遵循Goodfellow等人在2014年提出的算法来训练GAN。

最后,我们展示了另一种思考GAN损失函数的方法。我们用直觉思考这两个网络,并使用Logistic Loss作为损失函数。

完整的代码,请访问https://github.com/wiseodd/generative-models!

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import os

# xavier初始化
# 如果使用small random numbers,深层网络由于反复累乘会出现All activations become zero
# 如果初始值稍微大一点,会几乎所有的值集中在-1或1附近,神经元saturated
# 注意到tanh在-1和1附近的gradient都接近0,这同样导致了gradient太小,参数难以被更新。
def xavier_init(size):
    in_dim = size[0]
    xavier_stddev = 1. / tf.sqrt(in_dim / 2.)
    return tf.random_normal(shape=size, stddev=xavier_stddev)

# MNIST数据为28 x 28的灰度图
X = tf.placeholder(tf.float32, shape=[None, 784])

# discriminator的参数
# 第一层网络128个神经元
D_W1 = tf.Variable(xavier_init([784, 128]))
D_b1 = tf.Variable(tf.zeros(shape=[128]))

D_W2 = tf.Variable(xavier_init([128, 1]))
D_b2 = tf.Variable(tf.zeros(shape=[1]))

theta_D = [D_W1, D_W2, D_b1, D_b2]

# Z为generator最初的100个随机数
Z = tf.placeholder(tf.float32, shape=[None, 100])

# generator的参数
# 第一层网络128个神经元
G_W1 = tf.Variable(xavier_init([100, 128]))
G_b1 = tf.Variable(tf.zeros(shape=[128]))

G_W2 = tf.Variable(xavier_init([128, 784]))
G_b2 = tf.Variable(tf.zeros(shape=[784]))

theta_G = [G_W1, G_W2, G_b1, G_b2]

# 对generator生成[-1,1]均匀分布的随机数
def sample_Z(m, n):
    return np.random.uniform(-1., 1., size=[m, n])

# generator网络
def generator(z):
    G_h1 = tf.nn.relu(tf.matmul(z, G_W1) + G_b1)
    G_log_prob = tf.matmul(G_h1, G_W2) + G_b2
    G_prob = tf.nn.sigmoid(G_log_prob)

    return G_prob

# discriminator网络
def discriminator(x):
    D_h1 = tf.nn.relu(tf.matmul(x, D_W1) + D_b1)
    D_logit = tf.matmul(D_h1, D_W2) + D_b2
    D_prob = tf.nn.sigmoid(D_logit)

    return D_prob, D_logit

# 绘制“生成样本”的图
def plot(samples):
    fig = plt.figure(figsize=(4, 4))
    gs = gridspec.GridSpec(4, 4)
    gs.update(wspace=0.05, hspace=0.05)

    for i, sample in enumerate(samples):
        ax = plt.subplot(gs[i])
        #图片坐标轴的一些画法调整
        plt.axis('off')
        ax.set_xticklabels([])
        ax.set_yticklabels([])
        ax.set_aspect('equal')
        plt.imshow(sample.reshape(28, 28), cmap='Greys_r')

    return fig


G_sample = generator(Z)
D_real, D_logit_real = discriminator(X)
D_fake, D_logit_fake = discriminator(G_sample)

# D_loss = -tf.reduce_mean(tf.log(D_real) + tf.log(1. - D_fake))
# G_loss = -tf.reduce_mean(tf.log(D_fake))

# Alternative losses:
# -------------------
# discriminator希望把所有real预测为1,把所有fake预测为0
# generator希望把所有fake预测为1
D_loss_real = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=D_logit_real, labels=tf.ones_like(D_logit_real)))
D_loss_fake = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=D_logit_fake, labels=tf.zeros_like(D_logit_fake)))
D_loss = D_loss_real + D_loss_fake
G_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=D_logit_fake, labels=tf.ones_like(D_logit_fake)))

D_solver = tf.train.AdamOptimizer().minimize(D_loss, var_list=theta_D)
G_solver = tf.train.AdamOptimizer().minimize(G_loss, var_list=theta_G)

mb_size = 128
Z_dim = 100

mnist = input_data.read_data_sets('./MNIST_data', one_hot=True)

sess = tf.Session()
sess.run(tf.global_variables_initializer())

if not os.path.exists('out/'):
    os.makedirs('out/')

i = 0

for it in range(1000000):
    #每1000步输出一次
    if it % 1000 == 0:
        #使用当前的generator生成图片
        samples = sess.run(G_sample, feed_dict={Z: sample_Z(16, Z_dim)})

        fig = plot(samples)
        plt.savefig('out/{}.png'.format(str(i).zfill(3)), bbox_inches='tight')
        i += 1
        plt.close(fig)
    #取出mb_size个的样本和标签
    X_mb, _ = mnist.train.next_batch(mb_size)

    _, D_loss_curr = sess.run([D_solver, D_loss], feed_dict={X: X_mb, Z: sample_Z(mb_size, Z_dim)})
    _, G_loss_curr = sess.run([G_solver, G_loss], feed_dict={Z: sample_Z(mb_size, Z_dim)})

    if it % 1000 == 0:
        print('Iter: {}'.format(it))
        print('D loss: {:.4}'. format(D_loss_curr))
        print('G_loss: {:.4}'.format(G_loss_curr))
        print()
  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值