Compute the Min-SNR-γ per-timestep loss weights, for all three of the prediction targets a diffusion model can be trained on.
The DDPM "simple" loss weights every timestep equally in ε-space. Viewed in x0-space that is an implicit weight of SNR(t), which is enormous at low noise and tiny at high noise. The result is a multi-task optimisation where a handful of easy, low-noise timesteps dominate the gradient and the tasks fight each other.
Efficient Diffusion Training via Min-SNR Weighting (Hang et al., 2023) clamps that weight:
wt(x0)=min(SNR(t),γ)
with γ=5 working well in practice. Because the three parameterizations are related by fixed scalings of the same error, the clamped weight must be converted into whichever space the loss is actually computed in:
Note what the epsilon case does: wherever SNR(t)≤γ the weight is exactly 1, i.e. Min-SNR leaves the high-noise steps at the standard simple loss and only down-weights the easy low-noise ones.
Implement:
def min_snr_weights(alpha_bars, gamma=5.0, prediction_type="epsilon"):
Return a 1-D NumPy array of weights the same length as alpha_bars. Raise ValueError for an unknown prediction_type.
A 1-D NumPy array of weights.
ab = np.array([0.9, 0.5, 0.1])
print(np.round(min_snr_weights(ab, 5.0, "x0"), 4).tolist())
print(np.round(min_snr_weights(ab, 5.0, "epsilon"), 4).tolist())
print(np.round(min_snr_weights(ab, 5.0, "v"), 4).tolist())
[5.0, 1.0, 0.1111]
[0.5556, 1.0, 1.0]
[0.5, 0.5, 0.1]
The SNRs are [9.0, 1.0, 0.1111]. Clamping at 5 gives [5.0, 1.0, 0.1111] for "x0". Dividing by the SNR gives [0.5556, 1.0, 1.0] for "epsilon" -- only the first, easiest timestep is damped.
ab = np.array([0.9, 0.5, 0.1]) print(np.round(min_snr_weights(ab, 5.0, "x0"), 4).tolist()) print(np.round(min_snr_weights(ab, 5.0, "epsilon"), 4).tolist()) print(np.round(min_snr_weights(ab, 5.0, "v"), 4).tolist())
[5.0, 1.0, 0.1111] [0.5556, 1.0, 1.0] [0.5, 0.5, 0.1]
The SNRs are [9.0, 1.0, 0.1111]. Clamping at gamma = 5 gives the x0 weights [5.0, 1.0, 0.1111]. Dividing by the SNR converts to epsilon space: 5/9 = 0.5556, 1/1 = 1.0, 0.1111/0.1111 = 1.0 -- only the low-noise timestep is damped. Dividing by SNR + 1 gives the v-space weights.
gamma > 0; entries of alpha_bars lie in (0, 1).prediction_type must raise ValueError.