[DL](DCGAN)生成对抗网络代码实现

DCGAN,全称是 Deep Convolution Generative Adversarial Networks(深度卷积生成对抗网络),该模型在 Original GAN 的理论基础上,开创性地将CNNGAN相结合以实现对图像的处理。

GANs是一个深度学习模型框架,用于获取训练数据的分布,这样我们就可以从同一分布中生成新的数据。GANs是Ian Goodfellow在2014年提出的,并在论文Generative Adversarial Net中进行了首次描述。

它们由两个不同的模型组成,分别是生成器判别器。生成器的工作是生成看起来像训练图像的假图。判别器的任务是判别一张图像是真实的训练图像还是来自生成器的伪图像。在训练过程中,生成器通过生成越来越像真实图像的伪图来尝试骗过判别器,而判别器则是努力地想成为更好的侦探,这样才能正确地对真实和伪造的图像进行分类。

博弈的平衡点是当生成器生成的伪造图像看起来像直接来自训练数据,而判别器始终以50%的置信度推测生成器的输出是真的还是假的。

训练过程中,生成器在生成逼真图像方面逐渐变强,而判别器在辨别这些图像的能力上逐渐变强。当判别器不再能够区分真实图片和伪造图片时,训练过程达到平衡。

数据集:
手写数字,7、8的图片
在这里插入图片描述

链接:数据集百度网盘链接
提取码:doa4

数据集图片在data\img_78\ 路径下

在这里插入图片描述

data文件夹和代码放在同级目录

在这里插入图片描述

# 导入软件包
import random
import math
import time
import pandas as pd
import numpy as np
from PIL import Image

import torch
import torch.utils.data as data
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
from torchvision import transforms
import os
os.environ['KMP_DUPLICATE_LIB_OK']='True'
# Setup seeds
torch.manual_seed(1234)
np.random.seed(1234)
random.seed(1234)

Generator生成器的实现

生成器的目标是生成与真实图像相似的假图像,从而欺骗判别器模型。生成器的训练过程通常是通过优化生成图像与真实图像的差异来实现。

class Generator(nn.Module):
    '''
    实现生成器,利用转置卷积或微步卷积升维,生成64*64的灰度图像
    '''
    def __init__(self, z_dim=20, image_size=64):
        super(Generator, self).__init__()

        self.layer1 = nn.Sequential(
            nn.ConvTranspose2d(z_dim, 512, kernel_size=4, stride=1, padding=0),
            nn.BatchNorm2d(512),
            nn.ReLU(inplace=True)
        )
        self.layer2 = nn.Sequential(
            nn.ConvTranspose2d(512, 256, kernel_size=4, stride=2, padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(inplace=True)
        )
        self.layer3 = nn.Sequential(
            nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(inplace=True)
        )
        self.layer4 = nn.Sequential(
            nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True)
        )
        self.last = nn.ConvTranspose2d(64, 1, kernel_size=4, stride=2, padding=1)
        self.tanh = nn.Tanh()

    def forward(self, z):
        # Forward pass through layers
        z = self.layer1(z)
        z = self.layer2(z)
        z = self.layer3(z)
        z = self.layer4(z)
        z = self.tanh(self.last(z))
        return z

随机生成20维噪声,传入生成器模型G,得到生成的假图像fake_images(单通道、64x64像素大小的图像)

#生成器测试
import matplotlib.pyplot as plt
%matplotlib inline

G = Generator(z_dim=20, image_size=64)

# 输入的随机数
input_z = torch.randn(1, 20)

# 将张量尺寸变形为(1,20,1,1)
input_z = input_z.view(input_z.size(0), input_z.size(1), 1, 1)

#输出假图像
fake_images = G(input_z)

img_transformed = fake_images[0][0].detach().numpy()
plt.imshow(img_transformed, 'gray')
plt.show()

生成器测试输出

Discriminator判别器的实现

辨别器模型输出一个标量值,表示输入图像是真实图像的概率。辨别器的训练目标是最大化真实图像被判别为真实图像的概率,同时最小化生成假图像被判别为真实图像的概率。

class Discriminator(nn.Module):
    '''
        实现辨别器
    '''
    def __init__(self, z_dim=20, image_size=64):
        super(Discriminator, self).__init__()

        # Convolutional layers
        self.conv1 = nn.Conv2d(1, 64, kernel_size=4, stride=2, padding=1)
        self.conv2 = nn.Conv2d(64, 128, kernel_size=4, stride=2, padding=1)
        self.conv3 = nn.Conv2d(128, 256, kernel_size=4, stride=2, padding=1)

        # Fully connected layer
        self.fc = nn.Linear(256 * (image_size // 8) * (image_size // 8), 1)

    def forward(self, x):
        # Convolutional layers with leaky relu activation
        x = nn.functional.leaky_relu(self.conv1(x), 0.2)
        x = nn.functional.leaky_relu(self.conv2(x), 0.2)
        x = nn.functional.leaky_relu(self.conv3(x), 0.2)

        # Flatten the output for the fully connected layer
        x = x.view(x.size(0), -1)

        # Fully connected layer
        x = self.fc(x)

        return x

通过生成器模型G生成一个伪造的图像,将生成的伪造图像fake_images输入到辨别器D中,得到输出值d_out。这个输出值表示了辨别器对于输入图像是真实图像的概率。通过Sigmoid将其范围限制在0到1之间。

获取辨别器对于生成的伪造图像的判别结果,输出值接近1,则说明辨别器认为生成的图像是真实图像的概率较高;如果接近0,则说明辨别器认为生成的图像是伪造的概率较高。

#辨别器测试
D = Discriminator(z_dim=20, image_size=64)

#生成伪造图像
input_z = torch.randn(1, 20)
input_z = input_z.view(input_z.size(0), input_z.size(1), 1, 1)
fake_images = G(input_z)

#将伪造的图像输入判别器D中
d_out = D(fake_images)

#将输出值d_out乘以Sigmoid函数,将其转换成0~1的值
print(nn.Sigmoid()(d_out))

输出:
在这里插入图片描述

DataLoader 的实现

def make_datapath_list():
    """创建用于学习和验证的图像数据及标注数据的文件路径列表。 """

    train_img_list = list() #保存图像文件的路径

    for img_idx in range(200):
        img_path = "./data/img_78/img_7_" + str(img_idx)+'.jpg'
        train_img_list.append(img_path)

        img_path = "./data/img_78/img_8_" + str(img_idx)+'.jpg'
        train_img_list.append(img_path)

    return train_img_list

class ImageTransform():
    """图像的预处理类"""

    def __init__(self, mean, std):
        self.data_transform = transforms.Compose([
            transforms.ToTensor(),
            transforms.Normalize(mean, std)
        ])

    def __call__(self, img):
        return self.data_transform(img)

class GAN_Img_Dataset(data.Dataset):
    """图像的 Dataset 类,继承自 PyTorchd 的 Dataset 类"""

    def __init__(self, file_list, transform):
        self.file_list = file_list
        self.transform = transform

    def __len__(self):
        '''返回图像的张数'''
        return len(self.file_list)

    def __getitem__(self, index):
        '''获取经过预处理后的图像的张量格式的数据'''

        img_path = self.file_list[index]
        img = Image.open(img_path)  #[ 高度 ][ 宽度 ] 黑白

        #图像的预处理
        img_transformed = self.transform(img)

        return img_transformed

#创建DataLoader并确认执行结果

#创建文件列表
train_img_list=make_datapath_list()

#创建Dataset
mean = (0.5,)
std = (0.5,)
train_dataset = GAN_Img_Dataset(
    file_list=train_img_list, transform=ImageTransform(mean, std))

#创建DataLoader
batch_size = 64

train_dataloader = torch.utils.data.DataLoader(
    train_dataset, batch_size=batch_size, shuffle=True)

#确认执行结果
batch_iterator = iter(train_dataloader)  #转换为迭代器
imges = next(batch_iterator)  
print(imges.size())  # torch.Size([64, 1, 64, 64])

在这里插入图片描述

DCGAN的学习

#网络的初始化处理
def weights_init(m):
    classname = m.__class__.__name__
    if classname.find('Conv') != -1:
        #Conv2d和ConvTranspose2d的初始化
        nn.init.normal_(m.weight.data, 0.0, 0.02)
        nn.init.constant_(m.bias.data, 0)
    elif classname.find('BatchNorm') != -1:
        #BatchNorm2d的初始化
        nn.init.normal_(m.weight.data, 1.0, 0.02)
        nn.init.constant_(m.bias.data, 0)


#开始初始化
G.apply(weights_init)
D.apply(weights_init)

print("网络已经成功地完成了初始化")

在这里插入图片描述

def train_model(G, D, dataloader, num_epochs):

    #确认是否能够使用GPU加速
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    print("使用设备:", device)

    #设置最优化算法
    g_lr, d_lr = 0.0001, 0.0004
    beta1, beta2 = 0.0, 0.9
    g_optimizer = torch.optim.Adam(G.parameters(), g_lr, [beta1, beta2])
    d_optimizer = torch.optim.Adam(D.parameters(), d_lr, [beta1, beta2])

    #定义误差函数
    criterion = nn.BCEWithLogitsLoss(reduction='mean')

    #使用硬编码的参数
    z_dim = 20
    mini_batch_size = 64

    #将网络载入GPU中
    G.to(device)
    D.to(device)

    G.train()  #将模式设置为训练模式
    D.train()  #将模式设置为训练模式

    #如果网络相对固定,则开启加速
    torch.backends.cudnn.benchmark = True

    #图像张数
    num_train_imgs = len(dataloader.dataset)
    batch_size = dataloader.batch_size

    #设置迭代计数器
    iteration = 1
    logs = []

    #epoch循环
    for epoch in range(num_epochs):

        #保存开始时间
        t_epoch_start = time.time()
        epoch_g_loss = 0.0  #epoch的损失总和
        epoch_d_loss = 0.0  #epoch的损失总和

        print('-------------')
        print('Epoch {}/{}'.format(epoch, num_epochs))
        print('-------------')
        print('(train)')

        #以minibatch为单位从数据加载器中读取数据的循环
        for imges in dataloader:

            # --------------------
            #1.判别器D的学习
            # --------------------
            #如果小批次的尺寸设置为1,会导致批次归一化处理产生错误,因此需要避免
            if imges.size()[0] == 1:
                continue

            #如果能使用GPU,则将数据送入GPU中
            imges = imges.to(device)
            mini_batch_size = imges.size()[0]
            label_real = torch.full((mini_batch_size,), 1, dtype=torch.float32).to(device)
            label_fake = torch.full((mini_batch_size,), 0, dtype=torch.float32).to(device)
            #创建正确答案标签和伪造数据标签
            #在epoch最后的迭代中,小批次的数量会减少

           #对真正的图像进行判定
            d_out_real = D(imges)

            #生成伪造图像并进行判定
            input_z = torch.randn(mini_batch_size, z_dim).to(device)
            input_z = input_z.view(input_z.size(0), input_z.size(1), 1, 1)
            fake_images = G(input_z)
            d_out_fake = D(fake_images)

            #计算误差
            
            d_loss_real = criterion(d_out_real.view(-1), label_real)
            d_loss_fake = criterion(d_out_fake.view(-1), label_fake)
            d_loss = d_loss_real + d_loss_fake

            #反向传播处理
            g_optimizer.zero_grad()
            d_optimizer.zero_grad()

            d_loss.backward()
            d_optimizer.step()

            # --------------------
            #2.生成器G的学习
            # --------------------
            #生成伪造图像并进行判定
            input_z = torch.randn(mini_batch_size, z_dim).to(device)
            input_z = input_z.view(input_z.size(0), input_z.size(1), 1, 1)
            fake_images = G(input_z)
            d_out_fake = D(fake_images)

            #计算误差
            
            g_loss = criterion(d_out_fake.view(-1), label_real)

            #反向传播处理
            g_optimizer.zero_grad()
            d_optimizer.zero_grad()
            g_loss.backward()
            g_optimizer.step()

            # --------------------
            #3.记录结果
            # --------------------
            epoch_d_loss += d_loss.item()
            epoch_g_loss += g_loss.item()
            iteration += 1

       #epoch的每个phase的loss和准确率
        t_epoch_finish = time.time()
        print('-------------')
        print('epoch {} || Epoch_D_Loss:{:.4f} ||Epoch_G_Loss:{:.4f}'.format(
            epoch, epoch_d_loss/batch_size, epoch_g_loss/batch_size))
        print('timer:  {:.4f} sec.'.format(t_epoch_finish - t_epoch_start))
        t_epoch_start = time.time()

    return G, D

#执行学习和验证操作
num_epochs = 200
G_update, D_update = train_model(
    G, D, dataloader=train_dataloader, num_epochs=num_epochs)

在这里插入图片描述

#将生成的图像和训练数据可视化
#反复执行本单元中的代码,直到生成感觉良好的图像为止

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

#生成用于输入的随机数
batch_size = 8
z_dim = 20
fixed_z = torch.randn(batch_size, z_dim)
fixed_z = fixed_z.view(fixed_z.size(0), fixed_z.size(1), 1, 1)

#生成图像
fake_images = G_update(fixed_z.to(device))

#训练数据
batch_iterator = iter(train_dataloader) #转换成迭代器
imges = next(batch_iterator)  #取出位于第一位的元素


#输出结果
fig = plt.figure(figsize=(15, 6))
for i in range(0, 5):
    #将训练数据放入上层
    plt.subplot(2, 5, i+1)
    plt.imshow(imges[i][0].cpu().detach().numpy(), 'gray')

    #将生成数据放入下层
    plt.subplot(2, 5, 5+i+1)
    plt.imshow(fake_images[i][0].cpu().detach().numpy(), 'gray')

输出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值