作者在進行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關于資料集的下載
官方的資料集需要FQ下載,在查找相關網址后,上網找到了資料集,并成功下載,如下是資料集鏈接:
提取碼: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 zz 映射到真的資料空間,這兒我們的資料為圖片,意味著我們需要將輸入向量z zz轉換為 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
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()函式的流程
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
bitch_size=64
觀察圖片會有一些問題,比如面部不全,眼睛顏色不對或者人臉模糊等問題,轉換GAN型別或者增加資料集等可能使結果更好,
3.7代碼檔案
李宏毅老師的作業檔案是.ipynb檔案,我把函式分開寫進了PyCharm中,在使用時需要更改各個函式中
import的檔案名,還有資料的地址(在main函式中)改為自己的地址
提取碼:dkdd
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/401427.html
標籤:其他
上一篇:Rust單鏈表
