《智能系统与技术丛书 生成对抗网络入门指南》—3.4GAN的工程实践

举报
华章计算机 发表于 2019/05/30 23:34:24 2019/05/30
【摘要】 本书摘自《智能系统与技术丛书 生成对抗网络入门指南》一文中的第3章,第3.4节,作者是史丹青。

3.4 GAN的工程实践

       之前几节我们了解了生成对抗网络(GAN)的设计原理。但是,实际它是如何实现的呢?在这一节中我们会详细介绍GAN的实践方法以及代码的编写[4]。

       从之前的数学推导中我们知道,我们要做的是优化下面的式子。

image.png(3-36)

       计算公式中的期望值可以等价于计算真实数据分布与生成数据分布的积分,在实践中我们使用采样的方法来逼近期望值。

       首先我们从前置的随机分布pg(z)中取出m个随机数{z(1), z(2), …, z(m)},其次我们再从真实数据分布pdata(x)中取出m个真实样本{x(1), x(2), …, x(m)}我们使用平均数代替上式中的期望,公式改写如下。

image.png(3-37)

       在GAN的原始论文中给出了完整的伪代码,其中θd为判别器D的参数,θg为生成器G的参数。

       该伪代码每次迭代过程中的前半部分为训练判别器的过程,后半部分为训练生成器。对于判别器,我们会训练k次来更新参数θd,在论文的实验中研究者把k设为1,使得实验成本最小。生成器在每次迭代中仅更新一次,如果更新多次可能无法使得生成数据分布与真实数据分布的JS散度距离下降。

       下面我们尝试使用TensorFlow来实现上一节中GAN训练过程的可视化。

       伪代码2 基础GAN的伪代码实现(其中对于判别器会迭代k次,

     k为超参数,大多数情况下可以使k = 1)

       for训练的迭代次数do

     for重复k次do

  从生成器前置随机分布pg(z)取出m个小批次样本z(1), …, z(m);

  从真实数据分布pdata(x)取出m个小批次样本x(1), …, x(m);

  使用随机梯度下降更新判别器参数;

image.png

       end for

     从生成器前置随机分布pg(z)取出m个小批次样本z(1), …, z(m);

     使用随机梯度下降更新生成器参数;

image.png

       end for

       首先需要设置真实数据样本的分布,这里设置均值为3、方差为0.5的高斯分布。

       class DataDistribution(object):

       def __init__(self):

        self.mu = 3

        self.sigma = 0.5


    def sample(self, N):

        samples = np.random.normal(self.mu, self.sigma, N)

        samples.sort()

        return samples

       接着设定生成器的初始化分布,这里设定的是平均分布。

class GeneratorDistribution(object):

    def __init__(self, range):

        self.range = range


    def sample(self, N):

        return np.linspace(-self.range, self.range, N) + \

            np.random.random(N) * 0.01

       使用下面的代码设置一个最简单的线性运算函数,用于后面的生成器与判别器。

def linear(input, output_dim, scope=None, stddev=1.0):

    norm = tf.random_normal_initializer(stddev=stddev)

    const = tf.constant_initializer(0.0)

    with tf.variable_scope(scope or 'linear'):

        w = tf.get_variable('w', [input.get_shape()[1], output_dim], initializer=norm)

        b = tf.get_variable('b', [output_dim], initializer=const)

        return tf.matmul(input, w) + b

        基于该线性运算函数,我们可以完成简单的生成器和判别器代码。

def generator(input, h_dim):

    h0 = tf.nn.softplus(linear(input, h_dim, 'g0'))

    h1 = linear(h0, 1, 'g1')

    return h1


def discriminator(input, h_dim):

    h0 = tf.tanh(linear(input, h_dim * 2, 'd0'))

    h1 = tf.tanh(linear(h0, h_dim * 2, 'd1'))   

    h2 = tf.tanh(linear(h1, h_dim * 2, 'd2'))

    h3 = tf.sigmoid(linear(h2, 1, 'd3'))

    return h3

设置优化器,这里使用的是学习率衰减的梯度下降方法。

def optimizer(loss, var_list, initial_learning_rate):

    decay = 0.95

    num_decay_steps = 150

    batch = tf.Variable(0)

    learning_rate = tf.train.exponential_decay(

        initial_learning_rate,

        batch,

        num_decay_steps,

        decay,

        staircase=True

    )

    optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(

        loss,

        global_step=batch,

        var_list=var_list

    )

    return optimizer

下面来搭建GAN模型类的代码,除了初始化参数之外,其中核心的两个函数分别是模型的创建和模型的训练。

class GAN(object):

    def __init__(self, data, gen, num_steps, batch_size, log_every):

        self.data = data

        self.gen = gen

        self.num_steps = num_steps

        self.batch_size = batch_size

        self.log_every = log_every

        self.mlp_hidden_size = 4

        self.learning_rate = 0.03

        self._create_model()


    def _create_model(self):

            ......


    def train(self):

        ......

         创建模型。这里需要创建预训练判别器D_pre、生成器Generator和判别器Discriminator,按照之前的公式定义生成器和判别器的损失函数loss_g与loss_d以及它们的优化器opt_g与opt_d,其中D1与D2分别代表真实数据与生成数据的判别。

def _create_model(self):


    with tf.variable_scope('D_pre'):

        self.pre_input = tf.placeholder(tf.float32, shape=(self.batch_size, 1))

        self.pre_labels = tf.placeholder(tf.float32, shape=(self.batch_size, 1))

        D_pre = discriminator(self.pre_input, self.mlp_hidden_size)

        self.pre_loss = tf.reduce_mean(tf.square(D_pre - self.pre_labels))

        self.pre_opt = optimizer(self.pre_loss, None, self.learning_rate)


    with tf.variable_scope('Generator'):

        self.z = tf.placeholder(tf.float32, shape=(self.batch_size, 1))

        self.G = generator(self.z, self.mlp_hidden_size)


    with tf.variable_scope('Discriminator') as scope:

        self.x = tf.placeholder(tf.float32, shape=(self.batch_size, 1))

        self.D1 = discriminator(self.x, self.mlp_hidden_size)

        scope.reuse_variables()

        self.D2 = discriminator(self.G, self.mlp_hidden_size)


    self.loss_d = tf.reduce_mean(-tf.log(self.D1) - tf.log(1 - self.D2))

    self.loss_g = tf.reduce_mean(-tf.log(self.D2))


    self.d_pre_params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, 

        scope='D_pre')

    self.d_params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope=

        'Discriminator')

    self.g_params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope=

        'Generator')


    self.opt_d = optimizer(self.loss_d, self.d_params, self.learning_rate)

    self.opt_g = optimizer(self.loss_g, self.g_params, self.learning_rate)

       训练模型的代码如下所示,首先需要预先训练判别器D_pre,然后将训练后的参数共享给判别器Discriminator。接着就可以正式训练生成器Generator与判别器Discriminator了。

def train(self):

    with tf.Session() as session:

        tf.global_variables_initializer().run()


        # pretraining discriminator

        num_pretrain_steps = 1000

        for step in range(num_pretrain_steps):

            d = (np.random.random(self.batch_size) - 0.5) * 10.0

            labels = norm.pdf(d, loc=self.data.mu, scale=self.data.sigma)

            pretrain_loss, _ = session.run([self.pre_loss, self.pre_opt], {

                self.pre_input: np.reshape(d, (self.batch_size, 1)),

                self.pre_labels: np.reshape(labels, (self.batch_size, 1))

            })

        self.weightsD = session.run(self.d_pre_params)

        for i, v in enumerate(self.d_params):

            session.run(v.assign(self.weightsD[i]))


        for step in range(self.num_steps):

            # update discriminator

            x = self.data.sample(self.batch_size)

            z = self.gen.sample(self.batch_size)

            loss_d, _ = session.run([self.loss_d, self.opt_d], {

                self.x: np.reshape(x, (self.batch_size, 1)),

                self.z: np.reshape(z, (self.batch_size, 1))

            })


            # update generator

            z = self.gen.sample(self.batch_size)

            loss_g, _ = session.run([self.loss_g, self.opt_g], {

                self.z: np.reshape(z, (self.batch_size, 1))

            })


            if step % self.log_every == 0:

                print('{}:{}\t{}'.format(step, loss_d, loss_g))                

            if step % 100 == 0 or step==0 or step == self.num_steps -1 :

                self._plot_distributions(session)

        可视化代码如下,使用对数据进行采样的方式来展示生成数据与真实数据的分布。

def _samples(self, session, num_points=10000, num_bins=100):

    xs = np.linspace(-self.gen.range, self.gen.range, num_points)

    bins = np.linspace(-self.gen.range, self.gen.range, num_bins)


    # data distribution

    d = self.data.sample(num_points)

    pd, _ = np.histogram(d, bins=bins, density=True)


    # generated samples

    zs = np.linspace(-self.gen.range, self.gen.range, num_points)

    g = np.zeros((num_points, 1))

    for i in range(num_points // self.batch_size):

        g[self.batch_size * i:self.batch_size * (i + 1)] = session.run(self.G, {

            self.z: np.reshape(

                zs[self.batch_size * i:self.batch_size * (i + 1)],

                (self.batch_size, 1)

            )

        })

    pg, _ = np.histogram(g, bins=bins, density=True)

    return pd, pg


def _plot_distributions(self, session):

    pd, pg = self._samples(session)

    p_x = np.linspace(-self.gen.range, self.gen.range, len(pd))

    f, ax = plt.subplots(1)

    ax.set_ylim(0, 1)

    plt.plot(p_x, pd, label='Real Data')

    plt.plot(p_x, pg, label='Generated Data')

    plt.title('GAN Visualization')

    plt.xlabel('Value')

    plt.ylabel('Probability Density')

    plt.legend()

    plt.show()

最后设置主函数用于运行项目,分别设置迭代次数、批次数量以及希望展示可视化的间隔,这里的设置分别为1200、12和10。

def main(args):

    model = GAN(

        DataDistribution(),

        GeneratorDistribution(range=8),

        1200, #num_steps

        12, #batch_size

        10, #log_every

    )

    model.train()

运行过程中我们会分别看到图3-11到图3-13的几个阶段,GAN的生成器从平均分布开始会逐渐逼近最终的高斯分布,实现生成数据与真实数据分布的重合。

image.png

图3-11 GAN可视化初始阶段状态

image.png

图3-12 GAN可视化训练中状态

image.png

图3-13 GAN可视化最终状态


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。