一,GAN的原理介绍

\quad GAN的基本原理其实非常简单,这里以生成图片为例进行说明。假设我们有两个网络,G(Generator)和D(Discriminator)。正如它的名字所暗示的那样,它们的功能分别是:
\bullet G是一个生成图片的网络,它接收一个随机的噪声z,通过这个噪声生成图片,记做G(z)。
\bullet D是一个判别网络,判别一张图片是不是“真实的”。它的输入参数是x,x代表一张图片,输出D(x)代表x为真实图片的概率,如果为1,就代表100%是真实的图片,而输出为0,就代表不可能是真实的图片。
\quad 在训练过程中**,生成网络G的目标就是尽量生成真实的图片去欺骗判别网络D。而D的目标就是尽量把G生成的图片和真实的图片分别开来**。这样,G和D构成了一个动态的“博弈过程”。
\quad 最后博弈的结果是什么?在最理想的状态下,G可以生成足以“以假乱真”的图片G(z)。对于D来说,它难以判定G生成的图片究竟是不是真实的,因此D(G(z)) = 0.5。
\quad 这样我们的目的就达成了:我们得到了一个生成式的模型G,它可以用来生成图片。
\quad 以上只是大致说了一下GAN的核心原理,如何用数学语言描述呢?这里直接摘录论文里的公式:

简单分析一下这个公式:
\bullet 整个式子由两项构成。x表示真实图片,z表示输入G网络的噪声,而G(z)表示G网络生成的图片。
\bullet D(x)表示D网络判断真实图片是否真实的概率(因为x就是真实的,所以对于D来说,这个值越接近1越好)。而D(G(z))是D网络判断G生成的图片的是否真实的概率
\bullet G的目的:上面提到过,D(G(z))是D网络判断G生成的图片是否真实的概率,G应该希望自己生成的图片“越接近真实越好”。也就是说,G希望D(G(z))尽可能得大,这时V(D, G)会变小。因此我们看到式子的最前面的记号是min_G
\bullet D的目的:D的能力越强,D(x)应该越大,D(G(x))应该越小。这时V(D,G)会变大。因此式子对于D来说是求最大(max_D)

下面这幅图片很好地描述了这个过程:

那么如何用随机梯度下降法训练D和G?论文中也给出了算法:

这里红框圈出的部分是我们要额外注意的。第一步我们训练D,D是希望V(G,`D)越大越好,所以是加上梯度(ascending)。第二步训练G时,V(G, D)越小越好,所以是减去梯度(descending)。整个训练过程交替进行。
\quad 这个直接看代码就有很好的体现了。

二,DCGAN的原理介绍

\quad 我们知道深度学习中对图像处理应用最好的模型是CNN,那么如何把CNN与GAN结合?DCGAN是这方面最好的尝试之一,文章最后会附上论文地址:
\quad DCGAN的原理和GAN是一样的,这里就不在赘述。它只是把上述的G和D换成了两个卷积神经网络(CNN)。但不是直接换就可以了,DCGAN对卷积神经网络的结构做了一些改变,以提高样本的质量和收敛的速度,这些改变有:
\bullet 取消所有pooling层。G网络中使用转置卷积(transposed convolutional layer)进行上采样,D网络中用加入stride的卷积代替pooling。
\bullet 在D和G中均使用batch normalization
\bullet 去掉FC层,使网络变为全卷积网络
\bullet G网络中使用ReLU作为激活函数,最后一层使用tanh
\bullet D网络中使用LeakyReLU作为激活函数
\quad DCGAN中的G网络示意:

\quad 直接利用1个全连接层实现的GAN如下,补充:一开始的时候生成出来的图像中含有负数以至于无法显示,解决方案是在generate的输出激活函数中不要使用tanh,改为使用sigmoid即可。

\quad 训练的过程如下:

训练完成之后生成了20张随机产生的图片,是不是足够以假乱真啦。

#coding=utf-8
import tensorflow as tf
import tflearn
import tflearn.datasets.mnist as mnist
import matplotlib.pyplot as plt
import numpy as np
X, Y, X_test, Y_test = mnist.load_data()
img_dim = 784
z_dim = 200
total_sample = len(X)

#构建生成器和判别器
def generate(x, reuse=tf.AUTO_REUSE):
    with tf.variable_scope('Generate', reuse=reuse):
        x = tflearn.fully_connected(x,256,activation='relu')
        x = tflearn.fully_connected(x,img_dim,activation='sigmoid')
        return x
def discriminator(x, reuse=tf.AUTO_REUSE):
    with tf.variable_scope('Discriminator', reuse=reuse):
        x = tflearn.fully_connected(x, 256, activation='relu')
        x = tflearn.fully_connected(x, 1, activation='sigmoid')
    return x

#构建网络

gen_input = tflearn.input_data(shape=[None,z_dim], name='input_noise')
disc_input = tflearn.input_data(shape=[None,784], name='disc_input')

#生成器,判别器
gen_sample = generate(gen_input)
disc_real = discriminator(disc_input) #判别网络
disc_fake = discriminator(gen_sample) #欺骗网络D

disc_loss = -tf.reduce_mean(tf.log(disc_real)+tf.log(1. -disc_fake))
gen_loss = -tf.reduce_mean(tf.log(disc_fake))

gen_vars = tflearn.get_layer_variables_by_scope('Generate')
gen_model = tflearn.regression(gen_sample, placeholder=None, optimizer='adam', loss=gen_loss, trainable_vars=gen_vars,batch_size=64,name='target_gen',op_name='GEN')

disc_vars = tflearn.get_layer_variables_by_scope('Discriminator')
disc_model = tflearn.regression(disc_real,placeholder=None,optimizer='adam',loss=disc_loss,trainable_vars=disc_vars,batch_size=64,name='target_disc',op_name='DISC')

gan = tflearn.DNN(gen_model)

#训练并绘制图像
z = np.random.uniform(-1.,1.,[total_sample,z_dim])
gan.fit(X_inputs={gen_input: z,disc_input: X},Y_targets=None,n_epoch=200)

f, a = plt.subplots(2,10,figsize=(10,4))
for i in range(10):
    for j in range(2):
        #Noise input
        z = np.random.uniform(-1.,1.,size=[1,z_dim])
        #Generate image from noise. Extend to 3 channels for matplot figure.
        temp = [[temp,temp,temp] for temp in list(gan.predict([z])[0])]
        print(temp)
        a[j][i].imshow(np.reshape(temp,(28,28,3)))
f.show()
plt.show()

代码2:https://github.com/wiseodd/generative-models

GAN的TF实现

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


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)


X = tf.placeholder(tf.float32, shape=[None, 784])

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 = tf.placeholder(tf.float32, shape=[None, 100])

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]


def sample_Z(m, n):
    return np.random.uniform(-1., 1., size=[m, n])


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


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:
# -------------------
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):
    if it % 1000 == 0:
        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)

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

DCGAN待续:

参考链接:
https://zhuanlan.zhihu.com/p/24767059
http://blog.csdn.net/twt520ly/article/details/79420597
https://zhuanlan.zhihu.com/p/27295635
https://wiseodd.github.io/techblog/2016/09/17/gan-tensorflow/

维护了一个微信公众号,分享论文,算法,比赛,生活,欢迎加入。