GAN --- > 基础

背景

应用场景

开始之前,先看一下GAN的魔力:

  1. 创建动画角色
    通过GAN,能够自动生成动画角色并且为其上色:

  2. 更改人像姿势
    通过输入一个姿势引导,能够将人像的动作变化:

  3. 风格转换

将图片转化为另一种风格,比如斑马和马之间的转换:

我们甚至可以将生活中的场景转化为动漫类型:
  1. 提升分辨率

  2. 图像修复

还有三维物体的生成,图像到文字的转换等等,可以看出GAN带来非常惊艳的效果!当然,上述的效果由不同GAN的变种得到的,我们下面介绍GAN最基础的部分,包括它的模型结构和理论部分。

简要

GAN全名(Generative Adversarial Nets),即生成对抗网络。它是在什么背景下出现,为了解决什么呢?我们知道在深度学习领域中,判别模型可以利用反向传播算法等来进行求解,也即从建模,到最终的分类预测均能够顺利地实施并取到不错的效果;
但在深度学习中,生成模型常用的极大似然估计等,通常对其难以进行有效的概率计算,并且也难以利用在生成的中间内容以进行有效地学习。

基于上述在深度学习中利用生成模型的问题,GoodFellow于2014年提出该“生成对抗网络”。该网络有两个模型:(1)生成模型G,希望即尽可能捕获数据的分布 P r Pr Pr,也就是我们生成模型最终想模拟的部分;(2)判别模型D,用于辅助生成模型越来越好的近似数据的分布,用于对一个数据是否来自真实数据进行判别。

生成模型是假设样本来自未知的数据分布 P r Pr Pr中采样得到,它的学习过程就是要学习到一个Pr的近似概率分布 P θ P_{\theta} Pθ,其中 θ \theta θ是模型的参数。一般来说,其学习过程是即是通过极大似然函数 m a x θ ∈ R d 1 m ∑ i = 1 m l o g P θ ( x i ) max_{\theta \in R^d}\frac{1}{m}\sum\limits_{i=1}^m log P_{\theta}(x^i) maxθRdm1i=1mlogPθ(xi),来求解模型参数 θ \theta θ,该方法通常假设数据服从某一分布,我们要求的即是该分布的参数。

模型

网络结构

总体上看,在GAN中,生成模型由隐变量(通常是服从高斯分布的随机噪声)作为输入不断地生成数据,以尽可能让判别模型错误;而判别模型的输入则为真实样本和生成模型生成的样本,进行训练,以对样本是生成还是真实数据进行更准确地判断;

下面我们将上述表述严密的数学公式:

首先假设已有的数据为 x x x,其真实数据分为 P d a t a ( x ) P_{data}(x) Pdata(x);不同于普通的生成模型需要预先假设数据的分布,我们这里定义一个先验分布 p z ( z ) p_z(z) pz(z),由分布生成随机变量输入到多层感知机中,最终输出一个“假”的数据,该过程可定义为一个映射 G ( z ; θ g ) G(z; \theta_g) G(z;θg)

同时,我们定义另一个多层感知机 D ( x ; θ d ) D(x;\theta_d) D(x;θd),其输出单个值(样本为真实值的概率);
我们的优化目标即:
m i n G    m a x 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 ) ) ) ] (1) \underset{G}{min}\;\underset{D}{max}\;V(D, G) =\mathbb E_{x\sim p_{data(x)}}[logD(x) ] + \mathbb E_{z\sim p_z(z)}[log(1-D(G(z)))] \tag{1} GminDmaxV(D,G)=Expdata(x)[logD(x)]+Ezpz(z)[log(1D(G(z)))](1)

我们在训练的时候并不是同时求解上式,而是不断替换优化:
1)先固定G不动,经过k次迭代后找到最优的D,即:
m a x 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 ) ) ) ] \underset{D}{max}\;V(D, G) =\mathbb E_{x\sim p_{data(x)}}[logD(x) ] + \mathbb E_{z\sim p_z(z)}[log(1-D(G(z)))] DmaxV(D,G)=Expdata(x)[logD(x)]+Ezpz(z)[log(1D(G(z)))]

可以看出,我们此时目的是使得,当数据来自真实数据时,让 l o g D ( x ) log D(x) logD(x)尽可能大;数据来自生成模型得到的假数据时,使得模型 D ( G ( z ) ) D(G(z)) D(G(z))尽可能小,此时 D ( x ) D(x) D(x)的输出可以理解为来自真实数据的概率。该过程会更新分类部分神经网络参数 θ d \theta_d θd

2)固定分类模型,即保持 θ d \theta_d θd不变,我们希望V的值越小越好,让D分不开真假数据;对于 V ( D , G ) V(D,G) V(D,G),由于第一项无 G G G
所有优化目标为:
m i n G    E z ∼ p z ( z ) [ l o g ( 1 − D ( G ( z ) ) ) ] \underset{G}{min}\; \mathbb E_{z\sim p_z(z)}[log(1-D(G(z)))] GminEzpz(z)[log(1D(G(z)))]
此时我们目的是使得 D ( G ( z ) ) D(G(z)) D(G(z))尽可能大,即通过更新 θ g \theta_g θg,使得我们的分类模型对于假样本预测的概率变高。

迭代更新效果图:

其中黑点为真实数据,红实线为生成模型得到数据,蓝线为判别模型的结果;看出经多轮迭代后,生成模型已能得到和真实数据分布一致的数据,并且判别模型已无法将他们区分开来。

理论

虽然直观上,前述的方法中的生成器在经过若干次迭代后能够生成接近真实数据的样本,但理论上我们需要更近一步来确认。

  • 还是对于公式(1),首先固定G,我们对 V ( D , G ) V(D, G) V(D,G)求导,得到最优判别器:
    m a x D V ( D , G ) = m a x D ∫ x p d a t a ( x ) l o g ( D ( x ) ) + p g ( x ) l o g ( 1 − D ( x ) ) d x ∂ ∂ D ( x ) ( p d a t a ( x ) l o g ( D ( x ) ) + p g ( x ) l o g ( 1 − D ( x ) ) d x ) = 0 ⇒ p d a t a ( x ) D ( x ) − p g ( x ) 1 − D ( x ) = 0 ⇒ D ∗ ( x ) = p d a t a ( x ) p d a t a ( x ) + p g ( x ) \begin{aligned} & \underset{D}{max} V(D, G) = \underset{D}{max}\int_x p_{data}(x)log(D(x)) + p_g(x)log(1-D(x))dx\\ & \frac{\partial}{\partial D(x)}(p_{data}(x)log(D(x)) + p_g(x)log(1-D(x))dx) =0\\ &\Rightarrow \frac{p_{data}(x)}{D(x)}-\frac{p_g(x)}{1-D(x)}=0 \\ &\Rightarrow D^*(x)=\frac{p_{data}(x)}{p_{data}(x)+p_g(x)} \end{aligned} DmaxV(D,G)=Dmaxxpdata(x)log(D(x))+pg(x)log(1D(x))dxD(x)(pdata(x)log(D(x))+pg(x)log(1D(x))dx)=0D(x)pdata(x)1D(x)pg(x)=0D(x)=pdata(x)+pg(x)pdata(x)

  • 固定D,把最优分类器代入上述目标函数(公式1),可以进一步求出:
    m a x D V ( G , D ) = E x ∼ p d a t a ( x ) [ l o g p d a t a ( x ) p d a t a ( x ) + p g ( x ) ] + E z ∼ p z ( z ) [ l o g ( 1 − D ∗ ( G ( z ) ) ) ] = E x ∼ p d a t a ( x ) [ l o g p d a t a ( x ) p d a t a ( x ) + p g ( x ) ] + E x ∼ p g ( x ) [ l o g ( 1 − D ∗ ( x ) ) ] = E x ∼ p d a t a ( x ) [ l o g p d a t a ( x ) p d a t a ( x ) + p g ( x ) ] + E x ∼ p g ( x ) [ l o g p g ( x ) p d a t a ( x ) + p g ( x ) ] \begin{aligned} \underset{D}{max} V(G, D) &=\mathbb E_{x\sim p_{data(x)}}[log \frac{p_{data(x)}}{p_{data(x)} + p_g(x)}] + \mathbb E_{z\sim p_z(z)}[log(1-D^*(G(z)))] \\ &=\mathbb E_{x\sim p_{data(x)}}[log \frac{p_{data(x)}}{p_{data(x)} + p_g(x)}] + \mathbb E_{x\sim p_g(x)}[log(1-D^*(x))] \\ &=\mathbb E_{x\sim p_{data(x)}}[log \frac{p_{data(x)}}{p_{data(x)} + p_g(x)}] + \mathbb E_{x\sim p_g(x)}[log \frac{p_{g}(x)}{p_{data(x)} + p_g(x)}] \\ \end{aligned} DmaxV(G,D)=Expdata(x)[logpdata(x)+pg(x)pdata(x)]+Ezpz(z)[log(1D(G(z)))]=Expdata(x)[logpdata(x)+pg(x)pdata(x)]+Expg(x)[log(1D(x))]=Expdata(x)[logpdata(x)+pg(x)pdata(x)]+Expg(x)[logpdata(x)+pg(x)pg(x)]
    将上述展开,进一步推导

原 式 = − 2 l o g 2 + ∫ x p d a t a ( x ) l o g p d a t a ( x ) ( p d a t a ( x ) + p g ( x ) ) / 2 d x + ∫ x p g ( x ) l o g p g ( x ) ( p d a t a ( x ) + p g ( x ) ) / 2 d x = − 2 l o g 2 + K L ( p d a t a ( x ) ∣ ∣ p d a t a ( x ) + p g ( x ) 2 ) + K L ( p g ( x ) ∣ ∣ p d a t a ( x ) + p g ( x ) 2 ) \begin{aligned} 原式=&-2log2 + \int_x p_{data}(x)log \frac{p_{data}(x)}{(p_{data}(x) + p_g(x))/2}dx + \int_x p_{g}(x)log \frac{p_{g}(x)}{(p_{data} (x)+ p_g(x))/2}dx \\ &=-2log2 + KL(p_{data}(x)||\frac{p_{data}(x) + p_g(x)}{2}) + KL(p_g(x)||\frac{p_{data} (x)+ p_g(x)}{2}) \end{aligned} =2log2+xpdata(x)log(pdata(x)+pg(x))/2pdata(x)dx+xpg(x)log(pdata(x)+pg(x))/2pg(x)dx=2log2+KL(pdata(x)2pdata(x)+pg(x))+KL(pg(x)2pdata(x)+pg(x))

此时引入一个新的JS 散度,其实KL散度的对称平衡版本,表示了两个分布之间的差异性;
原 式 = − 2 l o g 2 + 2 J S D ( p d a t a ( x ) ∣ ∣ p g ( x ) ) \begin{aligned} 原式=-2log2 +2JSD(p_{data}(x)||p_g(x)) \end{aligned} =2log2+2JSD(pdata(x)pg(x))
可知,到 p g ( x ) = p d a t a ( x ) p_g(x)=p_{data}(x) pg(x)=pdata(x)时,G是最优的,也即是说,我们求解得到的最优情况,也即是得到的一个生成模型,该模型生成的数据与真实数据分布一致。

算法流程:

输入: 迭代次数N,先验分布 p g ( z ) p_g(z) pg(z),真实数据 p d a t a ( x ) p_{data}(x) pdata(x)
输出:生成模型和判别模型的参数
for i=1 to N:
  for k=1 to K:
      1. 从先验噪声分布 p g ( z ) p_g(z) pg(z)中采样m个样本{ z ( 1 ) , . . . , z ( m ) {z^{(1)},...,z^{(m)}} z(1),...,z(m)}
      2. 从真实数据分布 p d a t a ( x ) p_{data}(x) pdata(x)中采样得到m个样本{ x ( 1 ) , . . . , x ( m ) {x^{(1)},...,x^{(m)}} x(1),...,x(m)}
      3. 通过梯度上升法更新判别模型参数
▽ θ g 1 m ∑ i = 1 m [ l o g D ( x ( i ) ) + l o g ( 1 − D ( G ( z ( i ) ) ) ) ] \triangledown_{\theta_g}\frac{1}{m}\sum\limits_{i=1}^m[log D(x^{(i)})+log(1-D(G(z^{(i)})))] θgm1i=1m[logD(x(i))+log(1D(G(z(i))))]

  从先验噪声分布 p g ( z ) p_g(z) pg(z)中采样m个样本{ z ( 1 ) , . . . , z ( m ) {z^{(1)},...,z^{(m)}} z(1),...,z(m)}
  利用梯度下降法更新生成器的参数:
▽ θ g 1 m ∑ i = 1 m l o g ( 1 − D ( G ( z ( i ) ) ) ) \triangledown_{\theta_g}\frac{1}{m}\sum\limits_{i=1}^mlog(1-D(G(z^{(i)}))) θgm1i=1mlog(1D(G(z(i))))
这里的梯度更新规则原论文采用的是momentum方法,也可为其他方法。

代码部分

关于分类模型网络部分的定义:

class Discriminator(torch.nn.Module):
    def __init__(self, inp_dim=784):
        super(Discriminator, self).__init__()
        self.fc1 = nn.Linear(inp_dim, 128)
        self.nonlin1 = nn.LeakyReLU(0.2)
        self.fc2 = nn.Linear(128, 1)
    def forward(self, x):
        x = x.view(x.size(0), -1) # flatten (bs x 1 x 28 x 28) -> (bs x 784)
        h = self.nonlin1(self.fc1(x))
        out = self.fc2(h)
        out = torch.sigmoid(out)
        return out

对于生成模型部分的定义:

class Generator(nn.Module):
    def __init__(self, z_dim=100):
        super(Generator, self).__init__()
        self.fc1 = nn.Linear(z_dim, 128)
        self.nonlin1 = nn.LeakyReLU(0.2)
        self.fc2 = nn.Linear(128, 784)
    def forward(self, x):
        h = self.nonlin1(self.fc1(x))
        out = self.fc2(h)
        out = torch.tanh(out) # range [-1, 1]
        # convert to image 
        out = out.view(out.size(0), 1, 28, 28)
        return out

交叉迭代训练部分:


# --------STEP 1: Discriminator optimization step --------#
x_real, _ = iter(dataloader).next()
x_real = x_real.to(device)
# reset accumulated gradients from previous iteration
optimizerD.zero_grad()

D_x = D(x_real)
lossD_real = criterion(D_x, lab_real)

z = torch.randn(64, 100, device=device) # random noise, 64 samples, z_dim=100
x_gen = G(z).detach()
D_G_z = D(x_gen)
lossD_fake = criterion(D_G_z, lab_fake)

lossD = lossD_real + lossD_fake
lossD.backward()
optimizerD.step()
        
# --------STEP 2: Generator optimization step --------#
# reset accumulated gradients from previous iteration
optimizerG.zero_grad()

z = torch.randn(64, 100, device=device) # random noise, 64 samples, z_dim=100
x_gen = G(z)
D_G_z = D(x_gen)
lossG = criterion(D_G_z, lab_real) # -log D(G(z))

lossG.backward()
optimizerG.step()

欢迎关注公众号:

在这里插入图片描述

Ref:

  1. Goodfellow, I. J. et al. Generative Adversarial Networks. arXiv:1406.2661 [cs, stat] (2014).
  2. https://github.com/tomsercu/gan-tutorial-pytorch、
  3. https://zhuanlan.zhihu.com/p/58812258
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值