APP下载

理解和建立GANs|使用PyTorch来做深度学习

消息来源:baojiabao.com 作者: 发布时间:2024-05-19

报价宝综合消息理解和建立GANs|使用PyTorch来做深度学习

作者:Venkatesh Tata编译:ronghuaiyang生成对抗网络的一篇实践文章,使用PyTorch,用很简单的程式码搭建了一个GANs,非常通俗易懂。

我们建立了一个生成对抗网络,可以生成显示世界中没有的鸟。

在我们实际建立GAN之前,我们先看看GANs背后的思想。GANs是Ian Goodfellow发明的,他在斯坦福获得了本科和硕士学位,在蒙特利尔大学获得了博士学位。这是深度学习领域的一个新的大事。Yann LeCun说过:

"生成对抗网络是近年来机器学习领域最有趣的想法"

什么是GANs?我们为什么要创造GANs?

神经网络很擅长分类和预测事情,但是AI的研究者想要让神经网络更加像人类,通过创造东西而不仅仅是看见东西。 Ian Goodfellow成功的发明了这样一类深度学习模型,可以用来创造东西。

GANs是怎么工作的?

GANs有两个独立的神经网络。一个叫做“G”,代表了生成器,另外一个叫做“D”,代表了判别器。生成器首先随机的产生影象,判别器通过观察这些影象告诉生成器这些图片有多真实。

让我们考虑一个生成器

在开始的时候,生成器用一个随机噪声讯号作为输入,产生一个随机影象作为输出,通过判别器的帮助,开始产生越来越真实的影象。

判别器

判别器是生成器的一个对手,它的输入即有真实的影象,同时也有生成器生成的影象,判别器输出这个影象的真实程度。

到了某个点的时候,判别器无法判断出这个影象是否是真实影象了,这时我们可以发现某个由生成器输出的影象是之前从没有存在过的了。

GANs的应用

超分辨率

艺术辅助

元素抽取

开始写程式码 !

注意:下面的程式码并不适合深度学习的新手,我希望你有一些python深度学习的经验。

开始我们先汇入一些GAN需要的包。首先需要确保PyTorch已安装。

#importing required libraries

from __future__ import print_function

import torch

import torch.nn as nn

import torch.nn.parallel

import torch.optim as optim

import torch.utils.data

import torchvision.datasets as dset

import torchvision.transforms as transforms

import torchvision.utils as vutils

from torch.autograd import Variable

设定一些超引数,batch-size和影象的尺寸:

# Setting hyperparameters

batchSize = 64

imageSize = 64

第一行我们设定了batchsize为64,第二行设定了输出影象的尺寸为64x64。

然后我们建立一个影象的转换器的物件,如下:

# Creating the transformations

transform = transforms.Compose([transforms.Scale(imageSize),transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),])

上面的转化器是将影象作为判别器的输入所必须的。

注意:如果需要获取资料集,点选这里:https://github.com/venkateshtata/GAN_Medium.git>,clone这个仓库,然后替换 “dcgan.py” 档案为你需要写入的python档案, “data” 资料夹储存的是资料集。

现在我们载入资料集。这里我们使用的是 CIFAR-10的资料集。我们批量载入,确保你的python档案和你汇入的资料集在同一个资料夹。

# Loading the dataset

dataset = dset.CIFAR10(root = \'./data\', download = True, transform = transform)

dataloader = torch.utils.data.DataLoader(dataset, batch_size = batchSize, shuffle =True, num_workers = 2)

我们将资料集下载后放在./data目录下,应用我们之前定义的转化器。然后使用dataLoader 来获取训练影象。其中‘num_workers’ 表示的是读取资料用的执行绪的数量,其他的引数可以从字面意思理解。

由于这里我们需要处理两个神经网络,我们会定义一个全域性的函式来初始化给定的神经网络,只要将神经网络模型通过引数传给这个函式即可。

def weights_init(m):

classname = m.__class__.__name__

if classname.find(\'Conv\') != -1:

m.weight.data.normal_(0.0, 0.02)

elif classname.find(\'BatchNorm\') != -1:

m.weight.data.normal_(1.0, 0.02)

m.bias.data.fill_(0)

上面的函式获取神经网络的模型作为引数,初始化所有的引数。这个函式在训练开始时在每个迭代都会呼叫。

第一步就是定义我们的生成器神经网络。我们建立一个生成器的类,里面包含了一系列的层。

class G(nn.Module):

def __init__(self):

super(G, self).__init__()

self.main = nn.Sequential(

nn.ConvTranspose2d(100, 512, 4, 1, 0, bias = False),

nn.BatchNorm2d(512),

nn.ReLU(True),

nn.ConvTranspose2d(512, 256, 4, 2, 1, bias = False),

nn.BatchNorm2d(256),

nn.ReLU(True),

nn.ConvTranspose2d(256, 128, 4, 2, 1, bias = False),

nn.BatchNorm2d(128),

nn.ReLU(True),

nn.ConvTranspose2d(128, 64, 4, 2, 1, bias = False),

nn.BatchNorm2d(64),

nn.ReLU(True),

nn.ConvTranspose2d(64, 3, 4, 2, 1, bias = False),

nn.Tanh()

)

分解上面的程式码:

我们建立了一个类‘G’,继承了 ‘nn.module’,这个类里有构建模型所需要的各种功能,只要将各种应用和连线放到神经网络里即可。然后我们建立了一个模型,包含了一系列的模组,如卷积,全连线等。这里从图中可以看大,生成器和判别器是相互倒著的。生成器的输入时一个向量,所以这里我们使用了转置卷积 ‘ConvTranspose2d’。然后我们在batch的维度上对所有的特征进行了归一化,然后使用ReLU进行了非线性变换。我们重复上面的操作,输入的节点从100变到了512,特征数从512变到了256,bias保持为False。在最后的 ‘ConvTranspose2d’ 中,我们输出了3个通道,因为输出的是‘RGB’的影象,使用了‘Tanh’作为启用函式。现在我们建立一个forward函式来进行生成器讯号的前向传播。

def forward(self, input):

output = self.main(input)

return output

上面的函式的输入时长度为100的随机向量。返回的是一个生成的影象。随机向量产生随机影象。

建立生成器:

netG = G()

netG.apply(weights_init)

这里我们建立了一个生成器,然后进行了引数初始化。

现在我们再定义一个判别器类:

class D(nn.Module):

def __init__(self):

super(D, self).__init__()

self.main = nn.Sequential(

nn.Conv2d(3, 64, 4, 2, 1, bias = False),

nn.LeakyReLU(0.2, inplace = True),

nn.Conv2d(64, 128, 4, 2, 1, bias = False),

nn.BatchNorm2d(128),

nn.LeakyReLU(0.2, inplace = True),

nn.Conv2d(128, 256, 4, 2, 1, bias = False),

nn.BatchNorm2d(256),

nn.LeakyReLU(0.2, inplace = True),

nn.Conv2d(256, 512, 4, 2, 1, bias = False),

nn.BatchNorm2d(512),

nn.LeakyReLU(0.2, inplace = True),

nn.Conv2d(512, 1, 4, 1, 0, bias = False),

nn.Sigmoid()

)

判别器分解:

和G类似,判别器也是继承了‘nn.module’,输入是生成器生成的影象,返回一个0~1之间的数字。由于用生成器的输出作为输入,第一个操作时卷积,我们的启用函式使用了LeakyReLU。可以看到,不同于生成器,我们这里使用了LeakyReLU,这个是经验得来的。我们使用了‘BatchNorm2d’ 来进行特征归一化。最后,我们使用了sigmoid函式,输入0~1之间的概率。为了进行前向传播,我们定义一个forward函式,使用生成器的输出作为输入:

def forward(self, input):

output = self.main(input)

return output.view(-1)

最后一行,我们的输出值在0~1之间,由于我们需要把向量铺平,确保向量有相同的维度。

建立判别器 :

netD = D()

netD.apply(weights_init)

上面我们建立了判别器,初始化所有的引数:

现在,我们开始训练生成对抗网络。开始之前,我们需要得到一个损失函式,用来评价判别器的损失。我们使用 BCE Loss,非常适合对抗网络。然后生成器和判别器我们都需要一个优化器。

criterion = nn.BCELoss()

optimizerD = optim.Adam(netD.parameters(), lr = 0.0002, betas = (0.5, 0.999))

optimizerG = optim.Adam(netG.parameters(), lr = 0.0002, betas = (0.5, 0.999))

我们建立了一个评价函式用来度量预测和目标之间的差别。我们为判别器和生成器各建立了一个优化器。

我们使用了 ‘Adam’ 优化器,这是个SGD的升级版。

我们训练神经网络25个epochs:

for epoch in range(25):

我们从资料集中循环读取影象 :

for i, data in enumerate(dataloader, 0):

第一步需要更新判别器中的引数,我们把判别器中所有的梯度清零。

netD.zero_grad()

我们知道,判别器需要用真实和虚假的影象同时训练。这里我们先用一个真实影象来训练

real, _ = data

input = Variable(real)

target = Variable(torch.ones(input.size()[0]))

output = netD(input)

errD_real = criterion(output, target)

我们从资料集中获取一个真实影象训练判别器,然后包装成一个变数。然后前向传播,得到预测值,然后计算loss。

现在,使用生成器输出的虚假影象训练判别器:

noise = Variable(torch.randn(input.size()[0], 100, 1, 1))

fake = netG(noise)

target = Variable(torch.zeros(input.size()[0]))

output = netD(fake.detach())

errD_fake = criterion(output, target)

这里,我们先让一个随机向量通过生成器,得到一个虚假的影象。然后将这个虚假影象通过判别器,得到预测,计算损失。

误差反向传播:

errD = errD_real + errD_fake

errD.backward()

optimizerD.step()

这里我们计算判别器总的loss作为判别器的loss,更新判别器的时候,不更新生成器的权值。最后我们通过优化器来判别器更新权值。

下面我们更新生成器的权值:

netG.zero_grad()

target = Variable(torch.ones(input.size()[0]))

output = netD(fake)

errG = criterion(output, target)

errG.backward()

optimizerG.step()

就像之前一样,我们先将所有的梯度清零。然后将loss是通过计算生成器的梯度来反向传播,然后通过生成器的优化器来更新生成器的权值。

现在,我们最后的步骤就是在每100个steps时打印loss,储存真实的影象和生成的影象,可以这么做:

print(\'[%d/%d][%d/%d] Loss_D: %.4f Loss_G: %.4f\' % (epoch, 25, i, len(dataloader),errD.data[0], errG.data[0]))

if i % 100 == 0:

vutils.save_image(real, \'%s/real_samples.png\' % "./results", normalize = True)

fake = netG(noise)

vutils.save_image(fake.data, \'%s/fake_samples_epoch_%03d.png\' %("./results", epoch), normalize = True)

完整程式码 :

from __future__ import print_function

import torch

import torch.nn as nn

import torch.nn.parallel

import torch.optim as optim

import torch.utils.data

import torchvision.datasets as dset

import torchvision.transforms as transforms

import torchvision.utils as vutils

from torch.autograd import Variable

batchSize = 64

imageSize = 64

transform = transforms.Compose([transforms.Scale(imageSize),transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),]) # We create a list of transformations (scaling, tensor conversion, normalization) to apply to the input images.

dataset = dset.CIFAR10(root = \'./data\', download = True, transform = transform)

dataloader = torch.utils.data.DataLoader(dataset, batch_size = batchSize, shuffle =True, num_workers = 2)

def weights_init(m):

classname = m.__class__.__name__

if classname.find(\'Conv\') != -1:

m.weight.data.normal_(0.0, 0.02)

elif classname.find(\'BatchNorm\') != -1:

m.weight.data.normal_(1.0, 0.02)

m.bias.data.fill_(0)

class G(nn.Module):

def __init__(self):

super(G, self).__init__()

self.main = nn.Sequential(

nn.ConvTranspose2d(100, 512, 4, 1, 0, bias = False),

nn.BatchNorm2d(512),

nn.ReLU(True),

nn.ConvTranspose2d(512, 256, 4, 2, 1, bias = False),

nn.BatchNorm2d(256),

nn.ReLU(True),

nn.ConvTranspose2d(256, 128, 4, 2, 1, bias = False),

nn.BatchNorm2d(128),

nn.ReLU(True),

nn.ConvTranspose2d(128, 64, 4, 2, 1, bias = False),

nn.BatchNorm2d(64),

nn.ReLU(True),

nn.ConvTranspose2d(64, 3, 4, 2, 1, bias = False),

nn.Tanh()

)

def forward(self, input):

output = self.main(input)

return output

netG = G()

netG.apply(weights_init)

class D(nn.Module):

def __init__(self):

super(D, self).__init__()

self.main = nn.Sequential(

nn.Conv2d(3, 64, 4, 2, 1, bias = False),

nn.LeakyReLU(0.2, inplace = True),

nn.Conv2d(64, 128, 4, 2, 1, bias = False),

nn.BatchNorm2d(128),

nn.LeakyReLU(0.2, inplace = True),

nn.Conv2d(128, 256, 4, 2, 1, bias = False),

nn.BatchNorm2d(256),

nn.LeakyReLU(0.2, inplace = True),

nn.Conv2d(256, 512, 4, 2, 1, bias = False),

nn.BatchNorm2d(512),

nn.LeakyReLU(0.2, inplace = True),

nn.Conv2d(512, 1, 4, 1, 0, bias = False),

nn.Sigmoid()

)

def forward(self, input):

output = self.main(input)

return output.view(-1)

netD = D()

netD.apply(weights_init)

criterion = nn.BCELoss()

optimizerD = optim.Adam(netD.parameters(), lr = 0.0002, betas = (0.5, 0.999))

optimizerG = optim.Adam(netG.parameters(), lr = 0.0002, betas = (0.5, 0.999))

for epoch in range(25):

for i, data in enumerate(dataloader, 0):

netD.zero_grad()

real, _ = data

input = Variable(real)

target = Variable(torch.ones(input.size()[0]))

output = netD(input)

errD_real = criterion(output, target)

noise = Variable(torch.randn(input.size()[0], 100, 1, 1))

fake = netG(noise)

target = Variable(torch.zeros(input.size()[0]))

output = netD(fake.detach())

errD_fake = criterion(output, target)

errD = errD_real + errD_fake

errD.backward()

optimizerD.step()

netG.zero_grad()

target = Variable(torch.ones(input.size()[0]))

output = netD(fake)

errG = criterion(output, target)

errG.backward()

optimizerG.step()

print(\'[%d/%d][%d/%d] Loss_D: %.4f Loss_G: %.4f\' % (epoch, 25, i, len(dataloader),errD.data[0], errG.data[0]))

if i % 100 == 0:

vutils.save_image(real, \'%s/real_samples.png\' % "./results", normalize = True)

fake = netG(noise)

vutils.save_image(fake.data, \'%s/fake_samples_epoch_%03d.png\' %("./results", epoch), normalize = True)

你可以从我的GitHub仓库看到程式码:

https://github.com/venkateshtata/GAN_Medium

如果有好的建议,可以随便fork或者拉程式码,谢谢!

更多文章,请关注微信公众号:AI公园

2019-11-27 17:57:00

相关文章