GAN对抗生成网络学习笔记(三)DCGAN原理

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


提示:以下是本篇文章正文内容,下面案例可供参考

一、DCGAN简介

DCGAN全称Deep Convolutional Generative Adversarial Networks,中文名曰深度卷积对抗网络。
论文地址

CNN即卷积网络,在DCGAN中,判别器实际上就是一个CNN网络,输入一张图片然后输出 yes or no 的概率。
在这里插入图片描述
那么在DCGAN中,G网络的模型又是什么呢?
G网络刚好和CNN相反,它是由noise通过G网络生成一张图片,因为图片通过layer逐渐变大,这与卷积操作所造成的结果刚好相反,因此我们可以称之为反卷积。

1.1 DCGAN的特点

DCGAN除了G网络与CNN不同之外,它还有以下的不同:
1.取消所有pooling层。G网络中使用转置卷积(transposed convolutional layer)进行上采样,D网络中用加入stride的卷积代替pooling。
2.除了生成器模型的输出层和判别器模型的输入层,在网络其它层上都使用了Batch Normalization,使用BN可以稳定学习,有助于处理初始化不良导致的训练问题。
3.G网络中使用ReLu作为激活函数,最后一层使用tanh
4.D网络中使用LeakyRelu作为激活函数。

二、几个重要概念

2.1 下采样(SubSampled)

下采样实际上就是缩小图像,主要目的是为了使得图像符合显示区域的大小,生成对应图像的缩略图。比如说在CNN中得池化层或卷积层就是下采样。不过卷积过程导致的图像变小是为了提取特征,而池化下采样是为了降低特征的维度。

2.2 上采样(UpSampled)

有下采样也就必然有上采样,上采样实际上就是放大图像,指的是任何可以让图像变成更高分辨率的技术,这个时候我们也就能理解为什么在G网络中能够由噪声生成一张图片了。
它有反卷积(Deconvolution)、上池化(UnPooling)方法。这里我们只介绍反卷积,因为这是是我们需要用到的。

2.3反卷积(Deconvolution)

反卷积(Deconvolution)也称为分数步长的卷积和转置卷积(transposed convolution)。在下图中,左边的为卷积,右边的为反卷积。convolution过程是将4×4的图像映射为2×2的图像,而反卷积过程则是将2×2的图像映射为4×4的图像,两者的kernel size均为3。不过显而易见,反卷积只能恢复图片的尺寸大小,而不能准确的恢复图片的像素值(此时我们想一想,在CNN中,卷积层的kernel我们可以学习,那么在反卷积中的kernel我们是不是也可以学习呢?)。
在这里插入图片描述
具体的转置卷积可以参看另一篇博客。

扫描二维码关注公众号,回复: 14830542 查看本文章

三、G模型

下图是GCGAN的大体框架图,在生成器中,使用反卷积生成图像,在判别器中使用卷积进行判别。
在这里插入图片描述
下图是在Deep Convolutional Generative Adversarial Networks论文中介绍的DCGAN生成器。该网络接收一个表示为z的100x1噪声矢量,通过一系列layer,最终将noise映射到64x64x3的图像中。
在这里插入图片描述
上述的过程实际上就是将一个1×100的向量变成64×64×3的图片。

1.Project and reshape:将1×100通过骚操作变成4×4×1024的向量。这里我们可以使用全连接层加卷积的方法。
2.CONV:反卷积

三、实现代码:

Generator:

import torch
import torch.nn as nn
from torch import Tensor
from typing import List


class Generator(nn.Module):
    def __init__(self,in_channel:int,out_channel:int,kernel_size:List[int]=[5,5,5,5],padding:List[int]=[2,2,2,2],stride:List[int]=[2,2,2,2]):
        super(Generator,self).__init__()
        self.in_channel=in_channel
        self.last_out_channel=out_channel
        self.kernel_size=kernel_size
        self.padding=padding
        self.stride=stride
        self.project_layer=nn.Linear(self.in_channel,1024*4*4)
        self.in_channel=1024
        self.main=self._make_layers(self.kernel_size,self.padding,self.stride)
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.normal_(m.weight, 0.0, 0.02)
            if isinstance(m,nn.BatchNorm2d):
                nn.init.normal_(m.weight,1.0, 0.02)


    def _make_layers(self,kernel_size:List[int],padding:List[int],stride:List[int]):
        layers=[]
        for i in range(len(kernel_size)-1):
            self.out_channel=self.in_channel//2
            layer=[nn.ConvTranspose2d(self.in_channel,self.out_channel,kernel_size[i],output_padding=1,padding=padding[i],stride=stride[i],bias=False),
                   nn.BatchNorm2d(self.out_channel),
                   nn.ReLU(inplace=True)
                   ]
            layers.extend(layer)
            self.in_channel=self.out_channel
            self.out_channel=self.in_channel//2
        layers.extend([nn.ConvTranspose2d(self.in_channel,self.last_out_channel,kernel_size[i],output_padding=1,padding=padding[i],stride=stride[i]),
                       nn.Tanh()
                       ])
        return nn.Sequential(*layers)

    def forward(self,inputs:Tensor):
        batch_size=inputs.shape[0]
        proj=self.project_layer(inputs)
        reshape_proj=torch.reshape(proj,(batch_size,1024,4,4))
        out=self.main(reshape_proj)
        return out



if __name__=="__main__":

    generator=Generator(100,3)
    print(generator)

Discriminator

import torch
import torch.nn as nn
from torch import Tensor
from typing import List

class Discriminator(nn.Module):
    def __init__(self,in_channel:int,last_out_channel:int,stride:List[int]=[2,2,2,2],padding:List[int]=[2,2,2,2],kernel_size:List[int]=[5,5,5,5]):
        super(Discriminator,self).__init__()
        self.main=self._make_layer(in_channel,last_out_channel,stride,padding,kernel_size)
        self.fc1=nn.Linear(4*4*512,1)
        self.sigmoid=nn.Sigmoid()
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.normal_(m.weight.data, 0.0, 0.02)
            if isinstance(m, nn.BatchNorm2d):
                nn.init.normal_(m.weight, 1.0, 0.02)

    def _make_layer(self,in_channel,last_out_channel,stride:List[int],padding:List[int],kernel_size:List[int]):
        layers=[]

        for i in range(len(stride)-1):
            out_channel=max(in_channel*2,64)
            layer=[nn.Conv2d(in_channel,out_channel,kernel_size=kernel_size[i],padding=padding[i],stride=stride[i],bias=False),
                   nn.BatchNorm2d(out_channel),
                   nn.LeakyReLU(0.2,inplace=True),
                   ]
            in_channel=out_channel
            layers.extend(layer)
        layers.extend([nn.Conv2d(in_channel,last_out_channel,kernel_size=kernel_size[i],padding=padding[i],stride=stride[i]),
                       ])
        return nn.Sequential(*layers)

    def forward(self,inputs:Tensor)->Tensor:
        out=self.main(inputs)
        out=torch.flatten(out,start_dim=1)
        out=self.sigmoid(self.fc1(out))
        return out


if __name__=="__main__":
    fixed_noise = torch.randn(1,100)
    NetD=Discriminator(3,last_out_channel=512)
    print(NetD)

train脚本:

import torch
import torch.nn as nn
from data_set import MyDataset
from torch.utils.data import DataLoader
from torchvision import transforms
from tqdm import tqdm
from model.generater import Generator
from model.discriminator import Discriminator

def train(epochs:int=10,lr=2e-4):
    real_label = 1
    fake_label = 0
    device="cuda:0" if torch.cuda.is_available() else "cpu"
    my_transform = transforms.Compose([transforms.ToTensor(),
                                       transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
                                       ])
    dataset = MyDataset(transform=my_transform)
    train_loader = DataLoader(dataset, batch_size=32, shuffle=True)
    NetD = Discriminator(3, last_out_channel=512).to(device)
    NetG=Generator(100,3).to(device)
    loss_func=nn.BCELoss()
    optimizer_D=torch.optim.Adam(NetD.parameters(),lr=lr,betas=(0.5,0.999))
    optimizer_G=torch.optim.Adam(NetG.parameters(),lr=lr,betas=(0.5,0.999))
    for epoch in range(epochs):
        train_bar=tqdm(train_loader)
        for data in train_bar:
            optimizer_D.zero_grad()
            b_size = data.shape[0]
            label = torch.full((b_size,), real_label,dtype=torch.float32).to(device)
            # 分两步训练 是 ganhacks的建议
            output = NetD(data.to(device)).view(-1).to(device)
            loss_D_real=loss_func(output,label)
            loss_D_real.backward()
            noise = torch.randn(b_size,100).to(device)
            fake=NetG(noise).to(device)
            label.fill_(fake_label).to(device)
            output = NetD(fake.detach()).view(-1)
            loss_D_fake = loss_func(output, label)
            loss_D_fake.backward()
            loss_D_all=loss_D_real+loss_D_fake
            optimizer_D.step()
            NetG.zero_grad()
            label.fill_(real_label)
            output = NetD(fake).view(-1)
            errG = loss_func(output, label)
            errG.backward()
            optimizer_G.step()
            train_bar.desc = "train epoch[{}/{}] loss_D:{:.3f}   loss_G:{:.3f}".format(epoch + 1,
                                                                     epochs,
                                                                     loss_D_all,errG)


if __name__=="__main__":
    train()

猜你喜欢

转载自blog.csdn.net/weixin_43869415/article/details/121817179