自從 Stable Diffusion 風靡全球以來,人們一直在尋求如何更好地控制生成過程的方法。ControlNet 提供了一個簡單的遷移學習方法,能夠允許使用者在很大程度上自定義生成過程。透過 ControlNet,使用者可以輕鬆地使用多種空間語義條件資訊 (例如深度圖、分割圖、塗鴉圖、關鍵點等) 來控制生成過程。
具體來說,我們可以:
將卡通繪圖轉化為逼真的照片,同時保持極佳的佈局連貫性。
寫實版的 Lofi Girl (上: 原圖,下: 新圖)
進行室內設計。
原圖 | 新圖 |
---|---|
將塗鴉草圖變成藝術作品。
原圖 | 新圖 |
---|---|
甚至擬人化著名的 logo 形象。
原圖 | 新圖 |
---|---|
ControlNet,使一切皆有可能 ?
本文的主要內容:
- 介紹
StableDiffusionControlNetPipeline
- 展示多種控制條件樣例
讓我們開啟控制之旅!
ControlNet 簡述
ControlNet 在 Adding Conditional Control to Text-to-Image Diffusion Models 一文中提被出,作者是 Lvmin Zhang 和 Maneesh Agrawala。它引入了一個框架,支援在擴散模型 (如 Stable Diffusion) 上附加額外的多種空間語義條件來控制生成過程。
訓練 ControlNet 包括以下步驟:
- 克隆擴散模型的預訓練引數 (文中稱為 可訓練副本, trainable copy。如 Stable Diffusion 的 latent UNet 部分),同時保留原本的預訓練引數 (文中稱為 鎖定副本, locked copy)。這樣可以實現: a) 讓鎖定副本保留從大型資料集中學到的豐富知識;b) 讓可訓練副本學習特定任務的知識。
- 可訓練副本和鎖定副本的引數透過 “零卷積” 層 (詳見 此處) 連線。“零卷積” 層是 ControlNet 框架的一部分,會在特定任務中最佳化引數。這是一種訓練技巧,可以在新任務條件訓練時保留已凍結模型已經學到的語義資訊。
訓練 ControlNet 的過程如圖所示:
ControlNet 訓練集中的其中一種樣例如下 (額外的控制條件是 Canny 邊緣圖):
原始圖片 | 應用控制條件 |
---|---|
同樣地,如果我們使用的額外控制條件是語義分割圖,那麼 ControlNet 訓練集的樣例就是這樣:
原始圖片 | 應用控制條件 |
---|---|
每對 ControlNet 施加一種額外的控制條件,都需要訓練一份新的可訓練副本引數。論文中提出了 8 種不同的控制條件,對應的控制模型在 Diffusers 中均已支援!
推理階段需要同時使用擴散模型的預訓練權重以及訓練過的 ControlNet 權重。如要使用 Stable Diffusion v1-5 以及其 ControlNet 權重推理,其引數量要比僅使用 Stable Diffusion v1-5 多大約 7 億個,因此推理 ControlNet 需要消耗更多的記憶體。
由於在訓練過程中擴散模型預訓練引數為鎖定副本,因此在使用不同的控制條件訓練時,只需要切換 ControlNet 可訓練副本的引數即可。這樣在一個應用程式中部署多個 ControlNet 權重就非常簡單了,本文會在後面詳細介紹。
StableDiffusionControlNetPipeline
在開始之前,我們要向社群貢獻者 Takuma Mori (@takuma104) 表示巨大的感謝。將 ControlNet 整合到 Diffusers 中,他功不可沒 ❤️。
類似 Diffusers 中的 其他 Pipeline,Diffusers 同樣為 ControlNet 提供了 StableDiffusionControlNetPipeline
供使用者使用。StableDiffusionControlNetPipeline
的核心是 controlnet
引數,它接收使用者指定的訓練過的 ControlNetModel
例項作為輸入,同時保持擴散模型的預訓練權重不變。
本文將介紹 StableDiffusionControlNetPipeline
的多個不同用例。首先要介紹的第一個 ControlNet 模型是 Canny 模型,這是目前最流行的 ControlNet 模型之一,您可能已經在網上見識過一些它生成的精美圖片。在閱讀到各個部分的程式碼時,也歡迎您使用此 Colab 筆記本 執行相關程式碼片段。
執行程式碼之前,首先確保我們已經安裝好所有必要的庫:
pip install diffusers==0.14.0 transformers xformers git+https://github.com/huggingface/accelerate.git
為處理不同 ControlNet 對應的多種控制條件,還需要安裝一些額外的依賴項:
- OpenCV
- controlnet-aux - ControlNet 預處理模型庫
pip install opencv-contrib-python
pip install controlnet_aux
我們將以著名的油畫作品《戴珍珠耳環的少女》為例,首先讓我們下載這張影像並檢視一下:
from diffusers.utils import load_image
image = load_image(
"https://hf.co/datasets/huggingface/documentation-images/resolve/main/diffusers/input_image_vermeer.png"
)
image
然後將影像輸入給 Canny 前處理器:
import cv2
from PIL import Image
import numpy as np
image = np.array(image)
low_threshold = 100
high_threshold = 200
image = cv2.Canny(image, low_threshold, high_threshold)
image = image[:, :, None]
image = np.concatenate([image, image, image], axis=2)
canny_image = Image.fromarray(image)
canny_image
如圖可見,Canny 本質上是邊緣檢測器:
接下來,我們載入 runwaylml/stable-diffusion-v1-5 和 Canny 邊緣 ControlNet 模型。設定引數 torch.dtype=torch.float16
可以指定模型以半精度模式載入,可實現記憶體高效和快速的推理。
from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
import torch
controlnet = ControlNetModel.from_pretrained("lllyasviel/sd-controlnet-canny", torch_dtype=torch.float16)
pipe = StableDiffusionControlNetPipeline.from_pretrained(
"runwayml/stable-diffusion-v1-5", controlnet=controlnet, torch_dtype=torch.float16
)
這裡我們不使用 Stable Diffusion 預設的 PNDMScheduler 排程器,而使用改進的 UniPCMultistepScheduler (目前最快的擴散模型排程器之一),可以極大地加快推理速度。經測試,在保證生成影像質量的同時,我們能將推理階段的取樣步數從 50 降到 20。更多關於排程器的資訊可以點選 此處 檢視。
from diffusers import UniPCMultistepScheduler
pipe.scheduler = UniPCMultistepScheduler.from_config(pipe.scheduler.config)
我們透過呼叫 enable_model_cpu_offload
函式來啟用智慧 CPU 解除安裝,而不是直接將 pipeline 載入到 GPU 上。
智慧 CPU 解除安裝是一種降低視訊記憶體佔用的方法。擴散模型 (如 Stable Diffusion) 的推理並不是執行一個單獨的模型,而是多個模型元件的序列推理。如在推理 ControlNet Stable Diffusion 時,需要首先執行 CLIP 文字編碼器,其次推理擴散模型 UNet 和 ControlNet,然後執行 VAE 解碼器,最後執行 safety checker (安全檢查器,主要用於稽核過濾違規影像)。而在擴散過程中大多陣列件僅執行一次,因此不需要一直佔用 GPU 記憶體。透過啟用智慧模型解除安裝,可以確保每個元件在不需要參與 GPU 計算時解除安裝到 CPU 上,從而顯著降低視訊記憶體佔用,並且不會顯著增加推理時間 (僅增加了模型在 GPU-CPU 之間的轉移時間)。
注意: 啟用 enable_model_cpu_offload
後,pipeline 會自動進行 GPU 記憶體管理,因此請不要再使用 .to("cuda")
手動將 pipeline 轉移到 GPU。
pipe.enable_model_cpu_offload()
最後,我們要充分利用 FlashAttention/xformers 進行注意力層加速。執行下列程式碼以實現加速,如果該程式碼沒有起作用,那麼您可能沒有正確安裝 xformers
庫,此時您可以跳過該程式碼。
pipe.enable_xformers_memory_efficient_attention()
基本條件準備就緒,現在來執行 ControlNet pipeline!
跟執行 Stable Diffusion image-to-image pipeline 相同的是,我們也使用了文字提示語來引導影像生成過程。不過有一些不同的是,ControlNet 允許施加更多種類的控制條件來控制影像生成過程,比如使用剛才我們建立的 Canny 邊緣圖就能更精確的控制生成影像的構圖。
讓我們來看一些有趣的,將 17 世紀的名作《戴珍珠耳環的少女》中的少女一角換為現代的名人會是什麼樣?使用 ControlNet 就能輕鬆做到,只需要在提示語中寫上他們的名字即可!
首先建立一個非常簡單的幫助函式來實現生成影像的網格視覺化。
def image_grid(imgs, rows, cols):
assert len(imgs) == rows * cols
w, h = imgs[0].size
grid = Image.new("RGB", size=(cols * w, rows * h))
grid_w, grid_h = grid.size
for i, img in enumerate(imgs):
grid.paste(img, box=(i % cols * w, i // cols * h))
return grid
然後輸入名字提示語,並設定隨機種子以便復現。
prompt = ", best quality, extremely detailed"
prompt = [t + prompt for t in ["Sandra Oh", "Kim Kardashian", "rihanna", "taylor swift"]] # 分別為: 吳珊卓、金·卡戴珊、蕾哈娜、泰勒·斯威夫特
generator = [torch.Generator(device="cpu").manual_seed(2) for i in range(len(prompt))]
最後執行 pipeline,並視覺化生成的影像!
output = pipe(
prompt,
canny_image,
negative_prompt=["monochrome, lowres, bad anatomy, worst quality, low quality"] * 4,
num_inference_steps=20,
generator=generator,
)
image_grid(output.images, 2, 2)
我們還能輕鬆地將 ControlNet 與微調結合使用!例如使用 DreamBooth 對模型進行微調,然後使用 ControlNet 增加控制資訊,將其渲染到不同的場景中。
本文將以我們最愛的土豆先生為例,來介紹怎樣結合使用 ControlNet 和 DreamBooth。
相較於上文,pipeline 中使用的 ControlNet 部分保持不變,但是不使用 Stable Diffusion 1.5,而是重新載入一個 土豆先生 模型 (使用 Dreambooth 微調的 Stable Diffusion 模型) ?。
雖然 ControlNet 沒變,但仍然需要重新載入 pipeline。
model_id = "sd-dreambooth-library/mr-potato-head"
pipe = StableDiffusionControlNetPipeline.from_pretrained(
model_id,
controlnet=controlnet,
torch_dtype=torch.float16,
)
pipe.scheduler = UniPCMultistepScheduler.from_config(pipe.scheduler.config)
pipe.enable_model_cpu_offload()
pipe.enable_xformers_memory_efficient_attention()
現在來讓土豆先生擺一個《戴珍珠耳環的少女》的姿勢吧!
generator = torch.manual_seed(2)
prompt = "a photo of sks mr potato head, best quality, extremely detailed"
output = pipe(
prompt,
canny_image,
negative_prompt="monochrome, lowres, bad anatomy, worst quality, low quality",
num_inference_steps=20,
generator=generator,
)
output.images[0]
看得出來土豆先生盡力了,這場景著實不太適合他,不過他仍然抓住了精髓?。
ControlNet 還有另一個獨特應用: 從影像提取人體姿態,用姿態資訊控制生成具有相同姿態的新影像。因此在下一個示例中,我們將使用 Open Pose ControlNet 來教超級英雄如何做瑜伽!
首先,我們需要收集一些瑜伽動作影像集:
urls = "yoga1.jpeg", "yoga2.jpeg", "yoga3.jpeg", "yoga4.jpeg"
imgs = [
load_image("https://huggingface.co/datasets/YiYiXu/controlnet-testing/resolve/main/" + url)
for url in urls
]
image_grid(imgs, 2, 2)
透過 controlnet_aux
提供的 OpenPose 前處理器,我們可以很方便地提取瑜伽姿態。
from controlnet_aux import OpenposeDetector
model = OpenposeDetector.from_pretrained("lllyasviel/ControlNet")
poses = [model(img) for img in imgs]
image_grid(poses, 2, 2)
瑜伽姿態提取完成後,我們接著建立一個 Open Pose ControlNet pipeline 來生成一些相同姿態的超級英雄影像。Let's go ?
controlnet = ControlNetModel.from_pretrained(
"fusing/stable-diffusion-v1-5-controlnet-openpose", torch_dtype=torch.float16
)
model_id = "runwayml/stable-diffusion-v1-5"
pipe = StableDiffusionControlNetPipeline.from_pretrained(
model_id,
controlnet=controlnet,
torch_dtype=torch.float16,
)
pipe.scheduler = UniPCMultistepScheduler.from_config(pipe.scheduler.config)
pipe.enable_model_cpu_offload()
超級英雄的瑜伽時間!
generator = [torch.Generator(device="cpu").manual_seed(2) for i in range(4)]
prompt = "super-hero character, best quality, extremely detailed"
output = pipe(
[prompt] * 4,
poses,
negative_prompt=["monochrome, lowres, bad anatomy, worst quality, low quality"] * 4,
generator=generator,
num_inference_steps=20,
)
image_grid(output.images, 2, 2)
透過以上示例,我們對 StableDiffusionControlNetPipeline
的多種用法有了直觀的認識,也學會了如何使用 Diffusers 玩轉 ControlNet。不過,還有一些 ControlNet 支援的其他型別的控制條件示例,由於篇幅原因本文不再展開,如想了解更多資訊,可以點選以下連結檢視相應的模型文件頁面:
- lllyasviel/sd-controlnet-depth
- lllyasviel/sd-controlnet-hed
- lllyasviel/sd-controlnet-normal
- lllyasviel/sd-controlnet-scribble
- lllyasviel/sd-controlnet-seg
- lllyasviel/sd-controlnet-openpose
- lllyasviel/sd-controlnet-mlsd
- lllyasviel/sd-controlnet-mlsd
我們非常歡迎您嘗試組合不同的控制元件來生成精美的影像,並在 Twitter 上與 @diffuserslib 分享您的作品。如果您還沒有執行上述程式碼段,這裡再次建議您檢視剛才提到的 Colab 筆記本,親自執行程式碼體驗示例的效果!
在上文中,我們介紹了加速生成過程、減少視訊記憶體佔用的一些技巧,它們包括: 快速排程器、智慧模型解除安裝、xformers
。如果結合使用這些技巧,單張影像的生成過程僅需要: V100 GPU 上約 3 秒的推理時間以及約 4 GB 的 VRAM 佔用;免費 GPU 服務 (如 Google Colab 的 T4) 上約 5 秒的推理時間。如果沒有實現這些技巧,同樣的生成過程可達 17 秒!現已整合至 Diffusers 工具箱,來使用 Diffusers 吧,它真的非常強力!?
結語
本文介紹了 StableDiffusionControlNetPipeline
的多個用例,非常有趣!我們也非常期待看到社群在此 pipeline 的基礎上能構建出什麼好玩的應用。如果您想了解更多 Diffusers 支援的關於控制模型的其他 pipeline 和技術細節,請檢視我們的 官方文件。
如果您想直接嘗試 ControlNet 的控制效果,我們也能滿足!只需點選以下 HuggingFace Spaces 即可嘗試控制生成影像:
原文連結: https://huggingface.co/blog/controlnet
作者: Sayak Paul、YiYi Xu、Patrick von Platen
譯者: SuSung-boy
審校、排版: zhongdongy (阿東)