扩散模型实战(十一):剖析Stable Diffusion Pipeline各个组件

推荐阅读列表:

扩散模型实战(一):基本原理介绍

扩散模型实战(二):扩散模型的发展

扩散模型实战(三):扩散模型的应用

扩散模型实战(四):从零构建扩散模型

扩散模型实战(五):采样过程

扩散模型实战(六):Diffusers DDPM初探

扩散模型实战(七):Diffusers蝴蝶图像生成实战

扩散模型实战(八):微调扩散模型

扩散模型实战(九):使用CLIP模型引导和控制扩散模型

扩散模型实战(十):Stable Diffusion文本条件生成图像大模型

扩散模型实战(十):Stable Diffusion文本条件生成图像大模型中介绍了如何使用Stable Diffusion Pipeline控制图片生成效果以及大小,本文让我们剖析一下Stable Diffusion Pipeline内容细节。

Stable Diffusion Pipeline要比之前介绍的DDPMPipeline复杂一些,除了UNet和调度器还有其他组件,让我们来看一下具体包括的组件:

复制代码
from diffusers import (    StableDiffusionPipeline,     StableDiffusionImg2ImgPipeline,    StableDiffusionInpaintPipeline,     StableDiffusionDepth2ImgPipeline    )

# 载入管线model_id = "stabilityai/stable-diffusion-2-1-base"pipe = StableDiffusionPipeline.from_pretrained(model_id).to(device)pipe = StableDiffusionPipeline.from_pretrained(model_id,    revision="fp16",torch_dtype=torch.float16).to(device)

# 查看pipe的组件print(list(pipe.components.keys()))

# 输出组件['vae','text_encoder','tokenizer','unet','scheduler',  'safety_checker','feature_extractor']

这些组件的概念之前都介绍过了,下面通过代码分析一下这些组件的细节:

一、可变分自编码器(VAE)

可变分自编码器(VAE)是一种模型,模型结构如图所示:

在使用Stable Diffusion生成图片时,首先需要在VAE的"隐空间"中应用扩散过程以生成隐编码,然后在扩散之后对他们解码,得到最终的输出图片,实际上,UNet的输入不是完整的图片,而是经过VAE压缩后的特征,这样可以极大地减少计算资源,代码如下:

复制代码
# 创建取值区间为(-1, 1)的伪数据images = torch.rand(1, 3, 512, 512).to(device) * 2 - 1 print("Input images shape:", images.shape) # 编码到隐空间with torch.no_grad():    latents = 0.18215 * pipe.vae.encode(images).latent_dist.meanprint("Encoded latents shape:", latents.shape) # 再解码回来with torch.no_grad():    decoded_images = pipe.vae.decode(latents / 0.18215).sampleprint("Decoded images shape:", decoded_images.shape)

# 输出Input images shape: torch.Size([1, 3, 512, 512])Encoded latents shape: torch.Size([1, 4, 64, 64])Decoded images shape: torch.Size([1, 3, 512, 512])

在这个示例中,原本512X512像素的图片被压缩成64X64的隐式表示,图片的每个空间维度都被压缩至原来的八分之一,因此设定参数width和height时,需要将它们设置成8的倍数。

PS:VAE解码过程并不完美,图像质量有所损失,但在实际使用中已经足够好了。

二、分词器tokenizer和文本编码器text_encoder

Prompt文本描述如何控制Stable Diffusion呢?首先需要对Prompt文本描述使用tokenizer进行分词转换为数值表示的ID,然后将这些分词后的ID输入给文本编码器。实际使用户中,可以直接调用_encode_prompt方法来补全或者截断分词后的长度为77获得最终的Prompt文本表示,代码如下:

复制代码
# 手动对提示文字进行分词和编码# 分词input_ids = pipe.tokenizer(["A painting of a flooble"])['input_ids']print("Input ID -> decoded token")for input_id in input_ids[0]:    print(f"{input_id} -> {pipe.tokenizer.decode(input_id)}") # 将分词结果输入CLIP input_ids = torch.tensor(input_ids).to(device)with torch.no_grad():    text_embeddings = pipe.text_encoder(input_ids)['last_hidden_state']print("Text embeddings shape:", text_embeddings.shape)

# 输出Input ID -> decoded token49406 -> <|startoftext|>320 -> a3086 -> painting539 -> of320 -> a4062 -> floo1059 -> ble49407 -> <|endoftext|>Text embeddings shape: torch.Size([1, 8, 1024])

获取最终的文本特征

复制代码
text_embeddings = pipe._encode_prompt("A painting of a flooble",    device, 1, False, '')print(text_embeddings.shape)

# 输出torch.Size([1, 77, 1024])

可以看到最终的文本从长度8补全到了77。

三、UNet网络

在扩散模型中,UNet的作用是接收"带噪"的输入并预测噪声,以实现"去噪",网络结构如下图所示,与前面的示例不同,此次输入的并非是原始图片,而是图片的隐式表示,另外还有文本Prompt描述也作为UNet的输入。

下面让我们对上述三种输入使用伪输入让模型来了解一下UNet在预测过程中,输入输出形状和大小,代码如下:

复制代码
# 创建伪输入timestep = pipe.scheduler.timesteps[0]latents = torch.randn(1, 4, 64, 64).to(device)text_embeddings = torch.randn(1, 77, 1024).to(device) # 让模型进行预测with torch.no_grad():    unet_output = pipe.unet(latents, timestep, text_embeddings).sampleprint('UNet output shape:', unet_output.shape)

# 输出UNet output shape: torch.Size([1, 4, 64, 64])

四、调度器Scheduler

调度器保持了如何添加噪声的信息,并管理如何基于模型的预测更新"带噪"样本,默认的调度器是PNDMScheduler。

我们可以观察一下在添加噪声过程中,噪声水平随时间步增加的变化

复制代码
plt.plot(pipe.scheduler.alphas_cumprod, label=r'$\bar{\alpha}$')plt.xlabel('Timestep (high noise to low noise ->)')plt.title('Noise schedule')plt.legend()

下面看一下使用不同的调度器生成的效果对比,比如使用LMSDiscreteScheduler,代码如下:

复制代码
from diffusers import LMSDiscreteScheduler # 替换原来的调度器pipe.scheduler = LMSDiscreteScheduler.from_config(pipe.scheduler.config) # 输出配置参数print('Scheduler config:', pipe.scheduler) # 使用新的调度器生成图片pipe(prompt="Palette knife painting of an winter cityscape",    height=480, width=480, generator=torch.Generator(device=device).   manual_seed(42)).images[0]

# 输出Scheduler config: LMSDiscreteScheduler {  "_class_name": "LMSDiscreteScheduler",  "_diffusers_version": "0.11.1",  "beta_end": 0.012,  "beta_schedule": "scaled_linear",  "beta_start": 0.00085,  "clip_sample": false,  "num_train_timesteps": 1000,  "prediction_type": "epsilon",  "set_alpha_to_one": false,  "skip_prk_steps": true,  "steps_offset": 1,  "trained_betas": null}

生成的图片如下图所示:

五、复现完整Pipeline

到目前为止,我们已经分步骤剖析了Pipeline的每个组件,现在我们将组合起来手动实现一个完整的Pipeline,代码如下:

复制代码
guidance_scale = 8num_inference_steps=30prompt = "Beautiful picture of a wave breaking"negative_prompt = "zoomed in, blurry, oversaturated, warped" # 对提示文字进行编码text_embeddings = pipe._encode_prompt(prompt, device, 1, True,    negative_prompt) # 创建随机噪声作为起点latents = torch.randn((1, 4, 64, 64), device=device, generator=generator)latents *= pipe.scheduler.init_noise_sigma # 准备调度器pipe.scheduler.set_timesteps(num_inference_steps, device=device) # 生成过程开始for i, t in enumerate(pipe.scheduler.timesteps):     latent_model_input = torch.cat([latents] * 2)     latent_model_input = pipe.scheduler.scale_model_input(       latent_model_input, t)     with torch.no_grad():      noise_pred = pipe.unet(latent_model_input, t,          encoder_hidden_states=text_embeddings).sample       noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)      noise_pred = noise_pred_uncond + guidance_scale *           (noise_pred_text - noise_pred_uncond)     latents = pipe.scheduler.step(noise_pred, t, latents).prev_sample # 将隐变量映射到图片,效果如图6-14所示with torch.no_grad():    image = pipe.decode_latents(latents.detach()) pipe.numpy_to_pil(image)[0]

生成的效果,如下图所示:

六、其他Pipeline介绍

也提到一些其他Pipeline模型,比如图片到图片风格迁移Img2Img,图片修复Inpainting以及图片深度Depth2Image模型,本小节我们就来探索一下这些模型的具体使用效果。

6.1 Img2Img

到目前为止,我们的图片仍然是从完全随机的隐变量开始生成的,并且都使用了完整的扩展模型采样循环。Img2Img Pipeline不必从头开始,它首先会对一张已有的图片进行编码,在得到一系列的隐变量后,就在这些隐变量上随机添加噪声,并以此作为起点。

噪声的数量和"去噪"的步数决定了Img2Img生成的效果,添加少量噪声只会带来微小的变化,添加大量噪声并执行完整的"去噪"过程,可能得到与原始图片完全不同,近在整体结构上相似的图片。

复制代码
# 载入Img2Img管线model_id = "stabilityai/stable-diffusion-2-1-base"img2img_pipe = StableDiffusionImg2ImgPipeline.from_pretrained(    model_id).to(device)

result_image = img2img_pipe(    prompt="An oil painting of a man on a bench",    image = init_image, # 输入待编辑图片    strength = 0.6, # 文本提示在设为0时完全不起作用,设为1时作用强度最大).images[0] # 显示结果,如图6-15所示fig, axs = plt.subplots(1, 2, figsize=(12, 5))axs[0].imshow(init_image);axs[0].set_title('Input Image')axs[1].imshow(result_image);axs[1].set_title('Result')

生成的效果,如下图所示:

6.2 Inpainting

Inpainting是一个图片修复技术,它可以保留图片一部分内容不变,其他部分生成新的内容,Inpainting UNet网络结构如下图所示:

下面我们使用一个示例来展示一下效果:

复制代码
pipe = StableDiffusionInpaintPipeline.from_pretrained("runwayml/ stable-diffusion-inpainting")pipe = pipe.to(device)# 添加提示文字,用于让模型知道补全图像时使用什么内容prompt = "A small robot, high resolution, sitting on a park bench"image = pipe(prompt=prompt, image=init_image,     mask_image=mask_image).images[0] # 查看结果,如图6-17所示fig, axs = plt.subplots(1, 3, figsize=(16, 5))axs[0].imshow(init_image);axs[0].set_title('Input Image')axs[1].imshow(mask_image);axs[1].set_title('Mask')axs[2].imshow(image);axs[2].set_title('Result')

生成的效果,如下图所示:

这是个有潜力的模型,如果可以和自动生成掩码的模型结合就会非常强大,比如Huggingface Space上的一个名为CLIPSeg的模型就可以自动生成掩码。

6.3 Depth2Image

如果想保留图片的整体结构而不保留原有的颜色,比如使用不同的颜色或纹理生成新图片,Img2Img是很难通过"强度"来控制的。而Depth2Img采用深度预测模型来预测一个深度图,这个深度图被输入微调过的UNet以生成图片,我们希望生成的图片既能保留原始图片的深度信息和总体结构,同时又能在相关部分填入全新的内容,代码如下:

复制代码
# 载入Depth2Img管线pipe = StableDiffusionDepth2ImgPipeline.from_pretrained    ("stabilityai/stable-diffusion-2-depth")pipe = pipe.to(device)# 使用提示文字进行图像补全prompt = "An oil painting of a man on a bench"image = pipe(prompt=prompt, image=init_image).images[0] # 查看结果,如图6-18所示fig, axs = plt.subplots(1, 2, figsize=(16, 5))axs[0].imshow(init_image);axs[0].set_title('Input Image')axs[1].imshow(image);axs[1].set_title('Result')

PS:该模型在3D场景下加入纹理非常有用。

相关推荐
wei_shuo1 天前
GpuGeek 实操指南:So-VITS-SVC 语音合成与 Stable Diffusion 文生图双模型搭建,融合即梦 AI 的深度实践
人工智能·stable diffusion·gpu算力·gpuseek
这是一个懒人3 天前
Stable Diffusion WebUI 插件大全:功能详解与下载地址
stable diffusion
浪淘沙jkp3 天前
AI大模型学习十八、利用Dify+deepseekR1 +本地部署Stable Diffusion搭建 AI 图片生成应用
人工智能·stable diffusion·agent·dify·ollama·deepseek
Icoolkj4 天前
深入了解 Stable Diffusion:AI 图像生成的奥秘
人工智能·stable diffusion
这是一个懒人4 天前
mac 快速安装stable diffusion webui
macos·stable diffusion
璇转的鱼5 天前
Stable Diffusion进阶之Controlnet插件使用
人工智能·ai作画·stable diffusion·aigc·ai绘画
AloneCat20126 天前
stable Diffusion模型结构
stable diffusion
西西弗Sisyphus6 天前
Stable Diffusion XL 文生图
stable diffusion
霍志杰7 天前
stable-diffusion windows本地部署
windows·stable diffusion
昨日之日20067 天前
ACE-Step - 20秒生成4分钟完整歌曲,音乐界的Stable Diffusion,支持50系显卡 本地一键整合包下载
计算机视觉·stable diffusion·音视频