跳至主要內容

    Conditional GAN、Pix2Pix、CycleGAN 與 StyleGAN

    從條件輸入、配對監督到循環一致性,分清影像翻譯與逐層風格控制

    sunny 桑尼· · 34 MIN READ · 187 次瀏覽

    Conditional GAN、Pix2Pix、CycleGAN 與 StyleGAN
    本文目錄

    假設你手上有三種需求:指定類別生成圖片、把某張線稿轉成照片,以及讓生成影像的粗略結構和細部外觀能分開調整。它們都可能用 GAN,但你提供的資料、模型能看到的條件,以及「生成正確」的意思並不相同。

    一張圖片看起來像照片,只能回答一部分問題。線稿裡的窗戶有沒有被保留?沒有逐張配對的冬景與夏景,如何限制翻譯不要任意改變內容?改變某一層的 latent style,是否真的只會改變你想要的屬性?這些問題,無法只靠真假判別分數作答。

    本篇接續 DCGAN、WGAN 與 Mode Collapse,沿著四條方法線索閱讀原圖。最後有一支完整、只用 Python 標準庫的程式:不訓練 GAN,而是用兩個點證明「生成分布相同、往返完全正確」仍可能配錯答案。影像架構與論文結果屬於來源研究;本站實作只支持這個有限反例。

    先分清:你要提供什麼條件,資料又告訴了模型什麼?

    Conditional GAN 是把額外條件交給 G 與 D 的框架;Pix2Pix 是其中一種影像到影像的配對學習方法。CycleGAN 處理沒有逐張對應的兩個影像域;StyleGAN 則重新設計生成器內部接收 latent 的方式。它們不是四個可以不看資料條件、直接互換的產品版本。

    方法本篇關心的輸入與監督

    Conditional GAN,2014
    G 接收 noise 與條件;D 同時看候選資料與條件,學條件下的真假
    Pix2Pix,2016 預印本/CVPR 2017
    輸入影像是條件;訓練資料有對應的目標影像,可逐張計算誤差
    CycleGAN,2017
    只有 X、Y 兩組未配對影像;用兩個方向的對抗學習與往返一致性限制映射
    StyleGAN,2018 預印本/CVPR 2019
    把 latent 經 mapping network 變成逐層 style,另加入空間 noise;原始設計不要求語意類別標籤

    表中的年份是方法首次提出與會議年份。本文查核的固定版本分別為 cGAN v1、Pix2Pix v3(2018 更新)、CycleGAN v7(2020 更新)、原 StyleGAN v3(2019 更新);修訂版日期不表示方法到那一年才出現。後面另用一張 StyleGAN2 單一 block 圖說明後繼改動,會與原 StyleGAN 分開。

    先用一個實務問題判斷資料:如果你的資料夾只是有一批線稿、一批照片,卻不知道哪張線稿對應哪張照片,它就不具備 Pix2Pix 的逐張配對監督。檔名排在同一個位置,不會自動建立正確的配對。

    Conditional GAN:條件也要交給判別器

    原始 GAN 的生成器接收 noise z,再產生樣本。Conditional GAN 多提供條件 c;這裡用 c 避免與下一節 Pix2Pix 的目標影像 y 混淆,原 cGAN 論文則把條件記為 y。

    生成:fake = G(z, c)
    判別真實配對:D(real, c)
    判別生成配對:D(G(z, c), c)
    
    min_G max_D V = E_(real,c)[log D(real,c)]
                 + E_(z,c)[log(1 − D(G(z,c),c))]
    

    條件可以是類別標籤,也可以是另一種資料。以類別為例,若 c 指定「鞋」,G 的任務不只是產生一般看起來合理的物體,而是產生這個條件下合理的樣本。D 也必須看到 c;若 D 只看圖片,它能比較的是整體影像分布,無法直接檢查圖片與指定條件是否相符。

    圖 1|沿 C(class)的分支看:它連到 G,也連到 D;Z(noise)進入 G,生成樣本與同條件真實樣本交由 D 判別。這是 Dan Gao、Xiaofang Wu、Zhijin Wen、Yue Xu、Zhengchao Chen 於 2024 年文章繪製的說明圖,不是 2014 原論文圖或本站生成結果。來源:ISPRS Annals,Fig. 2(a),PDF p3/印刷頁85,CC BY 4.0。只裁完整 (a) 圖,保留全部標籤、箭頭與 (a) 記號,再等比縮放,未改圖內內容。

    圖中的 real、fake 是兩種送進同一個 D 的資料情況,不是兩個不同的判別器。C 指向真實樣本的支線可理解為使用符合該類別的真實資料;不是把類別數字透過某個已畫出的生成器製成真實照片。

    條件的監督來源也要說清楚。「鞋」是訓練資料已有的類別標註;某張線稿則可能是另一張照片的對應輸入。即使形式上都叫 conditioning,它們提供的約束並不一樣。原 cGAN 的 MNIST 實驗以 one-hot 類別與 100 維 uniform noise 示範,但這些是當時實驗設定,不是 cGAN 的定義要求。方法依據是 Mirza 與 Osindero,2014,§3.2。

    條件輸入也不保證模型一定服從條件、充分使用 noise,或涵蓋所有合理輸出。仍須分別檢查條件符合度、多樣性,以及在未見過資料上的行為。

    Pix2Pix:既要像目標域,也要對得上這張輸入

    在 Pix2Pix,條件不再只是類別,而是一張輸入影像 x。訓練時另有對應的目標影像 y,例如同一場景的結構標籤圖與照片。D 比較的是真實配對 (x,y) 與生成配對 (x,G(x)),而不是單獨比較兩張輸出照片。

    只有對抗學習,還不足以明確要求生成影像逐位置接近這一張 y。因此原方法加入 L1 項:

    L_cGAN = E_(x,y)[log D(x,y)]
           + E_(x,z)[log(1 − D(x,G(x,z)))]
    
    L_L1 = E_(x,y,z)[||y − G(x,z)||_1]
    
    min_G max_D [L_cGAN + λ L_L1]
    

    L1 提供可對照目標的訊號,對抗項則由資料學習哪些影像局部結構看起來合理。不能將它簡化成「L1 處理所有正確性,GAN 負責所有細節」的保證;兩項都受資料、架構與訓練影響,權重也會改變取捨。Pix2Pix 原論文 §3.1 定義了這個組合。

    L1 與 L2 也有不同的折衷。若只對某一個標量輸出做純誤差最小化,平方誤差的最佳常數是條件平均數,絕對誤差的最佳常數是條件中位數。手算三個可能目標 [0,0,9]:預測 0 的 MAE 是 3,預測 3 的 MAE 是 4;相反地,兩者 MSE 分別為 27 與 18。這是損失偏好的算術說明,並非訓練 Pix2Pix 的結果。加入對抗項、神經網路與多維影像之後,更不能僅由「使用 L1」就保證輸出銳利或語意正確。

    逐像素誤差還依賴配準。假如正確照片中的窗戶比輸入標籤平移了幾個像素,L1 仍會懲罰這個差異。遇到配對品質差的資料,先確認兩張圖是否真的應在同位置對應,比直接增加 λ 更能釐清問題。

    U-Net 的 skip 保留空間資訊,PatchGAN 的格子不是分割標籤

    Pix2Pix 原論文用 U-Net 型生成器,將 encoder 與對應 decoder 層的 feature maps 沿 channel 軸 concatenate。這讓低階邊界與位置資訊能跨過 bottleneck;它不表示網路只能做語意分割。任務由輸出、監督與損失決定,相同的 U 型骨架可用來預測影像。

    圖 2|TensorFlow 教材實作的 modified U-Net,輸入與輸出為 256×256×3。先沿完整圖追蹤所有 skip,再讀節點形狀;None 是未固定的 batch 維度。手機版先保留全圖,再將五個完整原節點框逐項放大供讀字;這不是新運算序列,完整 skip 關係以全圖為準,沒有新增連線或運算。來源:TensorFlow Pix2Pix tutorial,TensorFlow 文件貢獻者(單張圖作者未單列),CC BY 4.0。完整原教材 model graph 等比縮放;這是教材版本,不宣稱每個原論文實作都逐層相同。

    這張長圖可用三個位置讀懂,不必先背完每一層:

    位置從原圖核對的形狀

    下採樣路徑
    空間尺寸 256→128→64→32→16→8→4→2→1,最後為 1×1×512
    第一個上採樣與 skip
    1×1×512→2×2×512;與對應 2×2×512 拼接成 2×2×1024
    最後輸出
    128×128×128 經最後 Conv2DTranspose 變成 256×256×3

    拼接保留兩路 feature channels,不等於逐元素相加。原圖中通道加倍的 Concatenate,是辨認這件事的直接線索。生成器可利用輸入的空間結構,但 skip 本身仍不保證所有物體形狀或數量都維持正確。

    判別器的作用不同:它把輸入 x 與候選輸出 y 在 channel 軸拼接,再對局部區域判別真假。

    圖 3|兩個 256×256×3 輸入先拼成 6 channels,該教材最後輸出 30×30×1。這些格子是局部真假 logits,不是物件類別或語意 mask;70×70 指判斷的理論感受野,並非輸出格數。來源:TensorFlow Pix2Pix tutorial,TensorFlow 文件貢獻者,CC BY 4.0。完整原 model graph 等比縮放,未改節點、形狀或邊。

    為什麼叫 70×70 PatchGAN,圖上卻是 30×30?因為一個數字描述輸入範圍,另一個描述輸出網格。這個 D 使用 kernel size 4 的五層卷積,stride 為 2,2,2,1,1;依感受野遞推,每層理論範圍為 4,10,22,46,70。最後每個輸出位置參考輸入的一個局部範圍,鄰近位置的範圍會重疊;影像邊界則涉及 padding,不是每個位置都看到 70×70 個不同的真實影像像素。

    教材用 BinaryCrossentropy(from_logits=True) 直接接這張 logits map,再做損失聚合。它沒有把 30×30 格當成 900 個語意分割類別,也不是把影像先切成 900 張互不重疊的小圖獨立訓練。這種局部判別有其偏好;僅靠局部看起來合理,仍不能確保整張圖的全域關係正確。

    從兩條 loss 路徑讀懂 Pix2Pix 的生成器更新

    架構圖告訴我們資料怎麼流,更新圖則說明哪個誤差會推動 G 改參數。

    圖 4|左路比較生成影像與 Target Image 的 MAE,乘 Lambda;右路將輸入條件與生成影像送進 D,對 All 1's 計算 sigmoid cross-entropy。兩項相加後的梯度更新 G,並不是用同一個 G loss 同時更新 D。來源:TensorFlow Pix2Pix tutorial,TensorFlow 文件貢獻者,CC BY 4.0。保留完整原圖,透明底合成白底、等比縮放,未改節點或箭頭。

    右路的 All 1's 表示:在這一次 G 更新中,希望 D 把生成配對判為真。它不是把訓練 D 時的 fake label 改為真。D 自己仍用 real pairs 的真標籤、fake pairs 的假標籤計算另一個損失。

    TensorFlow 教材的生成器損失是 non-saturating 對抗 BCE 加上 100 × mean(abs(target−generated))。這裡的 100 是該設定的 λ,MAE 的 reduction 也已固定。原論文寫的是期望 L1 norm;若改影像尺寸、由 sum 換 mean,或改其他損失的平均方式,兩項相對尺度就會改變,不能把 λ=100 當成所有程式都通用的常數。

    更新 G 時,仍需要 D 對生成影像的輸入梯度,再沿生成路徑回到 G;將整個 D forward 包進 no_grad 的想法會切斷這條訊號。這個梯度邏輯與前篇 GAN 的兩階段更新相同;本篇沒有額外實跑 TensorFlow 的訓練。

    另一個常被省略的歷史細節是 noise。Pix2Pix 原論文發現 G 容易忽略直接輸入的 z,最後改以部分層的 dropout 提供隨機性,而且原實作在 training、test 都使用 dropout,仍只觀察到有限的隨機變化。不能據此保證同一條件能產生充分多樣的答案,也不能把這個原實作的推論模式,無條件套給每一個後來的 Pix2Pix API 或部署設定。使用教材時,應查其實際 training 旗標與正規化行為。

    CycleGAN:沒有逐張答案,改用兩個方向限制映射

    假設 X 是一組冬景,Y 是另一組夏景,兩邊並沒有相同位置、相同構圖的成對照片。CycleGAN 學習兩個映射:G:X→Y 與 F:Y→X,再分別用 D_Y、D_X 檢查翻譯結果像不像各自的目標域。

    圖 5|G 將 X 送往 Y,F 將 Y 送往 X;D_X 區分真實 X 與 F(y),D_Y 區分真實 Y 與 G(x)。這張圖只畫域、映射與判別關係,沒有完整畫出兩次生成器呼叫組成的 cycle loss,也不是生成器的逐層架構或影像結果。來源:TensorFlow CycleGAN tutorial,TensorFlow 文件貢獻者,CC BY 4.0。完整原示意圖等比縮放,未改箭頭或標籤;封面為同一完整圖 fit 在 1600×900 白底,未加文字或新圖元。

    只要求 G(x) 像 Y、F(y) 像 X,仍允許很多不同的對應。Cycle consistency 再要求兩個方向組合後能近似回到出發點:

    X方向:x → G(x) → F(G(x)) ≈ x
    Y方向:y → F(y) → G(F(y)) ≈ y
    
    L_cycle = E_x ||F(G(x)) − x||_1
            + E_y ||G(F(y)) − y||_1
    
    L_total = L_adv(G,D_Y) + L_adv(F,D_X) + λ L_cycle
    

    這裡可以比較的是原始 x 與回到 X 的重建、原始 y 與回到 Y 的重建。它不是拿某張任意 y 當成 G(x) 的逐像素標準答案,也不是把未配對資料偷偷當成 Pix2Pix 配對資料。CycleGAN 原論文 §3.1–3.3 定義這兩種約束。

    CycleGAN 常被放在 conditional/image-translation 的脈絡談,因為生成結果取決於輸入影像;但標準方法中的 D_Y 不需要接收那張來源 x 作為成對條件。它在 Y 域判真假,D_X 在 X 域判真假。這與上一節 D(x,y) 看配對的任務有別。

    來源版本也要分開看:原論文主要使用 ResNet 型生成器,實作採 least-squares adversarial loss;本文選用的 TensorFlow 教材為方便教學,改用 modified U-Net 與 instance normalization。不能將上圖叫成「原論文的完整 U-Net 架構」,也不能把 BCE 與 least-squares loss 的數值刻度直接比較。Optional identity loss 會另外鼓勵 G(y)≈y、F(x)≈x,它與 cycle loss 是不同限制,不是上述往返式自動包含的項。

    反例:cycle 完全正確,為什麼仍可能配錯答案?

    把影像先縮成能全部列完的兩點世界。已知真正的配對是 0→0、1→10;X 的兩點等機率,Y 的兩點也各占一半。

    我們設定兩組映射,各自都有反函數:

    組合G(0)、G(1)F(0)、F(10)
    對齊0、100、1
    對調10、01、0

    兩組 G 的輸出都包含一個 0、一個 10,所以 Y 的生成邊際分布完全相同。任何只看 Y 單點的固定判別函數 d,其期望都是 0.5d(0)+0.5d(10);光靠這個邊際,無法區分哪組配對才是我們想要的。

    對調組也可以完整往返:0→10→0、1→0→1;從 Y 出發的兩條路也都回到原點。因此兩個方向的 cycle error 都是零。

    但逐張配對的 L1 會分出差別。對調組的兩筆誤差分別為 |10−0|=10、|0−10|=10,平均就是 10;對齊組為 0。若 D 還看到來源與目標組成的 pair,它比較的資訊也已經不同。本反例沒有實作或訓練這種 D,只是在數學上區分它可以看到哪些資料。

    這證明一個有限的可識別性限制:匹配目標邊際分布,加上完美往返,仍不足以唯一選出預期的配對。 它不是在宣稱真實 CycleGAN 必定失敗,也沒有測量失敗率、影像品質或不同模型誰比較好。

    下面是完整附件。存成 paired_cycle_counterexample.py,以 Python 3 執行;只有標準庫,不需 pip、資料下載或 GPU。保存執行環境為 Python 3.10.12,已實際執行並另以有限集合與精確分數算術核對;不同平台的新環境不在這次實測範圍。

    python3 paired_cycle_counterexample.py
    
    """Finite paired-L1 vs cycle-consistency example; no GAN or image model is trained."""
    import json
    
    
    def mean_absolute_error(left, right):
        if len(left) != len(right) or not left:
            raise ValueError("expected equally sized non-empty lists")
        return sum(abs(a - b) for a, b in zip(left, right)) / len(left)
    
    
    def apply(mapping, values):
        return [mapping[value] for value in values]
    
    
    def main():
        # Two paired examples: x=0 maps to y=0, and x=1 maps to y=10.
        x_domain = [0, 1]
        y_domain = [0, 10]
        paired_targets = [0, 10]
    
        # A semantically aligned bijection and a consistently swapped bijection.
        forward_aligned = {0: 0, 1: 10}
        backward_aligned = {0: 0, 10: 1}
        forward_swapped = {0: 10, 1: 0}
        backward_swapped = {10: 0, 0: 1}
    
        aligned_y = apply(forward_aligned, x_domain)
        swapped_y = apply(forward_swapped, x_domain)
        aligned_x_cycle = apply(backward_aligned, aligned_y)
        swapped_x_cycle = apply(backward_swapped, swapped_y)
        aligned_y_cycle = apply(forward_aligned, apply(backward_aligned, y_domain))
        swapped_y_cycle = apply(forward_swapped, apply(backward_swapped, y_domain))
    
        aligned_paired_l1 = mean_absolute_error(aligned_y, paired_targets)
        swapped_paired_l1 = mean_absolute_error(swapped_y, paired_targets)
        aligned_x_cycle_l1 = mean_absolute_error(aligned_x_cycle, x_domain)
        swapped_x_cycle_l1 = mean_absolute_error(swapped_x_cycle, x_domain)
        aligned_y_cycle_l1 = mean_absolute_error(aligned_y_cycle, y_domain)
        swapped_y_cycle_l1 = mean_absolute_error(swapped_y_cycle, y_domain)
    
        # Independent finite-set checks: compare sorted empirical output multisets,
        # and enumerate both compositions directly on every domain element.
        same_output_multiset = sorted(aligned_y) == sorted(swapped_y)
        all_x_roundtrips = all(
            backward_swapped[forward_swapped[x]] == x for x in x_domain
        )
        all_y_roundtrips = all(
            forward_swapped[backward_swapped[y]] == y for y in y_domain
        )
    
        checks = {
            "paired_targets_match_declared_pairs": paired_targets == [0, 10],
            "output_marginals_are_identical": same_output_multiset,
            "aligned_mapping_has_zero_paired_l1": aligned_paired_l1 == 0.0,
            "swapped_mapping_has_paired_l1_10": swapped_paired_l1 == 10.0,
            "aligned_mapping_has_zero_x_and_y_cycle_error": aligned_x_cycle_l1 == 0.0 and aligned_y_cycle_l1 == 0.0,
            "swapped_mapping_has_zero_x_and_y_cycle_error": swapped_x_cycle_l1 == 0.0 and swapped_y_cycle_l1 == 0.0,
            "swapped_mapping_roundtrips_every_finite_domain_element": all_x_roundtrips and all_y_roundtrips,
        }
        assert all(checks.values()), checks
    
        print(json.dumps({
            "scope": "Exact two-point scalar mapping counterexample only. It does not implement a discriminator, adversarial optimization, image translation, GAN training, dataset evaluation, or quality/benchmark measurement.",
            "domains_and_pairs": {
                "X": x_domain,
                "Y": y_domain,
                "paired_supervision": [{"x": x, "target_y": y} for x, y in zip(x_domain, paired_targets)],
            },
            "aligned_bijection": {
                "G_X_to_Y": forward_aligned,
                "F_Y_to_X": backward_aligned,
                "G(X)": aligned_y,
                "paired_L1_mean": aligned_paired_l1,
                "F(G(X))": aligned_x_cycle,
                "X_cycle_L1_mean": aligned_x_cycle_l1,
                "G(F(Y))": aligned_y_cycle,
                "Y_cycle_L1_mean": aligned_y_cycle_l1,
            },
            "swapped_bijection": {
                "G_X_to_Y": forward_swapped,
                "F_Y_to_X": backward_swapped,
                "G(X)": swapped_y,
                "paired_L1_terms": [abs(pred - target) for pred, target in zip(swapped_y, paired_targets)],
                "paired_L1_mean": swapped_paired_l1,
                "F(G(X))": swapped_x_cycle,
                "X_cycle_L1_mean": swapped_x_cycle_l1,
                "G(F(Y))": swapped_y_cycle,
                "Y_cycle_L1_mean": swapped_y_cycle_l1,
            },
            "comparison": {
                "same_generated_empirical_marginal": same_output_multiset,
                "cycle_consistency_alone_does_not_identify_the_pairing_in_this_example": True,
                "paired_L1_distinguishes_the_two_mappings": True,
                "interpretation": "The swapped maps are exact inverses and produce the same finite Y marginal as the aligned map, so both directions have zero cycle error. Paired supervision penalizes the swapped correspondence by mean absolute error 10. This finite construction demonstrates an identifiability limitation; it is not evidence about real model quality.",
            },
            "checks": checks,
        }, indent=2, ensure_ascii=False, allow_nan=False))
    
    
    if __name__ == "__main__":
        main()
    

    保存 stdout 的主要結果如下;JSON 物件中的 mapping keys 會顯示為字串,是序列化格式,不代表程式把算術輸入改成字串。

    保存輸出欄位對齊組/對調組

    G(X)
    [0,10]/[10,0]
    paired L1 mean
    0/10
    X cycle L1 mean
    0/0
    Y cycle L1 mean
    0/0
    相同生成邊際分布
    true

    實際規劃未配對影像翻譯時,應先寫出哪些內容必須保留,再設計獨立於 cycle loss 的驗證。若任務要求物體數量、位置或身分不可變,僅以風格像目標域、往返誤差低作為驗收,仍缺少直接檢查這些要求的證據。

    StyleGAN:把 latent 變成逐層控制,而不是另一種配對翻譯

    前面問的是「從這張輸入翻成哪張輸出」;原 StyleGAN 問的則是,從 latent 生成新影像時,能否更明確地控制不同尺度的變化。它不要求輸入一張待翻譯照片,也不以某張照片的配對 L1 作為其定義。

    原 StyleGAN 的資料流可拆成四部分:

    構件在原始設計中的角色

    Mapping network
    將 z 映到中介向量 w;原論文使用 8 層 MLP
    Learned constant
    synthesis network 從共用、可學的 4×4×512 常數張量開始,而不是只在第一層把 z 投影進去
    每層 affine 與 AdaIN
    各層由 w 產生該層 scale/shift,調整 feature maps 的統計尺度
    獨立 noise maps
    另外提供空間上的 Gaussian noise,經學得的 per-channel scale 注入,支援隨機細節

    共用 constant 不表示所有輸出相同,因為逐層 style 與 noise 仍會改變生成過程。反過來說,增加 noise 也不等於你已經得到可精確命名、互不干擾的語意控制。

    原設計的 AdaIN 先對每個 feature channel,依該樣本的空間位置計算平均與標準差,再套由 style 給定的 scale、bias:

    AdaIN(feature_i, style)
      = scale_i(style) × (feature_i − spatial_mean_i) / spatial_std_i
        + bias_i(style)
    

    這裡的 style 來自 w 的 learned affine,不是由一張參考風格照片直接算得;「style」也不只指畫風,它是這個生成器內部控制特徵的方式。實際正規化仍需數值穩定處理,不能由簡式推出任意常數 channel 都可直接除以零。

    原論文對 noise 的觀察是:在其人臉實驗中,noise 常影響髮絲、雀斑等隨機細節,而 style 會影響較全域的變化。這是來源設定下的觀察,不是對所有資料、任意權重都成立的完美分離定理。中介空間 W 由 mapping 學出,也不保證仍是標準 Gaussian 或每一個維度都對應獨立的可命名屬性。依據可讀 原 StyleGAN §2–3。

    Style mixing:換的是哪些層使用哪個 w

    將 z_A、z_B 分別經 mapping 得到 w_A、w_B,再讓不同層使用不同來源的 style,就能做 style mixing。它不是把兩張照片的像素平均,也不是先把兩個 w 做平均後才送進所有層。原論文還在訓練中隨機選 crossover point 做 mixing regularization,減少網路只依賴相鄰層 style 固定相關的情況。

    可以開啟原論文 Fig. 3,PDF p4 對照讀;這裡只提供原圖連結,不重刊人臉網格。左欄是 Source A,上排是 Source B。交叉位置的影像,從對應欄的 B 取某一段解析度的 style,其餘 style 仍來自對應列的 A:

    原圖指定取自 B 的層原圖設定下觀察的變化

    Coarse,4×4–8×8
    姿態、整體臉形、概略髮型等較大尺度特徵
    Middle,16×16–32×32
    較小尺度的臉部特徵、髮型與眼睛開合等
    Fine,64×64–1024×1024
    主要是色彩組合與微細結構

    這是特定生成器與資料上的分組,不表示「第幾層永遠就是眼鏡開關」,也不表示任意混搭都不會改變其他屬性。要讀的是「改變哪一組層的 style 來源」,再看其觀察結果;不要把 A/B 行列讀反後推導控制效果。

    StyleGAN2:將 style 的調制搬到卷積權重

    原始 StyleGAN 在 2018 年提出;StyleGAN2 的預印本出現在 2019 年,CVPR 2020 的方法修訂了生成器的正規化方式。接下來這張圖是後者的局部 block,不能拿來當成前面 AdaIN 設計的原架構。

    圖 6|讀左側 w→affine A→modulate/demodulate,再沿右側 feature→3×3 convolution→加 noise/bias 的資料流。圖中的 image 是該 block 的 feature tensor,不必是最終 RGB 照片;這只是 StyleGAN2 單一 synthesis block,省略加 bias 後的 LeakyReLU 與 upsampling 細節,並非原 StyleGAN1 或完整網路。來源:Cosmia Nebula,StyleGAN-1 and StyleGAN-2,own work,CC BY-SA 4.0。選取右側 StyleGAN2 及完整輸入標籤;只以白底去除旁邊未採用 StyleGAN1 圖的裁斷片段,未改 StyleGAN2 節點、文字或箭頭。手機版先全圖,再獨立放大原圖 modulate、demodulate、per-channel B 三個完整框供讀字;三框的排列不表示新串聯網路,A/w、feature、bias 與 output 的關係仍看完整全圖。改作維持同授權。

    原 StyleGAN 的 AdaIN 依當前 feature map 計算統計量;StyleGAN2 的這個改動,則把每個樣本的 style 尺度作用寫進卷積權重,接著依權重估計輸出尺度作 demodulation。

    為了避免圖上的 latent w 與 convolution weights w 混淆,下面把卷積權重改記成 k;i 是輸入 channel、j 是輸出 channel、r 是 kernel 空間位置。

    style 經 affine 產生輸入 channel 尺度 s_i
    
    modulation:   k'_ijr = s_i × k_ijr
    
    demodulation: k''_ijr = k'_ijr / sqrt(sum_(i,r) (k'_ijr)^2 + epsilon)
    
    使用這個樣本對應的 k'' 做卷積,再處理 noise、bias 與 activation
    

    其尺度估計建立在輸入激活獨立同分布、單位標準差的分析假設上;epsilon 避免數值問題。這不是對當前 feature map 的實測均值/變異數再套 AdaIN,也不保證任意實際激活都被精確變成單位變異數,更不是讓它們必然服從 Gaussian。StyleGAN2 v2 §2.1–2.2、Eq. 1–3 說明了這個重寫與限制。

    圖上的 modulation 與 convolution 不是兩套同時重複套用的 scale:透過把 scale 併入權重,可以實作原本對輸入 feature 先縮放再卷積的效果。demodulation 再依各輸出 channel 的 modulated-weight norm 重縮放。把這三步混成「每張圖都先做一次全圖標準化」,就會失去 style 如何影響權重的重點。

    本篇沒有下載或執行 StyleGAN checkpoint,也沒有做 style mixing、影像品質或效能比較。讀這些方法時,最有用的第一個判斷仍是:你缺少的是可用的條件、可靠的配對、對映射的約束,還是對生成器內部變化的控制?條件餵給哪裡、誤差比較哪兩個東西,比單看模型名稱更能幫你選擇資料與驗證方式。

    原始方法與圖片來源

    本文由原始方法重新組織教學、加入兩點反例與逐圖讀法;沒有重刊原論文的實驗照片或將其結果冒稱本站實跑。實作附件只枚舉有限映射,未使用 TensorFlow/GAN 訓練。六張說明圖各自沿用下列來源授權;圖 1–5 的 CC BY 與圖 6 的 CC BY-SA 分開適用。

    留言0

    登入後就能留言。

    登入
    sunny 桑尼
    sunny 桑尼

    AI 資料科學 · 產品經理 · 蒙奇 D 桑尼

    專注於 AI 工具應用與資料科學教育,致力於讓每個人都能用 AI 提升工作效率。曾任職於科技業,現為全職教育創作者。

    讀更多文章 →想自己動手試?領免費 Starter Kit →