Diffusers 文件
Kandinsky
並獲得增強的文件體驗
開始使用
Kandinsky
Kandinsky 模型是一系列多語言文字到影像生成模型。Kandinsky 2.0 模型使用兩個多語言文字編碼器,並將它們的結果連線起來輸入到 UNet 中。
Kandinsky 2.1 改變了架構,引入了一個影像先驗模型(CLIP
)來生成文字和影像嵌入之間的對映。這種對映提供了更好的文字-影像對齊,並在訓練過程中與文字嵌入一起使用,從而產生更高質量的結果。最後,Kandinsky 2.1 使用了一個 調製量化向量(MoVQ) 解碼器——它增加了一個空間條件歸一化層來增強照片真實感——將潛在向量解碼成影像。
Kandinsky 2.2 在前一個模型的基礎上進行了改進,用一個更大的 CLIP-ViT-G 模型替換了影像先驗模型的影像編碼器,以提高質量。影像先驗模型還在不同解析度和長寬比的影像上進行了重新訓練,以生成更高解析度和不同尺寸的影像。
Kandinsky 3 簡化了架構,並放棄了涉及先驗模型和擴散模型的兩階段生成過程。取而代之的是,Kandinsky 3 使用 Flan-UL2 來編碼文字,一個帶有 BigGan-deep 塊的 UNet,以及 Sber-MoVQGAN 來將潛在向量解碼成影像。文字理解和生成影像的質量主要透過使用更大的文字編碼器和 UNet 來實現。
本指南將向您展示如何使用 Kandinsky 模型進行文字到影像、影像到影像、影像修復、插值等操作。
開始之前,請確保已安裝以下庫:
# uncomment to install the necessary libraries in Colab
#!pip install -q diffusers transformers accelerate
Kandinsky 2.1 和 2.2 的用法非常相似!唯一的區別是 Kandinsky 2.2 在解碼潛在向量時不再接受 prompt
作為輸入。相反,Kandinsky 2.2 在解碼時只接受 image_embeds
。
Kandinsky 3 的架構更簡潔,並且不需要先驗模型。這意味著它的用法與其他擴散模型(如 Stable Diffusion XL)完全相同。
文字到影像
要使用 Kandinsky 模型執行任何任務,您總是首先需要設定先驗 pipeline 來編碼提示詞並生成影像嵌入。先驗 pipeline 還會生成與負面提示詞 ""
對應的 negative_image_embeds
。為了獲得更好的結果,您可以向先驗 pipeline 傳遞一個實際的 negative_prompt
,但這會將先驗 pipeline 的有效批次大小增加一倍。
from diffusers import KandinskyPriorPipeline, KandinskyPipeline
import torch
prior_pipeline = KandinskyPriorPipeline.from_pretrained("kandinsky-community/kandinsky-2-1-prior", torch_dtype=torch.float16).to("cuda")
pipeline = KandinskyPipeline.from_pretrained("kandinsky-community/kandinsky-2-1", torch_dtype=torch.float16).to("cuda")
prompt = "A alien cheeseburger creature eating itself, claymation, cinematic, moody lighting"
negative_prompt = "low quality, bad quality" # optional to include a negative prompt, but results are usually better
image_embeds, negative_image_embeds = prior_pipeline(prompt, negative_prompt, guidance_scale=1.0).to_tuple()
現在,將所有提示詞和嵌入傳遞給 KandinskyPipeline 來生成一張影像
image = pipeline(prompt, image_embeds=image_embeds, negative_prompt=negative_prompt, negative_image_embeds=negative_image_embeds, height=768, width=768).images[0]
image

🤗 Diffusers 還透過 KandinskyCombinedPipeline 和 KandinskyV22CombinedPipeline 提供了端到端的 API,這意味著您不必分別載入先驗 pipeline 和文字到影像 pipeline。組合 pipeline 會自動載入先驗模型和解碼器。如果您願意,仍然可以使用 prior_guidance_scale
和 prior_num_inference_steps
引數為先驗 pipeline 設定不同的值。
使用 AutoPipelineForText2Image 在底層自動呼叫組合 pipeline
from diffusers import AutoPipelineForText2Image
import torch
pipeline = AutoPipelineForText2Image.from_pretrained("kandinsky-community/kandinsky-2-1", torch_dtype=torch.float16)
pipeline.enable_model_cpu_offload()
prompt = "A alien cheeseburger creature eating itself, claymation, cinematic, moody lighting"
negative_prompt = "low quality, bad quality"
image = pipeline(prompt=prompt, negative_prompt=negative_prompt, prior_guidance_scale=1.0, guidance_scale=4.0, height=768, width=768).images[0]
image
影像到影像
對於影像到影像,將初始影像和文字提示詞傳遞給 pipeline 以對影像進行條件化。首先載入先驗 pipeline
import torch
from diffusers import KandinskyImg2ImgPipeline, KandinskyPriorPipeline
prior_pipeline = KandinskyPriorPipeline.from_pretrained("kandinsky-community/kandinsky-2-1-prior", torch_dtype=torch.float16, use_safetensors=True).to("cuda")
pipeline = KandinskyImg2ImgPipeline.from_pretrained("kandinsky-community/kandinsky-2-1", torch_dtype=torch.float16, use_safetensors=True).to("cuda")
下載一張用於條件化的影像
from diffusers.utils import load_image
# download image
url = "https://raw.githubusercontent.com/CompVis/stable-diffusion/main/assets/stable-samples/img2img/sketch-mountains-input.jpg"
original_image = load_image(url)
original_image = original_image.resize((768, 512))

使用先驗 pipeline 生成 image_embeds
和 negative_image_embeds
prompt = "A fantasy landscape, Cinematic lighting"
negative_prompt = "low quality, bad quality"
image_embeds, negative_image_embeds = prior_pipeline(prompt, negative_prompt).to_tuple()
現在,將原始影像以及所有提示詞和嵌入傳遞給 pipeline 來生成一張影像
from diffusers.utils import make_image_grid
image = pipeline(prompt, negative_prompt=negative_prompt, image=original_image, image_embeds=image_embeds, negative_image_embeds=negative_image_embeds, height=768, width=768, strength=0.3).images[0]
make_image_grid([original_image.resize((512, 512)), image.resize((512, 512))], rows=1, cols=2)

🤗 Diffusers 還透過 KandinskyImg2ImgCombinedPipeline 和 KandinskyV22Img2ImgCombinedPipeline 提供了端到端的 API,這意味著您不必分別載入先驗 pipeline 和影像到影像 pipeline。組合 pipeline 會自動載入先驗模型和解碼器。如果您願意,仍然可以使用 prior_guidance_scale
和 prior_num_inference_steps
引數為先驗 pipeline 設定不同的值。
使用 AutoPipelineForImage2Image 在底層自動呼叫組合 pipeline
from diffusers import AutoPipelineForImage2Image
from diffusers.utils import make_image_grid, load_image
import torch
pipeline = AutoPipelineForImage2Image.from_pretrained("kandinsky-community/kandinsky-2-1", torch_dtype=torch.float16, use_safetensors=True)
pipeline.enable_model_cpu_offload()
prompt = "A fantasy landscape, Cinematic lighting"
negative_prompt = "low quality, bad quality"
url = "https://raw.githubusercontent.com/CompVis/stable-diffusion/main/assets/stable-samples/img2img/sketch-mountains-input.jpg"
original_image = load_image(url)
original_image.thumbnail((768, 768))
image = pipeline(prompt=prompt, negative_prompt=negative_prompt, image=original_image, strength=0.3).images[0]
make_image_grid([original_image.resize((512, 512)), image.resize((512, 512))], rows=1, cols=2)
影像修復
⚠️ Kandinsky 模型現在使用 ⬜️ 白色畫素 來表示蒙版區域,而不是黑色畫素。如果您在生產環境中使用 KandinskyInpaintPipeline,您需要將蒙版更改為使用白色畫素
# For PIL input
import PIL.ImageOps
mask = PIL.ImageOps.invert(mask)
# For PyTorch and NumPy input
mask = 1 - mask
對於影像修復,您需要原始影像、原始影像中要替換區域的蒙版,以及一個描述要修復內容的文字提示詞。載入先驗 pipeline
from diffusers import KandinskyInpaintPipeline, KandinskyPriorPipeline
from diffusers.utils import load_image, make_image_grid
import torch
import numpy as np
from PIL import Image
prior_pipeline = KandinskyPriorPipeline.from_pretrained("kandinsky-community/kandinsky-2-1-prior", torch_dtype=torch.float16, use_safetensors=True).to("cuda")
pipeline = KandinskyInpaintPipeline.from_pretrained("kandinsky-community/kandinsky-2-1-inpaint", torch_dtype=torch.float16, use_safetensors=True).to("cuda")
載入一張初始影像並建立一個蒙版
init_image = load_image("https://huggingface.co/datasets/hf-internal-testing/diffusers-images/resolve/main/kandinsky/cat.png")
mask = np.zeros((768, 768), dtype=np.float32)
# mask area above cat's head
mask[:250, 250:-250] = 1
使用先驗 pipeline 生成嵌入
prompt = "a hat"
prior_output = prior_pipeline(prompt)
現在,將初始影像、蒙版以及提示詞和嵌入傳遞給 pipeline 來生成一張影像
output_image = pipeline(prompt, image=init_image, mask_image=mask, **prior_output, height=768, width=768, num_inference_steps=150).images[0]
mask = Image.fromarray((mask*255).astype('uint8'), 'L')
make_image_grid([init_image, mask, output_image], rows=1, cols=3)

您也可以使用端到端的 KandinskyInpaintCombinedPipeline 和 KandinskyV22InpaintCombinedPipeline 在底層一起呼叫先驗和解碼器 pipeline。為此,請使用 AutoPipelineForInpainting
import torch
import numpy as np
from PIL import Image
from diffusers import AutoPipelineForInpainting
from diffusers.utils import load_image, make_image_grid
pipe = AutoPipelineForInpainting.from_pretrained("kandinsky-community/kandinsky-2-1-inpaint", torch_dtype=torch.float16)
pipe.enable_model_cpu_offload()
init_image = load_image("https://huggingface.co/datasets/hf-internal-testing/diffusers-images/resolve/main/kandinsky/cat.png")
mask = np.zeros((768, 768), dtype=np.float32)
# mask area above cat's head
mask[:250, 250:-250] = 1
prompt = "a hat"
output_image = pipe(prompt=prompt, image=init_image, mask_image=mask).images[0]
mask = Image.fromarray((mask*255).astype('uint8'), 'L')
make_image_grid([init_image, mask, output_image], rows=1, cols=3)
插值
插值允許您探索影像和文字嵌入之間的潛在空間,這是一種很酷的方式來檢視先驗模型的一些中間輸出。載入先驗 pipeline 和您想要插值的兩張影像
from diffusers import KandinskyPriorPipeline, KandinskyPipeline
from diffusers.utils import load_image, make_image_grid
import torch
prior_pipeline = KandinskyPriorPipeline.from_pretrained("kandinsky-community/kandinsky-2-1-prior", torch_dtype=torch.float16, use_safetensors=True).to("cuda")
img_1 = load_image("https://huggingface.co/datasets/hf-internal-testing/diffusers-images/resolve/main/kandinsky/cat.png")
img_2 = load_image("https://huggingface.co/datasets/hf-internal-testing/diffusers-images/resolve/main/kandinsky/starry_night.jpeg")
make_image_grid([img_1.resize((512,512)), img_2.resize((512,512))], rows=1, cols=2)


指定要插值的文字或影像,併為每個文字或影像設定權重。嘗試不同的權重,看看它們如何影響插值結果!
images_texts = ["a cat", img_1, img_2]
weights = [0.3, 0.3, 0.4]
呼叫 interpolate
函式生成嵌入,然後將它們傳遞給 pipeline 來生成影像
# prompt can be left empty
prompt = ""
prior_out = prior_pipeline.interpolate(images_texts, weights)
pipeline = KandinskyPipeline.from_pretrained("kandinsky-community/kandinsky-2-1", torch_dtype=torch.float16, use_safetensors=True).to("cuda")
image = pipeline(prompt, **prior_out, height=768, width=768).images[0]
image

ControlNet
⚠️ ControlNet 僅支援 Kandinsky 2.2!
ControlNet 能夠使用額外的輸入(如深度圖或邊緣檢測)來對大型預訓練擴散模型進行條件化。例如,您可以使用深度圖來對 Kandinsky 2.2 進行條件化,使模型能夠理解並保留深度影像的結構。
讓我們載入一張影像並提取其深度圖
from diffusers.utils import load_image
img = load_image(
"https://huggingface.co/datasets/hf-internal-testing/diffusers-images/resolve/main/kandinskyv22/cat.png"
).resize((768, 768))
img

然後,您可以使用 🤗 Transformers 中的 depth-estimation
Pipeline 來處理影像並獲取深度圖
import torch
import numpy as np
from transformers import pipeline
def make_hint(image, depth_estimator):
image = depth_estimator(image)["depth"]
image = np.array(image)
image = image[:, :, None]
image = np.concatenate([image, image, image], axis=2)
detected_map = torch.from_numpy(image).float() / 255.0
hint = detected_map.permute(2, 0, 1)
return hint
depth_estimator = pipeline("depth-estimation")
hint = make_hint(img, depth_estimator).unsqueeze(0).half().to("cuda")
文字到影像
載入先驗 pipeline 和 KandinskyV22ControlnetPipeline
from diffusers import KandinskyV22PriorPipeline, KandinskyV22ControlnetPipeline
prior_pipeline = KandinskyV22PriorPipeline.from_pretrained(
"kandinsky-community/kandinsky-2-2-prior", torch_dtype=torch.float16, use_safetensors=True
).to("cuda")
pipeline = KandinskyV22ControlnetPipeline.from_pretrained(
"kandinsky-community/kandinsky-2-2-controlnet-depth", torch_dtype=torch.float16
).to("cuda")
根據提示詞和負面提示詞生成影像嵌入
prompt = "A robot, 4k photo"
negative_prior_prompt = "lowres, text, error, cropped, worst quality, low quality, jpeg artifacts, ugly, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, dehydrated, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, gross proportions, malformed limbs, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck, username, watermark, signature"
generator = torch.Generator(device="cuda").manual_seed(43)
image_emb, zero_image_emb = prior_pipeline(
prompt=prompt, negative_prompt=negative_prior_prompt, generator=generator
).to_tuple()
最後,將影像嵌入和深度影像傳遞給 KandinskyV22ControlnetPipeline 來生成一張影像
image = pipeline(image_embeds=image_emb, negative_image_embeds=zero_image_emb, hint=hint, num_inference_steps=50, generator=generator, height=768, width=768).images[0]
image

影像到影像
對於使用 ControlNet 的影像到影像任務,您需要使用
- KandinskyV22PriorEmb2EmbPipeline 根據文字提示詞和影像生成影像嵌入
- KandinskyV22ControlnetImg2ImgPipeline 根據初始影像和影像嵌入生成影像
使用 🤗 Transformers 中的 depth-estimation
Pipeline 處理並提取一張初始貓影像的深度圖
import torch
import numpy as np
from diffusers import KandinskyV22PriorEmb2EmbPipeline, KandinskyV22ControlnetImg2ImgPipeline
from diffusers.utils import load_image
from transformers import pipeline
img = load_image(
"https://huggingface.co/datasets/hf-internal-testing/diffusers-images/resolve/main/kandinskyv22/cat.png"
).resize((768, 768))
def make_hint(image, depth_estimator):
image = depth_estimator(image)["depth"]
image = np.array(image)
image = image[:, :, None]
image = np.concatenate([image, image, image], axis=2)
detected_map = torch.from_numpy(image).float() / 255.0
hint = detected_map.permute(2, 0, 1)
return hint
depth_estimator = pipeline("depth-estimation")
hint = make_hint(img, depth_estimator).unsqueeze(0).half().to("cuda")
載入先驗 pipeline 和 KandinskyV22ControlnetImg2ImgPipeline
prior_pipeline = KandinskyV22PriorEmb2EmbPipeline.from_pretrained(
"kandinsky-community/kandinsky-2-2-prior", torch_dtype=torch.float16, use_safetensors=True
).to("cuda")
pipeline = KandinskyV22ControlnetImg2ImgPipeline.from_pretrained(
"kandinsky-community/kandinsky-2-2-controlnet-depth", torch_dtype=torch.float16
).to("cuda")
將文字提示詞和初始影像傳遞給先驗 pipeline 以生成影像嵌入
prompt = "A robot, 4k photo"
negative_prior_prompt = "lowres, text, error, cropped, worst quality, low quality, jpeg artifacts, ugly, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, dehydrated, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, gross proportions, malformed limbs, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck, username, watermark, signature"
generator = torch.Generator(device="cuda").manual_seed(43)
img_emb = prior_pipeline(prompt=prompt, image=img, strength=0.85, generator=generator)
negative_emb = prior_pipeline(prompt=negative_prior_prompt, image=img, strength=1, generator=generator)
現在您可以執行 KandinskyV22ControlnetImg2ImgPipeline,根據初始影像和影像嵌入生成影像
image = pipeline(image=img, strength=0.5, image_embeds=img_emb.image_embeds, negative_image_embeds=negative_emb.image_embeds, hint=hint, num_inference_steps=50, generator=generator, height=768, width=768).images[0]
make_image_grid([img.resize((512, 512)), image.resize((512, 512))], rows=1, cols=2)

最佳化
Kandinsky 的獨特之處在於它需要一個先驗 pipeline 來生成對映,以及第二個 pipeline 來將潛在向量解碼成影像。最佳化工作應集中在第二個 pipeline 上,因為大部分計算都在這裡完成。以下是一些在推理期間改進 Kandinsky 的技巧。
- 如果您使用的是 PyTorch < 2.0,請啟用 xFormers
from diffusers import DiffusionPipeline
import torch
pipe = DiffusionPipeline.from_pretrained("kandinsky-community/kandinsky-2-1", torch_dtype=torch.float16)
+ pipe.enable_xformers_memory_efficient_attention()
- 如果您使用的是 PyTorch >= 2.0,請啟用
torch.compile
以自動使用縮放點積注意力(SDPA)
pipe.unet.to(memory_format=torch.channels_last)
+ pipe.unet = torch.compile(pipe.unet, mode="reduce-overhead", fullgraph=True)
這與顯式地將注意力處理器設定為使用 AttnAddedKVProcessor2_0 是一樣的
from diffusers.models.attention_processor import AttnAddedKVProcessor2_0
pipe.unet.set_attn_processor(AttnAddedKVProcessor2_0())
- 使用 enable_model_cpu_offload() 將模型解除安裝到 CPU,以避免記憶體不足錯誤
from diffusers import DiffusionPipeline
import torch
pipe = DiffusionPipeline.from_pretrained("kandinsky-community/kandinsky-2-1", torch_dtype=torch.float16)
+ pipe.enable_model_cpu_offload()
- 預設情況下,文字到影像 pipeline 使用 DDIMScheduler,但您可以將其替換為其他排程器,如 DDPMScheduler,以觀察其如何影響推理速度和影像質量之間的權衡
from diffusers import DDPMScheduler
from diffusers import DiffusionPipeline
scheduler = DDPMScheduler.from_pretrained("kandinsky-community/kandinsky-2-1", subfolder="ddpm_scheduler")
pipe = DiffusionPipeline.from_pretrained("kandinsky-community/kandinsky-2-1", scheduler=scheduler, torch_dtype=torch.float16, use_safetensors=True).to("cuda")