基于PyTorch搭建你的生成对抗性网络

news2024/11/18 11:29:12

cd0651e77ef4cd23df4cfc0475b1bb07.jpeg

前言

你听说过GANs吗?还是你才刚刚开始学?GANs是2014年由蒙特利尔大学的学生 Ian Goodfellow 博士首次提出的。GANs最常见的例子是生成图像。有一个网站包含了不存在的人的面孔,便是一个常见的GANs应用示例。也是我们将要在本文中进行分享的。

生成对抗网络由两个神经网络组成,生成器和判别器相互竞争。我将在后面详细解释每个步骤。希望在本文结束时,你将能够从零开始训练和建立自己的生财之道对抗性网络。所以闲话少说,让我们开始吧。

目录

步骤0: 导入数据集

步骤1: 加载及预处理图像

步骤2: 定义判别器算法

步骤3: 定义生成器算法

步骤4: 编写训练算法

步骤5: 训练模型

步骤6: 测试模型

步骤0: 导入数据集

第一步是下载并将数据加载到内存中。我们将使用 CelebFaces Attributes Dataset (CelebA)来训练你的对抗性网络。主要分以下三个步骤:

1. 下载数据集:

https://s3.amazonaws.com/video.udacity-data.com/topher/2018/November/5be7eb6f_processed-celeba-small/processed-celeba-small.zip;

2. 解压缩数据集;

3. Clone 如下 GitHub地址:

https://github.com/Ahmad-shaikh575/Face-Generation-using-GANS

这样做之后,你可以在 colab 环境中打开它,或者你可以使用你自己的 pc 来训练模型。

导入必要的库

#import the neccessary libraries
import pickle as pkl
import matplotlib.pyplot as plt
import numpy as np
import torch.nn as nn
import torch.nn.functional as F
import torch
from torchvision import datasets
from torchvision import transforms
import torch
import torch.optim as optim

步骤1: 加载及预处理图像

在这一步中,我们将预处理在前一节中下载的图像数据。

将采取以下步骤:

  1. 调整图片大小

  2. 转换成张量

  3. 加载到 PyTorch 数据集中

  4. 加载到 PyTorch DataLoader 中

# Define hyperparameters
batch_size = 32
img_size = 32
data_dir='processed_celeba_small/'

# Apply the transformations
transform = transforms.Compose([transforms.Resize(image_size)
                                    ,transforms.ToTensor()])
# Load the dataset
imagenet_data = datasets.ImageFolder(data_dir,transform= transform)

# Load the image data into dataloader
celeba_train_loader = torch.utils.data.DataLoader(imagenet_data,
                                          batch_size,
                                          shuffle=True)

图像的大小应该足够小,这将有助于更快地训练模型。Tensors 基本上是 NumPy 数组,我们只是将图像转换为在 PyTorch 中所必需的 NumPy 数组。

然后我们加载这个转换成的 PyTorch 数据集。在那之后,我们将把我们的数据分成小批量。这个数据加载器将在每次迭代时向我们的模型训练过程提供图像数据。

随着数据的加载完成。现在,我们可以预处理图像。

图像的预处理

我们将在训练过程中使用 tanh 激活函数。该生成器的输出范围在 -1到1之间。我们还需要对这个范围内的图像进行缩放。代码如下所示:

def scale(img, feature_range=(-1, 1)):
  '''
  Scales the input image into given feature_range
  '''
    min,max = feature_range
    img = img * (max-min) + min
    return img

这个函数将对所有输入图像缩放,我们将在后面的训练中使用这个函数。

现在我们已经完成了无聊的预处理步骤。

接下来是令人兴奋的部分,现在我们需要为我们的生成器和判别器神经网络编写代码。

步骤2: 定义判别器算法

97127e11857e4e2d08f27fe2e2848c52.png

判别器是一个可以区分真假图像的神经网络。真实的图像和由生成器生成的图像都将提供给它。

我们将首先定义一个辅助函数,这个辅助函数在创建卷积网络层时非常方便。

# helper conv function
def conv(in_channels, out_channels, kernel_size, stride=2, padding=1, batch_norm=True):
    layers = []
    conv_layer = nn.Conv2d(in_channels, out_channels, 
                           kernel_size, stride, padding, bias=False)
    
    #Appending the layer
    layers.append(conv_layer)
    #Applying the batch normalization if it's given true
    if batch_norm:
        layers.append(nn.BatchNorm2d(out_channels))
     # returning the sequential container
    return nn.Sequential(*layers)

这个辅助函数接收创建任何卷积层所需的参数,并返回一个序列化的容器。现在我们将使用这个辅助函数来创建我们自己的判别器网络。

class Discriminator(nn.Module):

    def __init__(self, conv_dim):
        super(Discriminator, self).__init__()

        self.conv_dim = conv_dim

        #32 x 32
        self.cv1 = conv(3, self.conv_dim, 4, batch_norm=False)
        #16 x 16
        self.cv2 = conv(self.conv_dim, self.conv_dim*2, 4, batch_norm=True)
        #4 x 4
        self.cv3 = conv(self.conv_dim*2, self.conv_dim*4, 4, batch_norm=True)
        #2 x 2
        self.cv4 = conv(self.conv_dim*4, self.conv_dim*8, 4, batch_norm=True)
        #Fully connected Layer
        self.fc1 = nn.Linear(self.conv_dim*8*2*2,1)
        

    def forward(self, x):
        # After passing through each layer
        # Applying leaky relu activation function
        x = F.leaky_relu(self.cv1(x),0.2)
        x = F.leaky_relu(self.cv2(x),0.2)
        x = F.leaky_relu(self.cv3(x),0.2)
        x = F.leaky_relu(self.cv4(x),0.2)
        # To pass throught he fully connected layer
        # We need to flatten the image first
        x = x.view(-1,self.conv_dim*8*2*2)
        # Now passing through fully-connected layer
        x = self.fc1(x)
        return x

步骤3: 定义生成器算法

d30da4cfe3a34a4e28baacfc833e75f8.png

正如你们从图中看到的,我们给网络一个高斯矢量或者噪声矢量,它输出 s 中的值。图上的“ z”表示噪声,右边的 G (z)表示生成的样本。

与判别器一样,我们首先创建一个辅助函数来构建生成器网络,如下所示:

def deconv(in_channels, out_channels, kernel_size, stride=2, padding=1, batch_norm=True):
    layers = []
    convt_layer = nn.ConvTranspose2d(in_channels, out_channels, 
                           kernel_size, stride, padding, bias=False)
    
    # Appending the above conv layer
    layers.append(convt_layer)

    if batch_norm:
        # Applying the batch normalization if True
        layers.append(nn.BatchNorm2d(out_channels))
     
    # Returning the sequential container
    return nn.Sequential(*layers)

现在,是时候构建生成器网络了! !

class Generator(nn.Module):
    
    def __init__(self, z_size, conv_dim):
        super(Generator, self).__init__()

        self.z_size = z_size
        
        self.conv_dim = conv_dim
        
        #fully-connected-layer
        self.fc = nn.Linear(z_size, self.conv_dim*8*2*2)
        #2x2
        self.dcv1 = deconv(self.conv_dim*8, self.conv_dim*4, 4, batch_norm=True)
        #4x4
        self.dcv2 = deconv(self.conv_dim*4, self.conv_dim*2, 4, batch_norm=True)
        #8x8
        self.dcv3 = deconv(self.conv_dim*2, self.conv_dim, 4, batch_norm=True)
        #16x16
        self.dcv4 = deconv(self.conv_dim, 3, 4, batch_norm=False)
        #32 x 32

    def forward(self, x):
        # Passing through fully connected layer
        x = self.fc(x)
        # Changing the dimension
        x = x.view(-1,self.conv_dim*8,2,2)
        # Passing through deconv layers
        # Applying the ReLu activation function
        x = F.relu(self.dcv1(x))
        x= F.relu(self.dcv2(x))
        x= F.relu(self.dcv3(x))
        x= F.tanh(self.dcv4(x))
        #returning the modified image
        return x

为了使模型更快地收敛,我们将初始化线性和卷积层的权重。根据相关研究论文中的描述:所有的权重都是从0中心的正态分布初始化的,标准差为0.02

我们将为此目的定义一个功能如下:

def weights_init_normal(m):
    classname = m.__class__.__name__
    # For the linear layers
    if 'Linear' in classname:
        torch.nn.init.normal_(m.weight,0.0,0.02)
        m.bias.data.fill_(0.01)
    # For the convolutional layers
    if 'Conv' in classname or 'BatchNorm2d' in classname:
        torch.nn.init.normal_(m.weight,0.0,0.02)

现在我们将超参数和两个网络初始化如下:

# Defining the model hyperparamameters
d_conv_dim = 32
g_conv_dim = 32
z_size = 100   #Size of noise vector


D = Discriminator(d_conv_dim)
G = Generator(z_size=z_size, conv_dim=g_conv_dim)
# Applying the weight initialization
D.apply(weights_init_normal)
G.apply(weights_init_normal)

print(D)
print()
print(G)

输出结果大致如下:

b0dbbadcdafb3a9f8bf28da76d19bd8f.png

判别器损失:

根据 DCGAN Research Paper 论文中描述:

        判别器总损失 = 真图像损失 + 假图像损失,即:d_loss = d_real_loss + d_fake_loss。

       不过,我们希望鉴别器输出1表示真正的图像和0表示假图像,所以我们需要设置的损失来反映这一点。

我们将定义双损失函数。一个是真正的损失,另一个是假的损失,如下:

def real_loss(D_out,smooth=False):
    
    batch_size = D_out.size(0)
    if smooth:
        labels = torch.ones(batch_size)*0.9
    else:
        labels = torch.ones(batch_size)
    
    labels = labels.to(device)
    criterion = nn.BCEWithLogitsLoss()
    loss = criterion(D_out.squeeze(), labels)
    return loss

def fake_loss(D_out):

    batch_size = D_out.size(0)
    labels = torch.zeros(batch_size)
    labels = labels.to(device)
    criterion = nn.BCEWithLogitsLoss()
    loss = criterion(D_out.squeeze(), labels)
    return loss

生成器损失:

根据 DCGAN Research Paper 论文中描述:

        生成器的目标是让判别器认为它生成的图像是真实的。

现在,是时候为我们的网络设置优化器了:

lr = 0.0005
beta1 = 0.3
beta2 = 0.999 # default value
# Optimizers
d_optimizer = optim.Adam(D.parameters(), lr, betas=(beta1, beta2))
g_optimizer = optim.Adam(G.parameters(), lr, betas=(beta1, beta2))

我将为我们的训练使用 Adam 优化器。因为它目前被认为是对GANs最有效的。根据上述介绍论文中的研究成果,确定了超参数的取值范围。他们已经尝试了它,这些被证明是最好的!超参数设置如下:

步骤4: 编写训练算法

我们必须为我们的两个神经网络编写训练算法。首先,我们需要初始化噪声向量,并在整个训练过程中保持一致。

# Initializing arrays to store losses and samples
samples = []
losses = []

# We need to initilialize fixed data for sampling
# This would help us to evaluate model's performance
sample_size=16
fixed_z = np.random.uniform(-1, 1, size=(sample_size, z_size))
fixed_z = torch.from_numpy(fixed_z).float()

对于判别器:

我们首先将真实的图像输入判别器网络,然后计算它的实际损失。然后生成伪造图像并输入判别器网络以计算虚假损失。

在计算了真实和虚假损失之后,我们对其进行求和,并采取优化步骤进行训练。

# setting optimizer parameters to zero
# to remove previous training data residue
d_optimizer.zero_grad()

# move real images to gpu memory
real_images = real_images.to(device)

# Pass through discriminator network
dreal = D(real_images)

# Calculate the real loss
dreal_loss = real_loss(dreal)

# For fake images

# Generating the fake images
z = np.random.uniform(-1, 1, size=(batch_size, z_size))
z = torch.from_numpy(z).float()

# move z to the GPU memory
z = z.to(device)

# Generating fake images by passing it to generator
fake_images = G(z)

# Passing fake images from the disc network        
dfake = D(fake_images)
# Calculating the fake loss
dfake_loss = fake_loss(dfake)

#Adding both lossess
d_loss = dreal_loss + dfake_loss
# Taking the backpropogation step
d_loss.backward()
d_optimizer.step()

对于生成器:

对于生成器网络的训练,我们也会这样做。刚才在通过判别器网络输入假图像之后,我们将计算它的真实损失。然后优化我们的生成器网络。

## Training the generator for adversarial loss
#setting gradients to zero
g_optimizer.zero_grad()

# Generate fake images
z = np.random.uniform(-1, 1, size=(batch_size, z_size))
z = torch.from_numpy(z).float()
# moving to GPU's memory
z = z.to(device)

# Generating Fake images
fake_images = G(z)

# Calculating the generator loss on fake images
# Just flipping the labels for our real loss function
D_fake = D(fake_images)
g_loss = real_loss(D_fake, True)

# Taking the backpropogation step
g_loss.backward()
g_optimizer.step()

步骤5: 训练模型

现在我们将开始100个epoch的训练: D

经过训练,损失的图表看起来大概是这样的:

297583a673349a29e8b1e16942b38a73.png

我们可以看到,判别器 Loss 是相当平滑的,甚至在100个epoch之后收敛到某个特定值。而生成器的Loss则飙升。

我们可以从下面步骤6中的结果看出,60个时代之后生成的图像是扭曲的。由此可以得出结论,60个epoch是一个最佳的训练节点。

步骤6: 测试模型

10个epoch之后:

96d12f9703a12ac0e77a0c2f87c3f146.png

20个epoch之后:

dc196f60f97d770c6d0c79c8f4d85ae7.png

30个epoch之后:

33365e822eda268d5e048d296cbea7be.png

40个epoch之后:

0198316b876a53571d5550f9f97556d5.png

50个epoch之后:

bdcf6ad6c3a1977c55cdd7f6fd9a4816.png

60个epoch之后:

4152c319441182ae8907a6b354fb6a44.png

70个epoch之后:

b48254e7675b521a66d059da034db13b.png

80个epoch之后:

e567311618525ee9d2e67e0292fe067d.png

90个epoch之后:

c933353f759c001a2d1cd8d9ec81f3ba.png

100个epoch之后:

0f83fe27e164d062d3afad75e7960e75.png

总结

我们可以看到,训练一个生成对抗性网络并不意味着它一定会产生好的图像。

从结果中我们可以看出,训练40-60个 epoch 的生成器生成的图像相对比其他更好。

您可以尝试更改优化器、学习速率和其他超参数,以使其生成更好的图像!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1210972.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

11.3SpringMVC

一.概念 1.SpringMvc: a.构建在Servlet(api)基础上. b.是一个Web框架(HTTP). c.来自于Spring webMVC模块. 2.MVC 二.注册路由的注解 1.RequestMapping("/test") // 路由注册 注意: 这个注解在类和方法上都要使用,代表不同等级的路由. 2.RestController a)R…

FPGA_边沿检测电路设计

FPGA_边沿检测电路设计 边沿检测原理图波形图分析实现方法方法一:与逻辑实现方法二:或逻辑实现方法三:与逻辑实现 边沿检测原理图 由状态转移表可以看出,其转换条件中需要检测到下降沿以及上升沿,而边沿检测其原理就是…

「题解」相交链表

🍉题目 题目链接 🍉解析 “提示”部分有提示链表数不为零,所以讨论链表为空的情况。 最简单粗暴的思路就是:遍历链表,先使用循环遍历A链表,然后嵌套循环遍历B,比对A、B是否存在地址相同的…

Windows10配置深度学习环境

一、Anaconda安装与虚拟环境创建 Anaconda的出现极大的方便了研究人员的Python开发工作,anaconda可以创建多个虚拟环境,这些虚拟环境就像一间间教室一样,虚拟环境彼此之间、虚拟环境与基础环境(base)之间互不影响&…

C++之Max

背景 想学习数据结构,这是看的课程的习题,讲课老师用的是类C语言,具体的实现还是得自己来。 准备工作 用开发工具Microsoft Visual Studio(VS)建立一个空白的C控制台项目 选择项目的存储路径 成功建立 我在 Microsoft Visual Studio中用…

VSCode配置msvc编译调试环境

1.VS Code简介 VS Code 使用 Electron 框架构建用户界面,该框架使用 Chromium 和 Node.js 构建桌面应用程序。这使得 VS Code 能够在 Windows、Linux 和 macOS 上运行,并且可以使用 Web 技术 (HTML、CSS 和 JavaScript) 构建用户界面。 VS Code 使用 Monaco 引擎来提供文本编辑…

OpenCV入门——图像视频的加载与展示一些API

文章目录 OpenCV创建显示窗口OpenCV加载显示图片OpenCV保存文件利用OpenCV从摄像头采集视频从多媒体文件中读取视频帧将视频数据录制成多媒体文件OpenCV控制鼠标关于[np.uint8](https://stackoverflow.com/questions/68387192/what-is-np-uint8) OpenCV中的TrackBar控件TrackBa…

antd中的form表单数据不更新

antd中的form表单 initialValue导致数据不更新问题 理解 : initialValue就是所谓的defaultValue,只会在第一次赋值的时候改变,却又有一些不同,因为 initialValue又会因其他改动而改变。 解决: form.resetFields();

内网Jenkins 部署.net(dotnet)项目

一、前置条件 内网部署Jenkins,并安装好所需插件 此篇内容需承接内网搭建Jenkins自动化远程部署项目到Windows服务器_jenkins内网安装-CSDN博客 ,才更好操作与理解 二、在Jenkins中创建项目 三、配置项目 General Source Code Management Build Envi…

2023最新版本 从零基础入门C++与QT(学习笔记) -4- C/C++混合编程

🎏在C兼容C只需要调用C头文件 🎄格式 🎈 -1- extern(关键字) “C”{ }(花括号) 🎈 -2- 花括号里面填写要包含的头文件 🎄代码段格式 extern "C" {#include “stdio.h”#include “string.h” }&#x…

2023深圳高交会,11月15-19日在深圳会展中心盛大开幕,展出五天时间

跨越山海,共赴鹏城。 11月15日至19日,第二十五届中国国际高新技术成果交易会在深圳会展中心(福田展区)和深圳国际会展中心(宝安展区)两馆同时举行。一场不可错过的全球性高科技盛会如期而至。 科技赋能发…

2023年数据泄露事件频发,企业应该如何做好数据安全保护?

在当今数字化时代,每时每刻都会产生大量的数据,这些数据包含大量的敏感信息,网络犯罪者可以利用这些数据获取巨大的利益,由此他们会通过技术攻击、网络钓鱼等各种非法手段来获取,而这也导致数据泄露事件频频发生。 以下…

冰点还原精灵Deep Freeze for mac版

Deep Freeze是一种系统恢复软件,它可以保护计算机系统免受恶意软件和不必要的更改。它的基本功能是在计算机重启后恢复到原始状态,即使用户进行了任何更改也不例外。 Deep Freeze主要用于公共场所的计算机,如图书馆、学校实验室和互联网咖啡馆…

JavaScript基础入门05

目录 1.操作结点 1.1新增节点 1.1.1. 创建元素节点 1.1.2. 插入节点到 dom 树中 1.2删除节点 2.代码案例: 猜数字 2.1预期效果 2.2代码实现 3.代码案例: 表白墙 3.1预期效果 3.2创建页面布局 3.3调整样式 3.4实现提交 3.5实现点击按钮的动画效果 4.代码案例: 待办…

小波神经网络的时间序列预测——短时交通流量预测

大家好,我是带我去滑雪! 小波神经网络(Wavelet Neural Network,WNN)结合了小波变换和神经网络的特性,是一种在信号处理和模式识别领域应用广泛的神经网络模型。它的设计灵感来自于小波变换的多尺度分析特性…

Hive 查询优化

Hive 查询优化 -- 本地 set mapreduce.framework.namelocal; set hive.exec.mode.local.autotrue; set mapperd.job.trackerlocal; -- yarn set mapreduce.framework.nameyarn; set hive.exec.mode.local.autofalse; set mapperd.job.trackeryarn-- 向量模式 set hive.vectori…

算法萌新闯力扣:搜索插入位置

力扣热题:搜索插入位置 开篇 对于想要学会二分查找的算法萌新来说,这道题是非常友好的学习题目,完完全全按照二分查找模版就可以完成。大伙儿还不赶紧练起来! 题目链接:35.搜索插入位置 题目描述 代码思路 这道题时完完全全的…

使用VC++设计程序:对于一幅256级灰度图像,求其一元熵值、二维熵值

数字图像处理–实验二B图像的一维熵与二维熵算法 本文主要是对图像进行一维熵以及二维熵的计算,下面附有实现的代码 文章目录 数字图像处理--实验二B图像的一维熵与二维熵算法一、 实验内容二、 一维熵1. 一维熵的定义2. 一维熵的C代码实现 三、 二维熵1. 二维熵的定…

JSP注释方式演示 讲解显式与隐式注释

好 今天我们来了解一下jsp中的注释哦 它支持两种注释: 显式注释/隐式注释 显式注释 是 允许被客户端看到的 就是 打开浏览器 用查看源码方式能看到的注释 与之对应 隐式注释 就是 在客户端 是无法看到这些注释信息的 显式注释 的语法就是html的注释语法 <!-- 显式注释 --…

03-学成在线内容管理模块之课程查询

课程查询 需求分析 教学机构人员点击课程管理按钮进入课程查询界面,在课程列表页面输入查询条件查询课程的信息 当不输入查询条件时默认会全部课程信息,输入查询条件会查询符合条件的课程信息,约束条件是本教学机构查询本机构的课程信息 数据模型(model工程) 课程查询功能…