学习笔记-Stable Diffusion文字生成图片模型的手动搭建

一、准备阶段

  • 准备需要用到的模型pipeline

  • 准备图像及其掩膜

 import torch
 import requests
 from PIL import Image
 from io import BytesIO
 from matplotlib import pyplot as plt
 ​
 # StableDiffusionInpaintPipeline 是一个用于图像修复(Inpainting)的管道。稳定扩散算法是一种基于偏微分方程的图像修复方法,它通过在图像中填充缺失的区域来恢复图像的完整性。该管道实现了稳定扩散算法的各个步骤,包括数据预处理、扩散过程和结果后处理等
 # StableDiffusionDepth2ImgPipeline 是一个用于从深度图生成图像的管道。深度图是一种表示场景中物体距离相机的图像,而图像则是一种表示场景中物体外观的图像。该管道利用稳定扩散算法将深度图转换为图像,从而实现从深度图到图像的转换。
 ​
 # We'll be exploring a number of pipelines today!
 from diffusers import (
     StableDiffusionPipeline, 
     StableDiffusionImg2ImgPipeline,
     StableDiffusionInpaintPipeline, 
     StableDiffusionDepth2ImgPipeline
     )       
 ​
 # We'll use a couple of demo images later in the notebook
 def download_image(url):
     response = requests.get(url)
     return Image.open(BytesIO(response.content)).convert("RGB")
 ​
 # Download images for inpainting example
 img_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo.png"
 mask_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo_mask.png"
 ​
 init_image = download_image(img_url).resize((512, 512))
 mask_image = download_image(mask_url).resize((512, 512))

二、文本生成图像

1. 用到的pipeline——StableDiffusionPipeline

  • 创建生成器

  • pipe中的参数:

    • 图像提示:要生成什么内容

    • 不希望生成的图像中包含的内容

    • 指定生成图像时要遵循提示的程度

    • 指定推理步数

    • 指定生成器,确保结果可以重现

 # Set up a generator for reproducibility
 # 创建一个生成器用以确保结果的可重现性
 generator = torch.Generator(device=device).manual_seed(40)
 ​
 # 调用 pipe 函数来运行管道,展示一些可用的参数
 # Run the pipeline, showing some of the available arguments
 pipe_output = pipe(
     # 生成图像的提示(prompt),即要生成的内容
     prompt="A Bernese Mountain Dog chasing a ball on the lawn with its tongue sticking out", # What to generate
 ​
     # 不希望生成的内容的提示(negative_prompt)
     negative_prompt="Oversaturated, blurry, low quality", # What NOT to generate
     height=480, width=640,     # Specify the image size
     # 指定生成图像时要遵循提示的程度
     guidance_scale=8,          # How strongly to follow the prompt
     # 指定生成图像时要进行的推理步骤的数量。
     num_inference_steps=50,    # How many steps to take
     # generator:指定生成器,以确保结果的可重现性
     generator=generator        # Fixed random seed
 )
 ​
 # View the resulting image:
 pipe_output.images[0]

作者认为guidance_scale的最佳取值区间在8 到 12

2. pipeline组成

(1) VAE可分自编码器

VAE将输入编码成一种被压缩过的表示形式,再把这个“隐式的”表示形式解码成某种接近输入的输出。当我们使用 Stable Diffusion 生成图片时,我们先在VAE的“隐空间”应用扩散过程生成隐编码,然后在结尾对它们解码来查看结果图片。

 # Create some fake data (a random image, range (-1, 1))
 ​
 # * 2 将随机张量中的每个元素乘以 2,而 - 1 则将结果减去 1。这个操作的目的是将随机张量的值范围从 [0, 1] 缩放到 [-1, 1]
 images = torch.rand(1, 3, 512, 512).to(device) * 2 - 1 
 print("Input images shape:", images.shape)
 ​
 # Encode to latent space
 # .latent_dist.mean 是对潜在表示对象中的概率分布取其均值
 # pipe.vae.encode(images) 是一个模型 pipe 中的 vae 对象对输入图像 images 进行编码的操作。它返回一个包含潜在表示的对象
 # 整个代码的含义是将输入图像 images 通过 pipe.vae 模型进行编码,然后取得到的潜在表示的均值,并乘以 0.18215,最后将结果赋值给变量 latents
 with torch.no_grad():
   latents = 0.18215 * pipe.vae.encode(images).latent_dist.mean
 print("Encoded latents shape:", latents.shape)
 ​
 # Decode again
 # .sample 是对解码后的图像对象中的概率分布进行采样的操作,这个操作会从概率分布中随机采样一个样本,得到最终的解码图像
 # torch.no_grad(),表示在解码和采样过程中不进行梯度计算
 # 整个代码的含义是对潜在表示 latents 进行解码,然后从解码后的图像对象中随机采样一个图像,并将结果赋值给变量 decoded_images
 with torch.no_grad():
   decoded_images = pipe.vae.decode(latents / 0.18215).sample
 print("Decoded images shape:", decoded_images.shape)

原本 512x512 尺寸的图片被压缩到 64x64的隐式表示形式(有四个通道)中

【疑问:】为什么会被压缩成4*64*64?是普适规律吗?后面也涉及到这个问题。这里的VAE是作用于随机噪声的吗?(后面是作用于随机噪声的)

(2)分词器Tokenizer和文本编码器Text Encoder

  • Text Encoder结构

文本首先要被管线中的分词器(tokenizer)转换成一系列的分词(token),再被送入文本编码器模型,从而用数值表示字符

  • 集成表示代码

 # Get the final text embeddings using the pipeline's _encode_prompt function:
 # 这段代码的目的是使用 pipeline 的 _encode_prompt 函数获取最终的文本嵌入。
 text_embeddings = pipe._encode_prompt("A painting of a flooble", device, 1, False, '')
 text_embeddings.shape

(3) U-Net

UNet 模型接收一个带噪的输入,并预测噪声

3. DIY采样循环

 guidance_scale = 8 #@param
 num_inference_steps=30 #@param
 prompt = "Bernese Mountain Dogs happily run on the sunlit lawn" #@param
 negative_prompt = "zoomed in, blurry, oversaturated, warped" #@param
 ​
 # Encode the prompt
 # 文本嵌入
 text_embeddings = pipe._encode_prompt(prompt, device, 1, True, negative_prompt)
 ​
 # Create our random starting point
 # 创建一个形状为 (1, 4, 64, 64) 的随机张量 latents。
 # 对应这噪声的隐式分布
 # 这个随机张量包含了服从标准正态分布的随机值。
 latents = torch.randn((1, 4, 64, 64), device=device, generator=generator)
 # 随机张量 latents 中的每个元素与 pipe.scheduler.init_noise_sigma 相乘,
 # 并将结果重新赋值给 latents。这个操作的目的是将随机张量的值按照初始噪声的标准差进行缩放
 latents *= pipe.scheduler.init_noise_sigma
 ​
 # Prepare the scheduler
 # 调度器
 pipe.scheduler.set_timesteps(num_inference_steps, device=device)
 ​
 # Loop through the sampling timesteps
 for i, t in enumerate(pipe.scheduler.timesteps):
 ​
   # expand the latents if we are doing classifier free guidance
   # 将 latents 进行复制,然后使用 torch.cat 函数将复制后的 latents 连接起来,形成一个新的张量 latent_model_input。
   # 这个操作的目的是为了在进行分类器无关引导时扩展 latents
   latent_model_input = torch.cat([latents] * 2)
 ​
   # Apply any scaling required by the scheduler
   # 根据时间步 t 对 latent_model_input 进行缩放操作【随机生成的噪声隐式分布】
   latent_model_input = pipe.scheduler.scale_model_input(latent_model_input, t)
 ​
   # predict the noise residual with the unet
   with torch.no_grad():
     noise_pred = pipe.unet(latent_model_input, t, encoder_hidden_states=text_embeddings).sample
 ​
   # perform guidance
   # 使用 chunk 函数将 noise_pred 分割成两部分,分别存储在 noise_pred_uncond 和 noise_pred_text 中。
   # 这个操作的目的是将噪声残差分为无条件部分和文本引导部分
   noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
   noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)
 ​
   # compute the previous noisy sample x_t -> x_t-1
   # 从步进操作的结果中获取先前的样本 prev_sample,并将其赋值给 latents
   # 也就是计算先前的带噪声样本 x_t -> x_t-1
   latents = pipe.scheduler.step(noise_pred, t, latents).prev_sample
 ​
 # Decode the resulting latents into an image
 with torch.no_grad():
   image = pipe.decode_latents(latents.detach())
 ​
 # View
 pipe.numpy_to_pil(image)[0]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值