
生成式對抗網(wǎng)絡(luò)(Generative Adversarial Network, GAN)是近些年計(jì)算機(jī)視覺領(lǐng)域非常常見的一類方法,其強(qiáng)大的從已有數(shù)據(jù)集中生成新數(shù)據(jù)的能力令人驚嘆,甚至連人眼都無法進(jìn)行分辨。本文將會介紹基于最原始的DCGAN的動(dòng)漫人物生成任務(wù),通過定義生成器和判別器,并讓這兩個(gè)網(wǎng)絡(luò)在參數(shù)優(yōu)化過程中不斷“打架”,最終得到較好的生成結(jié)果。
01、生成動(dòng)漫人物任務(wù)概述
日本動(dòng)漫中會出現(xiàn)很多的卡通人物,這些卡通人物都是漫畫家花費(fèi)大量的時(shí)間設(shè)計(jì)繪制出來的,那么,假設(shè)已經(jīng)有了一個(gè)卡通人物的集合,那么深度學(xué)習(xí)技術(shù)可否幫助漫畫家們根據(jù)已有的動(dòng)漫人物形象,設(shè)計(jì)出新的動(dòng)漫人物形象呢?
本文使用的數(shù)據(jù)集包含已經(jīng)裁減完成的頭像如圖1所示,每張圖像的大小為96*96*3像素,總數(shù)為51000張。

圖 1 動(dòng)漫人物數(shù)據(jù)集
這項(xiàng)任務(wù)與之前的有監(jiān)督任務(wù)不同之處在于,監(jiān)督任務(wù)是有明確的輸入和輸出來對模型進(jìn)行優(yōu)化調(diào)整,而這一項(xiàng)任務(wù)是基于已有的數(shù)據(jù)集生成新的與原有數(shù)據(jù)集相似的新的數(shù)據(jù)。這是一個(gè)典型生成式任務(wù),即假設(shè)原始數(shù)據(jù)集中所有的動(dòng)漫圖像都服從于某一分布,數(shù)據(jù)集中的圖片是從這個(gè)分布隨機(jī)采樣得到的,倘若可以獲得這個(gè)分布是什么,那么就可以獲得與數(shù)據(jù)集中圖片分布相同但完全不同的新的動(dòng)漫形象。因此,生成式任務(wù)最重要核心任務(wù)就在于如何去獲得這個(gè)分布?,F(xiàn)有的基于圖像的生成式框架有VAE和GAN兩大分支,GAN的大名想必很多人都有所耳聞,其實(shí)驗(yàn)效果也是要由于VAE分支,本文將介紹基于GAN的動(dòng)漫人物生成任務(wù)。
02、反卷積網(wǎng)絡(luò)
反卷積層是GAN網(wǎng)絡(luò)的非常重要的一個(gè)部件。大多數(shù)卷積層會使特征圖的尺寸不斷變小,但反卷積層是為了使得特征圖逐漸變大,甚至與最初的輸入圖片一致。反卷積層最開始用于分割任務(wù),后來也被廣泛應(yīng)用于生成式任務(wù)中,如圖2所示,為一個(gè)反卷積層的正向傳播時(shí)的計(jì)算過程,下層藍(lán)色色塊的為輸入,白色虛線色塊為padding的部分,上層綠色的為反卷積層的輸出,原本3×3大小的特征圖經(jīng)過反卷積可以得到5×5的輸出。本文的網(wǎng)絡(luò)結(jié)構(gòu)中也使用了反卷積層作為重要的一環(huán)。

圖2 反卷積示意圖
在分類或者分割等計(jì)算機(jī)視覺的任務(wù)當(dāng)中,最終損失函數(shù)都需要對網(wǎng)絡(luò)的輸出與標(biāo)簽的差異進(jìn)行量化,比如常見的L1、L2、交叉熵等損失函數(shù),那么在生成式任務(wù)當(dāng)中,當(dāng)網(wǎng)絡(luò)輸出一張新的圖片,如何去評判這張圖片與原始數(shù)據(jù)集的分布是否一致?這是非常困難的一項(xiàng)事情,而GAN用很巧妙的思路規(guī)避了直接去判斷分布是否一致,通過引入另一個(gè)網(wǎng)絡(luò)(判別器)實(shí)現(xiàn)了判斷兩張圖片是否一致這一任務(wù)。
具體來說,假設(shè)原始的分布為Pdata(X) ,PG(X;θ) 指參數(shù)值為θ的卷積網(wǎng)絡(luò),其以隨機(jī)數(shù)x作為初入,輸出一張圖像,該卷積網(wǎng)絡(luò)稱為生成器,根據(jù)最大似然定理,希望每個(gè)樣例出現(xiàn)的概率的乘積最大,即最大化:

對 θ 進(jìn)行求解,可得:

即GAN的生成器目標(biāo)是找到PG(X;θ)的一組參數(shù),使其接近Pdata(X)分布,從而最小化生成器G生成結(jié)果與原始數(shù)據(jù)之間的差異

為了解決這個(gè)問題,GAN引入了判別器的概念,使用判別器D(X),來判斷PG(X;θ)生成的結(jié)果與Pdata(X)分布是否一致,判別器的目標(biāo)是給真樣本獎(jiǎng)勵(lì),假樣本懲罰,判別器的目的在于盡可能的區(qū)分生成器生成的樣本與數(shù)據(jù)集的樣本,當(dāng)輸入為數(shù)據(jù)集的樣本時(shí),判別器輸出為真,當(dāng)輸入為生成器生成的樣本時(shí),判別器輸出為假,GAN的結(jié)構(gòu)如圖 3所示。

圖3 GAN模型結(jié)構(gòu)
判別器希望最大化的目標(biāo)函數(shù),就是

這一優(yōu)化目標(biāo)與交叉熵函數(shù)的形式非常相似,需要注意的是,在優(yōu)化判別器時(shí),生成器中的參數(shù)是不變的。生成器與判別器的目標(biāo)不同,由于沒有像監(jiān)督學(xué)習(xí)那樣的標(biāo)簽用于生成器,因此,生成器的目標(biāo)為盡可能的騙過判別器,使判別器認(rèn)為生成器生成的樣本與原始數(shù)據(jù)集分布一致,即生成器的目標(biāo)函數(shù)為


至此,GAN的損失函數(shù)可寫為

03、DCGAN
本文中使用DCGAN作為網(wǎng)絡(luò)模型,其核心思想與GAN一致,只是將原始GAN的多層感知器替換為了卷積神經(jīng)網(wǎng)絡(luò),從而更符合圖像的性質(zhì)。下面介紹DCGAN的結(jié)構(gòu)。

圖4 DCGAN生成器網(wǎng)絡(luò)結(jié)構(gòu)
如圖4可知,DCGAN的生成器從一個(gè)100維的隨機(jī)變量開始,不斷疊加使用反卷積層,最終得到的64*64*3的輸出層。
其判別器為一個(gè)5層的卷積結(jié)構(gòu),以64*64*3大小作為輸入,單獨(dú)一個(gè)值作為輸出,為輸入判別器的圖像與數(shù)據(jù)集圖像同分布的概率。
訓(xùn)練步驟與損失函數(shù)與上文中GAN的一致,通過交替更新參數(shù)的方式,使生成器和判別器逐漸收斂。在下文中將具體介紹如何構(gòu)建DCGAN并實(shí)現(xiàn)動(dòng)漫人物生成。
04、基于DCGAN的動(dòng)漫人物生成
新建GanModel.py文件,并在這個(gè)腳本中構(gòu)建DCGAN的生成器和判別器模型,首先是生成器模型,由于本數(shù)據(jù)集的圖片大小為96*96,因此對原始DCGAN的參數(shù)做了一些調(diào)整,使得最終經(jīng)過生成器得到的圖片大小也是96*96。
如代碼清單1所示為經(jīng)過調(diào)整后的生成器網(wǎng)絡(luò),同樣包含有5層,出去最后一層,每層中都有一個(gè)卷積層、一個(gè)歸一化層以及一個(gè)激活函數(shù)。
代碼清單1 調(diào)整后的生成器網(wǎng)絡(luò)
1.import torch.nn as nn
2.# 定義生成器網(wǎng)絡(luò)G
3.class Generator(nn.Module):
4. def __init__(self, nz=100):
5. super(Generator, self).__init__()
6. # layer1輸入的是一個(gè)100x1x1的隨機(jī)噪聲, 輸出尺寸1024x4x4
7. self.layer1 = nn.Sequential(
8. nn.ConvTranspose2d(nz, 1024, kernel_size=4, stride=1, padding=0, bias=False),
9. nn.BatchNorm2d(1024),
10. nn.ReLU(inplace=True)
11. )
12. # layer2輸出尺寸512x8x8
13. self.layer2 = nn.Sequential(
14. nn.ConvTranspose2d(1024, 512, 4, 2, 1, bias=False),
15. nn.BatchNorm2d(512),
16. nn.ReLU(inplace=True)
17. )
18. # layer3輸出尺寸256x16x16
19. self.layer3 = nn.Sequential(
20. nn.ConvTranspose2d(512, 256, 4, 2, 1, bias=False),
21. nn.BatchNorm2d(256),
22. nn.ReLU(inplace=True)
23. )
24. # layer4輸出尺寸128x32x32
25. self.layer4 = nn.Sequential(
26. nn.ConvTranspose2d(256, 128, 4, 2, 1, bias=False),
27. nn.BatchNorm2d(128),
28. nn.ReLU(inplace=True)
29. )
30. # layer5輸出尺寸 3x96x96
31. self.layer5 = nn.Sequential(
32. nn.ConvTranspose2d(128, 3, 5, 3, 1, bias=False),
33. nn.Tanh()
34. )
35.
36. # 定義Generator的前向傳播
37. def forward(self, x):
38. out = self.layer1(x)
39. out = self.layer2(out)
40. out = self.layer3(out)
41. out = self.layer4(out)
42. out = self.layer5(out)
43. return out
定義判別器模型及前向傳播過程如代碼清單2所示。
代碼清單2 判別器模型的定義與前向傳播過程
1.# 定義鑒別器網(wǎng)絡(luò)D
2.class Discriminator(nn.Module):
3. def __init__(self):
4. super(Discriminator, self).__init__()
5. # layer1 輸入 3 x 96 x 96, 輸出 64 x 32 x 32
6. self.layer1 = nn.Sequential(
7. nn.Conv2d(3, 64, kernel_size=5, stride=3, padding=1, bias=False),
8. nn.BatchNorm2d(64),
9. nn.LeakyReLU(0.2, inplace=True)
10. )
11. # layer2 輸出 128 x 16 x 16
12. self.layer2 = nn.Sequential(
13. nn.Conv2d(64, 128, 4, 2, 1, bias=False),
14. nn.BatchNorm2d(128),
15. nn.LeakyReLU(0.2, inplace=True)
16. )
17. # layer3 輸出 256 x 8 x 8
18. self.layer3 = nn.Sequential(
19. nn.Conv2d(128, 256, 4, 2, 1, bias=False),
20. nn.BatchNorm2d(256),
21. nn.LeakyReLU(0.2, inplace=True)
22. )
23. # layer4 輸出 512 x 4 x 4
24. self.layer4 = nn.Sequential(
25. nn.Conv2d(256, 512, 4, 2, 1, bias=False),
26. nn.BatchNorm2d(512),
27. nn.LeakyReLU(0.2, inplace=True)
28. )
29. # layer5 輸出預(yù)測結(jié)果概率
30. self.layer5 = nn.Sequential(
31. nn.Conv2d(512, 1, 4, 1, 0, bias=False),
32. nn.Sigmoid()
33. )
34.
35. # 前向傳播
36. def forward(self, x):
37. out = self.layer1(x)
38. out = self.layer2(out)
39. out = self.layer3(out)
40. out = self.layer4(out)
41. out = self.layer5(out)
42. return out
定義完模型的基本結(jié)構(gòu)后,新建另一個(gè)python腳本DCGAN.py,并將數(shù)據(jù)集放在同一目錄下。如代碼清單3所示,首先是引入會用到的各種包以及超參數(shù),將超參數(shù)寫在最前面方便后續(xù)需要修改的時(shí)候進(jìn)行調(diào)整。其中超參數(shù)主要包含,一次迭代的batchsize大小,這個(gè)參數(shù)視GPU的性能而定,一般建議8以上,如果顯存足夠大,可以增大batchsize,batchsize越大,訓(xùn)練的速度也會越快。ImageSize為輸入的圖片大小,Epoch為訓(xùn)練要在數(shù)據(jù)集上訓(xùn)練幾個(gè)輪次,Lr是優(yōu)化器最開始的學(xué)習(xí)率的大小,Beta1為Adam優(yōu)化器的一階矩估計(jì)的指數(shù)衰減率,以及DataPath為數(shù)據(jù)集存放位置,OutPath為最終結(jié)果存放位置。
代碼清單3 DCGAN超參數(shù)定義
1.import torch
2.import torchvision
3.import torchvision.utils as vutils
4.import torch.nn as nn
5.from GanModel import Generator, Discriminator
6.
7.# 設(shè)置超參數(shù)
8.BatchSize = 8
9.ImageSize = 96
10.Epoch = 25
11.Lr = 0.0002
12.Beta1 = 0.5
13.DataPath = './faces/'
14.OutPath = './imgs/'
15.# 定義是否使用GPU
16.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
接下來定義train函數(shù),如代碼清單4所示。以數(shù)據(jù)集、生成器、鑒別器作為函數(shù)輸入,首先設(shè)置優(yōu)化器以及損失函數(shù)。
代碼清單4 train函數(shù)定義
1.def train(netG, netD, dataloader):
2. criterion = nn.BCELoss()
3. optimizerG = torch.optim.Adam(netG.parameters(), lr=Lr, betas=(Beta1, 0.999))
4. optimizerD = torch.optim.Adam(netD.parameters(), lr=Lr, betas=(Beta1, 0.999))
5.
6. label = torch.FloatTensor(BatchSize)
7. real_label = 1
8. fake_label = 0
再開始一輪一輪的迭代訓(xùn)練并輸出中間結(jié)果,方便debug。
代碼清單5 鑒別器訓(xùn)練
1.for epoch in range(1, Epoch + 1):
2. for i, (imgs, _) in enumerate(dataloader):
3. # 固定生成器G,訓(xùn)練鑒別器D
4. optimizerD.zero_grad()
5. # 讓D盡可能的把真圖片判別為1
6. imgs = imgs.to(device)
7. output = netD(imgs)
8. label.data.fill_(real_label)
9. label = label.to(device)
10. errD_real = criterion(output, label)
11. errD_real.backward()
12. # 讓D盡可能把假圖片判別為0
13. label.data.fill_(fake_label)
14. noise = torch.randn(BatchSize, 100, 1, 1)
15. noise = noise.to(device)
16. fake = netG(noise)
17. # 避免梯度傳到G,因?yàn)镚不用更新
18. output = netD(fake.detach())
19. errD_fake = criterion(output, label)
20. errD_fake.backward()
21. errD = errD_fake + errD_real
22. optimizerD.step()
如代碼清單5所示,首先固定生成器的參數(shù),并隨機(jī)一組隨機(jī)數(shù)送入生成器得到一組假圖片,同時(shí)從數(shù)據(jù)集中抽取同樣數(shù)目的真圖片,假圖片對應(yīng)標(biāo)簽為0,真圖片對應(yīng)標(biāo)簽為1,將這組數(shù)據(jù)送入判別器進(jìn)行參數(shù)更新。
代碼清單 6 生成器訓(xùn)練
1. # 固定鑒別器D,訓(xùn)練生成器G
2. optimizerG.zero_grad()
3. # 讓D盡可能把G生成的假圖判別為1
4. label.data.fill_(real_label)
5. label = label.to(device)
6. output = netD(fake)
7. errG = criterion(output, label)
8. errG.backward()
9. optimizerG.step()
10. if i % 50 == 0:
11. print('[%d/%d][%d/%d] Loss_D: %.3f Loss_G %.3f'
12. % (epoch, Epoch, i, len(dataloader), errD.item(), errG.item()))
13.
14.vutils.save_image(fake.data,
15. '%s/fake_samples_epoch_%03d.png' % (OutPath, epoch),
16. normalize=True)
17.torch.save(netG.state_dict(), '%s/netG_%03d.pth' % (OutPath, epoch))
18.torch.save(netD.state_dict(), '%s/netD_%03d.pth' % (OutPath, epoch))
如代碼清單6所示,接下來固定判別器參數(shù),訓(xùn)練生成器,生成器的目標(biāo)是根據(jù)隨機(jī)數(shù)生成得到的圖片能夠騙過判別器,使之認(rèn)為這些圖片為真,因此將生成得到的假圖經(jīng)過判別器得到判別結(jié)果,并設(shè)置標(biāo)簽全部為1,計(jì)算損失函數(shù)并反向傳播對生成器參數(shù)進(jìn)行更新。
在訓(xùn)練過程中,不斷打印生成器和判別器Loss的變化情況,從而方便進(jìn)行觀察并調(diào)整參數(shù)。每訓(xùn)練完一個(gè)Epoch,則將該Epoch中生成器得到的假圖保存下來,同時(shí)存儲生成器和判別器的參數(shù),防止訓(xùn)練過程突然被終止,可以使用存儲的參數(shù)進(jìn)行恢復(fù),不需要再從頭進(jìn)行訓(xùn)練。
最后完成mian函數(shù)主程序入口代碼的編寫,其包含了加載數(shù)據(jù)集、定義模型、訓(xùn)練等步驟,如代碼清單 7所示。
Transforms定義了對數(shù)據(jù)集中輸入圖片進(jìn)行預(yù)處理的步驟,主要包含scale對輸入圖片大小進(jìn)行調(diào)整,ToTensor轉(zhuǎn)化為PyTorch的Tensor類型以及Normalize中使用均值和標(biāo)準(zhǔn)差來進(jìn)行圖片的歸一化。
代碼清單7 主程序
1.if __name__ == "__main__":
2. # 圖像格式轉(zhuǎn)化與歸一化
3. transforms = torchvision.transforms.Compose([
4. torchvision.transforms.Scale(ImageSize),
5. torchvision.transforms.ToTensor(),
6. torchvision.transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
7. dataset = torchvision.datasets.ImageFolder(DataPath, transform=transforms)
8.
9. dataloader = torch.utils.data.DataLoader(
10. dataset=dataset,
11. batch_size=BatchSize,
12. shuffle=True,
13. drop_last=True,
14. )
15.
16. netG = Generator().to(device)
17. netD = Discriminator().to(device)
18. train(netG, netD, dataloader)
開始訓(xùn)練后,在命令行可得類似于如圖5的輸出。

圖5 訓(xùn)練過程命令行輸出
與手寫數(shù)字識別不同在于,可以發(fā)現(xiàn)生成器和判別器的Loss值都在一會高一會低的狀態(tài),這種狀態(tài)是我們想要的結(jié)果嗎?如果大家注意觀察,會發(fā)現(xiàn)很多情況下當(dāng)判別器的Loss值下降時(shí),生成器的Loss值會上升,而判別器的Loss出現(xiàn)了上升,生成器Loss會出現(xiàn)下降。這是由于判別器和生成器一直處于一種互相“打架”的狀態(tài),生成器想要騙過判別器,而判別器則努力不去被生成器騙過,才會有Loss值出現(xiàn)如此狀況。兩個(gè)網(wǎng)絡(luò)在循環(huán)打架過程中不斷增強(qiáng),最終就可以得到一個(gè)甚至能騙過人眼的生成器。
讓我們來看一下經(jīng)過一個(gè)Epoch迭代后的生成器得到的結(jié)果如圖6所示。

圖6 Epoch1測試結(jié)果可視化
好像已經(jīng)有了那么一些輪廓,但又像戴了近視鏡一樣看不清,頗有些印象派作家的畫風(fēng),繼續(xù)訓(xùn)練網(wǎng)絡(luò),如圖7所示,等到第5,第10個(gè)Epoch,會發(fā)現(xiàn)生成器生成的質(zhì)量越來越高。

圖7 Epoch15測試結(jié)果可視化
一直到第25個(gè)Epoch,得到的結(jié)果如圖 8所示,盡管生成的圖片中還是存在一些結(jié)構(gòu)性問題,但也有一些圖片逐漸開始接近于我們的期待。當(dāng)然,本文迭代次數(shù)較少,僅有25次,若進(jìn)一步升高迭代次數(shù),最終可獲得更加真實(shí)的動(dòng)漫頭像。

圖8 Epoch25測試結(jié)果可視化
05、文末送書

今天給大家送出的是由冀俊峰著【作者簡介】北京大學(xué)出版社出版的《數(shù)字身份與元宇宙信任治理》!
解析元宇宙框架及其信任治理底層邏輯,討論數(shù)字身份模式的發(fā)展趨勢,分解元宇宙數(shù)字身份的技術(shù)要素,建設(shè)元宇宙信任環(huán)境,助力未來元宇宙數(shù)字身份構(gòu)建、管理、應(yīng)用賦能及零信任安全管理。
內(nèi)容簡介
本書是一本介紹數(shù)字身份和元宇宙的普及型書籍,力求專業(yè)性與通俗性相平衡。全書共八章,其中前四章主要介紹數(shù)字身份管理及應(yīng)用,包括數(shù)字身份的相關(guān)概念及特性;身份認(rèn)證管理、應(yīng)用賦能及零信任安全管理;各國的數(shù)字身份實(shí)施;討論數(shù)字身份在公共治理、商業(yè)服務(wù)等領(lǐng)域的應(yīng)用價(jià)值。后面四章主要探究元宇宙框架及其信任治理,從Web技術(shù)架構(gòu)的演變,介紹元宇宙的網(wǎng)絡(luò)技術(shù)基礎(chǔ)Web 3.0,以及相關(guān)的數(shù)字身份模式的發(fā)展趨勢;討論元宇宙中的數(shù)字身份技術(shù)要素及形態(tài)特征,以及數(shù)字身份、數(shù)字分身等關(guān)鍵特征要素;探討利用數(shù)字身份對元宇宙的信任環(huán)境進(jìn)行治理的方法和技術(shù);探討如何構(gòu)建元宇宙的信任治理規(guī)則。
作者簡介
冀俊峰,中科院軟件所博士,高級工程師,論文曾獲得國際計(jì)算機(jī)圖形學(xué)會議CGI'2005 最佳論文。自2005 年以來,作者一直在國家信息中心及國家電子政務(wù)外網(wǎng)管理中心從事網(wǎng)絡(luò)規(guī)劃及數(shù)字經(jīng)濟(jì)等方面的發(fā)展研究工作,撰寫論文曾多次獲得國家發(fā)改委中青年經(jīng)濟(jì)論壇優(yōu)秀論文。主要做圖形學(xué)VR\AR\區(qū)塊鏈等。文章來源:http://www.zghlxwxcb.cn/news/detail-415792.html
參與方式:點(diǎn)擊文章置頂評論紅包,手氣王自動(dòng)獲得北京大學(xué)出版社《數(shù)字身份與元宇宙信任治理》1本。文章來源地址http://www.zghlxwxcb.cn/news/detail-415792.html
到了這里,關(guān)于PyTorch 深度學(xué)習(xí)實(shí)戰(zhàn) | 基于生成式對抗網(wǎng)絡(luò)生成動(dòng)漫人物的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!