深度學習-DRGAN對抗神經網絡生成動漫頭像

動漫頭像素材,項目中新建data文件夾放進去

        具體思路是,生成器是將一個噪點生成一副假圖片,然後將假圖片傳給判別器進行判斷,如果判別器判斷爲真,則代碼生成器性能很好,而判別器是從真實圖片中學習模型,對生成的假圖片進行判斷,如果判斷出來爲假則代碼判別器性能很好。

關於代碼的具體註釋已經寫上,需要單獨開博客講的內容在前幾篇博客裏寫了,直接上代碼吧:

train.py主運行頁面:

import argparse
import torch
import torchvision
import torchvision.utils as vutils
import torch.nn as nn
from model import NetD, NetG

# 定義是否使用GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")   #判斷是否能用GPU

# 圖像讀入與預處理
transforms = torchvision.transforms.Compose([
    torchvision.transforms.Resize(96),   #處理圖像尺寸,短邊變爲96,長邊對應比例縮放
    torchvision.transforms.ToTensor(),    #數據集加載時,默認的圖片格式是 numpy,所以通過 transforms 轉換成 Tensor,圖像範圍[0, 255] -> [0.0,1.0]
    torchvision.transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)), ])    #使用公式進行歸一化channel=(channel-mean)/std,因爲transforms.ToTensor()已經把數據處理成[0,1],那麼(x-0.5)/0.5就是[-1.0, 1.0]

dataset = torchvision.datasets.ImageFolder('data/', transform=transforms)

dataloader = torch.utils.data.DataLoader(
    dataset=dataset,
    batch_size=64,   #因爲需要生成包含64張小圖的大圖片,所以把64定位一個批度
    shuffle=True,
    drop_last=True,   #如何處理數據集長度除於batch_size餘下的數據。True就拋棄,否則保留
)

netG = NetG(64, 100).to(device)   #操作過程中通道數都是64的倍數,最後由64一下子變爲3,100是噪聲維度,噪聲用來生成假圖片
netD = NetD(64).to(device)   #操作過程中通道數都是64的倍數,最後由64一下子變爲1

criterion = nn.BCELoss()   #用於二分類
optimizerG = torch.optim.Adam(netG.parameters(), lr=0.0002, betas=(0.5, 0.999))   #生成器的優化器
optimizerD = torch.optim.Adam(netD.parameters(), lr=0.0002, betas=(0.5, 0.999))   #判別器的優化器

label = torch.FloatTensor(64)   #創建一個元素個數爲64的tensor標籤,隨機賦值
real_label = 1   #真圖片標籤爲1
fake_label = 0   #假圖片標籤爲0

for epoch in range(1, 25 + 1):
    for i, (imgs, _) in enumerate(dataloader):
        # 固定生成器G,訓練鑑別器D(判別器的好壞要通過是否能判斷出假圖片來體現)
        optimizerD.zero_grad()   #判別器優化器梯度全部降爲0
        ## 讓D儘可能的把真圖片判別爲1
        imgs = imgs.to(device)   #將圖片數據copy一份到device指定的GPU上去
        output = netD(imgs)   #判別器輸出
        label.data.fill_(real_label)   #標籤全部改爲1,一開始判斷真實圖片
        label = label.to(device)   #將labelcopy放入GPU
        output=output.squeeze()   #output.shape爲(64,1,1,1)維度,label爲(64),這一句是將output中所有維度爲一的去掉,不加這一句運行不會出錯,但是會有警告
        errD_real = criterion(output, label)   #計算判斷真實圖片的損失值
        errD_real.backward()   #反向傳播
        ## 讓D儘可能把假圖片判別爲0
        label.data.fill_(fake_label)   #標籤全部改爲0,一開始假圖片
        noise = torch.randn(64, 100, 1, 1)   #隨機生成64個噪點,每個噪點維度爲(100,1,1)
        noise = noise.to(device)   #將噪點放入GPU
        fake = netG(noise)  # 生成假圖
        output = netD(fake.detach())  # 使用判別器對一個批次假圖片進行分類
        output=output.squeeze()
        errD_fake = criterion(output, label)   #計算判斷假圖片爲假的損失值
        errD_fake.backward()   #反向傳播
        errD = errD_fake + errD_real   #判斷真圖片和判斷假圖片的損失值加和作爲總損失
        optimizerD.step()   #對判別器進行優化

        # 固定鑑別器D,訓練生成器G(生成器性能的好壞要通過判別器是否判斷爲真來體現)
        optimizerG.zero_grad()   #生成器梯度全部降爲0
        # 讓D儘可能把G生成的假圖判別爲1
        label.data.fill_(real_label)   #標籤全部改爲1,一開始判斷真實圖片
        label = label.to(device)
        output = netD(fake)   #判別剛纔生成的假圖片
        output=output.squeeze()
        errG = criterion(output, label)   #計算判斷假圖片爲真的損失值
        errG.backward()   #反向傳播
        optimizerG.step()   #對生成器進行優化

        print('[%d/%d][%d/%d] Loss_D: %.3f Loss_G %.3f'
              % (epoch, 25, i, len(dataloader), errD.item(), errG.item()))

    vutils.save_image(fake.data,
                      '%s/fake_samples_epoch_%03d.png' % ('imgs/', epoch),   #保存圖像
                      normalize=True)

model.py模型結構頁面:

import torch.nn as nn
# 定義生成器網絡G,從噪聲中生成一張彩色圖片
class NetG(nn.Module):   #注意這裏圖片都是四維,第一維都是批度64,爲了方便將第一個省去
    def __init__(self, ngf, nz):   #操作過程中通道數都是ngf的倍數,最後由ngf一下子變爲3,nz是噪點維度,噪聲用來生成假圖片
        super(NetG, self).__init__()
        # layer1輸入的是一個100x1x1的隨機噪聲, 輸出尺寸(ngf*8)x4x4
        self.layer1 = nn.Sequential(   #(100,1,1)-->(64*8,4,4)
            nn.ConvTranspose2d(nz, ngf * 8, kernel_size=4, stride=1, padding=0, bias=False),   #o=(i-1)*s-2*p+k
            nn.BatchNorm2d(ngf * 8),   #參數爲輸出通道數
            nn.ReLU(inplace=True)   #implace=True是原地操作,直接替換掉以前的變量,比如x=x+5
        )
        # layer2輸出尺寸(ngf*4)x8x8
        self.layer2 = nn.Sequential(   #(64*8,4,4)-->(64*4,8,8)
            nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ngf * 4),
            nn.ReLU(inplace=True)
        )
        # layer3輸出尺寸(ngf*2)x16x16
        self.layer3 = nn.Sequential(   #(64*4,8,8)-->(64*2,16,16)
            nn.ConvTranspose2d(ngf * 4, ngf * 2, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ngf * 2),
            nn.ReLU(inplace=True)
        )
        # layer4輸出尺寸(ngf)x32x32
        self.layer4 = nn.Sequential(   #(64*2,16,16)-->(64,32,32)
            nn.ConvTranspose2d(ngf * 2, ngf, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ngf),
            nn.ReLU(inplace=True)
        )
        # layer5輸出尺寸 3x96x96
        self.layer5 = nn.Sequential(   #(64,32,32)-->(3,96,96)
            nn.ConvTranspose2d(ngf, 3, 5, 3, 1, bias=False),
            nn.Tanh()
        )

    # 定義NetG的前向傳播
    def forward(self, x):
        out = self.layer1(x)
        out = self.layer2(out)
        out = self.layer3(out)
        out = self.layer4(out)
        out = self.layer5(out)
        return out


# 定義鑑別器網絡D
class NetD(nn.Module):
    def __init__(self, ndf):   #操作過程中圖片的通道數一直是ndf的倍數
        super(NetD, self).__init__()
        # layer1 輸入 3 x 96 x 96, 輸出 (ndf) x 32 x 32
        self.layer1 = nn.Sequential(   #(3,96,96)-->(64,32,32)
            nn.Conv2d(3, ndf, kernel_size=5, stride=3, padding=1, bias=False),
            nn.BatchNorm2d(ndf),
            nn.LeakyReLU(0.2, inplace=True)   #relu中f=maxy(0,x),而leakyrelu中f=x>0?x:ax(a=欄目大)
        )
        # layer2 輸出 (ndf*2) x 16 x 16
        self.layer2 = nn.Sequential(   #(64,32,32)-->(128,16,16)
            nn.Conv2d(ndf, ndf * 2, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ndf * 2),
            nn.LeakyReLU(0.2, inplace=True)
        )
        # layer3 輸出 (ndf*4) x 8 x 8
        self.layer3 = nn.Sequential(   #(128,16,16)-->(256,8,8)
            nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ndf * 4),
            nn.LeakyReLU(0.2, inplace=True)
        )
        # layer4 輸出 (ndf*8) x 4 x 4
        self.layer4 = nn.Sequential(   #(256,8,8)-->(512,4,4)
            nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ndf * 8),
            nn.LeakyReLU(0.2, inplace=True)
        )
        # layer5 輸出一個數(概率)
        self.layer5 = nn.Sequential(   #(512,4,4)-->(1,1,1)
            nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=False),
            nn.Sigmoid()
        )

    # 定義NetD的前向傳播
    def forward(self,x):
        out = self.layer1(x)
        out = self.layer2(out)
        out = self.layer3(out)
        out = self.layer4(out)
        out = self.layer5(out)
        return out

 

生成的假圖片:

epoch=1:

epoch=10:

epoch=20:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章