BotFlow

Entrance to AI

0%

HuggingFace Diffusers入门教程-快速导览

Text-to-Image(文生图)、Text-to-Video(文生视频)日新月异,翻阅资料时发现HuggingFace推出的基于Stable Diffusion的封装库diffusers(前文也有提到HuggingFace这个网站,可以认为是大模型时代的Github,几乎所有的开源大模型都放在这里)。考虑到其官方的中文文档更新并不及时,因此基于英文官方文档做了整理,文中可能穿插个人使用过程中的一些tips,如果大家有更多使用上的小技巧,也欢迎留言 :)

本文基于diffuser v0.25.0官方文档,后期如遇API不兼容请参考最新版接口。

扩散模型经过训练,可以逐步对随机高斯噪声进行去噪,以生成感兴趣的样本,例如图像或音频。这引发了人们对生成人工智能的巨大兴趣,并且您可能已经在互联网上看到了扩散生成图像的示例。Diffusers 是一个旨在让每个人都能广泛使用扩散模型的库。

无论您是开发人员还是日常用户,本文都会向您介绍 Diffusers,并帮助您快速入门和生成!首先介绍三个主要组件:

  • DiffusionPipeline(扩散管道):基于预训练扩散模型快速生成样本的封装类
  • Model(模型):预训练模型架构和模块可用作创建扩散系统的构建块。
  • Scheduler(调度器):用于控制如何在训练中添加噪声以及如何在推理过程中生成去噪图像的算法。

本文将向您展示如何使用DiffusionPipeline进行推理,然后引导您了解如何组合模型和调度程序来复现 DiffusionPipeline 内发生的情况。

本文是Diffusers Intro Notebook的简化版本,可帮助您快速入门。如果您想了解更多关于 Diffusers 的目标、设计理念以及其核心 API 的其他详细信息,请查看笔记本!

在开始之前,请确保已安装所有必需的库:

1
pip install --upgrade diffusers accelerate transformers

扩散管道

DiffusionPipeline是使用预训练扩散系统进行推理的最简单方法。它是一个包含模型和调度程序的端到端系统。您可以使用开箱即用的DiffusionPipeline来执行许多任务。请查看下表,了解一些支持的任务,有关支持的任务的完整列表,请查看扩散器摘要表

任务 描述 管道
无条件图像生成 从高斯噪声生成图像 无条件图像生成
文本引导图像生成 根据文本提示生成图像 条件图像生成
文本引导的图像到图像翻译 根据文本提示调整图像 img2img
文本引导图像修复 给定图像、蒙版和文本提示,填充图像的蒙版部分 inpaint
文本引导深度图像翻译 调整由文本提示引导的图像部分,同时通过深度估计保留结构 depth2img

首先创建DiffusionPipeline的实例并指定您要下载的管道检查点(checkpoint,即模型参数文件)。您可以使用托管在Hugging Face Hub 上的任何模型。本文,您将加载stable-diffusion-v1-5文本到图像生成的检查点。

对于Stable Diffusion模型,请在运行模型之前先仔细阅读许可证。Diffusers 实现了safety_checker防止冒犯性或有害内容的功能,但该模型改进的图像生成功能仍然可以产生潜在的有害内容。

使用from_pretrained()方法加载模型:

1
2
3
from diffusers import DiffusionPipeline
# 这里会下载模型,由于模型一般比较大,默认下载目录为~/.cache/huggingface,可通过export HF_HOME=指定目录,最好写入~/.bashrc持久化
pipeline = DiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5", use_safetensors=True)

DiffusionPipeline下载并缓存所有model、tokenization、scheduling组件。在本例中不难发现,StableDiffusionPipeline由UNet2DConditionModel和PNDMScheduler等组成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
pipeline
# StableDiffusionPipeline {
# "_class_name": "StableDiffusionPipeline",
# "_diffusers_version": "0.21.4",
# ...,
# "scheduler": [
# "diffusers",
# "PNDMScheduler"
# ],
# ...,
# "unet": [
# "diffusers",
# "UNet2DConditionModel"
# ],
# "vae": [
# "diffusers",
# "AutoencoderKL"
# ]
# }

我们强烈建议在 GPU 上运行,因为该模型由大约 14 亿个参数组成。通过to("cuda")即可将生成器对象移至GPU:

1
pipeline.to("cuda")

现在,您可以在pipeline中输入文本提示生成图像。默认情况下,图像输出包装在一个PIL.Image对象中。

1
2
image = pipeline("An image of a squirrel in Picasso style").images[0]
image

保存图像

1
image.save("image_of_squirrel_painting.png")

本地管道

您还可以在本地使用管道。唯一的区别是您需要先下载权重:

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

然后将保存的权重加载到管道中:

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

现在,您可以像上一节中那样运行管道。

自定义调度程序

不同的调度器具有不同的去噪速度和质量权衡。找出最适合您的最佳方法就是尝试一下!Diffusers 的主要功能之一是允许您轻松地在调度程序之间切换。例如,要将默认的PNDMScheduler替换为EulerDiscreteScheduler,请使用from_config()方法加载它:

1
2
3
4
from diffusers import EulerDiscreteScheduler

pipeline = DiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5", use_safetensors=True)
pipeline.scheduler = EulerDiscreteScheduler.from_config(pipeline.scheduler.config)

尝试使用新的调度程序生成图像,看看您是否注意到差异!

在下一节中,您将仔细查看构成DiffusionPipeline的组件(模型和调度程序),并了解如何使用这些组件生成猫的图像。

模型

大多数模型采用噪声样本,并在每个时间步预测噪声残差。您可以混合搭配模型来创建其他扩散系统。模型是使用from_pretrained()方法启动的,该方法还会在本地缓存模型权重,因此下次加载模型时速度会更快。本文教程中,您将加载 UNet2DModel,这是一个基本的无条件图像生成模型,带有在猫图像上训练的检查点:

1
2
3
4
from diffusers import UNet2DModel

repo_id = "google/ddpm-cat-256"
model = UNet2DModel.from_pretrained(repo_id, use_safetensors=True)

要访问模型参数,请调用model.config

1
model.config

模型配置是一个🧊冻结🧊字典,这意味着这些参数在模型创建后无法更改。这是有意为之,并确保一开始用于定义模型架构的参数保持不变,而其他参数仍然可以在推理过程中进行调整。
一些最重要的参数是:

  • sample_size:输入样本的高度和宽度尺寸。
  • in_channels:输入样本的输入通道数。
  • down_block_typesup_block_types:用于创建 UNet 架构的下采样和上采样模块的类型。
  • block_out_channels:下采样块的输出通道数;也以相反的顺序用于上采样块的输入通道的数量。
  • layers_per_block:每个 UNet 块中存在的 ResNet 块的数量。

如需使用推理,首先需要使用随机高斯噪声创建图像(在计算机视觉领域,图像往往通过一个复杂的多维张量表示,不同的维度代表不同的含义),具体来说,这里张量的shape是batch * channel * width * height。

  • batch:一个批次想生成的图片张数
  • channel:一般为3,RGB色彩空间
  • width: 图像宽
  • height: 图像高
1
2
3
4
5
import torch

torch.manual_seed(0)
noisy_sample = torch.randn(1, model.config.in_channels, model.config.sample_size, model.config.sample_size)
noisy_sample.shape

对于推理,将噪声图像(noisy_sample)和时间步长(timestep)传递给模型。时间步长表示输入图像的噪声程度,开始时噪声多,结束时噪声少。这有助于模型确定其在扩散过程中的位置,是更接近起点还是更接近终点。使用样例方法得到模型输出:

1
2
with torch.no_grad():
noisy_residual = model(sample=noisy_sample, timestep=2).sample

不过,要生成实际示例,您需要一个调度程序来指导去噪过程。在下一节中,您将学习如何将模型与调度程序结合起来。

调度程序

给定模型输出,调度程序管理从噪声样本到噪声较小的样本 - 在本例中,它是noisy_residual.

Diffusers 是用于构建扩散系统的工具箱。虽然DiffusionPipeline是使用预构建扩散系统的便捷方法,但您也可以单独选择自己的模型和调度程序组件来构建自定义扩散系统。

在本文,您将使用其DDPMScheduler的from_config()

1
2
3
4
from diffusers import DDPMScheduler

scheduler = DDPMScheduler.from_pretrained(repo_id)
scheduler

💡 与模型不同,调度程序没有可训练的权重并且是无参数的!

一些最重要的参数是:

  • num_train_timesteps:去噪过程的长度,或者换句话说,将随机高斯噪声处理为数据样本所需的时间步数。
  • beta_schedule:用于推理和训练的噪声计划类型。
  • beta_startbeta_end:噪声表的开始和结束噪声值。

要预测噪声稍低的图像,需要传入:模型输出(noisy residual)、步长(timestep) 和 当前样本(noisy sample)。

1
2
less_noisy_sample = scheduler.step(model_output=noisy_residual, timestep=2, sample=noisy_sample).prev_sample
less_noisy_sample.shape

如果将less_noisy_sample作为输入,递归调用,将得到一个噪音更小、质量更好的图像!现在让我们将所有内容放在一起并可视化整个去噪过程。

首先,创建一个函数,对去噪图像进行后处理并将其显示为PIL.Image

1
2
3
4
5
6
7
8
9
10
11
import PIL.Image
import numpy as np

def display_sample(sample, i):
image_processed = sample.cpu().permute(0, 2, 3, 1)
image_processed = (image_processed + 1.0) * 127.5
image_processed = image_processed.numpy().astype(np.uint8)

image_pil = PIL.Image.fromarray(image_processed[0])
display(f"Image at step {i}")
display(image_pil)

为了加速去噪过程,请将输入和模型移至 GPU:

1
2
model.to("cuda")
noisy_sample = noisy_sample.to("cuda")

现在创建一个去噪循环来预测噪声较小的样本的残差,并使用调度程序计算噪声较小的样本:

1
2
3
4
5
6
7
8
9
10
11
12
import tqdm

sample = noisy_sample
for i, t in enumerate(tqdm.tqdm(scheduler.timesteps)):
# 1. predict noise residual
with torch.no_grad():
residual = model(sample, t).sample
# 2. compute less noisy image and set x_t -> x_t-1
sample = scheduler.step(residual, t, sample).prev_sample
# 3. optionally look at image
if (i + 1) % 50 == 0:
display_sample(sample, i + 1)

坐下来看看猫是从噪音中诞生的!😻

希望您在本文教程中使用 扩散器生成了一些很酷的图像!对于后续步骤,您可以:

  • 训练教程中训练或微调模型以生成您自己的图像。
  • 请参阅各种用例的官方和社区训练或微调脚本示例
  • 在使用不同的调度程序指南中了解有关加载、访问、更改和比较调度程序的更多信息。
  • 使用Stable Diffusion指南探索即时工程、速度和内存优化以及生成更高质量图像的提示词和技巧。