美文网首页
GAN(代码待修改,数学公式待整理)

GAN(代码待修改,数学公式待整理)

作者: 路过的飞碟 | 来源:发表于2020-09-19 08:43 被阅读0次

    一.GAN简介:
    "生成模型"可以将一个输入噪音生成和真实数据相似的数据
    "判别模型"能够判断出真实数据和类真实数据。
    随着博弈的进行,"生成模型"能够输出越来越接近真实数据的数据

    总之GAN 网络的目标是:使得生成的数据和真实数据更接近。
    (1)G(X)(生成模型网络)要学到优秀的模型参数,使得判别模型网络无法区别真实数据和类真实数据
    (2)D(X)(判别模型网络)的判别能力要足够强,使得完成对数据的真实性做出很好的二分类任务。

    x 代表真实数据,z 代表噪音,G(z)代表一个输入噪音通过生成网络后的输出。
    一方面,要求判别网络能够准确判断出数据的真实性,即 D(x)尽可能接近 1,D(G(z))尽可能接近于 0;
    另一方面,要求生成网络产生的数据非常接近真实数据,即 D(G(z))尽可能接近于 1。

    损失函数

    损失函数目的是:最大化 D 的区分度,最小化 G 输出和真实数据的区别。
    将公式拆分
    (1)判别模型:log(D1(x))+log(1-D2(G(z)))
    (2)生成模型:log(D2(G(z)))
    当判别模型能力强时,D1(x)->1, D2(G(z))->0,(1)式趋近于 0
    当生成模型能力强时,D2(G(z))->1,(2)式趋近于 0

    训练过程

    其基本算法如图

    解释:

    GAN 的训练 在同一轮梯度反传的过程分为两步既是先训练 D 再训练 G:
    注意:不是等D训练完成后开始训练G, D 的训练也需要上一轮梯度反传中 G 的输出值作为输入。
    (1)训练D:上一轮G产生的图片,和真实图片,直接拼接在一起,作为 x。根据上图,按顺序摆放 0 和 1,假图对应 0,真图对应 1。x 输入生成一个 score(从 0 到 1 之间的数),通过 score 和 y 组成的损失函数,就可以进行梯度反传了。
    (注意:图片上举的例子是 batch = 1,len(y)=2*batch,训练时通常可以取较大的 batch)
    (2)训练G:将D和G看成整体(以下简称DG系统)DG系统输出仍是score。
    DG前向过程:输入一组随机向量,在G处生成图,在D处对图打分。
    score=1是优化目标,故而根据score和y=1的差异组成损失函数,从而进行反向传播梯度。
    注意:此处D不可训练,保证G的训练符合D的打分标准

    注意:GAN的过程都是无监督

    二.数学推导:

    数据集的分布 Pdata(x),其中是一个真实的图片。在GAN中,生成器G可以生成一个分布,这个分布的参数是 θ事实上,生成器可以是任何模型,如果它是一个高斯混合模型,就是其中的平均值和方差。在现在的处理中,我们更倾向于用神经网络来行使生成器的功能,因为它能形成比高斯模型复杂地多的分布。
    在 Pdata(x)中进行采样,得到{x1,x2,..xm},此时在生成器中得到一个似然L=Ⅱ(m,i=1)PG(xi,θ) θ*这里需要找到一个 来最大化这个似然。

    我们可以看到在寻找θ*来进行最大化似然的过程中,可以在外面套上 ㏒函数。这样做可以把相乘转化为相加,这样就近似于㏒PG在Pdata分布中的期望。期望又可以等价于求概率积分,因为这是在求 θ,可以将与其无关的项去掉,最后转化为使用KL divergence衡量的概率分布差异。最小化KL的过程就是寻找参数使得PG更接近于Pdata的过程。这里注意,PG 可以是任何模型的,这里我们就用神经网络来模拟。因为激活函数的存在,它可以你和任意的分布,我们可以用神经网络学习很复杂的分布。GAN的基本公式是:

    在这里,给定一个G,最大似然V就表示了生成器输出和样本之前的差异,在这个过程中调整的是D的参数,然后找到一个最好的G使得这个最大值最小。第一项表示真实数据,此时D要让这一项的D(x)尽量大,后一项是G产生的数据,D(x)则要尽量小。
    首先,对于给定的G,求解最优的D:

    所得的D*代入:

    JS Divergence也是衡量两个分布差异的指标,不同于KL,它是对称的。在上式中,找到 D后,就需要找到G来将maxDV(G,D)最小化,D0

    然后更新G0为G1,可能会有V(G1,D1)>V(G0,D0)这样更新G就没达到它原来应该要的效果,如下图所示:

    这时候可以控制更新的量,保证一次只做微小的改动。

    三.样例介绍:
    线性数据样例:
    目标:
    实现下图,绿线(生成网络输出数据)接近蓝线(真实数据)

    (1)定义模型

    def main(args):
      model = GAN(
        DataDistribution(), //真实数据真实分布
        GeneratorDistribution(range=8), //生成数据分布
        args.num_steps, //迭代次数,取 1200
        args.batch_size, //一次迭代更新 12个点的数据
        args.log_every, //每隔多少次迭代打印一次 loss 值
    )
      model.train()
    

    (2)初始化参数

    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 //神经网络中隐藏层个数设置为 4
      self.learning_rate = 0.03 //学习率
      self._create_model()
    

    (3) D_pre 网络
    注意:判别网络D的参数不能随机初始化,必须具有一定的判别能力,所以预先使用 D_pre 网络对 D 的参数进行训练。

    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)
    
     (4)G,D网络设置
    

    G网络:

        with tf.variable_scope('Gen'):
          self.z = tf.placeholder(tf.float32,
    shape=(self.batch_size, 1)) //输入,为一个随机输入
      self.G = generator(self.z, self.mlp_hidden_size)//产生输出数据
    

    D网络:
    注意:D网络有两个输入,一个是真实数据x,另一个是生成网络的输出数据G(z)

        with tf.variable_scope('Disc') 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)
    

    (5)损失函数

    self.loss_d = tf.reduce_mean(-tf.log(self.D1) - tf.log(1 - self.D2))
    //见(1)式,对(1)式取反
    self.loss_g = tf.reduce_mean(-tf.log(self.D2))
    //见(2)式
    
    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)
    

    (6)训练模型

    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) //将d_pre 网络的训练结果赋值
          # copy weights from pre-training over to new D network
          for i, v in enumerate(self.d_params): //将 d_pre 网
    络的训练结果拷贝给 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)
    //定义 x 真实数据
          z = self.gen.sample(self.batch_size)
    //定义 z 噪音输入
          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)
    

    (以上代码仍需修改)
    训练结果
    1200次迭代,可知下列结果

    四.小狗图片训练样例:
    算法图了解结构

    (1)初始化
    生成器接受一个 1000 维的随机生成的数组,然后输出一个 64×64×3
    通道的图片数据
    生成器就是一个神经网络,或看成一个函数,低维度的向量生成一个高维度的向量。训练完成后的生成器,输入向量每一个元素可以对应图片的一个象征。

    def generator_model():
          model = Sequential()
          model.add(Dense(input_dim=1000, output_dim=1024))
          model.add(Activation('tanh'))
          model.add(Dense(128 * 8 * 8))
          model.add(BatchNormalization())
          model.add(Activation('tanh'))
          model.add(Reshape((8, 8, 128), input_shape=(8 * 8 * 128,)))
          model.add(UpSampling2D(size=(4, 4)))
          model.add(Conv2D(64, (5, 5), padding='same'))
          model.add(Activation('tanh'))
          model.add(UpSampling2D(size=(2, 2)))
          model.add(Conv2D(3, (5, 5), padding='same'))
          model.add(Activation('tanh'))
          return model
    

    (2)判别器代码与结构:

    def discriminator_model():
          model = Sequential()
          model.add(Conv2D(64, (5, 5), padding='same', input_shape=(64, 64, 3)))
          model.add(Activation('tanh'))
          model.add(MaxPooling2D(pool_size=(2, 2)))
          model.add(Conv2D(128, (5, 5)))
          model.add(Activation('tanh'))
          model.add(MaxPooling2D(pool_size=(2, 2)))
          model.add(Flatten())
          model.add(Dense(1024))
          model.add(Activation('tanh'))
          model.add(Dense(1))
          model.add(Activation('sigmoid'))
          return model
    

    输入是 64,64,3 的图片,输出是一个数 1 或者 0
    (3)训练D

    # 随机生成的 1000 维的噪声
    noise = np.random.uniform(-1, 1, size=(BATCH_SIZE, 1000))
    # X_train 是训练的图片数据,这里取出一个 batchsize 的图片用于训练,这个是真图(64张)
    image_batch = X_train[index * BATCH_SIZE:(index + 1) * BATCH_SIZE]
    # 这里是经过生成器生成的假图
    generated_images = generator_model.predict(noise, verbose=0)
    # 将真图与假图进行拼接
    X = np.concatenate((image_batch, generated_images))
    # 与 X 对应的标签,前 64 张图为真,标签是 1,后 64 张图是假图,标签为 0
    y = [1] * BATCH_SIZE + [0] * BATCH_SIZE
    # 把真图与假图的拼接训练数据 1 送入判别器进行训练判别器的准确度
    d_loss = discriminator_model.train_on_batch(X, y)
    

    (4)训练G

    def generator_containing_discriminator(g, d):
      model = Sequential()
      model.add(g)
      # 判别器参数不进行修改
      d.trainable = False
      model.add(d)
      return model
    

    这个模型上半部分是生成网络,下半部分是判别网络,生成网络首先生成假图,然后送入判别网络中进行判断,这里有一个 d.trainable=False,意思是,只调整生成器,判别的的参数不做更改。
    (5)训练G

    # 训练一个 batchsize 里面的数据
    for index in range(int(X_train.shape[0]/BATCH_SIZE)):
        # 产生随机噪声
        noise = np.random.uniform(-1, 1, size=(BATCH_SIZE, 1000))
        # 这里面都是真图片
        image_batch = X_train[index*BATCH_SIZE:(index+1)*BATCH_SIZE]
        # 这里产生假图片
        generated_images = g.predict(noise, verbose=0)
        #  将真图片与假图片拼接在一起
        X = np.concatenate((image_batch, generated_images))
        # 前 64 张图片标签为 1,即真图,后 64 张照片为假图
        y = [1] * BATCH_SIZE + [0] * BATCH_SIZE
        # 对于判别器进行训练,不断提高判别器的识别精度
        d_loss = d.train_on_batch(X, y)
        # 再次产生随机噪声
        noise = np.random.uniform(-1, 1, (BATCH_SIZE, 1000))
        # 设置判别器的参数不可调整
        d.trainable = False
    # ××××××××××××××××××××××××××××××××××××××××××××××
    # 在此输入噪声,并认为这些噪声是真实的标签
    g_loss = generator_containing_discriminator.train_on_batch(noise, [1] *
    BATCH_SIZE)
    # ××××××××××××××××××××××××××××××××××××××××××××××
    
        # 此时设置判别器可以被训练,参数可以被修改
        d.trainable = True
        # 打印损失值
        print("batch %d d_loss : %s, g_loss : %f" % (index, d_loss, g_loss))
    

    (此处在前文已经介绍过工作过程,如果已经了解过程可以不看)
    先输入生成器中,然后生成器生成图片之后,把图片输入判别器中,标签此刻输入的是 1,真实的图片,但实际上是假图,此刻判别器就会判断为假图,然后模型就会不断调整生成器参数,此时判别器的参数被设置为为不可调整,
    d.trainable=False所以为了不断降低 loss 值,模型就会一直调整生成器的参数,直到判别器认为这是真图。此时判别器与生成器达到了一个平衡。也就是说生成器产生的假图,判别器已经分辨不出来了。所以继续迭代,提高判别器精度,如此往复循环,直到个人满意。

    训练结果


    1.参考https://www.pianshen.com/article/2495690365/;jsessionid=A929EB195A785EF5D377012CF21FB7FF
    2.参考:
    https://www.pianshen.com/article/3628327017/
    3.参考:
    https://github.com/jensleeGit/Kaggle_self_use/tree/master/Generative Dog Images
    4.参考:
    https://zhuanlan.zhihu.com/p/44218749
    5.参考百度百科

    相关文章

      网友评论

          本文标题:GAN(代码待修改,数学公式待整理)

          本文链接:https://www.haomeiwen.com/subject/ffteyktx.html