数据科学

【翻译】使用TensorFlow实现GAN

2017-06-10  本文已影响1516人  格列佛

本文翻译自这个博客,已获得作者授权,翻译的不好还请指教。

生成对抗网络(Generative Adversarial Nets)是一种非常流行的神经网络。它首先由Ian Goodfellow在2014年NIPS大会上发表。它点燃了神经网络中对抗学习的兴趣,这可以从论文被引用的次数中证明。一时之间,冒出来许多不同的GAN:DCGAN,Sequence-GAN,LSTM-GAN等。在NIPS 2016,甚至将会有一整场的对抗学习讨论会。

现在代码可以从https://github.com/wiseodd/generative-models得到。

首先让我们回顾一下论文的要点。之后我们会用TensorFlow来实现GAN,数据集是MNIST。

Generative Adversarial Net

让我们举一个假币制造商和警察的例子。假币制造商和警察各自的目标是什么呢?

现在就产生了冲突。这种情况可以认为是博弈论中的最大最小游戏。这个过程称为对抗过程。

GAN是对抗过程的一个特例,它的组成(警察和假币制造商)是神经网络。第一个网络试图生成数据,第二个网络试图分辨出真实数据和第一个网络生成的伪造数据。第二个网络会输出表示真实数据概率的张量[0,1]。

在GAN中,第一个网络称为生成器G(Z),第二个网络称为判别器D(X)。


在平衡点,也就是最大最小游戏中的最优点,第一个网络会生成真实数据,第二个网络输出的概率会是0.5,因为第一个网络生成了真实数据。

我们不禁会想“为什么要训练GAN呢?”,这是因为数据分布P_{data}可能非常复杂,很难推断。所以使用对抗网络可以从布P_{data}中生成样本而不用处理讨厌的概率分布问题。

GAN实现

根据GAN的定义我们需要两个网络。可以是任意的网络,比如卷积网络或仅仅是两层的感知器网络。首先我们使用简单的两层感知器网络。

# 判别器
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]

# 生成器
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维的矢量,输出一个784维的矢量(MNIST图片大小(28x28))。

discriminator(x)接受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))。

接下来我们来训练网络。

# 只更新 D(X)的参数, 所以 var_list = theta_D
D_solver = tf.train.AdamOptimizer().minimize(D_loss, var_list=theta_D)
# 只更新 G(X)的参数, 所以 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)开始越来越趋近P_{data}。

替代的损失函数

我们可以使用不同的方法来表示D_loss和G_loss。
让我们跟随自己的直觉。这个方法根据Brandon Amos’ blog.

让我们想一想,discriminator(x)试图将所有的输出变为1,也就是我们想最大化真实数据的概率。而discriminator(G_sample)试图将所有的输出变为0,即D(G(Z))希望最小化伪造数据的概率。

那么generator(z)呢?它当然想最大化伪造数据的概率!它与D(G(Z))正相反!

因此,代码可以写成:

# 另外的损失:
# -------------------
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的训练。

参考

Goodfellow, Ian, et al. “Generative adversarial nets.” Advances in Neural Information Processing Systems. 2014.

Image Completion with Deep Learning in TensorFlow

上一篇下一篇

猜你喜欢

热点阅读