美文网首页
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