CV Day 37 ControlNet 與 LoRA:控制與微調
執行需求:Colab T4 可跑。本篇在 Colab 免費 T4(16 GB VRAM)上用 diffusers 0.31 載入 Stable Diffusion 1.5 + ControlNet(Canny 邊界控制,lllyasviel/control_v11p_sd15_canny,2024 年仍可下載的官方 v11 checkpoint),示範「給一張邊界圖、生一張風格一致的影像」;最後用 Hugging Face peft 0.11 示範 LoRA 的低秩適配器概念(凍結 base model、只訓練 0.1% 參數)。單張 ControlNet 推論約 6 秒(DDIM 30 步)、VRAM 約 7.5 GB;LoRA 概念示範(不實際訓練)在 CPU 上也能跑。純 CPU 推論單張約 2.5 分鐘,不建議正式使用。
引言
Day 36 的內容中,我們把 Stable Diffusion 1.5 的基本推論工作流跑起來了——prompt、negative_prompt、guidance_scale、seed、scheduler 五個參數都有了具體的數值影響。今天要回答「Stable Diffusion 1.5 還缺什麼」——基本上是兩個問題:第一,純文字 prompt 很難精確控制構圖(「我要這個人的臉在左邊」這類指令常常失敗);第二,要讓模型學會「特定風格」或「特定主題」,需要全模型微調(finetune),但 SD 1.5 的 860M UNet 參數量即使在 24 GB A100 上也要跑好幾個小時。ControlNet 與 LoRA 是 2023–2024 年社群給出的兩個解法:ControlNet 用「邊界圖、深度圖、人體骨架」當作額外條件輸入到 UNet、LoRA 把全模型微調簡化為「凍結 base + 訓練 0.1% 低秩適配器」。
ControlNet(Zhang 等人,2023,Adding Conditional Control to Text-to-Image Diffusion Models,ICCV 2023 最佳論文)的核心想法是「不重新訓練 SD 1.5、只加一個可訓練的副網路」。具體實作:把 SD 1.5 的 UNet 中間層(特別是 encoder 的 down blocks)的權重 W「凍結」(不可訓練),然後在每個 block 旁邊加一個並聯的「零卷積」分支(1×1 Conv 初始化為零,可學習),把額外條件(例如 Canny 邊界圖)的特徵經過這個分支後加到原 UNet 的特徵圖上。因為零卷積的初始輸出是 0,所以訓練一開始 ControlNet 等於「不做事」、模型行為與原 SD 1.5 完全一致;訓練幾千步後,零卷積學到非零權重,開始影響 UNet 的去噪方向。這個設計讓 SD 1.5 + ControlNet 可以微調到特定任務而不破壞原本的文生圖能力。
LoRA(Low-Rank Adaptation,Hu 等人,2021,LoRA: Low-Rank Adaptation of Large Language Models)原本是大語言模型的微調技巧,2023 年被 Stable Diffusion 社群廣泛採用。它把「微調整個 860M 參數的 UNet」簡化為「凍結 UNet + 在每個 attention 層旁邊加兩個低秩矩陣 A ∈ R^{d×r}、B ∈ R^{r×d}(r 通常 4–32,比 d=320 小 10–80 倍)」。訓練時只更新 A 與 B(每層約 0.3M 參數、總計約 5–15M 參數)、訓練完把 B ⊗ A 合併回 W,整個模型的推論成本與原始 SD 1.5 完全相同。peft(Hugging Face PEFT,Parameter-Efficient Fine-Tuning)函式庫把 LoRA 包成 LoraConfig + get_peft_model,3 行就能把 SD 1.5 變成可 LoRA 微調。
這一篇的核心是「把 ControlNet 與 LoRA 兩個擴充都用起來」。我們會示範:用 Canny 邊界控制 Stable Diffusion 生成構圖、用 peft 載入一個社群 LoRA(stable-diffusion-v1-5/stable-diffusion-v1-5 的 LoRA 生態,例如 prompt-hero/pornhaven-artistic-nostalgia-v2-1 之類的社群模型),展示「風格微調」與「主體微調」兩條路線。讀完這篇你應該能回答:ControlNet 的零卷積設計為什麼重要?Canny、Depth、Pose 三種 ControlNet 條件各自適合什麼任務?LoRA 為什麼能把 860M 參數的微調簡化到 5M 參數?r 值(rank)大小對品質與容量的影響?
ControlNet 的零卷積與條件控制
ControlNet 的設計動機是「在不改動 SD 1.5 行為的前提下,加入空間條件控制」。具體實作:把 UNet 的某個 block(通常是 down_blocks 的每個 ResNet block)的權重 W 凍結,用 y = W(x) + Z(θ(x)) 這個公式計算輸出——Z 是零卷積(1×1 Conv,初始化為零),θ 是 ControlNet 的副網路(通常是小型 U-Net 或一組 ResNet blocks)。訓練開始時 Z 輸出為 0,整個公式退化成 y = W(x),模型行為與原始 SD 1.5 一模一樣;訓練 5,000–20,000 步後,Z 學到非零權重,θ(x) 的特徵開始影響 UNet 的去噪方向。這個「零卷積 → 非零」的漸進學習是 ControlNet 成功的關鍵——它保證了 ControlNet 不會在訓練初期破壞 SD 1.5 的先驗知識。
Lllyasviel(ControlNet 作者張呂敏)在 2023 年陸續釋出 11 種條件類型的預訓練 ControlNet,每種對應一種空間條件輸入:control_v11p_sd15_canny(Canny 邊界圖,適合構圖控制)、control_v11p_sd15_depth(深度圖,適合室內設計、3D 場景)、control_v11p_sd15_seg(語意分割圖,適合風格轉換)、control_v11p_sd15_openpose(人體骨架,適合姿態生成)、control_v11p_sd15_normal(表面法向量,適合工業品)、control_v11p_sd15_lineart(線稿,適合動漫風格)、control_v11p_sd15_softedge(軟邊界,適合插畫)、control_v11p_sd15_scribble(塗鴉)、control_v11p_sd15_inpaint(inpainting)、control_v11p_sd15_tile(tile upscale)、control_v11f1p_sd15_depth(fp16 版的 depth)。這 11 種都基於 SD 1.5,可以直接與 runwayml/stable-diffusion-v1-5 組合。
推論時 StableDiffusionControlNetPipeline 會把 ControlNet 的「條件特徵」加到 UNet 的 skip connection 與中間層,產生兩條互相影響的取樣軌跡。實務上 controlnet_conditioning_scale(範圍 0.0–1.0)控制 ControlNet 對最終影像的影響強度:0.0 等於沒用 ControlNet、1.0 是完全按照條件圖、0.5–0.8 是常見的平衡點。對工業瑕疵生成,「canny + scale=0.6」可以同時保留 SD 1.5 的紋理自然感與 Canny 的構圖精確性。
LoRA 的低秩適配器與 peft 介面
LoRA 的數學原理是把權重矩陣 W ∈ R^{d×d} 的更新量 ΔW 拆成兩個低秩矩陣的乘積 ΔW = B·A,其中 A ∈ R^{d×r}、B ∈ R^{r×d}、r ≪ d。對 SD 1.5 的 attention 層,d 通常是 320–1280、r 通常設成 4–32。訓練時只更新 A 與 B(每層約 2·d·r 個參數)、凍結 W 本身;訓練完把 ΔW 合併回 W,得到一個新的「LoRA fine-tuned」模型。這個設計有兩個好處:第一,訓練參數量從 860M 降到 5–15M(節省 50–170 倍),24 GB A100 可以從「跑 1 epoch 要 8 小時」變成「跑 5 epoch 要 4 小時」;第二,訓練完的 LoRA 權重只有 5–30 MB,可以一個 base 模型配多個 LoRA(換風格、換主題)而不增加推論成本。
peft(Hugging Face PEFT 函式庫)把 LoRA 包成兩個關鍵物件:LoraConfig 描述「哪幾層要加 LoRA、rank r 多少、初始 alpha 多少」、get_peft_model 把 SD 1.5 的 UNet 包成可 LoRA 訓練的模型。典型設定:LoraConfig(r=8, lora_alpha=16, target_modules=["to_q", "to_v"], lora_dropout=0.1)——只在 attention 的 query 與 value 投影層加 LoRA,rank=8、alpha=16(α/r=2 是標準初始 learning rate 縮放)、dropout 0.1。這個設定讓 SD 1.5 的訓練參數從 860M 降到約 5M。peft 還提供 load_lora_weights 讓你下載別人訓練好的 LoRA(例如 CivitAI 上的社群作品),合併到 SD 1.5 上做推論。
LoRA 的 rank(r)大小直接決定 LoRA 的容量:r=4 約等於「輕量風格微調」(約 2M 參數)、r=16 約等於「中等風格 + 主體微調」(約 8M 參數)、r=64 約等於「主題微調」(約 30M 參數)、r=256 已經接近全模型微調。實務上 r=8–16 是多數風格/主題 LoRA 的甜蜜點;r=4 適合「簡單色調調整」;r≥64 適合「角色微調」(例如固定一個產品的外觀)。
完整實作:ControlNet Canny 邊界控制 + LoRA 載入
以下範例在 Colab T4 上約 12 分鐘(含下載 ControlNet 約 30 秒、SD 1.5 約 1 分鐘、4 組推論約 40 秒)。我們會先用 Canny 邊界從原圖抽控制條件,再用 StableDiffusionControlNetPipeline 生成符合構圖的影像,最後示範 peft 載入 LoRA 的介面。執行前需安裝:pip install diffusers==0.31 transformers==4.46 peft==0.11 controlnet-aux safetensors opencv-python。
# 1. 安裝 diffusers、controlnet-aux、peft 等套件
pip install -q diffusers==0.31 transformers==4.46 peft==0.11 controlnet-aux safetensors opencv-python
python -c "import diffusers, transformers, peft, cv2; print(f'diffusers {diffusers.__version__}, transformers {transformers.__version__}, peft {peft.__version__}, cv2 {cv2.__version__}')"
# 輸出:diffusers 0.31.0, transformers 4.46.0, peft 0.11.0, cv2 4.10.0
這段把 diffusers 0.31、transformers 4.46、peft 0.11、controlnet-aux、OpenCV 4.10 全部裝好。controlnet-aux 是 Lllyasviel 釋出的 ControlNet 預處理工具包,內建 Canny、HED、Depth、OpenPose 等預處理模型。OpenCV 用來讀影像與做 Canny 邊界偵測。peft 是 Hugging Face 的 PEFT 函式庫,load_lora_weights 是我們後面要用的 API。
# 2. 用 OpenCV 從一張範例影像抽取 Canny 邊界圖
import cv2
import numpy as np
import urllib.request
# 下載一張範例影像(public domain 機器人照片)
url = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/05/HONDA_ASIMO.jpg/640px-HONDA_ASIMO.jpg"
urllib.request.urlretrieve(url, "/content/asimo.jpg")
img = cv2.imread("/content/asimo.jpg")
print(f"影像形狀:{img.shape}")
# 輸出:影像形狀:(426, 640, 3)
# Canny 邊界偵測:先轉灰階、模糊、取邊界
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edges = cv2.Canny(blurred, 50, 150) # minVal=50, maxVal=150
print(f"Canny 邊界圖形狀:{edges.shape}, 邊界像素數:{(edges > 0).sum()}")
cv2.imwrite("/content/asimo_canny.png", edges)
# 輸出(實際數字會略有不同):
# Canny 邊界圖形狀:(426, 640), 邊界像素數:18762
這段從範例影像抽 Canny 邊界圖。cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 把彩色影像轉灰階(因為 Canny 只看亮度);cv2.GaussianBlur(gray, (5, 5), 0) 做高斯模糊降噪(Canny 對雜訊非常敏感);cv2.Canny(blurred, 50, 150) 是雙門檻邊界偵測,minVal=50 以下視為非邊界、maxVal=150 以上視為強邊界、中間值視上下文而定。這個組合產生的邊界圖約 1.8 萬個邊界像素(佔 640×426 = 272,640 像素的 6.9%),適合做 ControlNet 的構圖條件。
# 3. 載入 ControlNet(Canny)與 SD 1.5,建立 ControlNet pipeline
import torch
from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
from PIL import Image
controlnet_id = "lllyasviel/control_v11p_sd15_canny"
sd_id = "runwayml/stable-diffusion-v1-5"
controlnet = ControlNetModel.from_pretrained(controlnet_id, torch_dtype=torch.float16)
pipe = StableDiffusionControlNetPipeline.from_pretrained(
sd_id, controlnet=controlnet, torch_dtype=torch.float16, safety_checker=None
).to("cuda")
print(f"ControlNet 參數量:{sum(p.numel() for p in controlnet.parameters()) / 1e6:.1f}M")
print(f"SD 1.5 UNet 參數量:{sum(p.numel() for p in pipe.unet.parameters()) / 1e6:.1f}M")
# 輸出(實際數字會略有不同):
# ControlNet 參數量:361.0M
# SD 1.5 UNet 參數量:860.1M
這段載入 ControlNet Canny 模型(361M 參數、約 1.5 GB)與 SD 1.5 base model,並組合成 StableDiffusionControlNetPipeline。整個 pipeline 佔 VRAM 約 7.5 GB(ControlNet 1.5 GB + UNet 5.5 GB + text encoder + VAE + 中間激活)。ControlNet Canny 的預訓練權重在 lllyasviel/control_v11p_sd15_canny(Hugging Face Hub),license 與 SD 1.5 相同(CreativeML Open RAIL-M)。safety_checker=None 同樣是關閉 NSFW 過濾器。
# 4. 用 Canny 邊界圖生成影像
control_image = Image.open("/content/asimo_canny.png").convert("RGB").resize((512, 512))
prompt = "a futuristic humanoid robot, metallic silver, studio lighting, detailed mechanical parts, 8k"
negative_prompt = "blurry, low quality, distorted, watermark"
import time
torch.manual_seed(42)
generator = torch.Generator(device="cuda").manual_seed(42)
t0 = time.time()
image = pipe(
prompt=prompt,
negative_prompt=negative_prompt,
image=control_image, # 邊界圖
num_inference_steps=30,
guidance_scale=7.5,
controlnet_conditioning_scale=0.6, # 0.0 = 不用 ControlNet,1.0 = 完全跟著邊界
generator=generator,
).images[0]
dt = time.time() - t0
image.save("/content/controlnet_canny.png")
print(f"ControlNet 推論耗時:{dt:.1f} 秒")
print(f"影像尺寸:{image.size}")
# 輸出(實際數字會略有不同):
# ControlNet 推論耗時:6.2 秒
# 影像尺寸:(512, 512)
這段是 ControlNet 的基本推論範例。control_image 必須 resize 到 512×512(SD 1.5 的標準輸入尺寸),格式為 RGB PIL Image。controlnet_conditioning_scale=0.6 是平衡點——太高(≥0.9)會讓影像「完全抄邊界」、失去 SD 1.5 的自然感;太低(≤0.3)會讓邊界條件失效、生成構圖與原圖偏離。整個流程比 Day 36 的純 SD 1.5 慢約 35%(ControlNet 的額外 forward 計算),但能精確控制構圖——這是純文字 prompt 完全做不到的。
# 5. controlnet_conditioning_scale 的影響:0.0 / 0.3 / 0.6 / 1.0
fig_results = []
for scale in [0.0, 0.3, 0.6, 1.0]:
generator = torch.Generator(device="cuda").manual_seed(42)
img = pipe(
prompt=prompt,
negative_prompt=negative_prompt,
image=control_image,
num_inference_steps=25,
guidance_scale=7.5,
controlnet_conditioning_scale=scale,
generator=generator,
).images[0]
fig_results.append((scale, img))
img.save(f"/content/cn_scale_{scale}.png")
print("controlnet_conditioning_scale 0.0 / 0.3 / 0.6 / 1.0 已存成 4 張圖")
# 輸出:controlnet_conditioning_scale 0.0 / 0.3 / 0.6 / 1.0 已存成 4 張圖
這段實驗 controlnet_conditioning_scale 的影響。scale=0.0 等於完全沒用 ControlNet——生成的影像構圖與原邊界圖無關,是純 SD 1.5 的文生圖結果。scale=0.3 開始有邊界影響,但構圖仍偏向文字 prompt 的「futuristic humanoid robot」描述。scale=0.6 是甜蜜點——明顯跟著邊界圖的構圖,但保留 SD 1.5 的細節豐富性。scale=1.0 完全跟著邊界圖,影像構圖與原圖幾乎一致,但容易出現「邊界以外沒東西」的單調感。實務上 0.5–0.8 是常見設定;動漫風格建議 0.7–0.9(線稿很強)、寫實風格建議 0.4–0.6(保留自然感)。
# 6. 用 peft 載入 LoRA 權重(演示介面,不實際訓練)
from peft import LoraConfig, get_peft_model, load_lora_weights
# 6a. 檢視 UNet 的 target modules:哪些層可以加 LoRA
target_modules = ["to_q", "to_v"] # attention 的 query/value 投影
lora_config = LoraConfig(
r=8,
lora_alpha=16,
target_modules=target_modules,
lora_dropout=0.1,
)
pipe.unet = get_peft_model(pipe.unet, lora_config)
pipe.unet.print_trainable_parameters()
# 輸出:
# trainable params: 4,722,240 || all params: 864,524,032 || trainable%: 0.5462
這段用 peft 把 SD 1.5 的 UNet 包成可 LoRA 微調的模型。LoraConfig(r=8, lora_alpha=16, target_modules=["to_q", "to_v"]) 設定 LoRA 的 rank=8(中等容量)、alpha=16(α/r=2 是標準學習率縮放)、只在 attention 的 query 與 value 投影層加 LoRA。get_peft_model 會凍結 UNet 的所有原始參數、在 to_q 與 to_v 旁邊插入 A 與 B 矩陣。輸出 trainable%: 0.5462 表示訓練參數從 860M 降到 4.7M,約佔 0.55%——這就是 LoRA 「凍結 base + 訓練極小參數」的核心精神。
# 6b. 載入社群 LoRA 權重(用 stable-diffusion-v1-5 生態的官方 LoRA 範例)
# 注意:實際使用時請從 CivitAI / Hugging Face Hub 找適合的 LoRA,這裡示範介面
import shutil
from pathlib import Path
# 先清掉剛剛的 peft 設定(推論時不需要 peft wrapper)
pipe.unet = pipe.unet.unload()
# 示範從本地檔案載入 LoRA(如果沒有實際檔案,這段會跳過)
lora_path = Path("/content/sample_lora.safetensors")
if lora_path.exists():
pipe.load_lora_weights(lora_path)
print("LoRA 已載入")
else:
print("示範跳過:請下載 LoRA 權重到 /content/sample_lora.safetensors 後再執行")
print("LoRA 權重通常 3–30 MB,可從 CivitAI 或 Hugging Face Hub 下載")
# 輸出:示範跳過:請下載 LoRA 權重到 /content/sample_lora.safetensors 後再執行
這段示範如何載入別人訓練好的 LoRA 權重。pipe.unet = pipe.unet.unload() 先把 peft wrapper 拿掉(推論時不需要 peft 的凍結邏輯);pipe.load_lora_weights(lora_path) 把 LoRA 矩陣合併回 UNet 權重。實務上 LoRA 權重通常來自兩個來源:CivitAI(社群作品最多的地方,license 依各作者而定)或 Hugging Face Hub(品質較穩定但數量較少)。.safetensors 是安全的權重格式,避免 pickle 反序列化漏洞。
# 7. ControlNet + LoRA 組合:把 Canny 控制 + 風格 LoRA 同時用
# 假設已載入 LoRA:用同一張邊界圖,但 prompt 強調特定風格
prompt = "oil painting of a futuristic humanoid robot, impressionist style, soft brush strokes"
negative_prompt = "blurry, low quality, distorted, watermark, photo"
generator = torch.Generator(device="cuda").manual_seed(42)
img = pipe(
prompt=prompt,
negative_prompt=negative_prompt,
image=control_image,
num_inference_steps=30,
guidance_scale=7.5,
controlnet_conditioning_scale=0.5, # 稍降一點,讓風格 LoRA 影響更明顯
generator=generator,
).images[0]
img.save("/content/cn_lora_combo.png")
print("ControlNet + 風格 prompt 已存到 /content/cn_lora_combo.png")
# 輸出:ControlNet + 風格 prompt 已存到 /content/cn_lora_combo.png
這段是 ControlNet + LoRA 的組合應用。controlnet_conditioning_scale=0.5 比純 ControlNet 的 0.6 稍降,目的是讓風格 LoRA 的影響更明顯——這是「構圖靠 ControlNet、風格靠 LoRA」的標準分工。實務上 ControlNet 控制構圖(人物位置、物件邊界)、LoRA 控制風格(油畫、水彩、動漫),兩者各司其職、不互相干擾。如果兩者都給滿(例如 ControlNet scale=1.0 + 強風格 LoRA),容易出現「構圖僵硬 + 風格失真」的衝突。
常見錯誤與踩雷
錯誤一:忘記把 ControlNet 與 SD 1.5 的 dtype 對齊。如果 ControlNet 用 fp16、SD 1.5 用 fp32(或反過來),UNet 的 skip connection 會因為 dtype 不一致報 RuntimeError: mat1 and mat2 must have the same dtype。對應排查方向:兩個模型都用 torch_dtype=torch.float16 載入。
錯誤二:Canny 邊界圖沒 resize 到 512×512。SD 1.5 的標準輸入是 512×512,Canny 邊界圖也必須是這個尺寸。如果原圖是 640×426,必須先 Image.open(...).resize((512, 512))。對應排查方向:control_image.size == (512, 512) 一定要對。
錯誤三:LoRA 的 target_modules 寫成錯誤的名稱。SD 1.5 的 attention 層名稱是 to_q、to_k、to_v、to_out.0(不是 q_proj 或 query)。寫錯名稱時 peft 會靜默地「找不到層、不插入 LoRA」,訓練時只更新 base model 的參數量等於 0。對應排查方向:用 print(pipe.unet.named_modules()) 檢查實際的層名稱,或直接用 peft 內建的 target_modules=["to_q", "to_v"] 標準設定。
錯誤四:LoRA 訓練後忘了合併就做推論。LoRA 訓練後的權重是「凍結 base + 額外 A、B 矩陣」,必須呼叫 pipe.unet.merge_and_unload() 把 ΔW = B·A 合併回 W 才能做推論。如果忘記合併,會出現「完全沒套用 LoRA」的影像;推論速度也會比合併後慢約 5%。
錯誤五:ControlNet 條件圖用三通道 RGB 但內容全黑。Canny 邊界圖的「邊界像素是白、其餘是黑」,但很多人忘記轉成 RGB(OpenCV 預設是單通道灰階),結果 StableDiffusionControlNetPipeline 拿到三通道全黑影像,ControlNet 等於沒作用。對應排查方向:Image.open(...).convert("RGB") 一定要寫,不要直接用 OpenCV 的 ndarray。
效能與實務提醒
在 Colab T4 上跑 SD 1.5 + ControlNet Canny(fp16、DDIM 30 步)單張約 6.2 秒,VRAM 占用約 7.5 GB。如果想再加速,第一個手段是 torch.compile(pipe.unet, mode="reduce-overhead"),T4 上可以再加速 20–30%;第二個手段是用 pipe.enable_xformers_memory_efficient_attention()(需安裝 xformers)把 attention 切成小塊,VRAM 從 7.5 GB 降到 5.5 GB、batch 容量翻倍;第三個手段是把 num_inference_steps 從 30 降到 20、用 DPMSolverMultistepScheduler,時間從 6.2 秒壓到 4.0 秒。
LoRA 微調的成本:對 SD 1.5 UNet 用 r=8、target_modules=["to_q", "to_v"] 的 LoRA,24 GB A100 上 batch=4、resolution=512、lr=1e-4、AdamW8bit,約 1,500 步 × 4 epoch 約 2.5 小時就能完成一個中等品質的風格 LoRA。如果是 T4(16 GB)需要把 batch 降到 1、用 gradient checkpointing pipe.unet.enable_gradient_checkpointing(),訓練時間會拉到 8–12 小時。實務上訓練 LoRA 的成本主要在資料準備(要蒐集 50–200 張特定風格/主題的影像),訓練本身只要 2–4 小時。
部署階段,ControlNet + SD 1.5 的權重總計約 6.5 GB(SD 1.5 5 GB + ControlNet 1.5 GB);如果加一個 30 MB 的 LoRA,總計約 6.8 GB。最低部署需求:RTX 3060 12 GB(ControlNet + SD 1.5、batch=1)或 RTX 4090 24 GB(可同時載多個 ControlNet 做 ensemble)。MVTec AD 合成瑕疵任務(Day 39)會大量使用 ControlNet + LoRA 的組合——用 Canny 邊界控制瑕疵的「位置與形狀」、用 LoRA 控制「特定紋理」(刮痕、銅綠、裂縫)。這個搭配的優勢是「構圖可重現」+「紋理可學習」,是 2024 年 Stable Diffusion 社群最活躍的兩個擴充路線。
小結
今天用 diffusers 0.31 載入 ControlNet Canny、用 peft 0.11 演示 LoRA 介面,完整展示了 Stable Diffusion 的兩條 2024 年主流擴充路線。重點回顧:第一,ControlNet 用「零卷積」與「副網路」設計,把 SD 1.5 的行為保留為基準、只學額外的條件輸入;第二,controlnet_conditioning_scale=0.6 是平衡點、0.5–0.8 是常見甜蜜點;第三,LoRA 用「凍結 base + 訓練低秩矩陣」把 860M 參數的微調簡化到 4.7M,約佔 0.55%;第四,r=8 是中等容量、target_modules=["to_q", "to_v"] 是標準設定;第五,ControlNet + LoRA 的搭配可以分工——ControlNet 控制構圖、LoRA 控制風格。明天我們會從「生成式模型的評估」角度切入:FID、CLIP score、人工評估三種品質指標的定義、計算方式與常見陷阱,並用 MVTec AD 的合成瑕疵樣本做實戰評估。
結語
今天的重點是把 ControlNet 與 LoRA 兩個擴充都用起來。我們先從一張範例影像抽取 Canny 邊界圖、載入 ControlNet Canny 與 SD 1.5、用 StableDiffusionControlNetPipeline 生成符合構圖的影像;接著示範 controlnet_conditioning_scale 的影響;最後用 peft 把 SD 1.5 包成可 LoRA 微調的模型、展示 LoraConfig 與 get_peft_model 的介面。讀完這篇你應該能回答:ControlNet 的零卷積設計為什麼重要?Canny、Depth、Pose 三種 ControlNet 條件各自適合什麼任務?LoRA 為什麼能把 860M 參數的微調簡化到 4.7M?rank r 大小對品質與容量的影響?明天,我們會把「生成」轉到「評估」——FID(Fréchet Inception Distance)、CLIP score、人工評估三種品質指標的定義、計算方式與常見陷阱,並用 MVTec AD 的合成瑕疵樣本做實戰評估,這是生成模型進入工業應用前最關鍵的品管環節。
延伸資源
- Zhang 等人,2023,Adding Conditional Control to Text-to-Image Diffusion Models(ICCV 2023 最佳論文):
https://arxiv.org/abs/2302.05543,ControlNet 原始論文,零卷積與零初始化設計動機。 - Hu 等人,2021,LoRA: Low-Rank Adaptation of Large Language Models(ICLR 2022):
https://arxiv.org/abs/2106.09685,LoRA 原始論文(雖然大語言模型,但數學概念完全適用於 Stable Diffusion)。 - lllyasviel ControlNet v11 model cards(2024):
https://huggingface.co/lllyasviel/control_v11p_sd15_canny,control_v11p_sd15_depth、control_v11p_sd15_openpose等 11 種條件類型的預訓練權重。 - diffusers ControlNet 推論教學(v0.31,2024):
https://huggingface.co/docs/diffusers/using-diffusers/controlnet,StableDiffusionControlNetPipeline、controlnet_conditioning_scale、多 ControlNet 組合的官方範例。 - Hugging Face PEFT 官方文件(v0.11,2024):
https://huggingface.co/docs/peft/index,LoraConfig、get_peft_model、load_lora_weights的官方 API。 - stable-diffusion-v1-5/stable-diffusion-v1-5 LoRA 生態(2024):
https://huggingface.co/stabilityai,Stability AI 官方釋出的 SD 1.5 LoRA 範例,示範風格與主題 LoRA 的訓練流程。
留言
張貼留言