模型文件和布局
扩散模型以各种文件类型保存,并以不同的布局组织。Diffusers 将模型权重作为 safetensors 文件存储在 Diffusers-多文件夹 布局中,并且还支持从 单文件 布局加载文件(如 safetensors 和 ckpt 文件),这在扩散生态系统中很常见。
每种布局都有其自身的优势和用例,本指南将向您展示如何加载不同的文件和布局,以及如何转换它们。
文件
PyTorch 模型权重通常使用 Python 的 pickle 实用程序保存为 ckpt 或 bin 文件。但是,pickle 不安全,pickle 文件可能包含可执行的恶意代码。考虑到模型共享的普及,此漏洞是一个严重的问题。为了解决此安全问题,开发了 Safetensors 库作为 pickle 的安全替代方案,该库将模型保存为 safetensors 文件。
safetensors
在 Safetensors 经过审核,非常安全,并成为默认格式 博文中了解有关设计决策以及为什么首选 safetensor 文件来保存和加载模型权重的更多信息。
Safetensors 是一种安全且快速的格式,用于安全存储和加载张量。Safetensors 将标头大小限制为限制某些类型的攻击,支持延迟加载(对于分布式设置很有用),并且通常具有更快的加载速度。
确保已安装 Safetensors 库。
!pip install safetensors
Safetensors 将权重存储在 safetensors 文件中。如果 safetensors 文件可用且已安装 Safetensors 库,则 Diffusers 默认加载 safetensors 文件。safetensors 文件可以以两种方式组织
- Diffusers-多文件夹布局:可能存在几个单独的 safetensors 文件,每个管道组件(文本编码器、UNet、VAE)一个,组织在子文件夹中(查看 runwayml/stable-diffusion-v1-5 存储库作为示例)
- 单文件布局:所有模型权重都可能保存在单个文件中(查看 WarriorMama777/OrangeMixs 存储库作为示例)
使用 from_pretrained() 方法加载存储在多个文件夹中的 safetensors 文件的模型。
from diffusers import DiffusionPipeline
pipeline = DiffusionPipeline.from_pretrained(
"runwayml/stable-diffusion-v1-5",
use_safetensors=True
)
LoRA 文件
LoRA 是一种轻量级适配器,训练速度快且易于训练,使其特别流行于以某种方式或风格生成图像。这些适配器通常存储在 safetensors 文件中,并在 civitai 等模型共享平台上广受欢迎。
使用 load_lora_weights() 方法将 LoRA 加载到基础模型中。
from diffusers import StableDiffusionXLPipeline
import torch
# base model
pipeline = StableDiffusionXLPipeline.from_pretrained(
"Lykon/dreamshaper-xl-1-0", torch_dtype=torch.float16, variant="fp16"
).to("cuda")
# download LoRA weights
!wget https://civitai.com/api/download/models/168776 -O blueprintify.safetensors
# load LoRA weights
pipeline.load_lora_weights(".", weight_name="blueprintify.safetensors")
prompt = "bl3uprint, a highly detailed blueprint of the empire state building, explaining how to build all parts, many txt, blueprint grid backdrop"
negative_prompt = "lowres, cropped, worst quality, low quality, normal quality, artifacts, signature, watermark, username, blurry, more than one bridge, bad architecture"
image = pipeline(
prompt=prompt,
negative_prompt=negative_prompt,
generator=torch.manual_seed(0),
).images[0]
image
ckpt
pickle 文件可能不安全,因为它们可能被利用来执行恶意代码。建议尽可能使用 safetensors 文件,或将权重转换为 safetensors 文件。
PyTorch 的 torch.save 函数使用 Python 的 pickle 实用程序来序列化和保存模型。这些文件保存为 ckpt 文件,其中包含整个模型的权重。
使用 from_single_file() 方法直接加载 ckpt 文件。
from diffusers import StableDiffusionPipeline
pipeline = StableDiffusionPipeline.from_single_file(
"https://huggingface.co/runwayml/stable-diffusion-v1-5/blob/main/v1-5-pruned.ckpt"
)
存储布局
模型文件有两种组织方式,一种是 Diffusers-多文件夹布局,另一种是单文件布局。Diffusers-多文件夹布局是默认布局,每个组件文件(文本编码器、UNet、VAE)都存储在单独的子文件夹中。Diffusers 还支持从所有组件捆绑在一起的单文件布局加载模型。
Diffusers-多文件夹
Diffusers-多文件夹布局是 Diffusers 的默认存储布局。每个组件(文本编码器、UNet、VAE)的权重都存储在单独的子文件夹中。权重可以存储为 safetensors 或 ckpt 文件。
要从 Diffusers-多文件夹布局加载,请使用 from_pretrained() 方法。
from diffusers import DiffusionPipeline
pipeline = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16,
variant="fp16",
use_safetensors=True,
).to("cuda")
使用 Diffusers-多文件夹布局的优势包括
单独或并行加载每个组件文件的速度更快。
减少内存使用量,因为您只需加载所需的组件。例如,SDXL Turbo、SDXL Lightning 和 Hyper-SD 等模型除了 UNet 之外,其他组件都相同。您可以使用 from_pipe() 方法重用它们的共享组件,而不会消耗任何额外内存(查看 重用管道 指南),并且仅加载 UNet。这样,您就不需要下载冗余组件并无谓地占用更多内存。
import torch from diffusers import StableDiffusionXLPipeline, UNet2DConditionModel, EulerDiscreteScheduler # download one model sdxl_pipeline = StableDiffusionXLPipeline.from_pretrained( "stabilityai/stable-diffusion-xl-base-1.0", torch_dtype=torch.float16, variant="fp16", use_safetensors=True, ).to("cuda") # switch UNet for another model unet = UNet2DConditionModel.from_pretrained( "stabilityai/sdxl-turbo", subfolder="unet", torch_dtype=torch.float16, variant="fp16", use_safetensors=True ) # reuse all the same components in new model except for the UNet turbo_pipeline = StableDiffusionXLPipeline.from_pipe( sdxl_pipeline, unet=unet, ).to("cuda") turbo_pipeline.scheduler = EulerDiscreteScheduler.from_config( turbo_pipeline.scheduler.config, timestep+spacing="trailing" ) image = turbo_pipeline( "an astronaut riding a unicorn on mars", num_inference_steps=1, guidance_scale=0.0, ).images[0] image
降低存储需求,因为如果某个组件(例如 SDXL 的 VAE)在多个模型中共享,您只需下载并存储其单个副本,而不是多次下载和存储它。对于 10 个 SDXL 模型,这可以节省约 3.5GB 的存储空间。对于 PixArt Sigma 等更新的模型,存储空间节省更大,其中仅 文本编码器 就约为 19GB!
灵活地用更新或更好的版本替换模型中的组件。
from diffusers import DiffusionPipeline, AutoencoderKL vae = AutoencoderKL.from_pretrained("madebyollin/sdxl-vae-fp16-fix", torch_dtype=torch.float16, use_safetensors=True) pipeline = DiffusionPipeline.from_pretrained( "stabilityai/stable-diffusion-xl-base-1.0", vae=vae, torch_dtype=torch.float16, variant="fp16", use_safetensors=True, ).to("cuda")
更清晰地了解模型组件的信息,这些信息存储在每个组件子文件夹中的 config.json 文件中。
单文件
单文件布局将所有模型权重存储在一个文件中。所有模型组件(文本编码器、UNet、VAE)的权重都保存在一起,而不是分别保存在子文件夹中。这可以是 safetensors 或 ckpt 文件。
要从单文件布局加载,请使用 from_single_file() 方法。
import torch
from diffusers import StableDiffusionXLPipeline
pipeline = StableDiffusionXLPipeline.from_single_file(
"https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/blob/main/sd_xl_base_1.0.safetensors",
torch_dtype=torch.float16,
variant="fp16",
use_safetensors=True,
).to("cuda")
使用单文件布局的好处包括
- 易于与诸如 ComfyUI 或 Automatic1111 等通常使用单文件布局的扩散接口兼容。
- 更容易管理(下载和共享)单个文件。
转换布局和文件
Diffusers 提供了许多脚本和方法来转换存储布局和文件格式,以便在整个扩散生态系统中获得更广泛的支持。
查看 diffusers/scripts 集合以查找适合您转换需求的脚本。
末尾附加有“to_diffusers
”的脚本表示它们将模型转换为 Diffusers 多文件夹布局。每个脚本都有自己的一组用于配置转换的特定参数,因此请确保检查可用的参数!
例如,要将存储在 Diffusers 多文件夹布局中的 Stable Diffusion XL 模型转换为单文件布局,请运行 convert_diffusers_to_original_sdxl.py 脚本。提供要转换的模型的路径和保存转换后的模型的路径。您可以选择指定是否要将模型保存为 safetensors 文件以及是否要以半精度保存模型。
python convert_diffusers_to_original_sdxl.py --model_path path/to/model/to/convert --checkpoint_path path/to/save/model/to --use_safetensors
您还可以使用 save_pretrained() 方法将模型保存到 Diffusers 多文件夹布局。如果目录尚不存在,它会为您创建一个目录,并且默认情况下它还会将文件保存为 safetensors 文件。
from diffusers import StableDiffusionXLPipeline
pipeline = StableDiffusionXLPipeline.from_single_file(
"https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/blob/main/sd_xl_base_1.0.safetensors",
)
pipeline.save_pretrained()
最后,还有一些 Spaces,例如 SD To Diffusers 和 SD-XL To Diffusers,它们为将模型转换为 Diffusers 多文件夹布局提供了更友好的用户界面。这是转换布局最简单、最方便的选择,它将在您的模型存储库中打开一个包含转换后文件的 PR。但是,此选项不如运行脚本可靠,并且对于更复杂的模型,Space 可能会失败。
单文件布局用法
现在您已经熟悉了 Diffusers 多文件夹布局和单文件布局之间的区别,本节将向您展示如何加载模型和管道组件、自定义加载的配置选项以及使用 from_single_file() 方法加载本地文件。
加载管道或模型
将管道或模型的文件路径传递给 from_single_file() 方法以加载它。
from diffusers import StableDiffusionXLPipeline
ckpt_path = "https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/blob/main/sd_xl_base_1.0_0.9vae.safetensors"
pipeline = StableDiffusionXLPipeline.from_single_file(ckpt_path)
通过将组件直接传递给 from_single_file() 方法来自定义管道中的组件。例如,您可以在管道中使用不同的调度器。
from diffusers import StableDiffusionXLPipeline, DDIMScheduler
ckpt_path = "https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/blob/main/sd_xl_base_1.0_0.9vae.safetensors"
scheduler = DDIMScheduler()
pipeline = StableDiffusionXLPipeline.from_single_file(ckpt_path, scheduler=scheduler)
或者您可以在管道中使用 ControlNet 模型。
from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
ckpt_path = "https://huggingface.co/runwayml/stable-diffusion-v1-5/blob/main/v1-5-pruned-emaonly.safetensors"
controlnet = ControlNetModel.from_pretrained("lllyasviel/control_v11p_sd15_canny")
pipeline = StableDiffusionControlNetPipeline.from_single_file(ckpt_path, controlnet=controlnet)
自定义配置选项
模型具有一个配置文件,用于定义其属性,例如 UNet 中的输入数量。管道配置选项可在管道的类中找到。例如,如果您查看 StableDiffusionXLInstructPix2PixPipeline 类,则可以使用 is_cosxl_edit
参数缩放图像潜伏变量。
这些配置文件可以在模型 Hub 存储库或配置文件来源的其他位置(例如,GitHub 存储库或您设备上的本地位置)中找到。
from_single_file() 方法会自动将检查点映射到相应的模型存储库,但在某些情况下,使用 config
参数很有用。例如,如果检查点中的模型组件与原始检查点不同,或者检查点没有正确确定要用于管道的配置的必要元数据。
from_single_file() 方法会自动从模型存储库中的配置文件确定要使用的配置。您还可以通过将存储库 ID 提供给 config
参数来显式指定要使用的配置。
from diffusers import StableDiffusionXLPipeline
ckpt_path = "https://huggingface.co/segmind/SSD-1B/blob/main/SSD-1B.safetensors"
repo_id = "segmind/SSD-1B"
pipeline = StableDiffusionXLPipeline.from_single_file(ckpt_path, config=repo_id)
虽然配置文件指定了管道或模型的默认参数,但您可以通过将参数直接提供给 from_single_file() 方法来覆盖它们。可以使用这种方式配置模型或管道类支持的任何参数。
例如,要在 StableDiffusionXLInstructPix2PixPipeline 中缩放图像潜伏变量,请传递 is_cosxl_edit
参数。
from diffusers import StableDiffusionXLInstructPix2PixPipeline
ckpt_path = "https://huggingface.co/stabilityai/cosxl/blob/main/cosxl_edit.safetensors"
pipeline = StableDiffusionXLInstructPix2PixPipeline.from_single_file(ckpt_path, config="diffusers/sdxl-instructpix2pix-768", is_cosxl_edit=True)
本地文件
在 Diffusers>=v0.28.0 中,from_single_file() 方法尝试通过从检查点文件中的键推断模型类型来配置管道或模型。推断出的模型类型用于确定 Hugging Face Hub 上用于配置模型或管道的相应模型存储库。
例如,任何基于 Stable Diffusion XL 基础模型的单个文件检查点都将使用 stabilityai/stable-diffusion-xl-base-1.0 模型仓库来配置管道。
但是,如果您在网络访问受限的环境中工作,则应使用 snapshot_download 函数下载配置文件,并使用 hf_hub_download 函数下载模型检查点。默认情况下,这些文件将下载到 Hugging Face Hub 的 缓存目录,但您可以使用 local_dir
参数指定首选的下载目录。
将配置和检查点路径传递给 from_single_file() 方法以加载本地文件。
from huggingface_hub import hf_hub_download, snapshot_download
my_local_checkpoint_path = hf_hub_download(
repo_id="segmind/SSD-1B",
filename="SSD-1B.safetensors"
)
my_local_config_path = snapshot_download(
repo_id="segmind/SSD-1B",
allow_patterns=["*.json", "**/*.json", "*.txt", "**/*.txt"]
)
pipeline = StableDiffusionXLPipeline.from_single_file(my_local_checkpoint_path, config=my_local_config_path, local_files_only=True)
本地文件(无符号链接)
在 huggingface_hub>=v0.23.0 中,hf_hub_download 和 snapshot_download 函数不再需要 local_dir_use_symlinks
参数。
from_single_file() 方法依赖于 huggingface_hub 的缓存机制来获取和存储模型和管道的检查点和配置文件。如果您使用的是不支持符号链接的文件系统,则应首先将检查点文件下载到本地目录,并在 hf_hub_download 函数和 snapshot_download 函数中使用 local_dir_use_symlink=False
参数禁用符号链接。
from huggingface_hub import hf_hub_download, snapshot_download
my_local_checkpoint_path = hf_hub_download(
repo_id="segmind/SSD-1B",
filename="SSD-1B.safetensors"
local_dir="my_local_checkpoints",
local_dir_use_symlinks=False
)
print("My local checkpoint: ", my_local_checkpoint_path)
my_local_config_path = snapshot_download(
repo_id="segmind/SSD-1B",
allow_patterns=["*.json", "**/*.json", "*.txt", "**/*.txt"]
local_dir_use_symlinks=False,
)
print("My local config: ", my_local_config_path)
然后,您可以将本地路径传递给 pretrained_model_link_or_path
和 config
参数。
pipeline = StableDiffusionXLPipeline.from_single_file(my_local_checkpoint_path, config=my_local_config_path, local_files_only=True)