stable diffusion代码学习笔记

news/2024/7/10 22:06:02 标签: stable diffusion, 学习

前言:本文没有太多公式推理,只有一些简单的公式,以及公式和代码的对应关系。本文仅做个人学习笔记,如有理解错误的地方,请指出。

本文包含stable diffusion入门文献和不同版本的代码。

文献资源

  1. 本文学习的代码;
  2. 相关文献:
  • Denoising Diffusion Probabilistic Models : DDPM,这个是必看的,推推公式
  • Denoising Diffusion Implicit Models :DDIM,对 DDPM 的改进
  • Pseudo Numerical Methods for Diffusion Models on Manifolds :PNMD/PLMS,对 DDPM 的改进
  • High-Resolution Image Synthesis with Latent Diffusion Models :Latent-Diffusion,必看
  • Neural Discrete Representation Learning : VQVAE,简单翻了翻,示意图非常形象,很容易了解其做法

代码资源

  1. stable diffusion v1.1-v1.4, https://github.com/CompVis/stable-diffusion
  2. stable diffusion v1.5,https://github.com/runwayml/stable-diffusion
  3. stable diffusion v2,https://github.com/Stability-AI/stablediffusion
  4. stable diffusion XL,https://github.com/Stability-AI/generative-models

前向过程(训练)

  • 输入一张图片+随机噪声,训练unet,网络预测图片加上的噪声

反向过程(推理)

  • 给个随机噪声,不断迭代去噪,输出一张图片

总体流程

  • 输入的prompt经过clip encoder编码成(3+3,77,768)特征,正负prompt各3个,默认negative prompt为空‘’,解码时正的和负的latent图片用公式计算一下才是最终结果;time step通过linear层得到(3+3,1280)特征;把prompt和time ebedding和随机生成的图片放入unet,得到的就是我们要的图片。

采样流程 text2img

  • 该函数在PLMSSampler中,输入x(噪声,(3,4,64,64))-----c(输入的prompt,(3,77,768)----t (输入的time step,第几次去噪(3,)。把这三个东西输入unet,得到预测的噪声e_t。
 def p_sample_plms(self, x, c, t, index, repeat_noise=False, use_original_steps=False, quantize_denoised=False,
                      temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None,
                      unconditional_guidance_scale=1., unconditional_conditioning=None, old_eps=None, t_next=None):
        b, *_, device = *x.shape, x.device

        def get_model_output(x, t):
            if unconditional_conditioning is None or unconditional_guidance_scale == 1.:
                e_t = self.model.apply_model(x, t, c)
            else:
                x_in = torch.cat([x] * 2)
                t_in = torch.cat([t] * 2)
                c_in = torch.cat([unconditional_conditioning, c]) # 积极消极的prompt,解码时按照公式减去消极prompt的图像
                e_t_uncond, e_t = self.model.apply_model(x_in, t_in, c_in).chunk(2)
                e_t = e_t_uncond + unconditional_guidance_scale * (e_t - e_t_uncond)

            if score_corrector is not None:
                assert self.model.parameterization == "eps"
                e_t = score_corrector.modify_score(self.model, e_t, x, t, c, **corrector_kwargs)

            return e_t

        alphas = self.model.alphas_cumprod if use_original_steps else self.ddim_alphas
        alphas_prev = self.model.alphas_cumprod_prev if use_original_steps else self.ddim_alphas_prev
        sqrt_one_minus_alphas = self.model.sqrt_one_minus_alphas_cumprod if use_original_steps else self.ddim_sqrt_one_minus_alphas
        sigmas = self.model.ddim_sigmas_for_original_num_steps if use_original_steps else self.ddim_sigmas

        def get_x_prev_and_pred_x0(e_t, index):
            # select parameters corresponding to the currently considered timestep
            a_t = torch.full((b, 1, 1, 1), alphas[index], device=device)
            a_prev = torch.full((b, 1, 1, 1), alphas_prev[index], device=device)
            sigma_t = torch.full((b, 1, 1, 1), sigmas[index], device=device)
            sqrt_one_minus_at = torch.full((b, 1, 1, 1), sqrt_one_minus_alphas[index],device=device)

            # current prediction for x_0
            pred_x0 = (x - sqrt_one_minus_at * e_t) / a_t.sqrt()
            if quantize_denoised:
                pred_x0, _, *_ = self.model.first_stage_model.quantize(pred_x0)
            # direction pointing to x_t
            dir_xt = (1. - a_prev - sigma_t**2).sqrt() * e_t
            noise = sigma_t * noise_like(x.shape, device, repeat_noise) * temperature
            if noise_dropout > 0.:
                noise = torch.nn.functional.dropout(noise, p=noise_dropout)
            x_prev = a_prev.sqrt() * pred_x0 + dir_xt + noise
            return x_prev, pred_x0

        e_t = get_model_output(x, t) # 模型预测的噪声
        if len(old_eps) == 0:
            # Pseudo Improved Euler (2nd order)
            x_prev, pred_x0 = get_x_prev_and_pred_x0(e_t, index) # 输入噪声减去预测噪声得到新的噪声,当前预测的latent图片
            e_t_next = get_model_output(x_prev, t_next)
            e_t_prime = (e_t + e_t_next) / 2 # 两次噪声的均值?
        elif len(old_eps) == 1:
            # 2nd order Pseudo Linear Multistep (Adams-Bashforth)
            e_t_prime = (3 * e_t - old_eps[-1]) / 2
        elif len(old_eps) == 2:
            # 3nd order Pseudo Linear Multistep (Adams-Bashforth)
            e_t_prime = (23 * e_t - 16 * old_eps[-1] + 5 * old_eps[-2]) / 12
        elif len(old_eps) >= 3:
            # 4nd order Pseudo Linear Multistep (Adams-Bashforth)
            e_t_prime = (55 * e_t - 59 * old_eps[-1] + 37 * old_eps[-2] - 9 * old_eps[-3]) / 24

        x_prev, pred_x0 = get_x_prev_and_pred_x0(e_t_prime, index)

        return x_prev, pred_x0, e_t
  • 接下来看公式:
    在这里插入图片描述
  • 网络得到e_t后,进入到get_x_prev_and_pred_x0函数,可以看到pred_x0 = (x - sqrt_one_minus_at * e_t) / a_t.sqrt()就是上述公式,也就是说网络的预测结果通过公式计算,我们可以得到预测的pred_x0原始图片和前一刻的噪声图像x_prev
        def get_x_prev_and_pred_x0(e_t, index):
            # select parameters corresponding to the currently considered timestep
            a_t = torch.full((b, 1, 1, 1), alphas[index], device=device)
            a_prev = torch.full((b, 1, 1, 1), alphas_prev[index], device=device)
            sigma_t = torch.full((b, 1, 1, 1), sigmas[index], device=device)
            sqrt_one_minus_at = torch.full((b, 1, 1, 1), sqrt_one_minus_alphas[index],device=device)

            # current prediction for x_0
            pred_x0 = (x - sqrt_one_minus_at * e_t) / a_t.sqrt()
            if quantize_denoised:
                pred_x0, _, *_ = self.model.first_stage_model.quantize(pred_x0)
            # direction pointing to x_t
            dir_xt = (1. - a_prev - sigma_t**2).sqrt() * e_t
            noise = sigma_t * noise_like(x.shape, device, repeat_noise) * temperature
            if noise_dropout > 0.:
                noise = torch.nn.functional.dropout(noise, p=noise_dropout)
            x_prev = a_prev.sqrt() * pred_x0 + dir_xt + noise
            return x_prev, pred_x0
  • 前一刻的噪声图像的推理公式如图:
    在这里插入图片描述
    在这里插入图片描述

  • 得到了上一刻的噪声图片x_prev后(也就是函数返回的img),继续迭代,最终生成需要的图片。
    在这里插入图片描述

额外说明

这部分代码应该就是PLMS加速采样用的,论文中有公式推理
在这里插入图片描述
另外,还有一些参数是训练时候保存的,betas逐渐增大,用来控制噪声的强度。变量名解析 log_one_minus_alphas_cumprod其实就是log(1-alpha(右下角t)(头上直线)),没有带prev的都是当前时刻t,带prev的是前一时刻t-1。

在这里插入图片描述

参考文献:

https://blog.csdn.net/Eric_1993/article/details/129600524?spm=1001.2014.3001.5502
https://zhuanlan.zhihu.com/p/630354327


http://www.niftyadmin.cn/n/5319696.html

相关文章

9个Linux网络命令

这些命令用于监控连接、排除网络故障、路由选择、DNS 查询和接口配置。 1. ping – 向网络主机发送 ICMP ECHO_REQUEST ping 是用于测试网络连接的最流行的网络终端工具。ping 有很多选项,但在大多数情况下,您将使用它来请求域或IP地址: p…

C# 快速模指数运算 快速求余运算

此方法解决这样一个问题,就是a^b mod m 的余数是多少。 如果直接计算a^b,方次很大的时候,会溢出,而且时间很长。 当然指数很小的时候直接用自带的Math函数就行,如果指数很大的时候,可以用以下的方法。 原…

pyqt5 pyinstaller 打包 QThread QLable QscrollArea 滑动 红果短剧

废话 不多说,直接上代码!!! UI.py self.scrollArea QtWidgets.QScrollArea(self.centralwidget)self.scrollArea.setGeometry(QtCore.QRect(20, 130, 541, 511))self.scrollArea.setWidgetResizable(True)self.scrollArea.setOb…

K8s:Pod生命周期

我们一般将pod对象从创建至终的这段时间范围称为pod的生命周期,它主要包含下面的过程: pod创建过程 运行初始化容器(init container)过程 运行主容器(main container) 容器启动后钩子(post st…

Docker安装Elesticsearch7详细步骤

​ 1、创建安装目录 mkdir -p /usr/local/docker/es-docker 2、配置虚拟内存 如果不配置,后面启动es会报错。 max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] 配置如下 vi /etc/sysctl.conf vm.max_map_coun…

微服务系统面经之四: 以秒杀系统为例 - 用户秒杀状态的通知【京东支付后端一面】

28 秒杀状态相关 28.1 对于一个秒杀系统,用户点击秒杀按钮后,怎么才能知道其秒杀状态?【京东支付后端一面】 对于秒杀系统,用户点击秒杀按钮后,确定其秒杀状态的过程通常包含以下几个关键步骤: 请求处理&…

树莓派 gpio 安装及简单使用

安装 git clone https://github.com/WiringPi/WiringPi cd WiringPi/ ./build查看版本 gpio -v gpio version: 2.70列出io gpio readall-----------------------------Model B1-----------------------------| BCM | wPi | Name | Mode | V | Physical | V | Mode | Name…

测试八年|对业务测试人员的一些思考

自从事测试工作八年多以来,经历过三个部门多条业务线,也经历过测试转型再回到测试,在此过程中对测试工作和角色的认知也逐步有些思考,想把这些思考分享给大家,希望为业务测试同学提供一些有价值的思路。 一、质量保障…