跳到主要內容

CV Day 36 Stable Diffusion 推論實戰

CV Day 36 Stable Diffusion 推論實戰

執行需求:Colab T4 可跑。本篇在 Colab 免費 T4(16 GB VRAM)上用 Hugging Face diffusers 0.31 載入 Stable Diffusion 1.5(runwayml/stable-diffusion-v1-5,2024 年仍存在的官方 v1.5 repo,license 為 CreativeML Open RAIL-M),生成一張 512×512 的彩色影像。單張推論時間:DDIM 50 步約 4.5 秒、Euler 30 步約 3.0 秒、DPM++ 20 步約 2.5 秒(實際數字會略有不同);VRAM 占用約 6.0–6.8 GB。CPU 純推論也可以跑(單張約 2 分鐘),但實務上沒人這樣用——Stable Diffusion 的設計前提就是有 GPU。

引言

Day 35 的內容中,我們用一個小型 U-Net 在 MNIST 28×28 上訓練了一個完整的 DDPM/DDIM 流程——加噪、取樣、50 步加速都跑過了。今天我們要跳到「真實世界的 Stable Diffusion」:用 Hugging Face diffusers 0.31 載入 Stability AI 與 RunwayML 合作釋出的 Stable Diffusion 1.5 模型,生成 512×512 的彩色影像。Stable Diffusion 1.5 在 2024 年仍是社群最常用的開源文生圖模型,原因有三:第一,runwayml/stable-diffusion-v1-5 在 Hugging Face Hub 上有完整的 fp16 版本(runwayml/stable-diffusion-v1-5 與 stable-diffusion-v1-5/stable-diffusion-v1-5 兩個相同內容的鏡像),license CreativeML Open RAIL-M 允許商業使用;第二,diffusers 把模型拆成四個子元件(VAE、UNet、text encoder、scheduler),使用者可以單獨換掉任一個;第三,ControlNet、LoRA、IP-Adapter 等所有擴充都是基於這個架構。

Stable Diffusion 不是單純的 DDPM——它是 Latent Diffusion Model(LDM,Rombach 等人,2022),把擴散過程從「像素空間」搬到「潛在空間」。具體設計:先用一個 VAE Encoder 把 512×512×3 影像壓縮到 64×64×4 的潛在向量、再對潛在向量做 DDPM 加噪/去噪、最後用 VAE Decoder 把 64×64×4 解回 512×512×3。這種設計讓擴散的維度從 512×512×3 ≈ 786,432 維降到 64×64×4 = 16,384 維(約 48× 縮小),單次 UNet forward 的時間與記憶體都大幅縮減。代價是 VAE 解碼時會損失一點高頻細節——這也是 Stable Diffusion 影像放大後偶爾會糊的原因之一。

這一篇的核心是「把 diffusers 的 StableDiffusionPipeline 用起來」。我們會展示四個控制參數的影響:negative_prompt(告訴模型不要什麼)、guidance_scale(CFG classifier-free guidance,控制「多認真遵守 prompt」的強度)、seed(隨機數種子,控制可重現性)、num_inference_steps(取樣步數,控制品質與速度的取捨)。讀完這篇你應該能回答:Stable Diffusion 為什麼不在像素空間擴散、要在潛在空間?scheduler、VAE、UNet、text encoder 四個元件各做什麼?guidance_scale 太低太高會怎樣?為什麼 seed 設 42 可以重現別人發的圖?

Stable Diffusion 的潛在空間擴散

Stable Diffusion 的四個核心元件各司其職。VAE(Variational Autoencoder) 負責像素 ↔ 潛在空間的轉換:Encoder 把 512×512×3 的影像壓到 64×64×4 的潛在向量 z,Decoder 把 z 解回像素空間。VAE 的潛在維度比像素空間小約 48 倍,這是 Stable Diffusion 能在消費級 GPU 跑的關鍵。UNet 是擴散模型本身(860M 參數),接收潛在向量 z_t 與時間步 t、預測噪音 ε_θ(z_t, t),跟 Day 35 的 MNIST 小型 U-Net 是同樣的設計、只是通道數從 64/128/256 拉到 320/640/1280。Text encoder 把 prompt 文字編碼成 77×768 的 token embedding(CLIP text encoder),這是「條件式生成」的核心——UNet 同時接收 z_t 與文字 embedding,學會根據文字調整去噪行為。Scheduler 控制加噪/去噪的數學細節:β schedule、DDIM/DDPM 切換、num_inference_steps。

「條件式生成」需要 classifier-free guidance(CFG)。訓練時 UNet 同時學「有條件去噪」(給文字 embedding)與「無條件去噪」(把文字 embedding 換成空字串);推論時把兩個預測線性組合:ε_with_cfg = ε_uncond + guidance_scale · (ε_cond - ε_uncond)。guidance_scale = 1 時退化成純條件生成(影像多樣但「不認真聽話」)、guidance_scale = 7.5 是 Stable Diffusion 1.5 的預設值(平衡品質與多樣性)、guidance_scale = 15–20 時影像會過度飽和(顏色過鮮豔、細節過度銳利)。負面 prompt 則是把「uncond 換成 negative 的文字 embedding」——這個機制來自 SD 1.5 的後續社群微調,現在是 diffusers 的標準介面。

Scheduler 是 Stable Diffusion 最容易踩雷的元件——同一個模型換不同的 scheduler,生成的影像品質、收斂速度、可重現性都會改變。主流選擇有:DDIM(Song 等人,2020,50 步為標準;Stable Diffusion 1.5 原始論文的預設)、EulerDiscrete(Karras 等人,2022,30 步就能達到 DDIM 50 步品質,2024 年的主流預設)、DPMSolverMultistep(Lu 等人,2022,20–25 步品質最佳)、UniPC(Zhao 等人,2023,15–20 步品質逼近 DPMSolver)。實務上要品質優先用 DPMSolverMultistep 25 步、要速度優先用 Euler 20 步、要可重現用 DDIM 50 步。順帶一提,這些 scheduler 之間的「品質」與「步數」關係不是單純線性:DPMSolver 20 步通常比 DDIM 20 步好得多,因為它是高階 ODE 解法;Euler 30 步比 DPMSolver 30 步略差,因為 Euler 是一階方法、需要更多步才能收斂。實務上「scheduler + 步數」必須一起挑,不能單獨調整其中一個。

diffusers 0.31 的 StableDiffusionPipeline 介面

diffusers 0.31 把 Stable Diffusion 包成 StableDiffusionPipeline.from_pretrained(...) 一個 call 就載入四個元件。from_pretrained("runwayml/stable-diffusion-v1-5", torch_dtype=torch.float16) 會從 Hugging Face Hub 下載 VAE、UNet、text encoder、tokenizer、scheduler(總計約 5 GB),並用 fp16 載入(VRAM 從 12 GB 降到 6 GB)。呼叫 pipe(prompt, negative_prompt=..., num_inference_steps=..., guidance_scale=..., generator=...).images[0] 就能拿到一張 PIL Image。

generator 參數是 torch 的隨機數生成器,控制初始雜訊——這就是「為什麼 seed=42 可以重現別人發的圖」的機制。diffusers 0.31 推薦用 torch.Generator(device="cuda").manual_seed(42),把 generator 與裝置綁在一起;如果你寫成 torch.manual_seed(42)(CPU 種子),結果會與 Generator(device="cuda").manual_seed(42) 不一樣——因為 CPU 與 CUDA 的 PRNG 演算法不同。實務上要保證可重現,必須固定 generator 與 device。

另一個常見踩雷點是 safety_checker。Stable Diffusion 1.5 預設帶一個 NSFW 內容過濾器,會把「看起來像裸露」的影像換成全黑圖。這個過濾器在 from_pretrained 時載入、推論時呼叫——對於「想生成工業瑕疵」這種完全無害的應用,反而會誤殺一些「瑕疵 = 黑色斑塊」的影像。關閉方法:pipe.safety_checker = None 或 pipe.safety_checker = lambda images, clip_input, **kwargs: (images, [False] * len(images))。這個改動是合法的(CreativeML Open RAIL-M license 允許關閉過濾器)、但要評估你的應用是否有合規需求。順帶一提,2024 年 10 月 Hugging Face 對 SD v1.5 等部分模型新增「需登入才能下載」的限制——如果你看到 403 Forbidden 或 gated repo,請先在 Hugging Face 帳號設定中接受該模型的使用條款,再執行 huggingface-cli login 重新整理 token。

diffusers 0.31 還提供兩種「記憶體優化」介面。pipe.enable_attention_slicing() 會把 attention 計算切成小塊(例如 512×512 → 256×256),VRAM 從 6 GB 降到 4 GB 左右,速度慢約 15%;pipe.enable_vae_tiling() 會把 VAE 解碼也切成 tile(例如 1024×1024 切成 4 個 512×512),讓 1024×1024 的高解析度生成不會爆 VRAM。這兩個介面都是「trade VRAM for time」的設計——VRAM 小的顯卡(如 RTX 3060 12 GB)會很需要。實務上 T4 / V100 等 16 GB 顯卡可以跳過這些設定,直接用預設 fp16。

完整實作:用 diffusers 0.31 生成 Stable Diffusion 影像

以下範例在 Colab T4 上約 8 分鐘(含下載模型約 1 分鐘、推論約 30 秒 × 6 組實驗)。我們會載入 Stable Diffusion 1.5、用四組 prompt + 不同參數生成影像、視覺化 guidance_scale 與 num_inference_steps 的影響。執行前需安裝:pip install diffusers==0.31 transformers==4.46 accelerate torch safetensors(Colab 預裝 torch、accelerate)。注意 2024 年 12 月 Hugging Face 的認證政策:sd-v1-5 仍可匿名下載,但部分模型需要先執行 huggingface-cli login 並接受 license。

# 1. 安裝 diffusers 0.31 與相關套件(Colab 預設 torch,accelerate 可不安裝)
pip install -q diffusers==0.31 transformers==4.46 safetensors
python -c "import diffusers, transformers; print(f'diffusers {diffusers.__version__}, transformers {transformers.__version__}')"
# 輸出:diffusers 0.31.0, transformers 4.46.0

這段把 diffusers 0.31 與 transformers 4.46 鎖版安裝。diffusers==0.31 是 2024 年底前的穩定版本,內建 StableDiffusionPipeline、DPMSolverMultistepScheduler、EulerDiscreteScheduler 等排程器。transformers 4.46 提供 CLIPTokenizer 與 CLIPTextModel,這是 Stable Diffusion text encoder 的依賴。safetensors 是模型權重的安全格式(避免 pickle 反序列化漏洞),diffusers 0.31 預設從 .safetensors 載入。如果你的 transformers 版本較新,可能需要 pip install transformers==4.46 鎖版。

# 2. 載入 Stable Diffusion 1.5(fp16、約 5 GB VRAM、約 1 分鐘下載)
import torch
from diffusers import StableDiffusionPipeline

model_id = "runwayml/stable-diffusion-v1-5"
pipe = StableDiffusionPipeline.from_pretrained(
    model_id,
    torch_dtype=torch.float16,  # VRAM 從 12 GB 降到 6 GB
    safety_checker=None,         # 關閉 NSFW 過濾器(瑕疵生成會誤判)
)
pipe = pipe.to("cuda")
print(f"Pipeline 已載入,UNet 參數量:{sum(p.numel() for p in pipe.unet.parameters()) / 1e6:.1f}M")
print(f"VAE 參數量:{sum(p.numel() for p in pipe.vae.parameters()) / 1e6:.1f}M")
# 輸出:Pipeline 已載入,UNet 參數量:860.1M
# 輸出:VAE 參數量:83.6M

這段從 Hugging Face Hub 載入 Stable Diffusion 1.5。torch_dtype=torch.float16 是 T4 / V100 等消費級 GPU 的標準做法——VRAM 從 12 GB 降到 6 GB,VRAM 占用不到 T4 的 16 GB 限制。safety_checker=None 把 NSFW 過濾器關掉,這對「生成工業瑕疵」應用是必要的:瑕疵的暗色斑塊有時會被誤判為裸露。Stable Diffusion 1.5 的權重約 5 GB(包括 fp32 與 fp16 各一份),首次下載約 60–90 秒,第二次執行會從 ~/.cache/huggingface/hub/ 讀取、不到 5 秒。UNet 860M 參數是 Stable Diffusion 1.5 的主體,VAE 83.6M 參數負責潛在空間的編解碼。

# 3. 基本推論:單張影像、DDIM 50 步、guidance_scale=7.5
import time

prompt = "a rusty metal gear with scratches, industrial close-up, soft lighting, 8k"
negative_prompt = "blurry, low quality, distorted, watermark"

torch.manual_seed(42)
generator = torch.Generator(device="cuda").manual_seed(42)
t0 = time.time()
image = pipe(
    prompt=prompt,
    negative_prompt=negative_prompt,
    num_inference_steps=50,
    guidance_scale=7.5,
    generator=generator,
).images[0]
dt = time.time() - t0
image.save("/content/sd_basic.png")
print(f"生成 1 張影像耗時:{dt:.1f} 秒")
print(f"影像尺寸:{image.size}")
# 輸出(實際數字會略有不同):
# 生成 1 張影像耗時:4.6 秒
# 影像尺寸:(512, 512)

這段是 Stable Diffusion 的最小推論範例。generator 與 torch.manual_seed(42) 雙重保險——把 CUDA 的隨機性也固定住,確保同一個 prompt + seed 永遠產生同一張影像。negative_prompt 把「模糊、低品質、變形、浮水印」這類常見生成瑕疵明確排除,這是 Stable Diffusion 1.5 社群累積出來的「標準負面 prompt 模板」。num_inference_steps=50 是 DDIM 的標準設定,guidance_scale=7.5 是 Stable Diffusion 1.5 原始論文的預設值。整個流程在 T4 上約 4.6 秒,比 Day 35 的 MNIST DDIM 慢約 27 倍(從 28×28 灰階升到 512×512 彩色 + 860M 參數 UNet),但生成品質是「真實可用的產品照」級別。

# 4. Scheduler 比較:DDIM 50 / Euler 30 / DPMSolver 20 的品質與時間
from diffusers import DDIMScheduler, EulerDiscreteScheduler, DPMSolverMultistepScheduler

prompt = "a rusty metal gear with scratches, industrial close-up, soft lighting, 8k"
results = {}
for name, scheduler_cls, steps in [
    ("DDIM 50 步", DDIMScheduler, 50),
    ("Euler 30 步", EulerDiscreteScheduler, 30),
    ("DPMSolver 20 步", DPMSolverMultistepScheduler, 20),
]:
    pipe.scheduler = scheduler_cls.from_config(pipe.scheduler.config)
    generator = torch.Generator(device="cuda").manual_seed(42)
    t0 = time.time()
    img = pipe(prompt=prompt, num_inference_steps=steps, guidance_scale=7.5, generator=generator).images[0]
    dt = time.time() - t0
    img.save(f"/content/sd_{name.split()[0].lower()}.png")
    results[name] = dt
for name, dt in results.items():
    print(f"  {name:18s}  耗時 {dt:.1f} 秒")
# 輸出(實際數字會略有不同):
#   DDIM 50 步        耗時 4.6 秒
#   Euler 30 步       耗時 2.9 秒
#   DPMSolver 20 步   耗時 2.1 秒

這段比較三個主流 scheduler 的推論時間。DDIM 50 步 是 Stable Diffusion 1.5 的原始預設,4.6 秒;Euler 30 步 用 Karras 的 stochastic sampler 達到 DDIM 50 步品質但只花 2.9 秒(37% 加速);DPMSolver 20 步 是 Lu 等人 2022 年的多步解法,達到接近品質但只花 2.1 秒(54% 加速)。實務上如果要批次生成(例如 100 張用於資料增強),DPMSolver 20 步是最佳選擇;如果要可重現的論文實驗,DDIM 50 步是預設。每個 scheduler 都透過 from_config 繼承 Stable Diffusion 原始的 β schedule 參數,再用自己的演算法解軌跡。

# 5. guidance_scale 的影響:1.0 / 4.5 / 7.5 / 12.5 的影像變化
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
prompt = "a rusty metal gear with scratches, industrial close-up, soft lighting, 8k"
fig_images = []
for gs in [1.0, 4.5, 7.5, 12.5]:
    generator = torch.Generator(device="cuda").manual_seed(42)
    img = pipe(prompt=prompt, num_inference_steps=20, guidance_scale=gs, generator=generator).images[0]
    fig_images.append((gs, img))
    img.save(f"/content/sd_gs_{gs}.png")
print("guidance_scale 1.0 / 4.5 / 7.5 / 12.5 已存成 4 張圖")
# 輸出:guidance_scale 1.0 / 4.5 / 7.5 / 12.5 已存成 4 張圖

這段實驗 guidance_scale 對生成品質的影響。guidance_scale=1.0 等於「完全不聽 prompt」——生成影像會偏模糊、偏「通用」、與 prompt 文字的對應關係很弱(這是 classifier-free guidance 的退化情況)。guidance_scale=4.5 是「保守聽話」——影像會明顯對應 prompt 文字,但細節較平滑。guidance_scale=7.5 是 Stable Diffusion 1.5 的預設——平衡品質與多樣性,多數場景的最佳選擇。guidance_scale=12.5 是「過度聽話」——影像會過度飽和、邊緣過度銳利、有時會出現 artifacts(如額外的紋理、重複的圖案)。實務上 7–8 是多數 prompt 的甜蜜點;動漫風格建議 9–11、寫實風格建議 5–7。

# 6. negative_prompt 的實驗:空 vs 「blurry」vs 「blurry, low quality」
pipe.scheduler = DDIMScheduler.from_config(pipe.scheduler.config)
prompt = "a rusty metal gear with scratches, industrial close-up, soft lighting, 8k"
fig_images = []
for neg in [
    "",
    "blurry",
    "blurry, low quality, distorted, watermark",
]:
    generator = torch.Generator(device="cuda").manual_seed(42)
    img = pipe(prompt=prompt, negative_prompt=neg, num_inference_steps=30, guidance_scale=7.5, generator=generator).images[0]
    fig_images.append((neg or "(空)", img))
    img.save(f"/content/sd_neg_{neg[:20] or 'empty'}.png")
print("3 種 negative_prompt 已存成 3 張圖")
# 輸出:3 種 negative_prompt 已存成 3 張圖

這段實驗 negative_prompt 的效果。空 negative_prompt 等於「不排除任何特徵」——影像可能出現模糊、雜訊、低解析度等常見瑕疵;加上 "blurry" 排除模糊後影像邊緣明顯變銳利;加上完整的社群標準模板 "blurry, low quality, distorted, watermark" 後影像品質最接近 Stable Diffusion 官方展示照。這證實了負面 prompt 並不是裝飾——它是 classifier-free guidance 中「uncond 文字」的內容;模型會學到「把這些不想要的特徵降低機率」。實務上「寫 prompt 的時間」應該有 30% 花在 negative_prompt 上,這對 2024 年的 Stable Diffusion 1.5 仍是必要投資。

# 7. VRAM 監控:在 T4 上跑 1 張、4 張、16 張 batch,看 VRAM 占用
import torch
pipe.scheduler = DDIMScheduler.from_config(pipe.scheduler.config)
prompt = "a rusty metal gear with scratches, industrial close-up, soft lighting, 8k"

for batch in [1, 4, 16]:
    prompts = [prompt] * batch
    generator = torch.Generator(device="cuda").manual_seed(42)
    torch.cuda.reset_peak_memory_stats()
    imgs = pipe(prompt=prompts, num_inference_steps=20, guidance_scale=7.5, generator=generator).images
    peak = torch.cuda.max_memory_allocated() / 1024 ** 3
    print(f"  batch={batch:2d}  生成 {len(imgs)} 張  峰值 VRAM:{peak:.2f} GB")
# 輸出(實際數字會略有不同):
#   batch= 1  生成 1 張  峰值 VRAM:6.21 GB
#   batch= 4  生成 4 張  峰值 VRAM:7.84 GB
#   batch=16  生成 16 張  峰值 VRAM:13.62 GB

這段量測不同 batch size 的 VRAM 占用。batch=1 時峰值 6.21 GB(這是 Stable Diffusion 1.5 fp16 的 baseline);batch=4 時 7.84 GB(每張增加 0.4 GB,主要是 UNet 中間特徵圖);batch=16 時 13.62 GB(已經接近 T4 的 16 GB 上限)。這個數字告訴我們:T4 上單卡最多批次生 8–10 張,要生 16 張以上必須加 pipe.enable_model_cpu_offload()(把 UNet 暫時搬到 CPU)或是用多卡。實務上 diffusers 0.31 推薦用 accelerate 的 device_map="balanced" 自動分配。

常見錯誤與踩雷

錯誤一:忘記設 torch_dtype=torch.float16。預設 fp32 載入會讓 VRAM 從 6 GB 升到 12 GB,T4 的 16 GB 剛好會超出,導致 CUDA out of memory。對應排查方向:永遠用 torch_dtype=torch.float16 載入 Stable Diffusion 1.5;如果你是 A100 / H100 等專業卡,可以改用 bf16。

錯誤二:torch.manual_seed(42) 與 Generator(device="cuda").manual_seed(42) 混用。這兩個 seed 設定的內部 PRNG 演算法不同——CPU seed 用 Philox 4×32-10、CUDA seed 用 Philox 4×32-7。如果你只設前者、Generator 沒設,產生的影像與「別人重現你的 seed」不會一樣。對應排查方向:用 generator = torch.Generator(device="cuda").manual_seed(42) 一個 call 就好,不要再額外呼叫 torch.manual_seed。

錯誤三:safety_checker 過濾掉合法影像。Stable Diffusion 1.5 預設的安全過濾器會把「接近裸露」的影像換成全黑——對於「生鏽齒輪」、「黑色瑕疵」這類 prompt 偶爾會誤判。對應排查方向:pipe.safety_checker = None 可以關閉;如果你的應用有合規需求,可以改用更精細的 NSFW 分類器(不會誤殺)。

錯誤四:把 Stable Diffusion 1.5 跑在 CPU。雖然 pipe.to("cpu") 可以強制 CPU 推論,但單張 512×512 影像約 2 分鐘,這對開發迭代完全不可接受。對應排查方向:即使是 Colab 免費 T4 也比 CPU 快 30 倍;如果真的沒 GPU,至少把 pipe.unet 換成 torch.compile(pipe.unet, mode="reduce-overhead") 可以再加速 20%。

錯誤五:把 num_inference_steps 設成 5 以為「夠快就好」。少於 10 步的 Stable Diffusion 影像會出現明顯 artifacts(如條紋、色塊、未收斂的區域)。對應排查方向:最少 15–20 步才有可接受的品質;20 步是 DPMSolver 的甜蜜點;30–50 步是其他 scheduler 的甜蜜點。低於 10 步只適合「快速預覽構圖」的場景,不適合正式輸出。

效能與實務提醒

在 Colab T4 上跑 Stable Diffusion 1.5(fp16、DDIM 50 步)單張約 4.6 秒。T4 的 16 GB VRAM 在 fp16 下能撐到 batch=8(峰值約 11 GB)。如果想再加速三個常用手段:第一,用 torch.compile(pipe.unet, mode="reduce-overhead", fullgraph=True) 把 UNet 編譯成融合 kernel,T4 上可以再加速 25–35%;第二,用 pipe.enable_xformers_memory_efficient_attention() 或 pipe.enable_attention_slicing() 把 attention 切成小塊,VRAM 從 6 GB 降到 4 GB、batch 容量翻倍;第三,用 pipe.enable_vae_tiling() 把 VAE 解碼也切成 256×256 的 tile,可以生 1024×1024 的影像而不爆 VRAM。

如果要部署 Stable Diffusion 做服務,模型權重約 5 GB(fp16),加上快取約 8 GB。最低硬體需求:NVIDIA 顯卡 8 GB VRAM(如 RTX 3060、RTX 4060),CPU 推論約 60 秒一張不建議;Mac M1/M2 用 torch.float16 + mps 後端約 25–35 秒一張(社群實測);AMD 顯卡要用 ROCm 5.7+ + torch.float16,但 diffusers 0.31 的官方支援有限。實務上工業部署最常見的選擇是「租 A100 / L4 雲端」或「用 sd-turbo 之類的蒸餾模型」——stableai 的 sdxl-turbo / stabilityai/sd-turbo 是 1–4 步的蒸餾版,T4 上單張約 0.5 秒、512×512,但品質略低於 SD 1.5。

貫穿專案的角度:MVTec AD 的合成罕見瑕疵任務(Day 39)會大量用到這篇的技術——用 Stable Diffusion 1.5 生成候選瑕疵、用 MVTec 預訓練分類器做品質過濾、最後把高品質瑕疵混入訓練集。比 DCGAN 的優勢是 Stable Diffusion 能直接從文字 prompt 生成「特定紋理」的瑕疵(如「黑色刮痕」、「銅綠氧化」、「裂縫網」),不需要收集對應的瑕疵影像做訓練。但代價是 Stable Diffusion 的單張推論成本比 DCGAN 高 100 倍,且 prompt 工程需要時間投入。實務上 Day 39 會把這兩個方法結合:DCGAN 生成「粗略構圖」、Stable Diffusion 做「細節 refine」。

小結

今天用 diffusers 0.31 載入 Stable Diffusion 1.5,從基本的單張推論、到 scheduler 比較、到 guidance_scale 與 negative_prompt 的影響、到 batch size 與 VRAM 的取捨,完整展示了 Stable Diffusion 推論工作流。重點回顧:第一,Stable Diffusion 是 Latent Diffusion Model,擴散在 VAE 的潛在空間進行,這讓 512×512 影像的擴散時間與記憶體都大幅縮減;第二,diffusers 把 Stable Diffusion 包成四個獨立元件(VAE、UNet、text encoder、scheduler),每個都能單獨換掉;第三,guidance_scale=7.5 是平衡預設、num_inference_steps=20–50 是品質甜蜜點、seed + Generator(device="cuda") 是可重現的關鍵;第四,torch_dtype=torch.float16 是 T4 / 消費級 GPU 的必要設定。明天我們會把 Stable Diffusion 1.5 升級到「可控的」版本——ControlNet 用 canny/depth 邊界控制構圖、LoRA 用 peft 做小樣本風格微調,這是 2024 年 Stable Diffusion 社群最活躍的兩條路線。

結語

今天的重點是把 Stable Diffusion 1.5 的推論工作流跑起來。我們從 diffusers 的 StableDiffusionPipeline.from_pretrained(...) 開始,展示了 prompt、negative_prompt、num_inference_steps、guidance_scale、generator 五個關鍵參數的影響,並用量化的 VRAM 數據討論 batch size 取捨。讀完這篇你應該能回答:Stable Diffusion 為什麼要在潛在空間擴散、不在像素空間?四個核心元件(VAE、UNet、text encoder、scheduler)各做什麼?guidance_scale 太低太高會怎樣?為什麼 seed=42 可以重現別人發的圖?明天,我們會把 Stable Diffusion 升級到「可控的」版本——ControlNet 透過 canny/depth 邊界條件控制構圖、LoRA 透過 peft 的低秩適配器做小樣本風格微調,這是 2024 年 Stable Diffusion 社群最活躍的兩條擴充路線,也是工業瑕疵生成、產品圖生成的標準配備。

延伸資源

  • Rombach 等人,2022,High-Resolution Image Synthesis with Latent Diffusion Models(CVPR 2022):https://arxiv.org/abs/2112.10752,Stable Diffusion 原始論文,VAE + UNet + text encoder 的潛在空間擴散設計。
  • Stable Diffusion 1.5 model card(runwayml/stable-diffusion-v1-5,CreativeML Open RAIL-M license):https://huggingface.co/runwayml/stable-diffusion-v1-5,2024 年仍可下載的官方 v1.5 model 與授權條款。
  • diffusers Stable Diffusion 推論教學(v0.31,2024):https://huggingface.co/docs/diffusers/using-diffusers/text2img,StableDiffusionPipeline、scheduler、enable_xformers_memory_efficient_attention 的官方範例。
  • Karras 等人,2022,Elucidating the Design Space of Diffusion-Based Generative Models(NeurIPS 2022):https://arxiv.org/abs/2206.00364,Euler / EDM scheduler 的設計空間分析。
  • Lu 等人,2022,DPM-Solver: A Fast ODE Solver for Diffusion Probabilistic Model Sampling(NeurIPS 2022):https://arxiv.org/abs/2206.00927,DPMSolverMultistep 的數學推導與品質/速度取捨分析。
  • Stability AI sd-turbo model card(stabilityai/sd-turbo,2024):https://huggingface.co/stabilityai/sd-turbo,單步生成的蒸餾 Stable Diffusion,2024 年的速度優選方案。

留言

這個網誌中的熱門文章

Day 2 變數與資料型別

Day 2 變數與資料型別 引言 寫程式的過程中,變數與資料型別是處理資料的基礎。變數是存放資料的容器,資料型別則決定這筆資料有哪些特性、可以進行哪些操作。學會定義變數、認識各種資料型別,是學好 Python 的關鍵一步。 這篇文章會帶你了解 Python 中變數的觀念、如何定義變數,以及常見的資料型別,包括整數、浮點數、字串、布林值,還有串列、元組、字典與集合等容器型別。我們也會介紹變數的命名規則與撰寫風格建議,以及如何用 type() 檢查資料型別。 什麼是變數?如何在 Python 中定義變數 變數是在程式執行時用來存放資料的名稱。透過定義變數,我們可以給一筆資料一個名字,並在程式的其他地方用這個名字取用該筆資料。在 Python 中,變數不需要事先宣告型別,因為 Python 是動態型別語言,變數的型別由指定給它的值決定。 定義變數的基本語法 在 Python 中定義變數非常簡單,只要用賦值符號 = 把值指定給變數即可。例如: x = 5 # 定義變數 x,並把整數 5 賦值給它 name = "Alice" # 定義變數 name,並把字串 "Alice" 賦值給它 在這裡,x 是一個變數,被賦予整數 5;name 是另一個變數,被賦予字串 "Alice"。 變數的更新與覆寫 變數的值可以修改,也就是說,我們可以在程式的不同地方給同一個變數新的值。例如: x = 10 # x 最初被賦予 10 x = 15 # x 的值現在被更新為 15 這樣就能依照需求,在程式執行過程中靈活調整變數的值。 Python 的動態型別系統 Python 和某些靜態型別語言不同,定義變數時不需要宣告型別。賦值時,Python 會根據值自動判斷變數的型別。例如: x = 5 # x 是整數 x = 3.14 # x 變成浮點數 x = "Hi" # x 變成字串 同一個變數在程式執行過程中可以存放不同型別的值,這是 Python 的彈性之一。 常見資料型別 在 Python 中,資料型別決定我們可以對變數進行哪些操作...

Day 1 Python 簡介與環境設定

Day 1 Python 簡介與環境設定 引言 在現在的科技環境裡,程式設計已經是一項重要技能。無論你是對資料科學有興趣、想成為開發者,或是想踏入人工智慧(AI)領域,學會寫程式都能明顯提升你的競爭力。在眾多程式語言中,Python 因為語法簡單、功能強大、應用範圍廣泛,成為許多人進入程式世界的第一選擇。這篇文章會帶你認識 Python 的背景與優勢,並一步步教你在不同系統上安裝與設定 Python 開發環境,最後寫出第一支 Python 程式。 為什麼選擇 Python? Python 是一種高階程式語言,由 Guido van Rossum 在 1991 年發布。Python 的設計哲學強調程式碼的可讀性,並用縮排來定義程式區塊,這點和許多使用大括號的語言不同。簡潔的語法讓它成為初學者的理想選擇;就算是經驗豐富的開發者,也能用它完成複雜的專案。 Python 的優勢如下: 簡單易學 :Python 的語法清楚、結構簡潔,初學者很快就能上手。和其他語言相比,學習曲線相對平緩,不需要先弄懂一堆複雜觀念,就能開始寫程式。 應用範圍廣泛 :從資料科學、網頁開發、人工智慧、機器學習、自動化測試到網路爬蟲,Python 都有大量開源函式庫與工具支援,而且在這些領域都扮演關鍵角色。 豐富的函式庫與框架 :Python 的函式庫生態系非常龐大。做資料分析有 NumPy、Pandas;開發網站有 Django、Flask;做深度學習有 TensorFlow、PyTorch。各種需求幾乎都能找到對應的套件,讓開發更有效率。 跨平台支援 :Python 支援 Windows、macOS、Linux 等作業系統,程式通常不需要太多修改就能跨平台執行,讓開發與部署更有彈性。 活躍的社群 :Python 擁有龐大的開發者社群。學習或開發上遇到問題,幾乎都能在社群與論壇(例如 Stack Overflow)找到答案,對初學者來說是很強的後盾,也能減少卡關時的挫折感。 Python 的應用領域 Python 的流行與強大功能,讓許多領域都開始大量使用它。以下是幾個常見的應用方向: 資料科學 :隨著大數據與人工智慧興起,資料科學大量使用 Python。NumPy、Pandas 與 Matplotlib 等工具能處理和分析龐...

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門 隨著人工智慧(AI)與深度學習(Deep Learning)快速發展,越來越多人對這些技術產生興趣。不論你是想踏入 AI 領域的初學者,還是已經有程式基礎的開發者,學好 Python 與深度學習框架(例如 PyTorch),都能為你打開更多可能。 為什麼選擇 Python? Python 已經是資料科學與人工智慧領域的首選語言。它的語法簡潔、容易上手,而且擁有龐大的生態系與大量開源函式庫。無論是資料處理、資料視覺化,還是建立機器學習與深度學習模型,Python 都能勝任。對想進入 AI 或資料科學領域的人來說,它幾乎是必備工具。 PyTorch 是什麼? PyTorch 是由 Meta(原 Facebook)AI 研究團隊開發的開源深度學習框架,以易用、靈活和動態計算圖著稱,是許多 AI 研究人員與開發者的首選。相較於其他框架,PyTorch 的寫法更貼近原生 Python,對初學者相對友善。無論是簡單的實驗,還是複雜的深度學習模型,PyTorch 都能提供強大的支援。 這個系列能帶給你什麼? 這個系列會從 Python 的基礎開始,帶你一步一步學習,最後能自己用 PyTorch 建立深度學習模型。即使你完全沒有寫過程式,也能跟著文章的節奏累積技能,理解 AI 與深度學習的核心觀念。 本系列涵蓋的主題 Python 基礎:從變數、條件判斷到函式與模組。 資料處理工具:用 NumPy 與 Pandas 有效率地操作資料。 資料視覺化:用 Matplotlib 與 Seaborn 把資料畫成圖表。 深度學習的數學基礎:線性代數、微積分與機率。 PyTorch 入門:理解張量、模型建構與 GPU 加速。 基礎深度學習模型:CNN 與 RNN 的實作應用。 深度學習專案實戰:從資料前處理到模型部署的端到端流程。 誰適合這個系列? 程式初學者 :如果你對 AI 充滿好奇,卻還沒寫過程式,系列的第一部分會帶你快速上手 Python,並幫助你理解深度學習的基本觀念。 資料科學愛好者 :如果你已經熟悉一些資料處理方法,進階部分會教你如何用 PyTorch 建構深度學習模型。 開發者與研究人員 :想更深入了...