生成對抗網絡GAN和DCGAN的理解(pytorch+李宏毅老師作業6)


作者在進行GAN學習中遇到的問題匯總到下方,並進行解讀講解,下面提到的題目是李宏毅老師機器學習課程的作業6(GAN)

一.GAN

網絡上有關GAN和DCGAN的講解已經很多,在這里不再加以贅述,放幾個我認為比較好的講解
1.GAN概念理解
2.理解GAN網絡基本原理
3.李宏毅機器學習課程
4.換個角度看GAN:另一種損失函數

二.DCGAN

1.從頭開始GAN【論文】(二) —— DCGAN
2.PyTorch教程之DCGAN
3.pytorch官方DCGAN樣例講解

三.示例代碼解讀

3.1關於數據集的下載

官方的數據集需要下載,在查找相關網址后,上網找到了數據集,並成功下載,如下是數據集鏈接:
提取碼:ctgr

成功下載並解壓,可以刪除作業代碼中的有關下載和解壓的部分

可以打開我最下面放的文件,改變數據地址即可(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"

3.2導入相關包和函數

  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是進度條,在訓練過程中可以顯示訓練進度)

3.3DateSet 數據預處理

Transfrom:

1.transforms.Compose():將一系列的transforms有序組合,實現時按照這些方法依次對圖像操作。

類似封裝函數,依次執行

2.transforms.ToPILImage:將數據轉換為PILImage。
3.transforms.Resize:圖像變換
4.transforms.ToTensor:轉為tensor,並歸一化至[0-1]
5.transforms.Normalize:數據歸一化處理

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

關於為什么要進行歸一化處理可以參考transforms.Normalize()

6.主函數進行數據加載:

  workspace_dir='D://機器學習//Jupyter//GAN學習//函數'
  dataset = get_dataset(os.path.join(workspace_dir, 'faces'))

3.4Model-模型的建立-DCGAN

3.4.1權重初始化

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)

3.4.2Generator-生成器模型

生成器的目的是將輸入向量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)
  • 運行后得到網絡結構
  (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()
  )
)

3.4.3 Discriminator-判別器模型

判別器的輸入為3 *64 *64,輸出為概率(分數),依次通過卷積層,BN層,LeakyReLU層,最后通過sigmoid函數輸出得分

from torch import nn

from 函數.weights_inition import weights_init


class Discriminator(nn.Module):
    """
    Input shape: (N, 3, 64, 64)
    Output shape: (N, )
    """

    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

類似生成器,具體框架不再分開說明

生成實例,觀察網絡結構

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()
  )
)

3.5Training-模型的訓練-DCGAN

3.5.1 創建網絡結構

  G = Generator(in_dim=z_dim).to(device)
  D = Discriminator(3).to(device)
  G.train()
  D.train()

  # Loss
  criterion = nn.BCELoss()


  # 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))
  • 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,學習率設置為0.0002 Betal=0.5。

3.5.2加載數據

  dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True, num_workers=2)
  • z為隨機生成64 *100的高斯分布數據(均值為0,方差為1)也叫噪聲。
    z為生成器的輸入。

3.5.3 訓練D(判別器)

  • z為隨機生成64*100的高斯分布數據(均值為0,方差為1)也叫噪聲。
    z為生成器的輸入。
      z = Variable(torch.randn(bs, z_dim)).to(device)
  • f_imgs大小為 64 *3 *64 *64(生成64張假圖片)
  • 將z直接傳入G (),可以直接調用forward()函數進行操作,參考forward
  • 下面展示forward()函數的流程
    avatar
      r_imgs = Variable(imgs).to(device)
      f_imgs = G(z)
  • 進行標簽定義,真實圖片的label為1,生成的圖片的label為0。
      r_label = torch.ones((bs)).to(device)
      f_label = torch.zeros((bs)).to(device)
  • 把兩種圖片放入判別器,將r_imgs設置為detach(),意為參數不再更新(很好理解,因為圖片的數據肯定不可以改變,只能改變網絡的參數,所以就鎖定了圖片數據)。r_logit表示真實圖片得分(越高越好),f_logit表示假圖片得分(越低越好)。
      r_logit = D(r_imgs.detach())
      f_logit = D(f_imgs.detach())
  • 計算損失,就是將兩種損失加起來除以二。
      r_loss = criterion(r_logit, r_label)
      f_loss = criterion(f_logit, f_label)
      loss_D = (r_loss + f_loss) / 2
  • module.zero_grad(),每一個batch的訓練將參數的梯度清零。
  • 對loss進行反向傳播算法,.backward()可以計算所有與loss_D有關的參數的梯度,參考backward
  • optimizer.step(),進行參數更新(Adam)。
      D.zero_grad()
      loss_D.backward()
      opt_D.step()

3.5.4 訓練G(生成器)

  • z為隨機生成64*100的高斯分布數據(均值為0,方差為1)也叫噪聲。
    z為生成器的輸入。
   z = Variable(torch.randn(bs, z_dim)).to(device)
  • 生成假圖片,並計算得分(越高越好)。
   f_imgs = G(z)
   f_logit = D(f_imgs)
   loss_G = criterion(f_logit, r_label)
  • 更新參數
   G.zero_grad()
   loss_G.backward()
   opt_G.step()

3.6結果展示

  • 生成最后的結果(測試圖片),每一個epoch生成一張圖片。

有關pytorch中dataloader,dataset以及數據顯示的學習可以參考PyTorch深度學習快速入門教程(絕對通俗易懂!)【小土堆】

        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'))
  • 下圖分別是我設置epoch為5,bitch_size為10和64生成的測試圖片
    bitch_size=10
    avatar
    bitch_size=64
    avatar

觀察圖片會有一些問題,比如面部不全,眼睛顏色不對或者人臉模糊等問題,轉換GAN類型或者增加數據集等可能使結果更好。

3.7代碼文件

李宏毅老師的作業文件是.ipynb文件,我把函數分開寫進了PyCharm中,在使用時需要更改各個函數中
import的文件名,還有數據的地址(在main函數中)改為自己的地址
提取碼:dkdd


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM