噪声调度器(Noise Schedulers)是扩散模型生成过程中的关键组件,它们决定了扩散过程中噪声水平如何随时间变化。合适的噪声调度可以显著提高生成质量和效率。我将详细解析 SD WebUI 中的各种调度器,包括其数学原理和具体实现。
在扩散模型中,噪声调度器定义了 σ (sigma) 值如何从高噪声水平逐渐降低到低噪声水平。这些 σ 值构成了采样过程中的离散步骤。
首先让我们查看 SD WebUI 中的调度器代码实现:
Read file: modules/sd_schedulers.py
Read file: repositories/k-diffusion/k_diffusion/sampling.py
Automatic
调度器不是一个实际的调度器,而是让系统基于当前选择的采样器自动选择最合适的调度器。
pythonScheduler('automatic', 'Automatic', None)
例如,当你使用DPM++ 2M采样器时,它默认会使用Karras调度器,因为这是该采样器的推荐调度器。
Uniform调度器使用模型内置的均匀间隔噪声调度,通常是线性间隔的噪声水平。
pythondef uniform(n, sigma_min, sigma_max, inner_model, device):
return inner_model.get_sigmas(n)
它直接从内部模型获取均匀分布的噪声水平,不考虑给定的 sigma_min
和 sigma_max
。
这种调度的特点是简单直接,但对于许多采样器来说并不是最优的选择,尤其是在较少步数的情况下。
Karras调度器来自Karras等人在2022年的论文,是目前最广泛使用的调度器之一,尤其适合DPM系列采样器。
pythondef get_sigmas_karras(n, sigma_min, sigma_max, rho=7., device='cpu'):
"""Constructs the noise schedule of Karras et al. (2022)."""
ramp = torch.linspace(0, 1, n)
min_inv_rho = sigma_min ** (1 / rho)
max_inv_rho = sigma_max ** (1 / rho)
sigmas = (max_inv_rho + ramp * (min_inv_rho - max_inv_rho)) ** rho
return append_zero(sigmas).to(device)
其中:
Karras调度器的特点是在高噪声区域分配更多步骤,在低噪声区域分配较少步骤,这与生成过程中最需要精确控制的区域相匹配。它显著提高了低步数采样的质量。
指数调度器在对数空间中线性安排噪声水平,使噪声按指数衰减。
pythondef get_sigmas_exponential(n, sigma_min, sigma_max, device='cpu'):
"""Constructs an exponential noise schedule."""
sigmas = torch.linspace(math.log(sigma_max), math.log(sigma_min), n, device=device).exp()
return append_zero(sigmas)
其中 从0到1线性变化。
指数调度器在高噪声区域提供稍快的初始去噪,然后在细节形成阶段逐渐减慢,它是一个比均匀调度更好,但比Karras调度更简单的选择。
多项式指数调度器结合了指数衰减和多项式控制,提供更灵活的噪声分布。
pythondef get_sigmas_polyexponential(n, sigma_min, sigma_max, rho=1., device='cpu'):
"""Constructs an polynomial in log sigma noise schedule."""
ramp = torch.linspace(1, 0, n, device=device) ** rho
sigmas = torch.exp(ramp * (math.log(sigma_max) - math.log(sigma_min)) + math.log(sigma_min))
return append_zero(sigmas)
其中:
当 时,它与指数调度器非常相似。但当 时,它会在初始阶段分配更多步骤,使去噪过程更平滑。当 时,初始去噪更快,后期细节生成更慢。
SGM均匀调度器是专为SGM (Score-based Generative Models) 设计的调度器,它在t空间(而非sigma空间)均匀采样。
pythondef sgm_uniform(n, sigma_min, sigma_max, inner_model, device):
start = inner_model.sigma_to_t(torch.tensor(sigma_max))
end = inner_model.sigma_to_t(torch.tensor(sigma_min))
sigs = [
inner_model.t_to_sigma(ts)
for ts in torch.linspace(start, end, n + 1)[:-1]
]
sigs += [0.0]
return torch.FloatTensor(sigs).to(device)
这个调度器首先将 sigma 值转换为 t 空间,在 t 空间中均匀采样点,然后再将这些点转换回sigma值。
这种方法特别适合某些扩散模型变体,比如来自Stability AI的SGM模型。
默认选择:对大多数情况,使用 Automatic
让系统自动选择最合适的调度器。
高质量结果:
Karras
调度器特定模型:
SGM Uniform
调度器Karras
通常也是最佳选择实验和微调:
Polyexponential
并调整 rho 参数不同调度器对生成结果有显著影响:
细节和清晰度:
Karras
调度器通常产生最清晰、最精细的细节Uniform
调度器在同样步数下通常细节较少噪点和纹理:
Exponential
和 Polyexponential
可能在某些纹理上表现更好速度和步数权衡:
Karras
通常有最佳性能扩散模型的去噪过程是从高噪声状态到低噪声状态的转换。这个过程不是均匀的:
因此,理想的噪声调度应该根据每个阶段的重要性分配步骤。这就是为什么Karras调度器在实践中表现优异 - 它在关键的噪声水平分配了更多步骤。
噪声调度器实质上控制着数值积分过程中的"时间步长"。较小的时间步长(在关键区域)提供更高的精度,但需要更多计算步骤。最优的调度器找到了这一权衡的良好平衡点。
这些参数定义了噪声范围:
控制噪声分布的"形状":
所有调度器都使用 append_zero
函数在噪声序列末尾添加一个零:
pythondef append_zero(x):
return torch.cat([x, x.new_zeros([1])])
这确保了最终状态完全无噪声,这对于生成干净的最终图像很重要。
某些调度器(Uniform 和 SGM Uniform)需要 inner_model
参数:
pythonneed_inner_model: bool = False
这是因为它们需要访问模型特定的函数来执行噪声转换。
噪声调度器是扩散模型生成过程中的关键组件,正确选择调度器可以显著提高生成质量和效率:
在实际使用中,除非你有特定需求,否则使用 Automatic 让系统自动选择是最简单有效的方法。如果你想要更精细的控制,Karras调度器在大多数情况下是最佳选择,尤其是与DPM++系列采样器结合使用时。
本文作者:Dong
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC。本作品采用《知识共享署名-非商业性使用 4.0 国际许可协议》进行许可。您可以在非商业用途下自由转载和修改,但必须注明出处并提供原作者链接。 许可协议。转载请注明出处!