DCGANs源码解析(二)

model.py

DCGANs大部分都在一个叫做 DCGAN 的 Python 类(class)中(model.py)。像这样把所有东西都放在一个类中非常有用,因为训练后中间状态可以被保存起来,以便后面使用。

首先让我们定义生成器和鉴别器(上一篇已经介绍过了)。
linear, conv2d_transpose, conv2d, 和 lrelu 函数都是在 ops.py 中定义的。

1.初始化DCGAN类

我们初始化DCGAN类时,就用generator和discriminator这些函数创造了模型。

我们需要两种版本的鉴别器,他们共享同样的参数。一个用于来自真实数据分布的小批图像,另一个用于来自生成器的小批图像。下面self.D等是来自真实图片数据的判别器,self.D_等是来自生成器图片的判别器。

self.G = self.generator(self.z)
self.D, self.D_logits = self.discriminator(self.images)
self.D_, self.D_logits_ = self.discriminator(self.G, reuse=True)

2.定义损失函数

接着,我们将定义损失函数。在这里不用求和(sums),我们用D的预测和我想让它更好地工作而对它的期望之间的交叉熵( cross entropy (https://en.wikipedia.org/wiki/Cross_entropy))。

鉴别器想让来自真实数据的预测都为1,而来自生成器的假造数据都为0。生成器想让鉴别器的所有预测都为1.下面是根据这个预期定义的损失函数

#d_loss_real是真实图片输入到判别器中的结果和预期的为1的结果之间的交叉熵
self.d_loss_real = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(self.D_logits,tf.ones_like(self.D)))  
#d_loss_fake是生成器生成的图片输入到判别器中的结果和预期为0的结果之间的交叉熵
self.d_loss_fake = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(self.D_logits_,tf.zeros_like(self.D_)))
#判别器的损失函数d_loss是d_loss_fake和d_loss_real之和
self.d_loss = self.d_loss_real + self.d_loss_fake

#生成器的损失函数d_loss是生成器生成的图片输入到判别器中的结果和预期为1的结果之间的交叉熵
self.g_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(self.D_logits_,tf.ones_like(self.D_)))

3.收集变量

分别从每个模型中收集变量,让它们可以被分开训练。

t_vars = tf.trainable_variables()
self.d_vars = [var for var in t_vars if 'd_' in var.name]
self.g_vars = [var for var in t_vars if 'g_' in var.name]

4.定义优化器

现在我们准备好优化参数了,我们要用的是 ADAM (https://arxiv.org/abs/1412.6980),这是一种适应的非凸优化方法,通常用于现代深度学习中。ADAM 经常会与 SGD 竞争,而且通常不需要手动调节学习速率,动量,及其他超参数(hyper-parameter)。

d_optim = tf.train.AdamOptimizer(config.learning_rate, beta1=config.beta1) \
                  .minimize(self.d_loss, var_list=self.d_vars)
g_optim = tf.train.AdamOptimizer(config.learning_rate, beta1=config.beta1) \
                  .minimize(self.g_loss, var_list=self.g_vars)

这里优化器选择ADAM ,最终目标是要最小化d_loss和g_loss。

5.训练

我们准备好遍历数据了。在每一个时期,我们在一个小批图片中取样,运行优化器升级网络。有趣的是,如果 G 只更新了一次,鉴别器的损耗就不会为零。而且,我认为最后对 d_loss_fake 和 d_loss_real 函数的额外调用引发了一点不必要的计算,而且是多余的,因为这些值已经作为 d_optim 和 g_optim 的一部分计算过了。作为 TensorFlow 中的一项练习,你可以试着用这个部分去优化,并给原始 repo 发送一个 PR 。

for epoch in xrange(config.epoch):
    ...
    for idx in xrange(0, batch_idxs):
        batch_images = ...
        batch_z = np.random.uniform(-1, 1, [config.batch_size, self.z_dim]).astype(np.float32)

        # Update D network
        #更新一个 D 网络
        _, summary_str = self.sess.run([d_optim, self.d_sum],
            feed_dict={ self.images: batch_images, self.z: batch_z })

        # Update G network
        #更新一个 G 网络
        _, summary_str = self.sess.run([g_optim, self.g_sum],
            feed_dict={ self.z: batch_z })

        # Run g_optim twice to make sure that d_loss does not go to zero*
        # (different from paper)
       #运行两次*g_optim 以确保 d_loss 不会变成0
       #(与论文里不一样)
        _, summary_str = self.sess.run([g_optim, self.g_sum],
            feed_dict={ self.z: batch_z })

        errD_fake = self.d_loss_fake.eval({self.z: batch_z})
        errD_real = self.d_loss_real.eval({self.images: batch_images})
        errG = self.g_loss.eval({self.z: batch_z})

这里 self.sess.run()函数是执行一个会话,第一个参数是图的输出节点,第二个参数图的输入节点。如

self.sess.run([d_optim, self.d_sum], feed_dict={ self.images: batch_images, self.z: batch_z }),

上面的会话会根据输出节点d_optim, self.d_sum在图中找到最初的输入节点。

d_optim———>d_loss——->D_logits, D_logits_。

其中D_logits的输入是self.images, D_logits_的输入是self.z。因此这里run的第二个参数应该为{self.images,self.z}。

但是self.images,self.z只是个用placeholder定义的占位符,因此需要指定实际的输入。所以,这里用feed_dict指定了个字典,key值为self.images的占位符对应的值为batch_images,即加载的真实图片数据。key值为self.z的占位符对应的值为batch_z,即噪音数据。

这里看一下self.images,self.z的定义,均是用placeholder生成的占位符。

self.images = tf.placeholder(tf.float32, [self.batch_size] + [self.output_size, self.output_size, self.c_dim],
                                name='real_images')

self.z = tf.placeholder(tf.float32, [None, self.z_dim],name='z') 

介绍tensorflow

张量(Tensor)

名字就是TensorFlow,直观来看,就是张量的流动。张量(tensor),即任意维度的数据,一维、二维、三维、四维等数据统称为张量。而张量的流动则是指保持计算节点不变,让数据进行流动。

这样的设计是针对连接式的机器学习算法,比如逻辑斯底回归,神经网络等。连接式的机器学习算法可以把算法表达成一张图,张量在图中从前到后走一遍就完成了前向运算;而残差从后往前走一遍,就完成了后向传播。

算子(operation)

在TF的实现中,机器学习算法被表达成图,图中的节点是算子(operation),节点会有0到多个输出,下图是TF实现的一些算子。

每个算子都会有属性,所有的属性都在建立图的时候被确定下来,比如,最常用的属性是为了支持多态,比如加法算子既能支持float32,又能支持int32计算。

边(edge)

TF的图中的边分为两种:

正常边,正常边上可以流动数据,即正常边就是tensor

特殊边,又称作控制依赖,(control dependencies)

  1. 没有数据从特殊边上流动,但是特殊边却可以控制节点之间的依赖关系,在特殊边的起始节点完成运算之前,特殊边的结束节点不会被执行。
  2. 也不仅仅非得有依赖关系才可以用特殊边,还可以有其他用法,比如为了控制内存的时候,可以让两个实际上并没有前后依赖关系的运算分开执行。
  3. 特殊边可以在client端被直接使用

会话(Session)

客户端使用会话来和TF系统交互,一般的模式是,建立会话,此时会生成一张空图;在会话中添加节点和边,形成一张图,然后执行。

下图有一个TF的会话样例和所对应的图示。

这里写图片描述

这里写图片描述
变量(Variables)

机器学习算法都会有参数,而参数的状态是需要保存的。而参数是在图中有其固定的位置的,不能像普通数据那样正常流动。因而,TF中将Variables实现为一个特殊的算子,该算子会返回它所保存的可变tensor的句柄。

猜你喜欢

转载自blog.csdn.net/nongfu_spring/article/details/54347996