gan的学习笔记

学习gan我参考的是这篇文章 链接

为了避免四处收集资料,所以把学到的全记到这里。

import argparse
import os
import numpy as np
import math
import torchvision.transforms as transforms
from torchvision.utils import save_image
from torch.utils.data import DataLoader
from torchvision import datasets
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F
import torch

这是导入模块,按理说我应该搞懂这些模块,但是我现在只要知道这些模块是干嘛的就可以了

argparse这个模块是python解释命令行参数和选项的标准模块,作用是用于解析命令行参数,
命令行参数:
int main(int argc, char** argv)
这个argv就是命令行参数

numpy(Numerical Python)提供了python对多维数组对象的支持:ndarray,具有矢量运算能力,快速、节省空间。numpy支持高级大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库

torchvision主要由三个字包组成torchvision.datasets、torchvision.models、torchvision.transforms
这三个包的具体介绍在这个链接里官网
翻译

简单的说,
torchvision.datasets里包含有许多数据集
torchvision.model包含主流的网络模型
torchvision.transforms主要用于数据增强(data augmentation)

vision.datasets : 几个常用视觉数据集,可以下载和加载,这里主要的高级用法就是可以看源码如何自己写自己的Dataset的子类
vision.models : 流行的模型,例如 AlexNet, VGG, ResNet 和 Densenet 以及 与训练好的参数。
vision.transforms : 常用的图像操作,例如:随机切割,旋转,数据类型转换,图像到tensor ,numpy 数组到tensor , tensor 到 图像等。
vision.utils : 用于把形似 (3 x H x W) 的张量保存到硬盘中,给一个mini-batch的图像可以产生一个图像格网。

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述torch,nn应该是包含有建网络的一些类,用这个的话建网络会简单一点。

os.makedirs("images", exist_ok=True)
parser = argparse.ArgumentParser()
parser.add_argument('--n_epochs', type=int, default=200, help='number of epochs of training')
parser.add_argument('--batch_size', type=int, default=64, help='size of the batches ')
parser.add_argument('--lr', type=float, default=0.0002, help='adam: learning rate')
parser.add_argument('--b1', type=float, default=0.5, help='adam: decay of first order momentum of gradient')
parser.add_argument('--b2', type=float, default=0.999, help='adam: decay of first order momentum of gradient')
parser.add_argument('--n_cpu', type=int, default=8, help='number of cpu threads to use during batch generation')
parser.add_argument('--latent_dim', type=int, default=100, help='dimensionality of the latent space')
parser.add_argument('--img_size', type=int, default=28, help='size of each image dimension')
parser.add_argument('--channels', type=int, default=1, help='number of image channels')
parser.add_argument('--sample_interval', type=int, default=400, help='interval betwen image samples')
opt = parser.parse_args()
print(opt)
img_shape = (opt.channels, opt.img_size, opt.img_size)  # 确定图片输入的格式为(1,28,28),由于mnist数据集是灰度图所以通道为1
cuda = True if torch.cuda.is_available() else False

os.makedirs(‘images’,exist_ok=Ture)
makedirs()方法是递归目录创建功能。如果exists_ok为False(默认值),则如果目标目录已存在,则引发OSError错误,True则不会

在这里插入图片描述这部分代码是进行参数的设定,刚开始可以不用懂这个,大概浏览一下,对括号里的有个印象就可以,等往下读程序的时候就会用到这些参数,然后就懂了。比如往下读程序读到了’–n_epochs’,就来前面找,找到第一个参数,’–n_epochs’,这个参数的默认值200和类型int都在后面,意思就是将’–n_epochs’的值赋值为200。其实在最后help中还有关于’–n_epochs’的解释,虽然都是英文,但是也不难,实在不行百度翻译一下就懂了。
img_shape = (opt.channels, opt.img_size, opt.img_size)
这些参数opt.channels, opt.img_size, opt.img_size便是需要去上一部分设定的参数的位置去找的,都是带有opt. 意思为图像的通道数为1,尺寸大小为28*28,通道数为1表示是灰度图

img_shape = (opt.channels, opt.img_size, opt.img_size)  # 确定图片输入的格式为(1,28,28),由于mnist数据集是灰度图所以通道为1
cuda = True if torch.cuda.is_available() else False

class Generator(nn.Module):   #生成网络
    def __init__(self):
        super(Generator, self).__init__() #超类继承
        def block(in_feat, out_feat, normalize=True):
            layers = [nn.Linear(in_feat, out_feat)]  #对传入数据应用线性转换(输入节点数,输出节点数)
            if normalize:
                layers.append(nn.BatchNorm1d(out_feat, 0.8))  #批规范化
            layers.append(nn.LeakyReLU(0.2, inplace=True))   #激活函数
            return layers
        self.model = nn.Sequential(
            *block(opt.latent_dim, 128, normalize=False),
            *block(128, 256),
            *block(256, 512),
            *block(512, 1024),
            nn.Linear(1024, int(np.prod(img_shape))),
            nn.Tanh()
        ) #快速搭建网络, np.prod 用来计算所有元素的乘积
    def forward(self, z):    #z代表输入
        img = self.model(z)
        img = img.view(img.size(0), *img_shape)
        return img

搭建生成器神经网络,学会了之后可以自己尝试做一些改动,现在就当作套路来用
block前面有*号就是作为元组元素
项目需要对数据进行分类或者识别,就需要全连接层Linear,这时候就需要将高维数据平铺变为低位数据。

class Discriminator(nn.Module):
    def __init__(self):
        super(Discriminator, self).__init__()

        self.model = nn.Sequential(
            nn.Linear(int(np.prod(img_shape)), 512),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Linear(512, 256),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Linear(256, 1),
            nn.Sigmoid()
        )

    def forward(self, img):
        img_flat = img.view(img.size(0), -1)
        validity = self.model(img_flat)

        return validity

一般地,在CNN等网络中,都是通过卷积过滤器对目标进行计算,然而这些计算都是建立在高维数据。
最后,项目需要对数据进行分类或者识别,就需要全连接层Linear,这时候就需要将高维数据平铺变为低位数据
这段也一样,定义了一个判别网络,可以先看看。

adversarial_loss = torch.nn.BCELoss()

定义了一个损失函数,

generator = Generator()
discriminator = Discriminator()

初始化

if cuda:
    generator.cuda()
    discriminator.cuda()
    adversarial_loss.cuda()

显卡加速

dataloader = torch.utils.data.DataLoader(
    datasets.MNIST('../../data/mnist', train=True, download=True,
                   transform=transforms.Compose([
                       transforms.ToTensor(),
                       transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
                   ])),
    batch_size=opt.batch_size, shuffle=True)

dataloader接口的应用:
PyTorch中数据读取的一个重要接口是torch.utils.data.DataLoader,该接口主要用来将自定义的数据读取接口的输出或者PyTorch已有的数据读取接口的输入按照batch size封装成Tensor,后续只需要再包装成Variable即可作为模型的输入,因此该接口有点承上启下的作用,比较重要。简单来说就是你训练的数据集不是一股脑的全部丢进来,而是分成了一批一批的,这个接口函数就是将数据集分批并转化成可以处理的Tensor类型。
transforms.Normalize(mean,std)的计算公式是:
在这里插入图片描述mean是均值,std是方差,作用是归一化
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))这里将minist手写数字集当作RGB三通道的图来看,所以才会有三个平均值和方差。shuffle是打乱排序。

optimizer_G = torch.optim.Adam(generator.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))
optimizer_D = torch.optim.Adam(discriminator.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))

这部分是定义了神经网络的优化器,Adam就是一种优化器,当然,优化器是有别的,你大可以选择SGD等别的优化器进行试验,当然,在这个网络中,代码的作者经过试验采用Adam优化器进行优化,里面的参数就是首先是网络类型,lr是学习率,可以在刚开始我们说的opt的参数那里找到数值,学习率的数值也是根据经验和试验进行设定的。Betas是动量梯度的下降,梯度下降是机器学习中用来使模型逼近真实分布的最小偏差的优化方法。

for epoch in range(opt.n_epochs):

训练的次数就是opt.n_epochs,

for i, (imgs, _) in enumerate(dataloader): 

dataloader中的数据是一张图片对应一个标签,所以imgs对应的是图片,_对应的是标签,而i是enumerate输出的功能,enumerate用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中,所以i就是相当于1,2,3……的数据下标。

valid = Variable(Tensor(imgs.size(0), 1).fill_(1.0), requires_grad=False)
fake = Variable(Tensor(imgs.size(0), 1).fill_(0.0), requires_grad=False)

这部分定义的相当于是一个标准,vaild可以想象成是64行1列的向量,就是为了在后面计算损失时,和1比较;fake也是一样是全为0的向量,用法和1的用法相同。

协助理解

import torch
from torch.autograd import Variable
w1 = Variable(torch.Tensor([1.0,2.0,3.0]),requires_grad=True)#需要求导的话,requires_grad=True属性是必须的。
w2 = Variable(torch.Tensor([1.0,2.0,3.0]),requires_grad=True)
 
z = w1*w2+w1 # 第二次BP出现问题就在这,不知道第一次BP之后销毁了啥。
res = torch.mean(z)
res.backward() #第一次求导没问题
res.backward() #第二次BP会报错,但使用 retain_variables=True,就好了。
real_imgs = Variable(imgs.type(Tensor))

这句将真实的图片转化为神经网络可以处理的变量。

optimizer_G.zero_grad() 

#optimizer.zero_grad()意思是把梯度置零
每次训练都将上一次的梯度置零,避免上一次的干扰。

z = Variable(
                Tensor(np.random.normal(0, 1, (imgs.shape[0], opt.latent_dim))))  # 生成的噪音,均值为0方差为1维度为(64,100)的噪音

这部分就是在上面训练生成网络的z的输入值,np.random.normal(0, 1, (imgs.shape[0], opt.latent_dim)意思就是输入0到1之间,形状为imgs.shape[0], opt.latent_dim的随机高斯数据。

np.random.normal()正态分布
高斯分布的概率密度函数
numpy中
numpy.random.normal(loc=0.0, scale=1.0, size=None)
参数的意义为:
  loc:float
  概率分布的均值,对应着整个分布的中心center
  scale:float
  概率分布的标准差,对应于分布的宽度,scale越大越矮胖,scale越小,越瘦高
  size:int or tuple of ints
  输出的shape,默认为None,只输出一个值
  我们更经常会用到np.random.randn(size)所谓标准正态分布(μ=0, σ=1),对应于np.random.normal(loc=0, scale=1, size)

gen_imgs = generator(z)

开始得到一个批次的图片,上面说了这些数据是分批进行训练,每一批是64张,所以,这这一批图片为64张。

g_loss = adversarial_loss(discriminator(gen_imgs), valid)

计算生成器的损失,adversarial_loss就是在前面定义的adversarial_loss = torch.nn.BCELoss()损失函数来计算损失。

g_loss.backward()
optimizer_G.step()

反向传播和模型更新,所有的优化器optimizer都实现了step()方法来对所有的参数进行更新。
接下来训练判别网络:
optimizer_D.zero_grad()
首先,梯度清零,和生成网络一样

real_loss = adversarial_loss(discriminator(real_imgs), valid)#判别器判别真实图片是真的的损失
fake_loss = adversarial_loss(discriminator(gen_imgs.detach()), fake)#判别器判别假图片是假的的损失
d_loss = (real_loss + fake_loss) / 2

在这里插入图片描述KL散度公式,交叉熵,计算两个整体分布之间的距离(需要达到最小)
其实我的理解就是判别器去判别真实图片是真的和生成图片是假的的损失之和,让这个和越大,说明判别器越准确。

d_loss.backward()
optimizer_D.step()
batches_done = epoch * len(dataloader) + i
if batches_done % opt.sample_interval == 0:
     save_image(gen_imgs.data[:25], 'images/%d.png' % batches_done, nrow=5, normalize=True)

这一部分就是将生成的图片的25张保存下来。

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值