美文网首页
STEP-8:Pytorch-从0实现DCGAN

STEP-8:Pytorch-从0实现DCGAN

作者: 可恶小林子 | 来源:发表于2020-02-24 14:55 被阅读0次
    感谢伯禹学习平台,本次学习将记录记录如何使用Pytorch高效实现网络,熟练掌握Pytorch的基础知识。记录不包含理论知识的细节展开。

    DCGAN

    DCGAN,实在GAN的基础上,使用卷积网络替换了原有的G和D中的全连接层,在图像生成上具有较好的表现。

    GAN的结构示意图
    初始准备

    使用的输入图像的大小为(64,64,3)彩色的RGB图像。在DCGAN中比较重要的一点是,G生成的图像大小需要和真实图片大小保持一致性,确定G的输入大小后,可以设计G的基本结构。

    Generator

    G的作用是将一个噪声z使用反卷积的操作,将其拉伸到真实数据大小,反卷积的参数是可以学习,所以与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中的反卷积输入大小可以参考如下计算公式,在输入n_k = n_h=16,k_h=k_w=4,p_h=p_w = 1,s_w=s_h=2的情况下输出大小为(32,32),也就w,h增大一倍
    \begin{aligned} n_h^{'} \times n_w^{'} &= [(n_h k_h - (n_h-1)(k_h-s_h)- 2p_h] \times [(n_w k_w - (n_w-1)(k_w-s_w)- 2p_w]\\ &= [(k_h + s_h (n_h-1)- 2p_h] \times [(k_w + s_w (n_w-1)- 2p_w]\\ &= [(4 + 2 \times (16-1)- 2 \times 1] \times [(4 + 2 \times (16-1)- 2 \times 1]\\ &= 32 \times 32 .\\ \end{aligned}
    在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的思路使得神经网络具有了可控的创造性,但距离人的差距还是很大。

    相关文章

      网友评论

          本文标题:STEP-8:Pytorch-从0实现DCGAN

          本文链接:https://www.haomeiwen.com/subject/uuqvqhtx.html