感谢伯禹学习平台,本次学习将记录记录如何使用Pytorch高效实现网络,熟练掌握Pytorch的基础知识。记录不包含理论知识的细节展开。
DCGAN
DCGAN,实在GAN的基础上,使用卷积网络替换了原有的G和D中的全连接层,在图像生成上具有较好的表现。
GAN的结构示意图初始准备
使用的输入图像的大小为(64,64,3)彩色的RGB图像。在DCGAN中比较重要的一点是,G生成的图像大小需要和真实图片大小保持一致性,确定G的输入大小后,可以设计G的基本结构。
Generator
G的作用是将一个噪声使用反卷积的操作,将其拉伸到真实数据大小,反卷积的参数是可以学习,所以与D的学习参数迭代,有了对抗。
import torch
import torchvision
import torch.nn as nn
# G_block,G与之前的网络设计思想一致,将复用的模块封装为块
class G_block(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size=4,strides=2, padding=1):
super(G_block,self).__init__()
self.conv2d_trans=nn.ConvTranspose2d(in_channels, out_channels, kernel_size=kernel_size,
stride=strides, padding=padding, bias=False)
# 前几层都是 trans_conv+bn+relu
self.batch_norm=nn.BatchNorm2d(out_channels,0.8)
self.activation=nn.ReLU()
def forward(self,x):
return self.activation(self.batch_norm(self.conv2d_trans(x)))
如上block中的反卷积输入大小可以参考如下计算公式,在输入的情况下输出大小为(32,32),也就增大一倍
在G的网络结构中,它每层的输出通道数时递减的,这点与D刚好有点对称的意思。
class net_G(nn.Module):
def __init__(self,in_channels):
super(net_G,self).__init__()
n_G=64
self.model=nn.Sequential(
G_block(in_channels,n_G*8,strides=1,padding=0),
G_block(n_G*8,n_G*4),
G_block(n_G*4,n_G*2),
G_block(n_G*2,n_G),
# 最后的输出卷积层使用的激活函数为Tanh,具有较好的泛化能力
nn.ConvTranspose2d(
n_G,3,kernel_size=4,stride=2,padding=1,bias=False
),
nn.Tanh()
)
def forward(self,x):
x=self.model(x)
return x
在给定输入为(1,1)的噪声情况下,不难验证输出为(64,64)
Discriminator
D的作用是区分真假,生成对抗网络是一个无监督学习,其标签区分只有0,1,分别对应生成数据和真实数据。D的结构,类似于G的反过来的意思。
# 定义D的复用块,使用正常的卷积网络
class D_block(nn.Module):
def __init__(self,in_channels,out_channels,kernel_size=4,strides=2,
padding=1,alpha=0.2):
super(D_block,self).__init__()
self.conv2d=nn.Conv2d(in_channels,out_channels,kernel_size,strides,padding,bias=False)
# 这里用的时conv+bn+leakyrelu,论文中给出这样有利于收敛。
self.batch_norm=nn.BatchNorm2d(out_channels,0.8)
self.activation=nn.LeakyReLU(alpha)
def forward(self,X):
return self.activation(self.batch_norm(self.conv2d(X)))
对应的D
class net_D(nn.Module):
def __init__(self,in_channels):
super(net_D,self).__init__()
n_D=64
self.model=nn.Sequential(
D_block(in_channels,n_D),
D_block(n_D,n_D*2),
D_block(n_D*2,n_D*4),
D_block(n_D*4,n_D*8)
)
self.conv=nn.Conv2d(n_D*8,1,kernel_size=4,bias=False)
# 最后使用的sigmoid激活,常的分类激活函数
self.activation=nn.Sigmoid()
def forward(self,x):
x=self.model(x)
x=self.conv(x)
x=self.activation(x)
return x
这里由于D输入尺寸为为(64,64)所以其最后的输出为(1,1)
如何训练这样的网络
在DCGAN中,网络通常是迭代训练的,固定G训练D,固定D训练G。。。
训练过程中的损失计算参考如下代码
def update_D(X,Z,net_D,net_G,loss,trainer_D):
batch_size=X.shape[0]
Tensor=torch.cuda.FloatTensor
ones=Variable(Tensor(np.ones(batch_size,)),requires_grad=False).view(batch_size,1)
zeros = Variable(Tensor(np.zeros(batch_size,)),requires_grad=False).view(batch_size,1)
#训练D的时候,给原始图1标签,生成图0标签
real_Y=net_D(X).view(batch_size,-1)
fake_X=net_G(Z)
fake_Y=net_D(fake_X).view(batch_size,-1)
loss_D=(loss(real_Y,ones)+loss(fake_Y,zeros))/2
loss_D.backward()
trainer_D.step()
return float(loss_D.sum())
def update_G(Z,net_D,net_G,loss,trainer_G):
batch_size=Z.shape[0]
Tensor=torch.cuda.FloatTensor
ones=Variable(Tensor(np.ones((batch_size,))),requires_grad=False).view(batch_size,1)
# 在训练G的时候我们需要给定生成图1标签
fake_X=net_G(Z)
fake_Y=net_D(fake_X).view(batch_size,-1)
loss_G=loss(fake_Y,ones)
loss_G.backward()
trainer_G.step()
return float(loss_G.sum())
读取数据训练参考如下
for epo in range(epochs):
for data in dataiter:
z = ..
d.zero_grad()
update_D(...)
g.zero_grad()
update_G
总结
DCGAN 可以说是入门GAN的开始,后面有很多基于GAN思路的改进,本质上来说,GAN的思路使得神经网络具有了可控的创造性,但距离人的差距还是很大。
网友评论