戰(zhàn):從DCGAN到條件生成,實(shí)現(xiàn)可控圖像生成與數(shù)據(jù)增強(qiáng))
簡(jiǎn)介這份資源是面向深度學(xué)習(xí)與生成模型學(xué)習(xí)者的 ACGAN PyTorch 實(shí)現(xiàn)代碼包適合已掌握 GAN 基礎(chǔ)、希望進(jìn)一步理解條件生成與類別可控圖像生成的開(kāi)發(fā)者。ACGAN 在傳統(tǒng)生成對(duì)抗網(wǎng)絡(luò)基礎(chǔ)上引入輔助分類器使生成器能依據(jù)隨機(jī)噪聲與 one-hot 類別標(biāo)簽合成指定類別的樣本常用于圖像生成、數(shù)據(jù)增強(qiáng)與半監(jiān)督學(xué)習(xí)等場(chǎng)景。資源包共 4 個(gè)文件包含 3 個(gè) py 腳本與 1 個(gè) md 說(shuō)明文檔分別對(duì)應(yīng)模型定義、訓(xùn)練主流程與判別邏輯整體約 3KB結(jié)構(gòu)精簡(jiǎn)便于快速閱讀與二次修改。目前已有 823 人學(xué)習(xí)下載。通過(guò)該實(shí)現(xiàn)讀者可掌握生成器、判別器與輔助分類器的網(wǎng)絡(luò)搭建方式理解對(duì)抗損失與類別條件損失的組合設(shè)計(jì)并參考交替訓(xùn)練策略完成模型調(diào)試與生成效果監(jiān)控。1. ACGAN PyTorch 實(shí)現(xiàn)從「生成得像」到「生成得準(zhǔn)」的那一步用 MNIST 跑通一個(gè) DCGAN看著數(shù)字從噪聲里慢慢浮現(xiàn)確實(shí)挺爽。但很快你會(huì)撞上一堵墻模型能生成「像數(shù)字的東西」卻完全不聽(tīng)你指揮——你想讓它畫(huà)個(gè) 7它給你個(gè) 3你想批量生成 100 張 8它給你一堆隨機(jī)數(shù)字。這不是訓(xùn)練不夠是 DCGAN 的判別器只判斷「真不真」壓根不關(guān)心「是幾」。ACGANAuxiliary Classifier GAN要解決的就是這件事在判別器上掛一個(gè)輔助分類頭讓生成器不僅騙過(guò)真假判別還得讓判別器把生成樣本分到正確的類別上。一句話ACGAN 把「生成得像」升級(jí)成了「生成得準(zhǔn)」。這篇筆記面向兩類人一類是已經(jīng)跑通過(guò)基礎(chǔ) GAN、想搞懂條件生成怎么落地的人另一類是直接想拿 ACGAN 做數(shù)據(jù)增強(qiáng)、類別補(bǔ)齊、小樣本擴(kuò)充的工程師。我會(huì)用 PyTorch 從零搭一個(gè)能在 MNIST 和 CIFAR-10 上跑的 ACGAN把損失函數(shù)怎么寫(xiě)、標(biāo)簽怎么嵌、參數(shù)怎么調(diào)、哪里容易翻車全部拆開(kāi)講清楚。代碼可以直接抄參數(shù)可以照著改坑我替你踩過(guò)了。2. ACGAN 的結(jié)構(gòu)拆解判別器為什么要多一個(gè)頭2.1 從 DCGAN 到 ACGAN判別器的職責(zé)變了DCGAN 的判別器只輸出一個(gè)標(biāo)量表示輸入圖像為真的概率。生成器的損失就是讓這個(gè)概率越大越好。問(wèn)題在于這個(gè)信號(hào)太弱了——它只告訴生成器「你畫(huà)得不夠真」但沒(méi)告訴它「你畫(huà)錯(cuò)了類別」。當(dāng)你要生成 10 類數(shù)字時(shí)生成器完全不知道當(dāng)前噪聲對(duì)應(yīng)的應(yīng)該是哪個(gè)數(shù)字它只能隨機(jī)覆蓋整個(gè)數(shù)據(jù)分布。ACGAN 的做法很直接判別器輸出兩個(gè)東西。第一個(gè)還是真假判別輸出維度 1第二個(gè)是類別分類輸出維度等于類別數(shù)。生成器的輸入除了噪聲 z還多了一個(gè)類別標(biāo)簽 c。生成器要同時(shí)騙過(guò)兩個(gè)頭讓真假頭認(rèn)為生成樣本是真的讓分類頭認(rèn)為生成樣本屬于標(biāo)簽 c。判別器則要同時(shí)做對(duì)兩件事分清真假以及把真實(shí)樣本和生成樣本都分到正確的類別。這里有個(gè)關(guān)鍵細(xì)節(jié)判別器的分類頭對(duì)真實(shí)樣本和生成樣本都要計(jì)算分類損失。對(duì)真實(shí)樣本標(biāo)簽就是它的真實(shí)類別對(duì)生成樣本標(biāo)簽是生成時(shí)用的那個(gè) c。這意味著判別器不僅要學(xué)會(huì)分類真實(shí)圖像還要學(xué)會(huì)分類生成圖像——后者其實(shí)是在幫生成器做「反向監(jiān)督」。2.2 損失函數(shù)兩個(gè)交叉熵怎么加ACGAN 的損失函數(shù)是兩部分之和。判別器的損失包括真假判別的二分類交叉熵或 BCEWithLogitsLoss加上類別分類的交叉熵。生成器的損失包括真假判別部分希望判別器把生成樣本判為真加上類別分類部分希望判別器把生成樣本分到正確類別。具體來(lái)說(shuō)設(shè)判別器對(duì)輸入 x 的輸出為 (adv_out, class_out)其中 adv_out 是標(biāo)量class_out 是長(zhǎng)度為 C 的向量。對(duì)于真實(shí)樣本 (x_real, y_real)判別器損失為L(zhǎng)_D BCE(adv_out_real, 1) BCE(adv_out_fake, 0) CE(class_out_real, y_real) CE(class_out_fake, y_fake)對(duì)于生成器損失為L(zhǎng)_G BCE(adv_out_fake, 1) CE(class_out_fake, y_fake)注意這里生成器的分類損失和判別器的分類損失方向是相反的判別器希望分類正確生成器希望判別器「誤以為」生成樣本屬于 y_fake——但 y_fake 本來(lái)就是生成時(shí)指定的標(biāo)簽所以實(shí)際上生成器是在推動(dòng)判別器把生成樣本分到那個(gè)標(biāo)簽。這兩個(gè)損失在訓(xùn)練中會(huì)形成對(duì)抗但不會(huì)像純 GAN 那樣完全零和因?yàn)榉诸惾蝿?wù)本身有真實(shí)標(biāo)簽作為錨點(diǎn)。一個(gè)常見(jiàn)的實(shí)現(xiàn)細(xì)節(jié)是分類損失用 CrossEntropyLoss真假損失用 BCEWithLogitsLoss。后者把 sigmoid 和 BCE 合在一起數(shù)值更穩(wěn)定。如果你用 BCELoss記得在判別器輸出后手動(dòng)加 sigmoid。2.3 生成器怎么把標(biāo)簽嵌進(jìn)去生成器的輸入是噪聲 z 和標(biāo)簽 c。標(biāo)簽 c 是整數(shù)需要先轉(zhuǎn)成 one-hot 向量或嵌入向量。兩種做法都行我一般用 nn.Embedding因?yàn)樗陬悇e數(shù)多的時(shí)候更省參數(shù)而且嵌入維度可以調(diào)。具體操作把 c 通過(guò) Embedding 層變成維度為 embed_dim 的向量然后把 z 和這個(gè)嵌入向量拼接torch.cat一起送進(jìn)生成器的反卷積網(wǎng)絡(luò)。嵌入維度一般取 50 到 100MNIST 這種 10 類問(wèn)題取 50 就夠了。拼接后的向量維度是 z_dim embed_dim通常設(shè) z_dim100embed_dim50總共 150 維。判別器這邊不需要嵌入它直接接收?qǐng)D像輸出 adv_out 和 class_out。class_out 的維度是類別數(shù)用全連接層從特征圖映射過(guò)去。2.4 一個(gè)最小可跑的 ACGAN 代碼骨架下面這段代碼是 MNIST 上的最小實(shí)現(xiàn)包含生成器、判別器和訓(xùn)練循環(huán)的核心部分。我把它拆成幾個(gè)代碼塊方便你逐段理解。import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torchvision import datasets, transforms # 超參數(shù) z_dim 100 embed_dim 50 num_classes 10 batch_size 128 lr 2e-4 epochs 50 device torch.device(cuda if torch.cuda.is_available() else cpu)這段定義了所有超參數(shù)。z_dim 是噪聲維度embed_dim 是標(biāo)簽嵌入維度num_classes 是類別數(shù)。學(xué)習(xí)率 2e-4 是 GAN 訓(xùn)練的常用值比普通分類網(wǎng)絡(luò)低因?yàn)?GAN 對(duì)學(xué)習(xí)率很敏感。batch_size 128 在 MNIST 上足夠穩(wěn)定。class Generator(nn.Module): def __init__(self): super().__init__() self.embed nn.Embedding(num_classes, embed_dim) self.fc nn.Sequential( nn.Linear(z_dim embed_dim, 256), nn.BatchNorm1d(256), nn.ReLU(True), nn.Linear(256, 512), nn.BatchNorm1d(512), nn.ReLU(True), nn.Linear(512, 1024), nn.BatchNorm1d(1024), nn.ReLU(True), nn.Linear(1024, 28 * 28), nn.Tanh() ) def forward(self, z, labels): c self.embed(labels) # (B, embed_dim) x torch.cat([z, c], dim1) # (B, z_dim embed_dim) x self.fc(x) return x.view(-1, 1, 28, 28)生成器用全連接層而不是反卷積因?yàn)?MNIST 只有 28x28全連接足夠。注意最后一層用 Tanh把輸出壓到 [-1, 1]因?yàn)檩斎雸D像也歸一化到了這個(gè)范圍。BatchNorm1d 在 1D 特征上做歸一化訓(xùn)練時(shí)要注意 batch 不能太小否則 BN 的統(tǒng)計(jì)量不準(zhǔn)。class Discriminator(nn.Module): def __init__(self): super().__init__() self.features nn.Sequential( nn.Linear(28 * 28, 512), nn.LeakyReLU(0.2, inplaceTrue), nn.Linear(512, 256), nn.LeakyReLU(0.2, inplaceTrue), ) self.adv_head nn.Linear(256, 1) # 真假判別 self.class_head nn.Linear(256, num_classes) # 類別分類 def forward(self, x): x x.view(x.size(0), -1) feat self.features(x) adv_out self.adv_head(feat) # (B, 1) class_out self.class_head(feat) # (B, num_classes) return adv_out, class_out判別器共享特征提取層然后分兩個(gè)頭。adv_head 輸出 1 維class_head 輸出 num_classes 維。注意這里沒(méi)有在 adv_head 后加 sigmoid因?yàn)楹竺嬗?BCEWithLogitsLoss它內(nèi)部會(huì)做 sigmoid。LeakyReLU 的負(fù)斜率 0.2 是 GAN 里的常見(jiàn)選擇比 ReLU 更不容易讓梯度死掉。# 初始化模型、優(yōu)化器、損失函數(shù) G Generator().to(device) D Discriminator().to(device) opt_G optim.Adam(G.parameters(), lrlr, betas(0.5, 0.999)) opt_D optim.Adam(D.parameters(), lrlr, betas(0.5, 0.999)) bce nn.BCEWithLogitsLoss() ce nn.CrossEntropyLoss() # 數(shù)據(jù)加載 transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,)) ]) dataset datasets.MNIST(root./data, trainTrue, downloadTrue, transformtransform) loader DataLoader(dataset, batch_sizebatch_size, shuffleTrue, drop_lastTrue)優(yōu)化器用 Adambetas 設(shè)為 (0.5, 0.999) 而不是默認(rèn)的 (0.9, 0.999)。這是 GAN 訓(xùn)練的一個(gè)經(jīng)驗(yàn)值降低動(dòng)量可以避免訓(xùn)練震蕩。drop_lastTrue 是為了避免最后一個(gè) batch 只有 1 個(gè)樣本導(dǎo)致 BatchNorm 報(bào)錯(cuò)。for epoch in range(epochs): for imgs, labels in loader: imgs, labels imgs.to(device), labels.to(device) B imgs.size(0) # 真實(shí)和生成標(biāo)簽 real_target torch.ones(B, 1, devicedevice) fake_target torch.zeros(B, 1, devicedevice) # --------------------- # 訓(xùn)練判別器 # --------------------- z torch.randn(B, z_dim, devicedevice) fake_labels torch.randint(0, num_classes, (B,), devicedevice) fake_imgs G(z, fake_labels) adv_real, class_real D(imgs) adv_fake, class_fake D(fake_imgs.detach()) loss_D (bce(adv_real, real_target) bce(adv_fake, fake_target) ce(class_real, labels) ce(class_fake, fake_labels)) opt_D.zero_grad() loss_D.backward() opt_D.step() # --------------------- # 訓(xùn)練生成器 # --------------------- adv_fake, class_fake D(fake_imgs) loss_G bce(adv_fake, real_target) ce(class_fake, fake_labels) opt_G.zero_grad() loss_G.backward() opt_G.step()訓(xùn)練循環(huán)里有兩個(gè)關(guān)鍵點(diǎn)。第一訓(xùn)練判別器時(shí)生成樣本要 detach()否則梯度會(huì)回傳到生成器打亂訓(xùn)練節(jié)奏。第二生成器的損失里真假部分用的是 real_target希望判別器把假樣本判為真分類部分用的是 fake_labels生成時(shí)指定的標(biāo)簽。這兩個(gè)損失一起反傳生成器就會(huì)同時(shí)優(yōu)化「像真」和「類別對(duì)」兩個(gè)目標(biāo)。3. 在 MNIST 和 CIFAR-10 上跑通參數(shù)、結(jié)構(gòu)和訓(xùn)練策略3.1 MNIST 上的收斂曲線和生成效果MNIST 上 ACGAN 通常 20 到 30 個(gè) epoch 就能收斂。判別器的真假損失會(huì)穩(wěn)定在 0.5 到 0.8 之間分類損失會(huì)降到 0.1 以下。生成器的真假損失會(huì)波動(dòng)但整體趨勢(shì)是下降的。如果你看到判別器損失迅速降到 0說(shuō)明判別器太強(qiáng)了生成器學(xué)不動(dòng)這時(shí)候要降低判別器的學(xué)習(xí)率或者減少它的更新次數(shù)。生成效果上ACGAN 的優(yōu)勢(shì)在于你可以指定標(biāo)簽生成。比如固定 z只改標(biāo)簽就能看到同一個(gè)噪聲在不同類別下的輸出。如果生成器學(xué)到了類別特征同一個(gè) z 在不同標(biāo)簽下應(yīng)該生成不同數(shù)字但風(fēng)格筆畫(huà)粗細(xì)、傾斜角度保持一致。如果風(fēng)格也變了說(shuō)明標(biāo)簽嵌入沒(méi)有真正起作用生成器可能只是把標(biāo)簽當(dāng)成了額外的噪聲。一個(gè)驗(yàn)證方法是做標(biāo)簽插值取兩個(gè)標(biāo)簽的嵌入向量做線性插值看生成圖像是否平滑過(guò)渡。如果從 0 到 1 的插值中間出現(xiàn)了 8 或 9說(shuō)明嵌入空間沒(méi)有組織好可能需要增大 embed_dim 或者加一個(gè)嵌入向量的正則項(xiàng)。3.2 CIFAR-10 上要改什么從全連接到卷積CIFAR-10 是 32x32 彩色圖像全連接生成器參數(shù)量會(huì)爆炸而且生成質(zhì)量很差。這時(shí)候要換成卷積結(jié)構(gòu)。生成器用 ConvTranspose2d 做上采樣判別器用 Conv2d 做下采樣。標(biāo)簽嵌入還是拼在噪聲后面但拼接后要 reshape 成 (B, z_dimembed_dim, 1, 1)然后通過(guò)反卷積逐層放大到 32x32。class ConvGenerator(nn.Module): def __init__(self): super().__init__() self.embed nn.Embedding(num_classes, embed_dim) self.fc nn.Linear(z_dim embed_dim, 256 * 4 * 4) self.net nn.Sequential( nn.BatchNorm2d(256), nn.ReLU(True), nn.ConvTranspose2d(256, 128, 4, 2, 1), # 4x4 - 8x8 nn.BatchNorm2d(128), nn.ReLU(True), nn.ConvTranspose2d(128, 64, 4, 2, 1), # 8x8 - 16x16 nn.BatchNorm2d(64), nn.ReLU(True), nn.ConvTranspose2d(64, 3, 4, 2, 1), # 16x16 - 32x32 nn.Tanh() ) def forward(self, z, labels): c self.embed(labels) x torch.cat([z, c], dim1) x self.fc(x).view(-1, 256, 4, 4) return self.net(x)ConvTranspose2d 的參數(shù)是 (in_channels, out_channels, kernel_size, stride, padding)。這里用 kernel_size4, stride2, padding1可以把特征圖尺寸翻倍。三層反卷積從 4x4 放大到 32x32。判別器對(duì)稱地用 Conv2d 下采樣最后接兩個(gè)頭。CIFAR-10 上訓(xùn)練時(shí)間更長(zhǎng)通常要 100 個(gè) epoch 以上。學(xué)習(xí)率可以保持 2e-4但 batch_size 建議降到 64因?yàn)?CIFAR-10 的圖像更復(fù)雜大 batch 容易讓判別器過(guò)強(qiáng)。另外CIFAR-10 上可以加譜歸一化Spectral Normalization來(lái)穩(wěn)定訓(xùn)練PyTorch 里用 nn.utils.spectral_norm 包一下卷積層就行。3.3 訓(xùn)練策略什么時(shí)候更新判別器什么時(shí)候更新生成器標(biāo)準(zhǔn) ACGAN 是每步同時(shí)更新 D 和 G。但實(shí)踐中如果判別器太強(qiáng)生成器會(huì)梯度消失如果生成器太強(qiáng)判別器會(huì)分類不準(zhǔn)。一個(gè)常用的策略是每訓(xùn)練 1 次生成器訓(xùn)練 1 到 2 次判別器。具體比例要看數(shù)據(jù)。MNIST 上 1:1 就夠了CIFAR-10 上可以試 1:2。另一個(gè)策略是標(biāo)簽平滑。把真實(shí)樣本的標(biāo)簽從 1 改成 0.9假樣本從 0 改成 0.1可以防止判別器過(guò)度自信。這個(gè)技巧在 ACGAN 上同樣有效尤其是分類頭標(biāo)簽平滑能緩解過(guò)擬合。還有一個(gè)細(xì)節(jié)生成器的分類損失權(quán)重。有些實(shí)現(xiàn)里會(huì)給分類損失乘一個(gè)系數(shù)比如 0.5 或 1.0。如果分類損失太大生成器會(huì)優(yōu)先保證類別正確犧牲圖像質(zhì)量如果太小類別控制會(huì)變?nèi)酢N乙话銖?1.0 開(kāi)始調(diào)如果生成圖像模糊但類別很準(zhǔn)就降到 0.5如果類別經(jīng)常錯(cuò)就升到 2.0。4. 避坑與排查ACGAN 訓(xùn)練中最容易翻車的 5 個(gè)地方4.1 生成器只生成一類圖像或者類別完全隨機(jī)現(xiàn)象訓(xùn)練一段時(shí)間后生成器對(duì)所有標(biāo)簽都輸出同一個(gè)數(shù)字或者標(biāo)簽和輸出完全不對(duì)應(yīng)。原因最常見(jiàn)的原因是判別器的分類頭太強(qiáng)生成器的分類損失梯度被真假損失淹沒(méi)。另一個(gè)原因是標(biāo)簽嵌入沒(méi)有學(xué)好生成器把標(biāo)簽當(dāng)成了噪聲。解決先檢查判別器的分類準(zhǔn)確率。如果真實(shí)樣本的分類準(zhǔn)確率超過(guò) 95%但生成樣本的分類準(zhǔn)確率很低說(shuō)明判別器在分類任務(wù)上過(guò)擬合了。這時(shí)候要降低判別器分類損失的權(quán)重或者給判別器加 dropout。如果標(biāo)簽嵌入沒(méi)學(xué)好可以嘗試增大 embed_dim或者在生成器里加一個(gè)輔助分類器讓生成器自己也預(yù)測(cè)類別。4.2 訓(xùn)練震蕩損失忽高忽低現(xiàn)象判別器和生成器的損失劇烈波動(dòng)生成圖像質(zhì)量時(shí)好時(shí)壞。原因?qū)W習(xí)率太大或者 Adam 的 beta1 太高。GAN 訓(xùn)練本身就不穩(wěn)定ACGAN 因?yàn)槎嗔朔诸惾蝿?wù)震蕩會(huì)更明顯。解決把學(xué)習(xí)率降到 1e-4 或 5e-5beta1 從 0.9 降到 0.5。如果還震蕩可以試 RMSprop 或者 SGD。另外減小 batch_size 也有幫助因?yàn)樾?batch 的梯度噪聲更大反而能平滑訓(xùn)練。4.3 生成圖像出現(xiàn)棋盤(pán)格偽影現(xiàn)象生成圖像上有規(guī)律的網(wǎng)格狀紋理尤其在反卷積層較多時(shí)明顯。原因ConvTranspose2d 的 stride 和 kernel_size 不匹配導(dǎo)致輸出像素重疊不均勻。解決把 kernel_size 設(shè)為 stride 的整數(shù)倍比如 stride2 時(shí) kernel_size 用 4 或 6。或者用最近鄰上采樣加普通卷積代替反卷積。PyTorch 里可以用 nn.Upsample(scale_factor2, modenearest) 接一個(gè) Conv2d效果通常更干凈。4.4 判別器分類頭對(duì)生成樣本的損失不下降現(xiàn)象判別器對(duì)真實(shí)樣本分類很準(zhǔn)但對(duì)生成樣本的分類損失一直很高。原因生成樣本的分布和真實(shí)樣本差異太大判別器學(xué)到的分類邊界不適用于生成樣本。這其實(shí)是正?,F(xiàn)象因?yàn)樯蓸颖驹谟?xùn)練初期質(zhì)量很差。但如果訓(xùn)練很久后仍然不下降說(shuō)明生成器沒(méi)有學(xué)到類別特征。解決檢查生成器的分類損失是否在下降。如果生成器的分類損失也不降說(shuō)明標(biāo)簽嵌入沒(méi)有起作用??梢試L試把標(biāo)簽嵌入和噪聲拼接的方式改成相乘或者用條件批歸一化Conditional BatchNorm。另一個(gè)方法是給生成器加一個(gè)獨(dú)立的分類器用真實(shí)分類器來(lái)監(jiān)督生成樣本的類別。4.5 顯存溢出或訓(xùn)練速度過(guò)慢現(xiàn)象CIFAR-10 上訓(xùn)練時(shí)顯存不夠或者每個(gè) epoch 要跑很久。原因判別器的兩個(gè)頭共享特征層但分類頭的參數(shù)量可能很大。另外如果 batch_size 太大BatchNorm 的統(tǒng)計(jì)量會(huì)占用額外顯存。解決減小 batch_size或者把判別器的特征層通道數(shù)減半。如果顯存還是不夠可以用混合精度訓(xùn)練torch.cuda.amp。速度方面把 DataLoader 的 num_workers 設(shè)為 4 或 8pin_memoryTrue能明顯加快數(shù)據(jù)加載。5. 進(jìn)階技巧用 ACGAN 做數(shù)據(jù)增強(qiáng)和類別補(bǔ)齊ACGAN 最實(shí)用的落地場(chǎng)景是數(shù)據(jù)增強(qiáng)。當(dāng)你手頭某個(gè)類別的樣本特別少可以用 ACGAN 生成該類的合成樣本擴(kuò)充訓(xùn)練集。但這里有個(gè)坑生成樣本的質(zhì)量直接決定增強(qiáng)效果。如果生成樣本和真實(shí)樣本分布差異大增強(qiáng)反而會(huì) hurt 模型。我的做法是先用真實(shí)數(shù)據(jù)訓(xùn)練一個(gè)分類器然后用 ACGAN 生成樣本把生成樣本喂給這個(gè)分類器看分類準(zhǔn)確率。如果準(zhǔn)確率下降超過(guò) 5%說(shuō)明生成樣本質(zhì)量不夠不能直接用。這時(shí)候可以調(diào)低生成樣本的權(quán)重或者只把生成樣本用于預(yù)訓(xùn)練再用真實(shí)數(shù)據(jù)微調(diào)。另一個(gè)技巧是類別插值。ACGAN 的標(biāo)簽嵌入空間是連續(xù)的你可以取兩個(gè)類別的嵌入向量做插值生成中間類別的圖像。這在做細(xì)粒度分類時(shí)很有用比如從「貓」到「狗」的插值可以生成一些介于兩者之間的樣本幫助模型學(xué)到更平滑的決策邊界。驗(yàn)證 ACGAN 是否真正學(xué)到了類別控制有一個(gè)簡(jiǎn)單方法固定噪聲 z遍歷所有標(biāo)簽生成一組圖像。如果這些圖像在類別上明顯不同但在風(fēng)格上一致說(shuō)明模型學(xué)到了解耦的表示。如果風(fēng)格也跟著變說(shuō)明標(biāo)簽和噪聲沒(méi)有解耦需要調(diào)整嵌入維度或者加正則。最后說(shuō)一個(gè)我踩過(guò)的坑ACGAN 的生成器在訓(xùn)練后期容易過(guò)擬合到訓(xùn)練集的類別分布。如果訓(xùn)練集里某個(gè)類別樣本少生成器對(duì)這個(gè)類別的生成質(zhì)量會(huì)明顯差。解決辦法是在訓(xùn)練時(shí)對(duì)類別進(jìn)行重采樣讓每個(gè) batch 里各類別樣本數(shù)均衡。這個(gè)細(xì)節(jié)在數(shù)據(jù)增強(qiáng)場(chǎng)景下特別重要因?yàn)槟阋a(bǔ)的恰恰是少樣本類別。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取