生成对抗网络GAN和DCGAN的理解(pytorch+李宏毅老师作业6)

作者在进行GAN学习中遇到的问题汇总到下方,并进行解读讲解,下面提到的题目是李宏毅老师机器学习课程的作业6(GAN)

一.GAN

二.DCGAN

三.示例代码解读

成功下载并解压,可以删除作业代码中的有关下载和解压的部分

可以打开我最下面放的文件,改变数据地址即可(main函数中的workspace_dir)

   You may replace the workspace directory if you want.

   workspace_dir = '.'
   Training progress bar
   !pip install -q qqdm
   !gdown --id 1IGrTr308mGAaCKotpkkm8wTKlWs9Jq-p --output "{workspace_dir}/crypko_data.zip"
  import random
  import torch
  import numpy as np
  import os
  import glob
  import torch.nn as nn
  import torch.nn.functional as F
  import torchvision
  import torchvision.transforms as transforms
  from torch import optim
  from torch.autograd import Variable
  from torch.utils.data import Dataset, DataLoader
  import matplotlib.pyplot as plt
  from qqdm.notebook import qqdm

如果没有qqdm或者matplotlib则需要pip或者conda下载(qqdm是进度条,在训练过程中可以显示训练进度)

Transfrom:

1.transforms.Compose():将一系列的transforms有序组合,实现时按照这些方法依次对图像操作。

类似封装函数,依次执行

2.transforms.ToPILImage:将数据转换为PILImage。
3.transforms.Resize:图像变换
4.transforms.ToTensor:转为tensor,并归一化至[0-1]
5.transforms.Normalize:数据归一化处理

  • mean:各通道的均值
  • std:各通道的标准差

6.主函数进行数据加载:

  workspace_dir='D://机器学习//Jupyter//GAN学习//函数'
  dataset = get_dataset(os.path.join(workspace_dir, 'faces'))

DCGAN指出,所有的权重都以均值为0,标准差为0.2的正态分布随机初始化。weights_init 函数读取一个已初始化的模型并重新初始化卷积层,转置卷积层,batch normalization 层。这个函数在模型初始化之后使用。

def weights_init(m):
    classname = m.__class__.__name__
    if classname.find('Conv') != -1:
        m.weight.data.normal_(0.0, 0.02)
    elif classname.find('BatchNorm') != -1:
        m.weight.data.normal_(1.0, 0.02)
        m.bias.data.fill_(0)

生成器的目的是将输入向量z映射到真的数据空间。这儿我们的数据为图片,意味着我们需要将输入向量z转换为 3x64x64的RGB图像。实际操作时,我们通过一系列的二维转置卷,每次转置卷积后跟一个二维的batch norm层和一个relu激活层。生成器的输出接入tanh函数以便满足输出范围为[−1,1]。值得一提的是,每个转置卷积后面跟一个 batch norm 层,是DCGAN论文的一个主要贡献。这些网络层有助于训练时的梯度计算。

  • 该函数由反卷积+batch norm+relu构成
      def dconv_bn_relu(in_dim, out_dim):
            return nn.Sequential(
                nn.ConvTranspose2d(in_dim, out_dim, 5, 2,
                                   padding=2, output_padding=1, bias=False),
                nn.BatchNorm2d(out_dim),
                nn.ReLU()
            )
  • 类似dconv_bn_relu,但特殊在l1为网络的第一层,输入输出和后面的l2不同(我其实觉得可以放到一起,但可能分开更加清除一些)
       self.l1 = nn.Sequential(
                  nn.Linear(in_dim, dim * 8 * 4 * 4, bias=False),
                  nn.BatchNorm1d(dim * 8 * 4 * 4),
                  nn.ReLU()
              )
  • 实例化生成器并调用weights_init函数
       self.l2_5 = nn.Sequential(
                  dconv_bn_relu(dim * 8, dim * 4),
                  dconv_bn_relu(dim * 4, dim * 2),
                  dconv_bn_relu(dim * 2, dim),
                  nn.ConvTranspose2d(dim, 3, 5, 2, padding=2, output_padding=1),
                  nn.Tanh()
              )
  • 构建cnn网络结构,tanh函数使输出在[-1,1]之间
      self.apply(weights_init)
  • 整体模型合并
      def forward(self, x):
          y = self.l1(x)
          y = y.view(y.size(0), -1, 4, 4)
          y = self.l2_5(y)
          return y
  • 主函数中生成实例,并输出网络结构
     netG=Generator(100)
      print(netG)
  • 运行后得到网络结构

generator( (l1): Sequential( (0): Linear(in_features=100, out_features=8192, bias=False) (1): BatchNorm1d(8192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (2): ReLU() ) (l2_5): Sequential( (0): Sequential( (0): ConvTranspose2d(512, 256, kernel_size=(5, 5), stride=(2, 2), padding=(2, 2), output_padding=(1, 1), bias=False) (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (2): ReLU() ) (1): Sequential( (0): ConvTranspose2d(256, 128, kernel_size=(5, 5), stride=(2, 2), padding=(2, 2), output_padding=(1, 1), bias=False) (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (2): ReLU() ) (2): Sequential( (0): ConvTranspose2d(128, 64, kernel_size=(5, 5), stride=(2, 2), padding=(2, 2), output_padding=(1, 1), bias=False) (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (2): ReLU() ) (3): ConvTranspose2d(64, 3, kernel_size=(5, 5), stride=(2, 2), padding=(2, 2), output_padding=(1, 1)) (4): Tanh() ) )</p> <pre><code> 判别器的输入为3 *64 *64,输出为概率(分数),依次通过卷积层,BN层,LeakyReLU层,最后通过sigmoid函数输出得分 </code></pre> <p>from torch import nn</p> <p>from 函数.weights_inition import weights_init</p> <p>class Discriminator(nn.Module): """ Input shape: (N, 3, 64, 64) Output shape: (N, ) """</p> <pre><code>def __init__(self, in_dim, dim=64): super(Discriminator, self).__init__() def conv_bn_lrelu(in_dim, out_dim): return nn.Sequential( nn.Conv2d(in_dim, out_dim, 5, 2, 2), nn.BatchNorm2d(out_dim), nn.LeakyReLU(0.2), ) """ Medium: Remove the last sigmoid layer for WGAN. """ self.ls = nn.Sequential( nn.Conv2d(in_dim, dim, 5, 2, 2), nn.LeakyReLU(0.2), conv_bn_lrelu(dim, dim * 2), conv_bn_lrelu(dim * 2, dim * 4), conv_bn_lrelu(dim * 4, dim * 8), nn.Conv2d(dim * 8, 1, 4), nn.Sigmoid(), ) self.apply(weights_init) def forward(self, x): y = self.ls(x) y = y.view(-1) return y </code></pre> <pre><code> > 类似生成器,具体框架不再分开说明 生成实例,观察网络结构 </code></pre> <p>Discriminator( (ls): Sequential( (0): Conv2d(3, 64, kernel_size=(5, 5), stride=(2, 2), padding=(2, 2)) (1): LeakyReLU(negative_slope=0.2) (2): Sequential( (0): Conv2d(64, 128, kernel_size=(5, 5), stride=(2, 2), padding=(2, 2)) (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (2): LeakyReLU(negative_slope=0.2) ) (3): Sequential( (0): Conv2d(128, 256, kernel_size=(5, 5), stride=(2, 2), padding=(2, 2)) (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (2): LeakyReLU(negative_slope=0.2) ) (4): Sequential( (0): Conv2d(256, 512, kernel_size=(5, 5), stride=(2, 2), padding=(2, 2)) (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (2): LeakyReLU(negative_slope=0.2) ) (5): Conv2d(512, 1, kernel_size=(4, 4), stride=(1, 1)) (6): Sigmoid() ) )</p> <pre><code> </code></pre> <p>G = Generator(in_dim=z_dim).to(device) D = Discriminator(3).to(device) G.train() D.train()</p> <p># Loss criterion = nn.BCELoss()</p> <p># Optimizer opt_D = torch.optim.Adam(D.parameters(), lr=lr, betas=(0.5, 0.999)) opt_G = torch.optim.Adam(G.parameters(), lr=lr, betas=(0.5, 0.999))</p> <pre><code> * in_dim=z_dim=100,z的分布(高斯分布)深度为100 * 因为input的是图片,3channels,所以Discriminator(3) * 如果模型中有BN层,需要在训练时添加model.train(),在测试时添加model.eval()。其中model.train()是保证BN层用每一批数据的均值和方差,而model.eval()是保证BN用全部训练数据的均值和方差。 * 损失函数使用二元交叉熵损失(BCELoss) * 这里使用Adam优化器更新参数[Adam+pytorch](https://blog.csdn.net/weixin_39228381/article/details/108548413),学习率设置为0.0002 Betal=0.5。 </code></pre> <p>dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True, num_workers=2)</p> <pre><code> * z为随机生成64 *100的高斯分布数据(均值为0,方差为1)也叫噪声。 z为生成器的输入。 * z为随机生成64*100的高斯分布数据(均值为0,方差为1)也叫噪声。 z为生成器的输入。 </code></pre> <pre><code> z = Variable(torch.randn(bs, z_dim)).to(device) </code></pre> <pre><code> * f_imgs大小为 64 *3 *64 *64(生成64张假图片) * 将z直接传入G (),可以直接调用forward()函数进行操作,参考[forward](https://blog.csdn.net/HJC256ZY/article/details/106462417)。 * 下面展示forward()函数的流程 </code></pre> <pre><code> r_imgs = Variable(imgs).to(device) f_imgs = G(z) </code></pre> <pre><code> * 进行标签定义,真实图片的label为1,生成的图片的label为0。 </code></pre> <pre><code> r_label = torch.ones((bs)).to(device) f_label = torch.zeros((bs)).to(device) </code></pre> <pre><code> * 把两种图片放入判别器,将r_imgs设置为detach(),意为参数不再更新(很好理解,因为图片的数据肯定不可以改变,只能改变网络的参数,所以就锁定了图片数据)。r_logit表示真实图片得分(越高越好),f_logit表示假图片得分(越低越好)。 </code></pre> <pre><code> r_logit = D(r_imgs.detach()) f_logit = D(f_imgs.detach()) </code></pre> <pre><code> * 计算损失,就是将两种损失加起来除以二。 </code></pre> <pre><code> r_loss = criterion(r_logit, r_label) f_loss = criterion(f_logit, f_label) loss_D = (r_loss + f_loss) / 2 </code></pre> <pre><code> * module.zero_grad(),每一个batch的训练将参数的梯度清零。 * 对loss进行反向传播算法,.backward()可以计算所有与loss_D有关的参数的梯度,参考[backward](https://zhuanlan.zhihu.com/p/27808095)。 * optimizer.step(),进行参数更新(Adam)。 </code></pre> <pre><code> D.zero_grad() loss_D.backward() opt_D.step() </code></pre> <pre><code> * z为随机生成64*100的高斯分布数据(均值为0,方差为1)也叫噪声。 z为生成器的输入。 </code></pre> <p>z = Variable(torch.randn(bs, z_dim)).to(device)</p> <pre><code> * 生成假图片,并计算得分(越高越好)。 * 更新参数 </code></pre> <p>G.zero_grad() loss_G.backward() opt_G.step()</p> <pre><code> * 生成最后的结果(测试图片),每一个epoch生成一张图片。 </code></pre> <pre><code> G.eval() f_imgs_sample = (G(z_sample).data + 1) / 2.0 filename = os.path.join(log_dir, f'Epoch_{epoch + 1:03d}.jpg') torchvision.utils.save_image(f_imgs_sample, filename, nrow=10) print(f' | Save some samples to {filename}.') # Show generated images in the jupyter notebook. grid_img = torchvision.utils.make_grid(f_imgs_sample.cpu(), nrow=10) plt.figure(figsize=(10, 10)) plt.imshow(grid_img.permute(1, 2, 0)) plt.show() G.train() if (e + 1) % 5 == 0 or e == 0: # Save the checkpoints. torch.save(G.state_dict(), os.path.join(ckpt_dir, 'G.pth')) torch.save(D.state_dict(), os.path.join(ckpt_dir, 'D.pth')) </code></pre> <p>

  • 下图分别是我设置epoch为5,bitch_size为10和64生成的测试图片
    bitch_size=10

bitch_size=64

观察图片会有一些问题,比如面部不全,眼睛颜色不对或者人脸模糊等问题,转换GAN类型或者增加数据集等可能使结果更好。

李宏毅老师的作业文件是.ipynb文件,我把函数分开写进了PyCharm中,在使用时需要更改各个函数中
import的文件名,还有数据的地址(在main函数中)改为自己的地址
提取码:dkdd

Original: https://www.cnblogs.com/lvzhiyi/p/15754977.html
Author: 富士山上
Title: 生成对抗网络GAN和DCGAN的理解(pytorch+李宏毅老师作业6)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/576560/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球