2024不可不会的StableDiffusion之Unet(四)

本文详细介绍了StableDiffusion模型中的Unet组件,包括其在去噪过程中的作用,以及如何结合VAE和CLIP进行文本引导的图像生成。作者通过代码示例展示了Unet的工作原理和在模型中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 引言

这是我关于StableDiffusion学习系列的第四篇文章,如果之前的文章你还没有阅读,强烈推荐大家翻看前篇内容。在本文中,我们将学习构成StableDiffusion的第三个基础组件基于Unet的扩散模型,并针该组件的功能进行详细的阐述。

闲话少说,我们直接开始吧!

2. 概览

通常来说一个U-Net包含两个输入:
Noisy latent/Noise : 该Noisy latent主要是由VAE编码器产生并在其基础上添加了噪声;或者如果我们想仅根据文本描述来创建随机的新图像,则可以采用纯噪声作为输入。
Text embeddings: 基于CLIP的将文本输入提示转化为文本语义嵌入(embedding)
在这里插入图片描述

U-Net模型的输出是从包含输入噪声的Noisy Latents中预测其所包含的噪声。换句话说,它预测输出的为Noisy Latents减去de-noised latents后的结果。

3. 导入所需的库

让我们接着通过代码来了解 U-Net。我们将首先导入所需的库并加载我们的U-Net模型。

from diffusers import UNet2DConditionModel,LMSDiscreteScheduler

## Initializing a scheduler
scheduler = LMSDiscreteScheduler(beta_start=0.00085, beta_end=0.012, beta_schedule="scaled_linear", num_train_timesteps=1000)
## Initializing the U-Net model
sd_path = r'/media/stable_diffusion/stable-diffusion-v1-4'
unet = UNet2DConditionModel.from_pretrained(sd_path,subfolder="unet",
                                            local_files_only=True,
                                            torch_dtype=torch.float16).to("cuda")

4. 可视化Scheduler

正如大家从上面的代码中观察到的那样,我们不仅导入了 unet,还导入了一个scheduler库。scheduler的目的是确定在扩散过程中的给定的步骤中向latent 添加多少噪声。我们使用以下代码来可视化 scheduler 函数:

## Setting number of sampling steps
scheduler.set_timesteps(51)
plt.plot(scheduler.sigmas)
plt.xlabel("Sampling step")
plt.ylabel("sigma")
plt.title("Schedular routine")
plt.show()

得到结果如下:
在这里插入图片描述

可以看到,随着sample step的增大,我们添加噪声的权重在逐渐减小。

5. 可视化扩散过程

扩散过程遵循上述采样Scheduler,我们从高噪声开始,然后逐渐按照schedulerlatent_img添加对应权重的噪声。让我们可视化一下这个过程:

img_path = r'/home/VAEs/Scarlet-Macaw-2.jpg'
img = Image.open(img_path).convert("RGB").resize((512, 512))
latent_img = pil_to_latents(img, vae)
# Random noise
noise = torch.randn_like(latent_img) 
fig, axs = plt.subplots(2, 3, figsize=(16, 12))
for c, sampling_step in enumerate(range(0,51,10)):
    encoded_and_noised = scheduler.add_noise(latent_img, noise, 
                   timesteps=torch.tensor([scheduler.timesteps[sampling_step]]))
    axs[c//3][c%3].imshow(latents_to_pil(encoded_and_noised,vae)[0])
    axs[c//3][c%3].set_title(f"Step - {sampling_step}")
plt.show()

得到结果如下:
在这里插入图片描述

6. 生成Unet输入

接着我们需要对latent_img, 随机添加一些噪声,用以作为我们Unet输入的Noisy Latent , 代码如下:

encoded_and_noised = scheduler.add_noise(latent_img, noise, 
                     timesteps=torch.tensor([scheduler.timesteps[40]])) 
latents_to_pil(encoded_and_noised,vae)[0]

得到结果如下:

在这里插入图片描述

7. 调用Unet去噪

接着我们就可以使用Unet对其进行去噪了,相应的调用代码如下:

prompt = [""]
text_input = tokenizer(prompt, padding="max_length",
                       max_length=tokenizer.model_max_length,
                       truncation=True,return_tensors="pt")
with torch.no_grad():
    text_embeddings = text_encoder(text_input.input_ids.to("cuda"))[0]

latent_model_input = torch.cat( [encoded_and_noised.to("cuda").float()]).half()
with torch.no_grad():
    noise_pred = unet(latent_model_input,40,encoder_hidden_states=text_embeddings
        )["sample"]
out_images = latent_to_pil((encoded_and_noised - noise_pred),vae)
plt.imshow(out_images[0])
plt.show()

得到去噪后结果如下:

在这里插入图片描述

我们也可以使用以下代码来将Unet预测出来的噪声进行可视化,代码如下:

out_noises = latent_to_pil( noise_pred,vae)
plt.imshow(out_noises[0])
plt.show()

得到结果如下:
在这里插入图片描述

8. Unet 在SD中的用途

潜在扩散模型使用U-Net分几个步骤通过逐渐减去潜伏空间中的噪声,来达到所需要的输出。在每一步中,添加到latents中的噪声量都会减少,直到我们达到最终的去噪输出。

我们知道,在深度学习领域U-Nets最先被引入用于生物医学图像分割任务。U-Net 有一个编码器和解码器,它们由 ResNet blocks组成。在stable diffusion中的U-Net还具有交叉注意力层,使它们能够根据提供的文本描述来控制调整输出。交叉注意力层通常添加到U-Net的编码器和解码器之间。可以在此处了解有关此 U-Net 架构的更多信息。

9. 总结

本文重点介绍了SD模型中的Unet组件的相关功能和具体工作原理,并详细介绍了其去噪过程;至此,我们完成了稳定扩散模型的三个关键组件,即 CLIP 文本编码器、VAE U-Net。在下一篇文章中,我们将研究使用这些组件的扩散过程。

您学废了嘛!

### UNet 中保持输入输出图像尺寸不变的配置方法 为了确保 UNet 的输入和输出图像尺寸一致,需要特别注意编码器(下采样路径)和解码器(上采样路径)之间的对称性和跳跃连接的设计。具体来说: #### 1. 使用合适的卷积层填充方式 通过适当设置卷积操作中的 `padding` 参数可以有效防止特征图尺寸缩小。通常情况下,在每一层卷积中都应使用相同的 padding 方式来维持空间分辨率。 对于标准的二维卷积层而言,当 kernel size 设置为 3×3 或其他奇数值时,可以通过设定 `padding='same'` 来保证经过该层处理后的图片宽度高度不会发生变化[^1]。 ```python from tensorflow.keras.layers import Conv2D conv_layer = Conv2D(filters=64, kernel_size=(3, 3), strides=(1, 1), activation='relu', padding='same') ``` #### 2. 控制池化/反池化的比例因子 最大池化(MaxPooling) 和转置卷积(TransposedConvolution) 是实现尺度变化的主要手段之一。然而,它们可能会引入额外的空间缩放效应。为了避免这种情况发生,建议采用 stride=1 进行降维或升维变换;或者利用可学习参数较少但能精确控制输出shape的插值法替代传统意义上的pooling/downsampling过程。 ```python from tensorflow.keras.layers import UpSampling2D, MaxPooling2D downsample = MaxPooling2D(pool_size=(2, 2), strides=None, padding='valid') # 不推荐用于保持尺寸 upsample = UpSampling2D(size=(2, 2)) # 推荐做法 ``` #### 3. 调整批处理大小 (Batch Size) 虽然这并不是直接影响到单张图片最终输出形状的因素,但是过大的 batch size 可能在某些硬件环境下造成内存不足错误,进而间接影响模型训练稳定性甚至导致无法正常运行。因此合理规划每批次数据量也是十分重要的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赵卓不凡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值