生成式对抗网络(GAN)

生成式对抗网络(GAN)

GAN的初衷就是生成不存在于真实世界的数据

应用场景如下:

  1. AI作家,AI画家等需要创造力的AI体;
  2. 将模糊图变清晰(去雨,去雾,去抖动,去马赛克等),这需要AI具有所谓的“想象力”,能脑补情节;
  3. 进行数据增强,根据已有数据生成更多新数据供以feed,可以减缓模型过拟合现象。

GAN的原理

GAN有两个网络分别为:

G(Generator),生成器: 负责凭空捏造数据出来

​ 接收一个随机的噪声z,通过这个噪声生成图片,记做G(z)。

D(Discriminator),判别器: 负责判断数据是不是真数据

​ 判别一张图片是不是“真实的”。它的输入参数是x,x代表一张图片,输出D(x)代表x为真实图片的概率,如果为1,就代表100%是真实的图片,而输出为0,就代表不可能是真实的图片。

基本思路生成网络G的目标就是尽量生成真实的图片去欺骗判别网络D,而判别网络D的目标就是尽量把G生成的图片和真实的图片分别开来。这样,G和D构成了一个动态的“博弈过程”:如下图,G根据一串随机数z就可以捏造一个“假图像”出来,用这些假图去欺骗D,D负责辨别这是真图还是假图,会给出一个score。比如,G生成了一张图,在D这里得分很高,那证明G是很成功的;如果D能有效区分真假图,则G的效果还不太好,需要调整参数。

训练过程

  • 在噪声数据分布中随机采样,输入生成模型,得到一组假数据,记为D(z);
  • 在真实数据分布中随机采样,作为真实数据,记做x;
  • 将前两步中某一步产生的数据作为判别网络的输入(因此判别模型的输入为两类数据,真/假),判别网络的输出值为该输入属于真实数据的概率,real为1,fake为0.
  • 然后根据得到的概率值计算损失函数;
  • 根据判别模型和生成模型的损失函数,可以利用反向传播算法,更新模型的参数。(先更新判别模型的参数,然后通过再采样得到的噪声数据更新生成器的参数)

生成模型与对抗模型是完全独立的两个模型,训练采用的大原则是单独交替迭代训练

一、判别网络训练过程:

  1. 假设现在有了生成网络(当然可能不是最好的),那么给一堆随机数组,就会得到一堆假的样本集(因为不是最终的生成模型,现在生成网络可能处于劣势,导致生成的样本不太好,很容易就被判别网络判别为假)
  2. 现在有了这个假样本集(真样本集一直都有),我们再人为地定义真假样本集的标签,很明显,这里我们默认真样本集的类标签为1,而假样本集的类标签为0,因为我们希望真样本集的输出尽可能为1,假样本集为0。
  3. 现在有了真样本集以及它们的label(都是1)、假样本集以及它们的label(都是0)。这样一来,单就判别网络来说,问题变成了有监督的二分类问题了,直接送进神经网络中训练就好。

二、生成网络训练过程:

  1. 对于生成网络,我们的目的是生成尽可能逼真的样本。
  2. 而原始的生成网络生成的样本的真实程度只能通过判别网络才知道,所以在训练生成网络时,需要联合判别网络才能达到训练的目的。
  3. 所以生成网络的训练其实是对生成-判别网络串接的训练,像上图显示的那样。因为如果只使用生成网络,那么无法得到误差,也就无法训练。
  4. 当通过原始的噪声数组Z生成了假样本后,把这些假样本的标签都设置为1,即认为这些假样本在生成网络训练的时候是真样本。因为此时是通过判别器来生成误差的,而误差回传的目的是使得生成器生成的假样本逐渐逼近为真样本(当假样本不真实,标签却为1时,判别器给出的误差会很大,这就迫使生成器进行很大的调整;反之,当假样本足够真实,标签为1时,判别器给出的误差就会减小,这就完成了假样本向真样本逐渐逼近的过程),起到迷惑判别器的目的。
  5. 现在对于生成网络的训练,有了样本集(只有假样本集,没有真样本集),有了对应的label(全为1),有了误差,就可以开始训练了。
  6. 在训练这个生成网络时,一个很重要的操作是固定判别网络的参数,不让判别网络参数更新,只是让判别网络将误差传到生成网络,更新生成网络的参数。

三、在生成网络训练完后,可以根据用新的生成网络对先前的噪声Z生成新的假样本了,不出意外,这次生成的假样本会更真实。

四、有了新的真假样本集(其实是新的假样本集),就又可以重复上述过程了。

五、整个过程就叫单独交替训练。可以定义一个迭代次数,交替迭代到一定次数后停止即可。不出意外,这时噪声Z生成的假样本就会很真实了。

整个过程体现了生成网络和判别网络的博弈过程。

GAN设计的巧妙处之一,在于假样本在训练过程中的真假变换,这也是博弈得以进行的关键之处。

GAN的另一个强大之处在于可以自动定义潜在损失函数,即判别网络可以自动学习到一个好的判别方法(损失函数),来比较好或者不好的判别出来结果。

目标函数

从真实数据分布中取样m个点,根据给定的参数 我们可以计算如下的概率 ,那么生成这m个样本数据的似然(likelihood)就是
L = ∏ i = 1 m P G ( x i ; θ ) L=\prod _{i=1}^mP_G(x^i;θ) L=i=1mPG(xi;θ)
我们要做的就是找到 θ ∗ θ^∗ θ 来最大化这个似然估计
θ ∗ = a r g   max ⁡ θ ∏ i = 1 m p G ( x i ; θ ) ⇔ a r g   max ⁡ θ l o g ∏ i = 1 m P G ( x i ; θ ) θ^∗=arg\space \max_θ\prod_{i=1}^mp_G(x^i;θ)⇔arg\space \max_θlog\prod_{i=1}^mP_G(x^i;θ) θ=arg θmaxi=1mpG(xi;θ)arg θmaxlogi=1mPG(xi;θ)

= a r g   max ⁡ θ ∑ i m l o g P G ( x i ; θ ) =arg\space \max_θ\sum_i^mlogP_G(x^i;θ) =arg θmaximlogPG(xi;θ)

≈ a r g   max ⁡ θ E x ∼ P d a t a [ l o g P G ( x ; θ ) ] ≈arg \space \max_θE_{x∼P_{data}}[logP_G(x;θ)] arg θmaxExPdata[logPG(x;θ)]

⇔ a r g   max ⁡ θ ∫ x P d a t a ( x ) l o g P G ( x ; θ ) d x − ∫ x P d a t a ( x ) l o g P d a t a ( x ) d x ⇔arg\space \max_θ∫_xP_{data}(x)logP_G(x;θ)dx−∫_xP_{data}(x)logP_{data}(x)dx arg θmaxxPdata(x)logPG(x;θ)dxxPdata(x)logPdata(x)dx

= a r g   max ⁡ θ ∫ x P d a t a ( x ) l o g P G ( x ; θ ) P d a t a ( x ) d x =arg\space \max_θ∫_xP_{data}(x)log\frac{P_G(x;θ)}{P_{data}(x)}dx =arg θmaxxPdata(x)logPdata(x)PG(x;θ)dx

= a r g   min ⁡ θ K L ( P d a t a ( x ) ∣ ∣ P G ( x ; θ ) ) =arg\space \min_θKL(P_{data}(x)||PG(x;θ)) =arg θminKL(Pdata(x)PG(x;θ))

这里在前面添加一个负号,将log里面的分数倒一下,就变成了KL散度(KL divergence)或称相对熵

相对熵是两个随机分布间距离的度量。
P G ( x ) = ∫ z P p r i o r ( z ) I [ G ( z ) = x ] d z P_G(x)=∫_zP_{prior}(z)I_{[G(z)=x]}dz PG(x)=zPprior(z)I[G(z)=x]dz

其 中 I G ( z ) = { 0 G ( z ) ≠ x 1 G ( z ) = x 表 示 示 性 函 数 其中I_{G(z)}=\begin {cases} 0 & G(z)≠x \\ 1 & G(z)=x \end {cases}表示示性函数 IG(z)={01G(z)=xG(z)=x

进一步地

G是生成器,给定先验分布 , 我们希望得到的生成分布是 ,这里很难通过极大似然估计得到结果;
D是一个函数,可以衡量差距,被用来取代极大似然估计;

定义函数V(G, D)如下:
V ( D , G ) = E x ∼ P d a t a ( x ) [ l o g D ( X ) ] + E z ∼ P z ( z ) [ l o g ( 1 − D ( G ( z ) ) ) ] V(D,G)=E_{x∼P_{data}(x)}[logD(X)]+E_{z∼P_{z}(z)}[log(1−D(G(z)))] V(D,G)=ExPdata(x)[logD(X)]+EzPz(z)[log(1D(G(z)))]
我们可以通过下面的式子求得最优的生成模型:
G ∗ = a r g   min ⁡ G max ⁡ D V ( D , G ) = E x ∼ P d a t a ( x ) [ l o g D ( X ) ] + E z ∼ P z ( z ) [ l o g ( 1 − D ( G ( z ) ) ) ] G^∗=arg\space \min_G\max_DV(D,G) =E_{x∼P_{data}(x)}[logD(X)]+E_{z∼P_z(z)}[log(1−D(G(z)))] G=arg GminDmaxV(D,G)=ExPdata(x)[logD(X)]+EzPz(z)[log(1D(G(z)))]
这是一个最大最小优化问题,先优化D,然后再优化G,本质上是两个优化问题,拆解后得到下面两个公式:

优化D:
max ⁡ D V ( D , G ) = E x ∼ p d a t a ( x ) [ l o g ( D ( x ) ) ] + E z ∼ p z ( z ) [ l o g ( 1 − D ( G ( z ) ) ) ] \max_{D}V(D,G)=E_{x∼p_{data}(x)}[log(D(x))]+E_{z∼p_z(z)}[log(1−D(G(z)))] DmaxV(D,G)=Expdata(x)[log(D(x))]+Ezpz(z)[log(1D(G(z)))]
优化G:
min ⁡ G V ( D , G ) = E z ∼ p z ( z ) [ l o g ( 1 − D ( G ( z ) ) ) ] \min_GV(D,G)=E_{z∼p_z(z)}[log(1−D(G(z)))] GminV(D,G)=Ezpz(z)[log(1D(G(z)))]
优化D(判别网络)时,不关生成网络的事,后面的G(z)相当于已经得到的假样本。优化D的公式的第一项,使的真样本x输入时,得到的结果越大越好,因为需要真样本的预测结果越接近于1越好。对于假样本,需要优化使其结果越小越好,也就是D(G(z))越小越好,因为它的标签为0。但是第一项越大,第二项就越小,这就矛盾了,所以把第二项改成1-D(G(z)),这样就是越大越好,两者合起来就是越大越好。

优化G(生成网络)时,不关真样本的事,所以把第一项直接去掉,只剩下假样本,这时希望假样本的标签是1,所以D(G(z))越大越好,但为了统一成1-D(G(z))的形式,就变成最小化1-D(G(z)),本质上没有区别,只是为了形式的统一。

这两个优化模型合并起来,就成了上面的最大最小目标函数了,里面既包含了判别模型的优化,同时也包含了生成模型的以假乱真的优化。


代码

图像增强,将0-9的手写数字集,变得更清楚

程序思路:

1.加载MNIST数据:

​ 手写数字图片,每一张都是0-9的单个数字,且每一张都是抗锯齿(Anti-aliasing)的灰度图。

2.判别网络:

​ 判别器是一个卷积神经网络,接收图片大小为28×28×1的输入图像,之后返还一个单一标量值来描述输入图像的真伪——判断到底是来自MNIST图像集还是生成器。

​ 它有两层特征为5×5像素特征的卷积层,还有两个全连接层按图像中每个像素计算增加权重的层。

​ 创建了神经网络后,通常需要将权重和偏差初始化,这项任务可以在tf.get_variable中完成。权重在截断正态分布中被初始化,偏差在0处被初始化。

​ tf.nn.conv2d()是TensorFlow中的标准卷积函数,它包含四个参数:首个参数就是输入图像(input volume),也就是本示例中的28×28像素的图片;第二个参数是滤波器/权矩阵,最终你也可以改变卷积的“步幅”和“填充”。这两个参数控制着输出图像的尺寸大小。

# 定义判别网络
def discriminator(images, reuse_variables=None):
    with tf.variable_scope(tf.get_variable_scope(), reuse=reuse_variables) as scope:
        # 第一个卷积和池化层
        # 生成32个不同的5 x 5特征
        d_w1 = tf.get_variable('d_w1', [5, 5, 1, 32], initializer=tf.truncated_normal_initializer(stddev=0.02))
        d_b1 = tf.get_variable('d_b1', [32], initializer=tf.constant_initializer(0))
        d1 = tf.nn.conv2d(input=images, filter=d_w1, strides=[1, 1, 1, 1], padding='SAME')
        d1 = d1 + d_b1
        d1 = tf.nn.relu(d1)
        # 平均池化,输出的每个条目为输入张量 中 ksize 大小的窗口所有值的平均值
        d1 = tf.nn.avg_pool(d1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

        # 第二个卷积和池化层
        # 生成64个不同的5 x 5特征
        d_w2 = tf.get_variable('d_w2', [5, 5, 32, 64], initializer=tf.truncated_normal_initializer(stddev=0.02))
        d_b2 = tf.get_variable('d_b2', [64], initializer=tf.constant_initializer(0))
        d2 = tf.nn.conv2d(input=d1, filter=d_w2, strides=[1, 1, 1, 1], padding='SAME')
        d2 = d2 + d_b2
        d2 = tf.nn.relu(d2)
        d2 = tf.nn.avg_pool(d2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

        # 第一个全连接层
        d_w3 = tf.get_variable('d_w3', [7 * 7 * 64, 1024], initializer=tf.truncated_normal_initializer(stddev=0.02))
        d_b3 = tf.get_variable('d_b3', [1024], initializer=tf.constant_initializer(0))
        d3 = tf.reshape(d2, [-1, 7 * 7 * 64])
        d3 = tf.matmul(d3, d_w3)
        d3 = d3 + d_b3
        d3 = tf.nn.relu(d3)

        # 第二个全连接层
        d_w4 = tf.get_variable('d_w4', [1024, 1], initializer=tf.truncated_normal_initializer(stddev=0.02))
        d_b4 = tf.get_variable('d_b4', [1], initializer=tf.constant_initializer(0))
        d4 = tf.matmul(d3, d_w4) + d_b4

        return d4

3.生成网络

​ 需要d维度向量,并需要将其变为28*28的图像。之后再ReLU和批量标准化用于稳定每一层的输出。

​ 用了三个卷积层和插值,直到形成28*28像素的图像。

​ 在输出层添加了一个tf.sigmoid() 激活函数,主要作用是将其挤压灰色呈现白色或黑色相,从而产生一个更清晰的图像。

# 定义生成网络
def generator(z, batch_size, z_dim):
    g_w1 = tf.get_variable('g_w1', [z_dim, 3136], dtype=tf.float32, initializer=tf.truncated_normal_initializer(stddev=0.02)) #从截断的正态分布中输出随机值,标准偏差0.02
    g_b1 = tf.get_variable('g_b1', [3136], initializer=tf.truncated_normal_initializer(stddev=0.02))
    g1 = tf.matmul(z, g_w1) + g_b1
    g1 = tf.reshape(g1, [-1, 56, 56, 1])
    g1 = tf.contrib.layers.batch_norm(g1, epsilon=1e-5, scope='g_b1')
    g1 = tf.nn.relu(g1)

    # Generate 50 features
    g_w2 = tf.get_variable('g_w2', [3, 3, 1, z_dim/2], dtype=tf.float32, initializer=tf.truncated_normal_initializer(stddev=0.02))
    g_b2 = tf.get_variable('g_b2', [z_dim/2], initializer=tf.truncated_normal_initializer(stddev=0.02))
    g2 = tf.nn.conv2d(g1, g_w2, strides=[1, 2, 2, 1], padding='SAME')
    g2 = g2 + g_b2
    g2 = tf.contrib.layers.batch_norm(g2, epsilon=1e-5, scope='g_b2') # 批量标准化
    g2 = tf.nn.relu(g2)
    g2 = tf.image.resize_images(g2, [56, 56])

    # Generate 25 features
    g_w3 = tf.get_variable('g_w3', [3, 3, z_dim/2, z_dim/4], dtype=tf.float32, initializer=tf.truncated_normal_initializer(stddev=0.02))
    g_b3 = tf.get_variable('g_b3', [z_dim/4], initializer=tf.truncated_normal_initializer(stddev=0.02))
    g3 = tf.nn.conv2d(g2, g_w3, strides=[1, 2, 2, 1], padding='SAME')
    g3 = g3 + g_b3
    g3 = tf.contrib.layers.batch_norm(g3, epsilon=1e-5, scope='g_b3')
    g3 = tf.nn.relu(g3)
    g3 = tf.image.resize_images(g3, [56, 56])

    # Final convolution with one output channel
    g_w4 = tf.get_variable('g_w4', [1, 1, z_dim/4, 1], dtype=tf.float32, initializer=tf.truncated_normal_initializer(stddev=0.02))
    g_b4 = tf.get_variable('g_b4', [1], initializer=tf.truncated_normal_initializer(stddev=0.02))
    g4 = tf.nn.conv2d(g3, g_w4, strides=[1, 2, 2, 1], padding='SAME')
    g4 = g4 + g_b4
    g4 = tf.sigmoid(g4)

    # Dimensions of g4: batch_size x 28 x 28 x 1
    return g4

4.初始化

z_dimensions = 100
batch_size = 50
z_placeholder = tf.placeholder(tf.float32, [None, z_dimensions], name='z_placeholder')
# z_placeholder is for feeding input noise to the generator

x_placeholder = tf.placeholder(tf.float32, shape = [None,28,28,1], name='x_placeholder')
# x_placeholder is for feeding input images to the discriminator

Gz = generator(z_placeholder, batch_size, z_dimensions)
# Gz holds the generated images

Dx = discriminator(x_placeholder)
# Dx will hold discriminator prediction probabilities
# for the real MNIST images

Dg = discriminator(Gz, reuse_variables=True)
# Dg will hold discriminator prediction probabilities for generated images
  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值