生成模型:扩散模型(DDPM, DDIM, 条件生成)

时间:2025-01-31 09:15:56

扩散模型的理论较为复杂,论文公式与开源代码都难以理解。现有的教程大多侧重推导公式。为此,本文通过精简代码(约300行),从代码运行角度讲解扩散模型。

本文包括扩散模型的3项技术复现:

1.DDPM (Denoising Diffusion Probabilistic Models,去噪扩散概率模型,SDE-包含训练与推理)

2.DDIM (Denoising Diffusion Implicit Models,ODE-加速推理)

3.Classifier_free(以标签为条件的控制生成)

1. 训练-加噪过程

1.1 参数设置

  • timesteps是加噪次数,默认为1000次,具体某一次视为一个时间步 t {t} t

步数越多过程越稳定,Mnist实验中100次以上能保证效果。但t在训练中是随机的,并没有时序性,即值为[0,timesteps]内的随机正整数

注:本文time_steps = 300

  • α \alpha α是接近但小于1的递减时序序列,有timesteps个, α t \alpha_t αt α \alpha α序列第t个元素的值

本文 α \alpha α的取值范围是:[0.9997, 0.97]

  • β \beta β是接近但大于0的递增时序序列,有 β = 1 − α \beta = 1- \alpha β=1α. 同理, β t \beta_t βt β \beta β序列第t个元素的值

范围是:[0.0003, 0.03]

  • α ˉ \bar{\alpha} αˉ α \alpha α的累积值,是一个1到0的递减序列(序列元素值不包含边界0和1).

假设alphas = [0.9, 0.8, 0.7] (即timesteps = 3), 则 α ˉ \bar{\alpha} αˉ = [0.9, 0.9 * 0.8, 0.9 * 0.8 * 0.7] = [0.9, 0.72, 0.504]

α ˉ t \bar{\alpha}_t αˉt则是第t步的序列值

  • 1 − α ˉ \sqrt{1-\bar{\alpha}} 1αˉ ,与 α ˉ \bar{\alpha} αˉ相反,是一个0到1的递增序列(序列元素同样不包含边界0与1),

区别于 β ˉ \bar{\beta} βˉ,即 β \beta β的累积值,它是一个接近0的递减序列(代码中没有用到)

  • x 0 x_0 x0是数据集样本,即训练集的图像

  • ϵ \epsilon ϵ是服从标准正态分布的高维张量(tensor)样本, ϵ ∼ N ( 0 , 1 ) \epsilon \sim \mathcal{N}(0,1) ϵN(0,1),即随机噪声

1.2 加噪方程

顺序加噪过程是一个Markov Chain如下:

x t = α t x t − 1 + 1 − α t ϵ x_t = \sqrt{\alpha_t}x_{t-1} + \sqrt{1 - \alpha_t}\epsilon xt=αt xt1+1αt ϵ

上式表示从 x 0 x_0 x0 x t x_t xt是一个加噪的高斯过程,即对图像进行timesteps次加噪,通过 α ˉ t \bar{\alpha}_t αˉt 1 − α ˉ t 1-\bar{\alpha}_t 1αˉt控制图像和噪声的比例,

最终随着t的增长, α t \alpha_t αt的值变小,图像在信号的比例降低,而噪声的比例增大, 最终图像变为全高斯噪声。

但是,这个训练过程每次需要迭代timesteps次,训练效率太低(类似RNN), 这里可以简化,概率表述如下:

q ( x t ∣ x 0 ) ∼ N ( α ˉ t   x 0 , ( 1 − α ˉ t )   I ) q(x_t \mid x_0) \sim \mathcal{N}(\sqrt{\bar{\alpha}_t} \, x_0, (1 - \bar{\alpha}_t) \, \mathbf{I}) q(xtx0)N(αˉt x0,(1αˉt)I)

即只需要 x 0 x_0 x0即可完成训练,其中每个sample都可以是随机的t,即有下述公式:

x t = α ˉ t ⋅ x 0 + 1 − α ˉ t ⋅ ϵ x_t = \sqrt{\bar{\alpha}_t} \cdot x_0 + \sqrt{1 - \bar{\alpha}_t} \cdot \epsilon xt=αˉt x0+1αˉt ϵ

该公式描述了训练过程,随机取batch_size个t,每个t加到上述公式即可得到前向过程的 x t x_t xt, 并与模型的输出做MSE即可。

1.3 加噪过程

1.3.1 数据预处理

  • 训练模型每次送入batch_size个图片,每个图片分一个随机的正数t,t的值域为[0, timesteps],

因此,根据timesteps和batch_size可算出t值。计算代码如下:

t = torch.randint(0, timesteps, (batch_size,), device=device).long() 
  • 根据timesteps, 算出 β \beta β, α \alpha α,进一步算出 α ˉ \bar{\alpha} αˉ (alphas_cumprod), 其shape = [timesteps].
def linear_beta_schedule(timesteps):
    scale = 1000 / timesteps
    beta_start = 0.0003 * scale # 该值过小,去燥不充分
    beta_end = 0.03 * scale # 该值过小,生成胡乱条纹
    return torch.linspace(beta_start, beta_end, timesteps, dtype=torch.float64)
    
betas = linear_beta_schedule(timesteps)

tensor([0.0010, 0.0013, 0.0017, 0.0020, 0.0023, 0.0027, 0.0030, 0.0033, 0.0036,
        0.0040, 0.0043, 0.0046, 0.0050, 0.0053, 0.0056, 0.0060, 0.0063, 0.0066,
        0.0070, 0.0073, 0.0076, 0.0080, 0.0083, 0.0086, 0.0089, 0.0093, 0.0096,
        ...
        0.0934, 0.0937, 0.0940, 0.0944, 0.0947, 0.0950, 0.0954, 0.0957, 0.0960,
        0.0964, 0.0967, 0.0970, 0.0974, 0.0977, 0.0980, 0.0983, 0.0987, 0.0990,
        0.0993, 0.0997, 0.1000], dtype=torch.float64)

alphas = 1. - betas

tensor([0.9990, 0.9987, 0.9983, 0.9980, 0.9977, 0.9973, 0.9970, 0.9967, 0.9964,
        0.9960, 0.9957, 0.9954, 0.9950, 0.9947, 0.9944, 0.9940, 0.9937, 0.9934,
        0.9930, 0.9927, 0.9924, 0.9920, 0.9917, 0.9914, 0.9911, 0.9907, 0.9904,
         ...
        0.9066, 0.9063, 0.9060, 0.9056, 0.9053, 0.9050, 0.9046, 0.9043, 0.9040,
        0.9036, 0.9033, 0.9030, 0.9026, 0.9023, 0.9020, 0.9017, 0.9013, 0.9010,
        0.9007, 0.9003, 0.9000], dtype=torch.float64)

alphas_cumprod = torch.cumprod(self.alphas, axis=0)

tensor([9.9900e-01, 9.9767e-01, 9.9601e-01, 9.9403e-01, 9.9172e-01, 9.8908e-01,
        9.8613e-01, 9.8286e-01, 9.7927e-01, 9.7537e-01, 9.7117e-01, 9.6666e-01,
        9.6185e-01, 9.5675e-01, 9.5136e-01, 9.4568e-01, 9.3973e-01, 9.3350e-01,
         ...