LadaGAN 做得太逼真之後:一場生成器與判別器的拉鋸

從 LadaGAN 人臉生成、150K 合成資料到 EfficientNetV2-B1 真假分類,整理模型比較、生成樣本與評估結果。

18 分鐘閱讀
LadaGAN 做得太逼真之後:一場生成器與判別器的拉鋸
文章目錄

這是我在國立東華大學資訊工程學系「深度學習基石與實務」課程的第二個作業。我在 CelebA 上訓練 LadaGAN,從不同訓練階段建立合成人臉資料集,再訓練模型辨別真實與生成影像。

我一開始以為生成器越逼真,成果就越好。跑過幾輪模型後,卻遇到清晰度提高、臉型變得單一,以及分類模型接近隨機猜測的情況。下面用生成樣本、訓練曲線與評估結果,說明這些問題如何影響模型選擇。

這個實驗真正要回答什麼?

這份作業的目標不是只交出一張「看起來像人臉」的圖片,而是一條可以被檢查、被重跑的影像生成與分類流程:

  1. 在 CelebA 上訓練 LadaGAN,產生有多樣性的 64×64 人臉。
  2. 依報告記錄的不同 epoch checkpoint,從每個 checkpoint 生成固定數量的圖片,建立足夠大的 synthetic dataset。
  3. 把真實圖片和生成圖片混合,訓練多個影像分類模型,找出比較能辨識兩種影像分布的判別器。
  4. 使用最後的 EfficientNetV2-B1 對測試資料做真偽判斷,輸出 test.csv。

所以我想回答的問題不是「能不能生成一張好看的臉」,而是:

當生成器已經做得很逼真時,判別器到底還能不能學到穩定、可泛化的差異?

為什麼要把生成與判別放在同一條 pipeline?

HackMD 報告把整個工作拆成三個階段。第一階段訓練 LadaGAN;第二階段從生成 checkpoint 建立判別器資料;第三階段用教師提供的 1,000 張測試圖片產生最後的分類結果。

從 CelebA、LadaGAN checkpoint 到 EfficientNetV2-B1 真假影像評估的資料流程
依照 repository README 與 原始 HackMD 報告整理的資料流程。

箭頭表示資料與模型的先後關係:生成器先留下多個 checkpoint,再從它們建立 synthetic faces;判別器的資料集才把 real 與 fake 合併,最後輸出評估結果或教師測試集的 test.csv。圖中最重要的界線是 30,000 張評估資料與 1,000 張盲測圖片不是同一組資料。

三個階段的實際數字

第一階段以 64×64、像素值正規化到 [-1, 1] 的 CelebA 人臉訓練 LadaGAN。訓練曲線記錄到第 36 個 epoch;生成資料的腳本則以第 1 到第 30 個 checkpoint、每個 checkpoint 5,000 張圖片為主要設定:

30 checkpoints × 5,000 images = 150,000 synthetic faces

第二階段再把 150,000 張生成圖片和 202,600 張真實圖片按報告中的比例抽樣,組成 300,000 張判別器資料。報告中提到的分布是 127,650 張 fake、172,350 張 real;這個比例比單純各半抽樣更接近原始資料配置。

第三階段使用教師提供的 1,000 張測試圖片,由模型產生分類結果並輸出 test.csv。

生成資料腳本使用 30 個模型存檔,每個產生 5,000 張圖片,合計 150,000 張。

LadaGAN 的架構與訓練方式

LadaGAN 的生成器不是單純把卷積層一路堆高,而是把 Transformer-style attention、residual connection 和逐步上採樣放在同一個生成流程裡。報告中記錄的主要設定是:

元件設定
latent noise128 維
generator output64×64 RGB
generator model dimensions[1024, 256, 64]
attention heads[4, 4, 4]
MLP dimensions[512, 512, 512]
discriminator input64×64 影像 patch embedding
discriminator patch size4

我把訓練流程理解成兩個互相拉扯的目標:

  • 生成器要讓 fake image 通過判別器,同時維持 feature matching,避免只追求某一種容易成功的臉型。
  • 判別器要同時看 real 和 fake,學會把生成影像的細微分布差異轉成可用的 binary signal。

報告中的生成器 loss 可以概括成:

generator_loss = adversarial_loss + 10 × feature_matching_loss

判別器則使用 real/fake 的 sigmoid cross entropy。這個 10 不是裝飾性超參數:它直接改變了生成器在「騙過判別器」和「維持特徵分布」之間的重心。

先看失敗:模型演進的四個階段

我沒有把最終的 LadaGAN 當成一開始就知道的答案。生成器只反覆產生少數幾種樣式時,這種現象叫做 mode collapse(模式崩潰);實驗中比較過的模型,剛好把 GAN 常見的問題逐一暴露出來:

階段得到的改善暴露出的問題
DCGAN建立最基本的生成流程圖片較模糊、局部紋理不足,容易出現 mode collapse
DCGAN + Self-Attention更早捕捉全局關係,細節變清楚模式集中更嚴重,生成結果可能突然崩潰
WGAN-GP多樣性與訓練穩定度改善gradient penalty 加上多次 critic update,速度約慢 3–4 倍,細節反而變糊
LadaGAN以 Linear Additive-Attention 取得清晰度、多樣性和效率的折衷圖片太像真的之後,判別器更難找到穩定的泛化線索

下面是各模型在不同訓練階段留下的生成圖片,可以觀察模糊程度、臉型多樣性與樣式集中情況。

DCGAN epoch 196 generated face samples
DCGAN · epoch 196:建立基準,但局部細節仍不穩定。
Attention DCGAN epoch 18 generated face samples
Attention-DCGAN · epoch 18:細節變清楚,樣式開始集中。
Attention DCGAN epoch 19 generated face samples
Attention-DCGAN · epoch 19:短時間內出現更明顯的模式偏移。
WGAN GP epoch 10 generated face samples
WGAN-GP · epoch 10:多樣性仍在,但影像偏模糊。
WGAN GP epoch 30 generated face samples
WGAN-GP · epoch 30:穩定度改善,細節仍需要更多成本。
WGAN GP epoch 60 generated face samples
WGAN-GP · epoch 60:沒有明顯 collapse,但清晰度沒有跟上。
LadaGAN epoch 36 generated face samples
LadaGAN · epoch 36:清晰度與多樣性的折衷結果。

為什麼 attention 不是萬靈丹

Self-Attention 讓模型可以在更大的範圍內交換資訊,這確實有助於眼睛、鼻子、臉型等區域形成比較一致的關係。但它也會把「一組已經成功的特徵組合」快速傳播到更多位置。當梯度主要鼓勵某幾種臉型時,attention 可能讓這些成功模式被反覆複製,清晰度提高的同時,多樣性反而下降。

WGAN-GP 解決的是另一個問題:讓負責評分的一方(critic)的學習訊號更平滑,降低 mode collapse 的風險。但每次 generator update 前需要多次 critic update,再加上 gradient penalty,訓練成本明顯增加。這就是為什麼我最後沒有只看「哪一個模型的理論保證比較漂亮」,而是回到這份作業要交付的完整 pipeline,選擇成本可接受、輸出也足夠清楚的 LadaGAN。

36 個 epoch 的 loss:生成器變強,也讓判別器更難工作

下面是 HackMD 報告中的原始 loss 與 score 圖。左側是 generator/discriminator loss,右側是 real/fake score;圖上的 fake score 是判別器記錄的 raw score,不是 sigmoid 後的機率。

LadaGAN epoch 36 generator discriminator loss and real fake score curves
LadaGAN 第 36 個 epoch 的 loss 與 real/fake score。圖片來自 HackMD 原始報告。

報告把曲線分成四個階段,這樣比只看最後一個數字更能看出訓練發生了什麼:

訓練階段Generator lossDiscriminator lossReal scoreFake score
epoch 0 → 53.74 → 0.981.55 → 1.170.26 → 0.38-3.71 → -0.44
epoch 6 → 150.98 → 0.951.17 → 1.180.38 → 0.35-0.44 → -0.38
epoch 16 → 250.95 → 0.941.18 → 1.190.35 → 0.34-0.38 → -0.36
epoch 26 → 360.94 → 0.941.19 → 1.190.34 → 0.34-0.36 → -0.36

最後一個記錄點是:

  • generator loss:0.936
  • discriminator loss:1.185
  • real score:0.339
  • fake score:-0.358

前五個 epoch 是主要的快速學習期,生成器 loss 大幅下降,real/fake score 也快速靠近相對穩定的區間;第 6 個 epoch 之後,兩個 loss 幾乎進入平台期。各階段的生成圖片則呈現了人臉細節與多樣性的變化。

把每個 checkpoint 都留下來

這一組圖是報告中不同 epoch 的實際生成結果。每張大圖是 contact sheet,裡面包含同一個 checkpoint 產生的多張人臉;我保留 epoch 1、2、5、10、15、20、25、30、36,讓讀者可以沿著同一條時間軸看品質與多樣性如何變化。

LadaGAN epoch 1 generated samples
Epoch 01
LadaGAN epoch 2 generated samples
Epoch 02
LadaGAN epoch 5 generated samples
Epoch 05
LadaGAN epoch 10 generated samples
Epoch 10
LadaGAN epoch 15 generated samples
Epoch 15
LadaGAN epoch 20 generated samples
Epoch 20
LadaGAN epoch 25 generated samples
Epoch 25
LadaGAN epoch 30 generated samples
Epoch 30
LadaGAN epoch 36 generated samples final
Epoch 36 · final sample grid

這組圖片展示同一個模型在不同訓練階段產生的人臉,可以觀察臉型、背景與細節如何變化。

判別器:從接近隨機猜測到 EfficientNetV2-B1

生成器完成之後,真正的第二個實驗才開始。判別器資料不是只拿一張 real 和一張 fake,而是把不同 checkpoint 的生成結果集中起來,再和真實資料混合。這個設計讓判別器面對的是一個比較寬的 fake distribution,而不是某一個 epoch 的單一風格。

Backbone 比較

初期比較 EfficientNet B0–B5 和 MobileNetV3-Large 時,結果一度接近隨機猜測(約 50%,常被形容為 coin flip)。後續改用 EfficientNetV2-B1,調整輸入尺寸與微調設定,再進行真假分類。

Backbone報告中的觀察我怎麼使用這個結果
EfficientNet B0–B5validation 約 0.48–0.53視為初期 baseline,沒有直接沿用
MobileNetV3-Large從頭訓練約 0.76作為分類模型的比較對照
EfficientNetV2-B1repository README 摘要約 90% validation;原始報告另記錄 30,000 張評估接近 99.907%作為最後的判別器,但兩個數字不合併成單一準確率

EfficientNetV2-B1 的設定

最後使用 ImageNet 預訓練的 EfficientNetV2-B1,輸入尺寸調整為 240×240×3。backbone(負責抽取影像特徵的主幹網路)大部分先 freeze,只解凍最後 20 層做微調;分類 head 則是:

GlobalAveragePooling2D → Dropout(0.3) → Dense(256, relu) → BatchNorm → Dropout(0.5) → Dense(1, sigmoid)

訓練時使用 mixed precision、AdamW(learning rate 1e-4、weight decay 1e-5),並搭配 EarlyStopping、ReduceLROnPlateau 和 ModelCheckpoint。這裡的重點不是把網路堆到最大,而是讓輸入尺度、資料數量和微調範圍一起對齊。

EfficientNetV2 training and validation history with accuracy overfit and learning rate curves
EfficientNetV2-B1 的訓練歷史圖。原始檔名保留報告中的命名。

這張圖比單一 validation accuracy 多提供了三種線索:

  1. loss 是否一起下降,還是只有 accuracy 看起來變好。
  2. train/validation 的差距是否逐步放大,作為 overfit 警訊。
  3. learning rate 調整後,模型是否還能繼續改善。

30,000 張評估資料的結果

EfficientNetV2 discriminator ROC PR curve confusion matrix and evaluation metrics
報告中的 discriminator evaluation:ROC/PR、confusion matrix 與進階指標。

依照 HackMD 評估圖中的 confusion matrix:

  • 12,755 張 fake 被正確判為 fake。
  • 0 張 fake 被誤判為 real。
  • 28 張 real 被誤判為 fake。
  • 17,217 張 real 被正確判為 real。
  • 總數 30,000 張,正確 29,972 張,計算為約 99.907%。

評估圖另外列出 balanced accuracy 0.9992、sensitivity 0.9984、specificity 1.0000,ROC AUC 和 PR AUC 都是 1.0。

各階段的結果記錄

這份專案的 README 和 HackMD 報告有不同層次的結果摘要:

  • README 寫的是約 90% validation accuracy。
  • HackMD 另外記錄 30,000 張評估資料中 28 張誤判,也就是約 99.907%。
  • 教師提供的 1,000 張測試圖片,分類結果輸出為 test.csv。

工程問題:150K 張圖不是按一下就完成

GPU 記憶體

一次把 150,000 張圖片全部放進記憶體,生成階段很容易遇到 GPU memory overflow。我最後採用 batch generation,定期清理 TensorFlow session,並在適合的地方使用 mixed precision。這些做法不改變模型概念,但決定了實驗能不能跑完。

訓練環境

主要環境是 autoDL 上的 RTX 4090 24GB、Ubuntu 20.04、Python 3.9.16、TensorFlow 2.13.1、PyTorch 2.0.1、CUDA 11.8、cuDNN 8.6;另外在 Ubuntu 22.04、Python 3.10.12 的本地環境做驗證。

HackMD 附錄另記錄了 PyTorch 2.7.1 和 120GB RAM 的環境。

公開原始碼包含模型訓練、圖片生成與資料建立腳本。

分類不應只看模型大小

初期模型接近 coin flip 時,我沒有直接換成更大的 backbone,而是一起檢查:

  • fake/real 的數量比例與 label 產生腳本。
  • 生成圖片與真實圖片的 resize、normalize 和色彩格式。
  • EfficientNet 的輸入尺寸是否適合目前的資訊量。
  • validation split 是否把同一個 checkpoint 的風格洩漏到 train 和 validation。
  • classification head 是否太容易記住背景、壓縮痕跡或前處理差異。

這些檢查比單純把 B0 換成 B5 更接近問題本身。

報告中的主要取捨

LadaGAN 作者的 benchmark 記錄 FID 1.81;FID 用來衡量生成影像與真實影像的分布差異。我的作業報告另外記錄約 19M 參數,以及單張 RTX 4090/3080Ti 上 30 epochs 小於 5 小時的訓練時間。

這次最重要的取捨可以濃縮成下面這張表:

方案清晰度多樣性collapse成本整體判斷
DCGAN + Attention高低嚴重1×細節好看,但不夠可靠
WGAN-GP中高明顯改善約 3–4×穩定,但速度與細節成本高
LadaGAN高高輕微中等、報告記錄小於 2×最符合這次作業的交付條件

我從這份作業真正學到的事

生成模型的比較同時觀察清晰度、多樣性、訓練穩定性與生成成本。Attention-DCGAN 的細節較清楚,但臉型逐漸集中;WGAN-GP 改善了多樣性,也增加了訓練時間。

分類實驗的記錄包括 README 中約 90% 的驗證準確率、報告中的 30,000 張評估結果,以及教師提供的 1,000 張測試圖片。每一項結果都列在對應的資料與評估階段旁。

最後採用 LadaGAN,是因為它在這份作業中取得了較合適的影像清晰度、多樣性與運算成本。前面各模型的生成樣本與訓練曲線,呈現了這個選擇的比較過程。

如果再做一次

下一輪我會先補上資料治理,再追求更高的指標:

  • 為每張圖片保存 source_type、source_epoch、checkpoint、seed 和 preprocessing 版本。
  • 明確分開 train、validation、hold-out test,避免同一個 checkpoint 的風格同時出現在不同 split。
  • 對每個 epoch 做固定 seed 的 sample grid,並加上 FID、KID、precision/recall for generative models。
  • 讓判別器在跨 checkpoint、跨來源與不同壓縮設定的資料上測試,確認它學到的是生成分布,而不是資料管線的指紋。
  • 用同一份 environment lock 和命名規則重跑全部 pipeline,讓別人可以從 train_celeba.py、generate_image.py、LabelData.py、efficientnetv2.py 到 test_face.py 逐段核對。