论文:Unsupervised Representations Learning With Deep Convolutional Generative Adversarial Networks
本文是将CNN引入GAN的标志性论文。GAN有各种各样的优点,但是训练起来非常不稳定,经常会使得生成器产生没有意义的输出。本文的贡献在于:
- 为CNN的网络拓扑结构设置了一系列的限制来使得它可以稳定的训练。
- 使用得到的特征表示来进行图像分类,得到比较好的效果来验证生成的图像特征表示的表达能力
- 对GAN学习到的filter进行了定性的分析。
- 展示了生成的特征表示的向量计算特性。
模型结构
模型结构上需要做如下几点变化:
- 将 pooling层用卷积替代,其中,在 判别器D 上用 strided convolutions 替代,在 生成器G 上用 转置卷积 替代。
- 在 G、D 上都使用 BN 。
- 解决初始化差的问题
- 帮助梯度传播到每一层
- 防止generator把所有的样本都收敛到同一个点。
- 直接将 BN 应用到所有层会导致样本震荡和模型不稳定,通过在 generator 输出层和 discriminator 输入层不采用 BN 可以防止这种现象。
- 移除全连接层
- 全局池化 增加了模型的稳定性,但伤害了收敛速度。
- 在 generator 的除了输出层外的所有层使用 ReLU,输出层采用 tanh。
- 在 discriminator 的所有层上使用 LeakyReLU。
DCGAN的generator网络结构:

这里的conv层是反卷积
训练细节
- 预处理环节,将图像scale到tanh的[-1, 1]。
- mini-batch训练,batch size是128.
- 所有的参数初始化由(0, 0.02)的正态分布中随即得到
- LeakyReLU的斜率是0.2.
- 虽然之前的GAN使用momentum来加速训练,DCGAN使用调好超参的Adam optimizer。
- learning rate=0.0002
- 将momentum参数beta从0.9降为0.5来防止震荡和不稳定。
代码:
# Generator Code
class Generator(nn.Module):
def __init__(self, ngpu):
super(Generator, self).__init__()
self.ngpu = ngpu
self.main = nn.Sequential(
# input is Z, going into a convolution
nn.ConvTranspose2d( nz, ngf * 8, 4, 1, 0, bias=False),
nn.BatchNorm2d(ngf * 8),
nn.ReLU(True),
# state size. (ngf*8) x 4 x 4
nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, bias=False),
nn.BatchNorm2d(ngf * 4),
nn.ReLU(True),
# state size. (ngf*4) x 8 x 8
nn.ConvTranspose2d( ngf * 4, ngf * 2, 4, 2, 1, bias=False),
nn.BatchNorm2d(ngf * 2),
nn.ReLU(True),
# state size. (ngf*2) x 16 x 16
nn.ConvTranspose2d( ngf * 2, ngf, 4, 2, 1, bias=False),
nn.BatchNorm2d(ngf),
nn.ReLU(True),
# state size. (ngf) x 32 x 32
nn.ConvTranspose2d( ngf, nc, 4, 2, 1, bias=False),
nn.Tanh()
# state size. (nc) x 64 x 64
)
def forward(self, input):
return self.main(input)
# Discriminator Code
class Discriminator(nn.Module):
def __init__(self, ngpu):
super(Discriminator, self).__init__()
self.ngpu = ngpu
self.main = nn.Sequential(
# input is (nc) x 64 x 64
nn.Conv2d(nc, ndf, 4, 2, 1, bias=False),
nn.LeakyReLU(0.2, inplace=True),
# state size. (ndf) x 32 x 32
nn.Conv2d(ndf, ndf * 2, 4, 2, 1, bias=False),
nn.BatchNorm2d(ndf * 2),
nn.LeakyReLU(0.2, inplace=True),
# state size. (ndf*2) x 16 x 16
nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False),
nn.BatchNorm2d(ndf * 4),
nn.LeakyReLU(0.2, inplace=True),
# state size. (ndf*4) x 8 x 8
nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1, bias=False),
nn.BatchNorm2d(ndf * 8),
nn.LeakyReLU(0.2, inplace=True),
# state size. (ndf*8) x 4 x 4
nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=False),
nn.Sigmoid()
)
def forward(self, input):
return self.main(input)
这篇paper的主要贡献看似简单,但其实工作量很大,充分展现出作者在调参大法上的卓越功力。
网友评论