Diffusers 文件

載入Diffusion管道

Hugging Face's logo
加入 Hugging Face 社群

並獲得增強的文件體驗

開始使用

載入Diffusion管道

擴散系統由引數化模型和排程器等多個元件組成,它們以複雜的方式相互作用。因此,我們設計了DiffusionPipeline來將整個擴散系統的複雜性封裝到一個易於使用的API中。同時,DiffusionPipeline是完全可定製的,因此您可以修改每個元件,為您的用例構建一個擴散系統。

本指南將向您展示如何載入

  • 來自Hub和本地的管道
  • 將不同的元件整合到管道中
  • 在不增加記憶體使用量的情況下載入多個管道
  • 檢查點變體,例如不同的浮點型別或非指數平均(EMA)權重

載入管道

如果您對DiffusionPipeline類的工作原理感興趣,請跳到DiffusionPipeline解釋部分。

有兩種方法可以為任務載入管道

  1. 載入通用DiffusionPipeline類,並允許它自動從檢查點檢測正確的管道類。
  2. 為特定任務載入特定的管道類。
通用管道
特定管道

DiffusionPipeline類是一種簡單通用的方式,可以從Hub載入最新的流行擴散模型。它使用from_pretrained()方法自動從檢查點檢測任務的正確管道類,下載並快取所有必需的配置和權重檔案,並返回一個可用於推理的管道。

from diffusers import DiffusionPipeline

pipeline = DiffusionPipeline.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5", use_safetensors=True)

這個相同的檢查點也可以用於影像到影像任務。DiffusionPipeline類可以處理任何任務,只要您提供適當的輸入。例如,對於影像到影像任務,您需要將初始影像傳遞給管道。

from diffusers import DiffusionPipeline

pipeline = DiffusionPipeline.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5", use_safetensors=True)

init_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/img2img-init.png")
prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k"
image = pipeline("Astronaut in a jungle, cold color palette, muted colors, detailed, 8k", image=init_image).images[0]

使用下面的空間在下載和載入管道之前評估其記憶體需求,以檢視它是否能在您的硬體上執行。

指定元件特定資料型別

您可以透過向`torch_dtype`引數傳遞字典來自定義單個子模型的資料型別。這允許您以不同的浮點精度載入管道的不同元件。例如,如果您想以`torch.bfloat16`載入transformer,並以`torch.float16`載入所有其他元件,您可以傳遞一個對映字典

from diffusers import HunyuanVideoPipeline
import torch

pipe = HunyuanVideoPipeline.from_pretrained(
    "hunyuanvideo-community/HunyuanVideo",
    torch_dtype={"transformer": torch.bfloat16, "default": torch.float16},
)
print(pipe.transformer.dtype, pipe.vae.dtype)  # (torch.bfloat16, torch.float16)

如果在字典中未明確指定元件且未提供`default`,它將以`torch.float32`載入。

本地管道

要在本地載入管道,請使用git-lfs手動將檢查點下載到本地磁碟。

git-lfs install
git clone https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5

這會在您的磁碟上建立一個名為`./stable-diffusion-v1-5`的本地資料夾,您應該將其路徑傳遞給from_pretrained()

from diffusers import DiffusionPipeline

stable_diffusion = DiffusionPipeline.from_pretrained("./stable-diffusion-v1-5", use_safetensors=True)

from_pretrained()方法在檢測到本地路徑時不會從Hub下載檔案,但這也意味著它不會下載和快取檢查點的最新更改。

自定義管道

您可以透過向管道載入不同的元件來自定義管道。這很重要,因為您可以

  • 根據您的需求更改為具有更快生成速度或更高生成質量的排程器(在您的管道上呼叫`scheduler.compatibles`方法以檢視相容的排程器)
  • 將預設管道元件更改為更新、效能更好的元件

例如,讓我們使用以下元件自定義預設的stabilityai/stable-diffusion-xl-base-1.0檢查點:

  • HeunDiscreteScheduler,以生成更高質量的影像,但代價是生成速度較慢。您必須在from_pretrained()中傳遞`subfolder="scheduler"`引數,以便將排程器配置載入到管道倉庫的正確子資料夾中。
  • 一個更穩定的VAE,以fp16執行。
from diffusers import StableDiffusionXLPipeline, HeunDiscreteScheduler, AutoencoderKL
import torch

scheduler = HeunDiscreteScheduler.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", subfolder="scheduler")
vae = AutoencoderKL.from_pretrained("madebyollin/sdxl-vae-fp16-fix", torch_dtype=torch.float16, use_safetensors=True)

現在將新的排程器和VAE傳遞給StableDiffusionXLPipeline

pipeline = StableDiffusionXLPipeline.from_pretrained(
  "stabilityai/stable-diffusion-xl-base-1.0",
  scheduler=scheduler,
  vae=vae,
  torch_dtype=torch.float16,
  variant="fp16",
  use_safetensors=True
).to("cuda")

複用管道

當您載入多個共享相同模型元件的管道時,複用共享元件而不是再次將所有內容重新載入到記憶體中是合理的,尤其是當您的硬體記憶體受限時。例如:

  1. 您使用StableDiffusionPipeline生成了一張影像,但您想使用StableDiffusionSAGPipeline來提高其質量。這兩個管道共享相同的預訓練模型,因此載入兩次相同的模型會浪費記憶體。
  2. 您想向從現有StableDiffusionPipeline例項化的AnimateDiffPipeline新增一個模型元件,例如MotionAdapter。同樣,兩個管道共享相同的預訓練模型,因此再次載入一個全新的管道會浪費記憶體。

透過DiffusionPipeline.from_pipe() API,您可以在多個管道之間切換,利用它們的不同功能,而不會增加記憶體使用量。這類似於在管道中開啟和關閉某個功能。

要切換任務(而不是功能),請使用from_pipe()方法和AutoPipeline類,後者會根據任務自動識別管道類(在AutoPipeline教程中瞭解更多資訊)。

讓我們從一個StableDiffusionPipeline開始,然後重用已載入的模型元件來建立一個StableDiffusionSAGPipeline以提高生成質量。您將使用帶有IP-AdapterStableDiffusionPipeline來生成一隻吃披薩的熊。

from diffusers import DiffusionPipeline, StableDiffusionSAGPipeline
import torch
import gc
from diffusers.utils import load_image
from accelerate.utils import compute_module_sizes

image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/load_neg_embed.png")

pipe_sd = DiffusionPipeline.from_pretrained("SG161222/Realistic_Vision_V6.0_B1_noVAE", torch_dtype=torch.float16)
pipe_sd.load_ip_adapter("h94/IP-Adapter", subfolder="models", weight_name="ip-adapter_sd15.bin")
pipe_sd.set_ip_adapter_scale(0.6)
pipe_sd.to("cuda")

generator = torch.Generator(device="cpu").manual_seed(33)
out_sd = pipe_sd(
    prompt="bear eats pizza",
    negative_prompt="wrong white balance, dark, sketches,worst quality,low quality",
    ip_adapter_image=image,
    num_inference_steps=50,
    generator=generator,
).images[0]
out_sd

供參考,您可以檢視此過程消耗了多少記憶體。

def bytes_to_giga_bytes(bytes):
    return bytes / 1024 / 1024 / 1024
print(f"Max memory allocated: {bytes_to_giga_bytes(torch.cuda.max_memory_allocated())} GB")
"Max memory allocated: 4.406213283538818 GB"

現在,使用from_pipe()方法將StableDiffusionPipeline中的相同管道元件重新用於StableDiffusionSAGPipeline

某些管道方法可能無法在透過from_pipe()建立的新管道上正常執行。例如,enable_model_cpu_offload()方法會根據每個管道獨特的解除安裝序列在模型元件上安裝鉤子。如果模型在新管道中以不同的順序執行,CPU解除安裝可能無法正常工作。

為確保一切按預期執行,我們建議在透過from_pipe()建立的新管道上重新應用管道方法。

pipe_sag = StableDiffusionSAGPipeline.from_pipe(
    pipe_sd
)

generator = torch.Generator(device="cpu").manual_seed(33)
out_sag = pipe_sag(
    prompt="bear eats pizza",
    negative_prompt="wrong white balance, dark, sketches,worst quality,low quality",
    ip_adapter_image=image,
    num_inference_steps=50,
    generator=generator,
    guidance_scale=1.0,
    sag_scale=0.75
).images[0]
out_sag

如果您檢查記憶體使用情況,您會發現它與之前保持相同,因為StableDiffusionPipelineStableDiffusionSAGPipeline共享相同的管道元件。這使您可以互換使用它們,而不會產生任何額外的記憶體開銷。

print(f"Max memory allocated: {bytes_to_giga_bytes(torch.cuda.max_memory_allocated())} GB")
"Max memory allocated: 4.406213283538818 GB"

讓我們用AnimateDiffPipeline來動畫化影像,並向管道新增一個`MotionAdapter`模組。對於AnimateDiffPipeline,您需要先解除安裝IP-Adapter,並在建立新管道後重新載入它(這僅適用於AnimateDiffPipeline)。

from diffusers import AnimateDiffPipeline, MotionAdapter, DDIMScheduler
from diffusers.utils import export_to_gif

pipe_sag.unload_ip_adapter()
adapter = MotionAdapter.from_pretrained("guoyww/animatediff-motion-adapter-v1-5-2", torch_dtype=torch.float16)

pipe_animate = AnimateDiffPipeline.from_pipe(pipe_sd, motion_adapter=adapter)
pipe_animate.scheduler = DDIMScheduler.from_config(pipe_animate.scheduler.config, beta_schedule="linear")
# load IP-Adapter and LoRA weights again
pipe_animate.load_ip_adapter("h94/IP-Adapter", subfolder="models", weight_name="ip-adapter_sd15.bin")
pipe_animate.load_lora_weights("guoyww/animatediff-motion-lora-zoom-out", adapter_name="zoom-out")
pipe_animate.to("cuda")

generator = torch.Generator(device="cpu").manual_seed(33)
pipe_animate.set_adapters("zoom-out", adapter_weights=0.75)
out = pipe_animate(
    prompt="bear eats pizza",
    num_frames=16,
    num_inference_steps=50,
    ip_adapter_image=image,
    generator=generator,
).frames[0]
export_to_gif(out, "out_animate.gif")

AnimateDiffPipeline更佔用記憶體,消耗15GB記憶體(有關這對您的記憶體使用意味著什麼,請參閱from_pipe的記憶體使用部分)。

print(f"Max memory allocated: {bytes_to_giga_bytes(torch.cuda.max_memory_allocated())} GB")
"Max memory allocated: 15.178664207458496 GB"

修改from_pipe元件

透過from_pipe()載入的管道可以使用不同的模型元件或方法進行定製。但是,每當您修改模型元件的*狀態*時,它都會影響所有共享相同元件的其他管道。例如,如果您在StableDiffusionSAGPipeline上呼叫unload_ip_adapter(),您將無法與StableDiffusionPipeline一起使用IP-Adapter,因為它已從其共享元件中刪除。

pipe.sag_unload_ip_adapter()

generator = torch.Generator(device="cpu").manual_seed(33)
out_sd = pipe_sd(
    prompt="bear eats pizza",
    negative_prompt="wrong white balance, dark, sketches,worst quality,low quality",
    ip_adapter_image=image,
    num_inference_steps=50,
    generator=generator,
).images[0]
"AttributeError: 'NoneType' object has no attribute 'image_projection_layers'"

from_pipe的記憶體使用

無論您建立多少個管道,使用from_pipe()載入多個管道所需的記憶體量取決於記憶體使用量最高的管道。

流水線 記憶體使用量 (GB)
StableDiffusionPipeline 4.400
StableDiffusionSAGPipeline 4.400
AnimateDiffPipeline 15.178

AnimateDiffPipeline具有最高的記憶體需求,因此*總記憶體使用量*僅基於AnimateDiffPipeline。如果您建立更多管道,只要它們的記憶體需求不超過AnimateDiffPipeline,您的記憶體使用量就不會增加。每個管道都可以互換使用,而不會有任何額外的記憶體開銷。

安全檢查器

Diffusers為Stable Diffusion模型實現了一個安全檢查器,該模型可以生成有害內容。安全檢查器會根據已知的硬編碼的 NSFW(不適合工作)內容篩選生成的輸出。如果出於某種原因您想停用安全檢查器,請將`safety_checker=None`傳遞給from_pretrained()方法。

from diffusers import DiffusionPipeline

pipeline = DiffusionPipeline.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5", safety_checker=None, use_safetensors=True)
"""
You have disabled the safety checker for <class 'diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline'> by passing `safety_checker=None`. Ensure that you abide by the conditions of the Stable Diffusion license and do not expose unfiltered results in services or applications open to the public. Both the diffusers team and Hugging Face strongly recommend keeping the safety filter enabled in all public-facing circumstances, disabling it only for use cases that involve analyzing network behavior or auditing its results. For more information, please have a look at https://github.com/huggingface/diffusers/pull/254 .
"""

檢查點變體

檢查點變體通常是指其權重為以下形式的檢查點:

  • 以不同的浮點型別儲存,例如torch.float16,因為它只需要一半的頻寬和儲存空間來下載。如果您正在繼續訓練或使用CPU,則不能使用此變體。
  • 非指數平均(EMA)權重不應用於推理。您應該使用此變體繼續微調模型。

當檢查點具有相同的模型結構,但它們在不同的資料集和不同的訓練設定上進行訓練時,它們應該儲存在單獨的倉庫中。例如,stabilityai/stable-diffusion-2stabilityai/stable-diffusion-2-1儲存在單獨的倉庫中。

否則,變體與原始檢查點**相同**。它們具有完全相同的序列化格式(如safetensors),模型結構,並且它們的權重具有相同的張量形狀。

檢查點型別 權重名稱 載入權重的引數
原始 diffusion_pytorch_model.safetensors
浮點 diffusion_pytorch_model.fp16.safetensors variant, torch_dtype
非EMA diffusion_pytorch_model.non_ema.safetensors variant

載入變體有兩個重要引數

  • torch_dtype指定載入檢查點的浮點精度。例如,如果您想透過載入fp16變體來節省頻寬,您應該設定variant="fp16"torch_dtype=torch.float16來將權重*轉換為*fp16。否則,fp16權重將轉換為預設的fp32精度。

    如果您只設置torch_dtype=torch.float16,則會首先下載預設的fp32權重,然後將其轉換為fp16。

  • variant指定應從倉庫載入哪些檔案。例如,如果您想從stable-diffusion-v1-5/stable-diffusion-v1-5載入UNet的非EMA變體,請設定variant="non_ema"以下載non_ema檔案。

fp16
非EMA
from diffusers import DiffusionPipeline
import torch

pipeline = DiffusionPipeline.from_pretrained(
    "stable-diffusion-v1-5/stable-diffusion-v1-5", variant="fp16", torch_dtype=torch.float16, use_safetensors=True
)

使用DiffusionPipeline.save_pretrained()方法中的`variant`引數,將檢查點儲存為不同的浮點型別或非EMA變體。您應該嘗試將變體儲存到與原始檢查點相同的資料夾中,這樣您就可以選擇從同一資料夾載入兩者。

fp16
non_ema
from diffusers import DiffusionPipeline

pipeline.save_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5", variant="fp16")

如果您沒有將變體儲存到現有資料夾,則必須指定`variant`引數,否則它將丟擲`Exception`,因為它找不到原始檢查點。

# 👎 this won't work
pipeline = DiffusionPipeline.from_pretrained(
    "./stable-diffusion-v1-5", torch_dtype=torch.float16, use_safetensors=True
)
# 👍 this works
pipeline = DiffusionPipeline.from_pretrained(
    "./stable-diffusion-v1-5", variant="fp16", torch_dtype=torch.float16, use_safetensors=True
)

DiffusionPipeline解釋

作為類方法,DiffusionPipeline.from_pretrained()負責兩件事

  • 下載推理所需的最新資料夾結構並快取它。如果最新資料夾結構在本地快取中可用,DiffusionPipeline.from_pretrained()會重用快取,而不會重新下載檔案。
  • 將快取的權重載入到正確的管道中(從`model_index.json`檔案檢索),並返回其例項。

管道的底層資料夾結構直接與其類例項對應。例如,StableDiffusionPipeline對應於stable-diffusion-v1-5/stable-diffusion-v1-5中的資料夾結構。

from diffusers import DiffusionPipeline

repo_id = "stable-diffusion-v1-5/stable-diffusion-v1-5"
pipeline = DiffusionPipeline.from_pretrained(repo_id, use_safetensors=True)
print(pipeline)

您將看到管道是StableDiffusionPipeline的一個例項,它由七個元件組成:

StableDiffusionPipeline {
  "feature_extractor": [
    "transformers",
    "CLIPImageProcessor"
  ],
  "safety_checker": [
    "stable_diffusion",
    "StableDiffusionSafetyChecker"
  ],
  "scheduler": [
    "diffusers",
    "PNDMScheduler"
  ],
  "text_encoder": [
    "transformers",
    "CLIPTextModel"
  ],
  "tokenizer": [
    "transformers",
    "CLIPTokenizer"
  ],
  "unet": [
    "diffusers",
    "UNet2DConditionModel"
  ],
  "vae": [
    "diffusers",
    "AutoencoderKL"
  ]
}

將管道例項的元件與stable-diffusion-v1-5/stable-diffusion-v1-5資料夾結構進行比較,您會發現倉庫中每個元件都有一個單獨的資料夾

.
├── feature_extractor
│   └── preprocessor_config.json
├── model_index.json
├── safety_checker
│   ├── config.json
|   ├── model.fp16.safetensors
│   ├── model.safetensors
│   ├── pytorch_model.bin
|   └── pytorch_model.fp16.bin
├── scheduler
│   └── scheduler_config.json
├── text_encoder
│   ├── config.json
|   ├── model.fp16.safetensors
│   ├── model.safetensors
│   |── pytorch_model.bin
|   └── pytorch_model.fp16.bin
├── tokenizer
│   ├── merges.txt
│   ├── special_tokens_map.json
│   ├── tokenizer_config.json
│   └── vocab.json
├── unet
│   ├── config.json
│   ├── diffusion_pytorch_model.bin
|   |── diffusion_pytorch_model.fp16.bin
│   |── diffusion_pytorch_model.f16.safetensors
│   |── diffusion_pytorch_model.non_ema.bin
│   |── diffusion_pytorch_model.non_ema.safetensors
│   └── diffusion_pytorch_model.safetensors
|── vae
.   ├── config.json
.   ├── diffusion_pytorch_model.bin
    ├── diffusion_pytorch_model.fp16.bin
    ├── diffusion_pytorch_model.fp16.safetensors
    └── diffusion_pytorch_model.safetensors

您可以將管道的每個元件作為屬性訪問以檢視其配置

pipeline.tokenizer
CLIPTokenizer(
    name_or_path="/root/.cache/huggingface/hub/models--runwayml--stable-diffusion-v1-5/snapshots/39593d5650112b4cc580433f6b0435385882d819/tokenizer",
    vocab_size=49408,
    model_max_length=77,
    is_fast=False,
    padding_side="right",
    truncation_side="right",
    special_tokens={
        "bos_token": AddedToken("<|startoftext|>", rstrip=False, lstrip=False, single_word=False, normalized=True),
        "eos_token": AddedToken("<|endoftext|>", rstrip=False, lstrip=False, single_word=False, normalized=True),
        "unk_token": AddedToken("<|endoftext|>", rstrip=False, lstrip=False, single_word=False, normalized=True),
        "pad_token": "<|endoftext|>",
    },
    clean_up_tokenization_spaces=True
)

每個管道都期望有一個model_index.json檔案,該檔案會告訴DiffusionPipeline

  • 從`_class_name`載入哪個管道類
  • 在`_diffusers_version`中建立模型時使用了哪個版本的🧨 Diffusers
  • 哪些庫的哪些元件儲存在子資料夾中(`name`對應於元件和子資料夾名稱,`library`對應於載入類的庫名稱,`class`對應於類名稱)
{
  "_class_name": "StableDiffusionPipeline",
  "_diffusers_version": "0.6.0",
  "feature_extractor": [
    "transformers",
    "CLIPImageProcessor"
  ],
  "safety_checker": [
    "stable_diffusion",
    "StableDiffusionSafetyChecker"
  ],
  "scheduler": [
    "diffusers",
    "PNDMScheduler"
  ],
  "text_encoder": [
    "transformers",
    "CLIPTextModel"
  ],
  "tokenizer": [
    "transformers",
    "CLIPTokenizer"
  ],
  "unet": [
    "diffusers",
    "UNet2DConditionModel"
  ],
  "vae": [
    "diffusers",
    "AutoencoderKL"
  ]
}
< > 在 GitHub 上更新

© . This site is unofficial and not affiliated with Hugging Face, Inc.