Introduction
最近几年,人们对人工智能和大数据的热情达到了膨胀,现在技术达到了瓶颈,但加载到新的产品里面应用还有很大的发展空间。不知道各位最近看过爱情公寓5电视剧里面的小黑做的高科技吗——平均脸。对,就是平均脸,利用大量地人们所谓人的“平均脸”数据分析,获取数据特征,进行提取融合,然后再生成一张平均脸。最后,我们通过了大量的数据合成了一张现阶段的平均脸了,那么我们又怎么样来达到电视剧那种效果呢?
首先,我们的目标是将生成的平均脸和你想要的平均脸进行融合,具体需要我们将人脸五官对齐,
通过对图像进行预处理,大小格式相同,检测到人脸68个关键点,通过人脸特征矩阵,给定算法,
进行五官融合,最终达到所期待的效果是不是突然之间觉得理论上很简单,没错,理论上就这么简单,那理论上的使用的方面是什么呢?那就是今天我要讲解的一个知识点——DCGAN.
DCGAN
何为DCGAN?DCGAN的全文是Deep Convolution Generative Adversarial Networks。对应的中文就是:深度卷积生成对抗网络——一个GAN和卷积网络的结合,从而极大地提升了原始GAN训练的稳定性以及生成结果质量。原论文地址为:点击这里
DCGAN主要是在网络架构上改进了原始GAN,DCGAN的生成器与判别器都利用CNN架构替换了原始GAN的全连接网络,主要改进之处有如下几个方面:
1、DCGAN的生成器和判别器都舍弃了CNN的池化层,判别器保留CNN的整体架构,生成器则是将卷积层替换成了反卷积层(fractional-strided convolution)或者叫转置卷积层(Convolution Transpose)。
2、在判别器和生成器中在每一层之后都使用了Batch Normalization(BN)层,有助于处理初始化不良导致的训练问题,加速模型训练,提升了训练的稳定性。
3、利用1*1卷积层替换到所有的全连接层。
4、在生成器中除输出层使用Tanh(Sigmoid)激活函数,其余层全部使用ReLu激活函数。
5、在判别器所有层都使用LeakyReLU激活函数,防止梯度稀疏。
如下图所示:生成网络的结构图示意
for example
Defining a generator network(Here, the code of a big guy is quoted)
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):
if input.is_cuda and self.ngpu > 1:
output = nn.parallel.data_parallel(self.main, input, range(self.ngpu))
else:
output = self.main(input)
return output
Define discriminator network
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):
if input.is_cuda and self.ngpu > 1:
output = nn.parallel.data_parallel(self.main, input, range(self.ngpu))
else:
output = self.main(input)
return output.view(-1, 1).squeeze(1)
Task
粗略地讲解了DCGAN的网络结构,下面就来说明一下今天的任务:使用Pytorch搭建DCGAN生成动漫图像。Maybe you will say:“说了这么多,为什么不是生成人脸或者平均脸”答案是:一:万事都先从简单的开始,二是:你得有平均脸数据,毕竟搞深度学习的人都知道,最难弄的就是数据了。
so,let’s go on ! 你可以从上面copy代码,或许你可以按照自己的思路构建/修改网络。
然后就是,在DCGAN论文中,作者指定所有模型权重均应从 mean=0, stdev=0.02 的正态分布中随机初始化。该weights_init函数以已初始化的模型作为输入,并重新初始化所有卷积,卷积转置和批处理规范化层,以符合此条件。初始化后立即将此功能应用于模型。
# 在netG和netD上调用的自定义权重初始化函数
def weights_init(m):
classname = m.__class__.__name__
if classname.find('Conv') != -1:
nn.init.normal_(m.weight.data, 0.0, 0.02)
elif classname.find('BatchNorm') != -1:
nn.init.normal_(m.weight.data, 1.0, 0.02)
nn.init.constant_(m.bias.data, 0)
当然,你也可以不加进去,就是训练的时候,稍微收敛得不太好。
接着,我们需要定义一下关键参数
# 数据集根目录
dataroot = "data/xxxx"
# 数据加载器能够使用的进程数量
workers = 2
# 训练时的批大小
batch_size = 128
# 训练图片的大小,所有的图片给都将改变到该大小
# 转换器使用的大小.
image_size = 64
# 训练图片的通道数,彩色图片是3
nc = 3
# 本征向量z的大小(生成器的输入大小)
nz = 100
# 生成器中特征图大小
ngf = 64
# 判别器中特征图大小
ndf = 64
# 训练次数
num_epochs = 5
# 优化器学习率
lr = 0.0002
# Adam优化器的Beta1超参
beta1 = 0.5
# 可利用的GPU数量,使用0将运行在CPU模式。
ngpu = 1
最后,就是开始训练了。其实跟网上的大同小异
# 初始化 BCE损失函数
criterion = nn.BCELoss()
# 创建一个批次的本征向量用于可视化生成器训练的过程。
fixed_noise = torch.randn(64, nz, 1, 1, device=device)
# 建立一个在训练中使用的真实和假的标记
real_label = 1
fake_label = 0
# 为G和D都设置Adam优化器
optimizerD = optim.Adam(netD.parameters(), lr=lr, betas=(beta1, 0.999))
optimizerG = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999))
训练过程要求:为了能够更好地训练DCGAN网络,我们一般都是按照论文上的思路,将训练分成了两个部分。
1.Training discriminator:要求最大化将给定输入正确分类为真实(label=1)或假(label=0),也就是说我们想要最大损失:
log(D(x))+log(1-D(G(z)))。其中log(D(x))代表为实际真实样本损失,log(1-D(G(z)))为生成器生成的假样本损失。
2Training generator:要求实现通过最小化log(1-D(G(z)))来训练生成器,以便生成较完美的样本,达到以假换真的目的
但Goodfellow表明这不会提供足够的梯度,尤其是在学习过程的早期阶段。 作为修改,我们希望最大化 log(D(G(z)))。
对第1部分的 Generator 输出进行分类,使用真实标签作为 GT计算G的损失,在反向传递中计算G的梯度,最后使用优化器步骤更新G的参数。
最后,想要完整代码的,可以参考我的GitHub点击这里
注:由于上传时发生了错误,缺少了一些文件,想要训练成功的话,记得添加一些文件。
还有就是得助于某位网友网上爬下来的数据,因为可能存在一些脏的数据,因此本人已经通过相关滤波算法和非线性转化对图像进行了预处理。相比未处理前,模型提升了百分之几的提升性能。
最后,有用的话,记得点个赞吧!