AIGC-Stable Diffusion进阶1(附代码)

在上篇文章中对Stable Diffusion进行了初步的认识,也给出了使用案例,这篇文章将进一步的去刨析一下SD模型。

SD模型的组成

在之前也讲过了,SD由CLIP、UNet、ScheduleVAE组成。再来回顾一下知识点:

1.CLIP Text Encoder(文本编码器)

作用:将文本信息进行编码生成对应的特征矩阵方便输入到SD模型中。

2.VAE Encoder(变分自编码器)

作用:生成Latent Feature(隐空间特征)和文本特征同时作为模型输入。如果是图生图任务则将图像进行编码生成Latent Feature;如果是文生图则用随机生成的高斯噪声矩阵作为Latent Feature作为输入。【也就是在输入SD模型前有两个输入,文本特征和隐空间特征】

3.U-Net网络

作用:用于不断地预测噪声,并在每次预测噪声过程中加入文本语义特征。

4.Schedule

作用:对UNet预测的噪声进行优化处理(动态调整预测的噪声,控制U-Net预测噪声的强度)

5.VAE Decoder(解码器)

作用:将最终得到的Latent Feature经过解码器生成图像。

在SD的迭代过程中(去噪过程),噪声会不断的减少,图像信息和文本语义信息会不断的增加。


也就是说Unet的输入有两部分组成,一个是latent、一个是输入文本特征(prompt)。

1.图生图:需要将图像通过VAE encoder生成latent,并与文本特征进行输入

2.文生图:使用随机高斯噪声代替图像latent,并与文本特征进行输入。

在上一篇文章中可以教了大家如何快速使用diffusers搭建SD模型。但这种方法不够灵活,我们对每个部分进行分析,在这篇文章中会分模块加载Model并手动搭建SD模型。

这里以文生图为例。(文末会附上完整的代码)


CLIP Model单独加载

在生成文本特征的时候需要有两个步骤:1.prompt->token 2.token->text_embedding

    # 1.prompt特征提取
    # 1.1 prompt转token
    text_tokenizer = CLIPTokenizer.from_pretrained(sd_weight_path, subfolder="tokenizer")
    text_token_ids = text_tokenizer(
        prompt,
        padding='max_length',
        max_length=text_tokenizer.model_max_length,
        truncation=True,
        return_tensors='pt'
    ).input_ids

   # 1.2提取prompt_embedding
    text_encoder = CLIPTextModel.from_pretrained(sd_weight_path,subfolder='text_encoder')
    text_embeddings = text_encoder(text_token_ids)[0]

Schedule单独加载

Schedule是和UNet相结合的,用于优化不同时间步t时刻的latent。

或者可以这样理解,不同时间t对应不同的噪声级别和潜在表示。缩放输入可以确保输入在每个时间步都是标准化的,使其对应当前时间t所对应的噪声分布,使模型能够稳定地处理不同程度的噪声。

这里我的理解感觉和CNN中的BN层有点相似,但也有不同,这里我谈一下自己的理解:

扩散模型中的schedule,对输入进行缩放的目的是控制和调整不同时间t对应的噪声程度,使得latent在每个时间t的数据分布和训练时是一样的,从而可以稳定的去噪。也就是和BN类似,都是对输入进行标准化或者缩放使数据在特定分布下更稳定。但不同的是,BN层是为了解决梯度消失和爆炸,加快训练速度,而扩散模型的缩放是每个时刻t对latent的缩放,为的是控制噪声水平。

 # 2.单独加载调度算法,生成噪声表
scheduler = PNDMScheduler(beta_start=0.00085, beta_end=0.012, beta_schedule="scaled_linear", num_train_timesteps=1000)
scheduler.set_timesteps(25)
timesteps = scheduler.timesteps

UNet单独加载

 # 3.单独加载UNet
unet = UNet2DConditionModel.from_pretrained(sd_weight_path,subfolder='unet')

UNet预测噪声 

UNet做噪声预测的时候代码如下,其中latents_model_input是经过Schedule优化后的特征图,t是对应的时间t,text_embeddings是输入的文本特征

输出是预测噪声,然后对噪声进行分块(chunk操作)得到两个噪声,一个是无条件噪声,一个是条件噪声。

无条件噪声:

无条件噪声是指模型在没有任何(如文本、标签等)输入情况下,单纯的基于latents得到噪声,这部分噪声预测代表了生成过程中的基础噪声水平。

条件噪声:

条件噪声预测是模型在给定某种条件(如文本嵌入)输入的情况下,基于潜在变量和这些条件预测的噪声。这部分噪声预测代表了生成过程在特定条件下的噪声水平。

noise_pred = unet(
            latents_model_input,
            t,
            encoder_hidden_states=text_embeddings,
            cross_attention_kwargs=None,
            return_dict=False,
        )[0]  # 预测噪声
        noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
        # 7.5为相关因子
        noise_pred = noise_pred_uncond + 7.5 * (noise_pred_text - noise_pred_uncond)

VAE单独加载

    # 4.单独加载VAE
    # 进行解码
    vae = AutoencoderKL.from_pretrained(sd_weight_path, subfolder='vae').to("cuda", dtype=torch.float32)

去噪可视化

比如这里输入的prompt为:'a girl, beautiful',neg pormpt为空时,对每次去噪后的latents可视化,代码如下:

    # 3.单独加载UNet
    unet = UNet2DConditionModel.from_pretrained(sd_weight_path,subfolder='unet')
    for i, t in tqdm(enumerate(timesteps)):
        latents_model_input = torch.cat([latents] * 2)
        latents_model_input = scheduler.scale_model_input(latents_model_input, t)
        noise_pred = unet(
            latents_model_input,
            t,
            encoder_hidden_states=text_embeddings,
            cross_attention_kwargs=None,
            return_dict=False,
        )[0]  # 预测噪声
        noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
        # 7.5为相关因子
        noise_pred = noise_pred_uncond + 7.5 * (noise_pred_text - noise_pred_uncond)
        # x_t->x_t-1  去噪操作
        latents = scheduler.step(noise_pred, t, latents,return_dict=False)[0]
        save_noise_pred_feat(latents, i)

可视化结果(部分图):

latents可视化

latents解码

# 进行解码
vae = AutoencoderKL.from_pretrained(sd_weight_path, subfolder='vae').to("cuda", dtype=torch.float32)
image = vae.decode(latents.to("cuda", dtype=torch.float32) / vae.config.scaling_factor, return_dict=False)[0]

最终解码的效果图如下: 

(大体还是可以,就是可以看到多了一只手,我这里neg prompt为空)

VAE解码

 比如这里输入的neg_prompt为:

neg_prompt = 'extra limbs, extra arms, extra hands, disfigured, distorted anatomy, unnatural proportions, unrealistic, malformed'

生成图像为:

 如果此时输入的neg_prompt为:

neg_prompt = 'extra limbs, extra arms, extra hands, disfigured, distorted anatomy, unnatural proportions, unrealistic, malformed, unnatural expression, creepy eyes, unrealistic facial features, disfigured face, distorted expression, awkward smile, disproportionate eyes, unrealistic gaze, malformed eyes'

生成图像如下:


完整代码

 

import torch
from diffusers.pipelines.alt_diffusion.pipeline_alt_diffusion import rescale_noise_cfg
from diffusers.utils import randn_tensor
from transformers import CLIPTokenizer, CLIPTextModel
from diffusers import UNet2DConditionModel, PNDMScheduler, LMSDiscreteScheduler, AutoencoderKL
from tqdm import tqdm
import numpy as np
from PIL import Image
import cv2
'''
这是一个最小化实现算法
快速学习sd过程
'''
# 用以保存特征图
def save_noise_pred_feat(noise_pred, save_name):
    noise_pred = (noise_pred / 2 + 0.5).clamp(0, 1)
    # 将Tensor转换为 NumPy 数组
    noise_pred_np = noise_pred.detach().cpu().numpy()
    # 将通道数从第二个维度移动到最后一个维度
    # (前提假设通道数是第二个维度)
    noise_pred_np = np.moveaxis(noise_pred_np, 1, -1)
    # 反归一化
    noise_pred_np = (noise_pred_np * 255).round().astype(np.uint8)
    # 如果你想将所有特征图合并成一个图像保存,可以使用 PIL 的拼接功能
    # 这里仅作示例
    combined_img = Image.fromarray(np.concatenate(noise_pred_np, axis=1))
    combined_img.save(f'{save_name}_combined_feature_map.png')
@torch.no_grad()
def UNET():
    # 随机种子
    generator = torch.Generator("cpu").manual_seed(1024)
    # 权重路径
    sd_weight_path = 'F:/Stable_webui/stable-diffusion-webui/models/Stable-diffusion/stable-diffusion-v1-5'
    # prompt
    prompt = 'a girl, beautiful'
    # neg prompt
    neg_prompt = 'extra limbs, extra arms, extra hands, disfigured, distorted anatomy, unnatural proportions, unrealistic, malformed, unnatural expression, creepy eyes, unrealistic facial features, disfigured face, distorted expression, awkward smile, disproportionate eyes, unrealistic gaze, malformed eyes'
    # 1.prompt特征提取
    # 1.1 prompt转token
    text_tokenizer = CLIPTokenizer.from_pretrained(sd_weight_path, subfolder="tokenizer")
    text_token_ids = text_tokenizer(
        prompt,
        padding='max_length',
        max_length=text_tokenizer.model_max_length,
        truncation=True,
        return_tensors='pt'
    ).input_ids
    neg_token_ids = text_tokenizer(
        neg_prompt,
        padding='max_length',
        max_length=77,
        truncation=True,
        return_tensors='pt'
    )

    # 1.2提取prompt_embedding
    text_encoder = CLIPTextModel.from_pretrained(sd_weight_path,subfolder='text_encoder')
    text_embeddings = text_encoder(text_token_ids)[0]
    text_embeddings = text_embeddings.repeat(1,1,1)
    text_embeddings = text_embeddings.view(1*1,77,-1)
    neg_prompt_embeds = text_encoder(
        neg_token_ids.input_ids,
        attention_mask=None,
    )[0]
    del text_encoder, text_tokenizer
    neg_prompt_embeds = neg_prompt_embeds.repeat(1, 1, 1)
    neg_prompt_embeds = neg_prompt_embeds.view(1*1, 77, -1)
    # 文本特征向量
    text_embeddings = torch.cat([neg_prompt_embeds, text_embeddings])

    # 2.单独加载调度算法,生成噪声表,在总的时间步数num_train_timesteps中,这个噪声表的值在beta_start到beta_end中,对应公式的αt
    scheduler = PNDMScheduler(beta_start=0.00085, beta_end=0.012, beta_schedule="scaled_linear", num_train_timesteps=1000)
    scheduler.set_timesteps(25)
    timesteps = scheduler.timesteps
    print("timesteps :" + str(timesteps))
    # 如果是文生图,那么就用一个随机噪声来代替这个图
    latents = randn_tensor((1, 4, 64, 64), generator=generator, dtype=torch.float32)
    latents = scheduler.init_noise_sigma * latents
    # 3.单独加载UNet
    unet = UNet2DConditionModel.from_pretrained(sd_weight_path,subfolder='unet')
    for i, t in tqdm(enumerate(timesteps)):
        latents_model_input = torch.cat([latents] * 2)
        latents_model_input = scheduler.scale_model_input(latents_model_input, t)
        noise_pred = unet(
            latents_model_input,
            t,
            encoder_hidden_states=text_embeddings,
            cross_attention_kwargs=None,
            return_dict=False,
        )[0]  # 预测噪声
        noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
        # 7.5为相关因子
        noise_pred = noise_pred_uncond + 7.5 * (noise_pred_text - noise_pred_uncond)
        # x_t->x_t-1  去噪操作
        latents = scheduler.step(noise_pred, t, latents,return_dict=False)[0]
        # save_noise_pred_feat(latents, i)
    # 4.单独加载VAE
    # 进行解码
    vae = AutoencoderKL.from_pretrained(sd_weight_path, subfolder='vae').to("cuda", dtype=torch.float32)
    image = vae.decode(latents.to("cuda", dtype=torch.float32) / vae.config.scaling_factor, return_dict=False)[0]
    # 后处理
    rec_image = (image / 2 + 0.5).clamp(0, 1)  # 归一化到0~1
    rec_image = rec_image.cpu().permute(0, 2, 3, 1).numpy()

    # 反归一化
    rec_image = (rec_image * 255).round().astype("uint8")
    rec_image = rec_image[0]

    # 保存重建后图像
    cv2.imwrite("mysd.png", cv2.cvtColor(rec_image, cv2.COLOR_RGB2BGR))
    torch.cuda.empty_cache()
UNET()

<think>好的,我现在需要处理用户关于在Colab中使用AIGC生成图像的教程请求。首先,用户提到了几个引用,我需要看看这些引用内容是否相关。引用[1]和[2]主要讲AIGC的概述和应用,引用[3]和[4]提到Stable Diffusion模型的下载地址和安装步骤,这些可能和用户的问题相关。 用户的问题是如何在Colab中使用AIGC生成图像,可能需要指导他们设置环境、安装模型、编写代码等步骤。根据引用[3]和[4],用户可能需要下载Stable Diffusion模型,并导入到Colab中。不过,通常在使用Colab时,可以直接从Hugging Face等平台加载模型,而不需要手动下载,所以可能需要调整步骤。 接下来,我需要确保回答的结构清晰,分步骤说明。可能需要包括:安装必要库、加载预训练模型、编写生成图像的代码、调整参数等。同时,要引用相关的参考资料,比如引用[3]提到的模型导入步骤,引用[4]的模型地址可能需要给出。 另外,用户可能遇到常见问题,比如依赖冲突、显存不足等,需要提供解决方案。例如,Colab可能需要升级到高版本的库,或者使用特定的版本兼容Stable Diffusion。此外,生成图像的质量和参数调整也是关键点,需要指导用户如何修改提示词或参数。 需要检查是否有遗漏的步骤,比如是否需要挂载Google Drive来保存生成的图像,或者如何处理运行时断开的情况。同时,要确保代码示例正确,用户可以直接复制到Colab中运行。 最后,生成相关问题时要围绕教程内容,比如模型选择、参数调整、性能优化等,确保问题与主题相关,帮助用户进一步探索。</think>以下是在Google Colab中使用AIGC生成图像的详细教程,结合Stable Diffusion模型实现: ### 一、环境准备 1. 打开Google Colab(https://colab.research.google.com/) 2. 新建笔记本,将运行时类型改为GPU(菜单栏 > 修改 > 笔记本设置 > GPU) 3. 安装必要依赖库: ```python !pip install diffusers transformers accelerate torch ``` 建议使用>=Python3.8环境[^3] ### 二、加载模型 ```python from diffusers import StableDiffusionPipeline import torch # 从Hugging Face加载预训练模型 model_id = "CompVis/stable-diffusion-v1-4" # 对应引用[4]的模型版本 pipe = StableDiffusionPipeline.from_pretrained( model_id, torch_dtype=torch.float16, revision="fp16" ).to("cuda") ``` ### 三、图像生成 ```python prompt = "赛博朋克风格的未来城市,霓虹灯光,4k高清" image = pipe(prompt).images[0] image.save("generated_image.png") ``` ### 四、参数调整(进阶) ```python # 调整生成参数 image = pipe( prompt, height=768, # 图像高度 width=512, # 图像宽度 num_inference_steps=50, # 迭代次数 guidance_scale=7.5, # 提示词相关性系数 ).images[0] ``` ### 五、保存结果 ```python from google.colab import files files.download("generated_image.png") # 下载到本地 ``` ### 常见问题解决 1. 显存不足:尝试减小图像尺寸(如512x512) 2. 模型加载失败:检查Colab是否开启GPU加速 3. 生成质量优化:增加num_inference_steps(建议50-100步) [^3]: 模型导入方法参考Stable Diffusion官方文档 [^4]: 模型源地址:https://huggingface.co/CompVis/stable-diffusion-v-1-4-original
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱吃肉的鹏

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

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

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

打赏作者

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

抵扣说明:

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

余额充值