10.3 生成模型與現代影像AI#
概念說明#
前兩節是「判別模型」:輸入影像 → 輸出標籤/框。生成模型 (generative model) 學的是資料本身的分布 p(image),從而能創造新影像——這是 2020 年代影像 AI 最劇烈的變革。
GAN (Generative Adversarial Network, 2014):兩個網路博弈。生成器 G 從隨機向量 z 造影像;判別器 D 學習分辨真圖與 G 的贗品。目標函數:
min_G max_D E[log D(x)] + E[log(1 − D(G(z)))]
D 訓練得越好,G 得到越尖銳的梯度去騙它;均衡點是 G 的輸出分布等於真實分布。GAN 產出的影像銳利(判別器懲罰模糊),但訓練不穩(兩方失衡 → 模式崩潰:G 只會產出幾種圖)、需小心調參。經典變體:DCGAN(卷積版)、StyleGAN(風格分層控制,人臉生成的里程碑)、CycleGAN(無配對資料的風格轉換)。
擴散模型 (Diffusion Model, DDPM 2020):反其道而行,走「破壞與重建」路線。前向過程對真實影像逐步加高斯雜訊,T 步後變成純雜訊(這個過程不需要學習,是固定的數學);反向過程訓練一個網路 ε_θ(x_t, t) 預測「當前這步加進去的雜訊」,學會了就能從純雜訊一步步減雜訊,還原出一張全新影像:
x_{t−1} = 1/√α_t · (x_t − (1−α_t)/√(1−ᾱ_t) · ε_θ(x_t, t)) + σ_t·z
訓練目標只是回歸雜訊(MSE!),比 GAN 穩定得多;生成時迭代數十步換取高品質。文生圖 (text-to-image) 的組合拳:把文字用語言模型編碼 → 作為條件注入去雜訊網路(U-Net 的交叉注意力層)→ Stable Diffusion (2022) 更把去雜訊搬到壓縮的潛空間 (latent space) 執行,算力降一個量級,促成開源生態爆發。控制生成(ControlNet 的邊緣/深度條件)、影像修復 (inpainting)、超解析度都是同一框架的條件化變體——注意:低解析度 → 高解析度的擴散超解析,本質上是第 7 章插值問題的「學習版」。
現代圖景的另外兩塊:
- Vision Transformer (ViT, 2020):把影像切成 16×16 的 patch,攤平成 token 序列,直接用為文字設計的 Transformer 處理。CNN 的局部性先驗被「全局自注意力 + 大量資料」取代;在超大資料上超越 CNN,兩者如今混合(ConvNeXt、Swin)。
- 自監督學習 (self-supervised):不需要人工標籤,用「遮住一部分請網路猜」(MAE)、或「同圖兩個增廣的表徵應相似」(對比學習 SimCLR/DINO)學出通用特徵。第 9 章「手工特徵 → 學習特徵」的旅程在這裡走完最後一公里:連標籤都不需要了。
生成內容的評估:重建類任務沿用 PSNR/SSIM(6.3 節),但生成影像「沒有標準答案」,改用統計距離 FID(生成分布與真實分布在 InNet 特徵空間的 Fréchet 距離)與感知指標 LPIPS(深度特徵的感知距離,比 SSIM 更貼近人眼)。
演算法步驟(迷你擴散:一維玩具版)#
- 前向:x_t = √ᾱ_t·x₀ + √(1−ᾱ_t)·ε,ᾱ_t 由雜訊表 (schedule) 累乘而來。
- 訓練:抽 (x₀, t),預測 ε̂ ≈ ε,損失 = ‖ε̂ − ε‖²。
- 生成:從純雜訊出發,逐步用預測的雜訊回推 t = T…1。
Python 程式碼範例#
import numpy as np
rng = np.random.default_rng(15)
# 一維玩具資料:常數影像 x0 = 0.5(用純量示範擴散數學)
T = 50
betas = np.linspace(1e-4, 0.02, T) # 雜訊表
alphas = 1 - betas
alpha_bar = np.cumprod(alphas) # ᾱ_t
def forward(x0, t):
e = rng.normal()
return np.sqrt(alpha_bar[t])*x0 + np.sqrt(1-alpha_bar[t])*e, e
# 「完美去雜訊器」:因為資料是常數 0.5,預測雜訊 = (x_t - √ᾱ·0.5)/√(1-ᾱ)
def denoise_step(x_t, t):
e_hat = (x_t - np.sqrt(alpha_bar[t]) * 0.5) / np.sqrt(1 - alpha_bar[t])
return (x_t - (1-alphas[t]) / np.sqrt(1-alpha_bar[t]) * e_hat) / np.sqrt(alphas[t])
# 生成:從純雜訊回推
x = rng.normal()
for t in reversed(range(T)):
x = denoise_step(x, t)
print("擴散生成結果:", round(float(x), 4), "(目標 0.5)")
# 前向的統計驗證:t 大時影像資訊消失
for t in [1, 10, 30, 49]:
xs = [forward(0.5, t)[0] for _ in range(2000)]
print(f"t={t:2d}: x_t 平均 {np.mean(xs):.3f} 標準差 {np.std(xs):.3f} "
f"(理論 √(1-ᾱ)={np.sqrt(1-alpha_bar[t]):.3f})")
# LPIPS 的精神示範:特徵空間距離 vs 像素距離
a = np.zeros((8, 8)) # 原圖
b = a + 10 # 均勻亮度漂移
c = a.copy(); c[::2, :] = 100 # 結構改變(條紋)
print("像素 MSE: 漂移 =", ((b-a)**2).mean(), " 條紋 =", ((c-a)**2).mean())
print("『特徵』視角:條紋破壞了結構(邊緣從 0 → 100),人眼覺得差很多,"
"但 MSE 相同——LPIPS 用 CNN 特徵距離捕捉這一點。")
執行後:玩具擴散從純雜訊回推 50 步,收斂到 0.5——完整重現「加雜訊→學去雜訊→從雜訊創作」的迴路;前向統計驗證 ᾱ_t 表的正確性(t 大時訊號衰減、雜訊方差趨近 1);最後的 MSE 對比示範了為何生成模型的品質評估需要超越像素距離的指標。
複雜度分析#
GAN:訓練每步 = G 前向 + D 前向 + 兩者反向;推理只需 G,一次前向 O(1)(對固定架構)。擴散:訓練一步便宜(單次前向/反向),推理卻要 T 次去雜訊網路前向——生成一張圖 = T 次完整 CNN 推理,這是擴散慢的根源。加速路線:減步採樣(DDIM、蒸餾把 T 從 1000 壓到 4~50)、潛空間(Stable Diffusion 在 64×64 潛空間而非 512×512 像素空間去雜訊,計算量除以 64)、一致性模型(一步生成)。ViT:自注意力對 n 個 patch 是 O(n²)(n = (224/16)² = 196,平方 38k 尚可;高解析度影像則需要 Swin 的視窗注意力把平方壓回線性)。生成式 AI 的算力敘事——「訓練貴、推理也貴」——本質上仍是第 1 章 O(H·W) 的故事在現代硬體上的續章。
練習#
- 把玩具擴散的資料改成兩個模式 {0.2, 0.8} 各半,用「回推到最近的模式」當去雜訊器,觀察生成結果的分布——這是模式崩潰與多峰分布的最小示範。
- 實作 DDIM 採樣(跳步:只用 t, t−Δt 的確定性回推),把 50 步壓到 10 步,比較生成結果偏離 0.5 的程度。
- 用 6.3 節的 SSIM 與本節描述的「特徵距離」視角,分別評估「亮度漂移」與「條紋破壞」兩種失真,論述為何生成模型的評測幾乎不使用 PSNR。