Диффузия и потоки

Расписания шума

Как распределить шум по времени — и почему это выбор про бюджет, а не про эстетику

Шаг 96 из 117 · ~26 мин

Расписание — это распределение бюджета

Из урока 010 известно, что состояние процесса описывается одним числом αˉt\bar\alpha_t. Значит расписание — это просто функция αˉ(t)\bar\alpha(t), монотонно падающая от единицы к нулю. Всё остальное — способ её записать.

Правильная величина для сравнения расписаний — не αˉ\bar\alpha, а отношение сигнала к шуму, и лучше в логарифме:

λt=logαˉt1αˉt\htmlData{k=snr}{\lambda_t} = \log\frac{\bar\alpha_t}{1-\bar\alpha_t}

Причина в том, что напрямую отвечает на вопрос «насколько трудна задача на этом шаге», а αˉ\bar\alpha — нет. И вот главный вопрос всего урока: сколько шагов расписание тратит на трудные задачи, а сколько на бессмысленные?

Линейное против косинусного

Посчитаем, как тысяча шагов распределяется по режимам:

доля шагов, у которыхлинейноекосинусное
SNR <1< 1 (шум преобладает)74%74\%50%50\%
SNR <0.1< 0.152%52\%20%20\%
SNR <0.01< 0.01 (почти чистый шум)33%33\%6%6\%

Треть бюджета линейного расписания уходит на шаги, где от данных не осталось практически ничего. Там нечему учиться и нечего восстанавливать: сеть предсказывает шум по шуму. Косинусное тратит на это же шесть процентов.

Проверьте по виджету, где сигнал и шум сравниваются:

расписаниеSNR =1= 1 при tt
линейное0.260.26
косинусное0.500.50
квадратичное αˉ=1t2\bar\alpha = 1-t^20.710.71

данные при t

сигнал/шум, log-шкала

время t 0.5
0.0784
√ᾱ
0.28
√(1−ᾱ)
0.96
сигнал/шум
0.085
равенство при t
0.259
Кривая падает круто и рано: к t = 0.26 сигнал и шум сравнялись, к t = 0.5 отношение уже 0.085. Оставшаяся половина расписания работает почти впустую — облако к этому моменту неотличимо от гауссова.

Шум преобладает. Шаги в этом режиме дёшевы по информации: восстанавливать почти нечего, и модель на них учится мало.

Здесь стоит быть точным: косинусное расписание не «лучше» само по себе. Оно перераспределяет бюджет туда, где происходит содержательная работа. Если бы данные требовали иного распределения трудности, оптимальным было бы другое расписание — и именно поэтому существуют обучаемые расписания (Variational Diffusion Models), где λt\lambda_t подбирается вместе с моделью.

Оговорка про сам косинус: он загоняет αˉ\bar\alpha к 3.710333.7\cdot10^{-33} на последнем шаге, что бессмысленно и численно опасно. На практике β\beta обрезают сверху (обычно 0.9990.999), и в оригинальной работе это сделано именно так.

Дискретизация: сколько шагов на самом деле нужно

Тысяча шагов при обучении — не то же самое, что тысяча при сэмплировании. Обучение выбирает tt случайно, а сэмплирование обязано пройти путь целиком, и каждый шаг — прогон сети.

DDIM показывает, что подмножество шагов работает: берём каждый kk-й, и

каждый kkпрогонов сети
1110001000
1010100100
20205050
50502020

Почему это законно, если обратное ядро выводилось для соседних шагов? Потому что DDIM переформулирует процесс как детерминированный (probability-flow ОДУ из урока 030), а для ОДУ шаг не обязан быть малым по причине гауссовости — только по причине точности. Требование «β\beta мало, иначе обратное ядро негауссово» исчезает вместе с шумом.

Из этого следует практическое разделение, которое стоит держать в голове:

обучениесэмплирование
ttслучайноепо сетке
число шаговне важноравно числу прогонов
расписаниезадаёт распределение трудностизадаёт узлы сетки
можно ли поменять после обучениянетда

Последняя строка объясняет, почему одну и ту же обученную модель гоняют с двадцатью шагами и с двумя сотнями: сетка сэмплирования — свойство сэмплера, а не модели. Модель знает лишь функцию от αˉ\bar\alpha, и спросить её можно про любое значение.

Сдвиг расписания по разрешению

Тонкость, которая на практике важна и объясняется в одну строку. Изображение 1024×10241024\times1024 при том же αˉ\bar\alpha менее зашумлено, чем 64×6464\times64: соседние пиксели коррелированы, и усреднение по большему числу пикселей восстанавливает низкие частоты даже из сильного шума.

Значит расписание, настроенное на малом разрешении, при переносе на большое оказывается слишком мягким — модель никогда не видит по-настоящему разрушенных данных и на генерации не умеет строить крупную структуру. Отсюда приём «сдвига» расписания: при росте разрешения log-SNR сдвигают вниз, обычно на логарифм отношения сторон.

Итог

  • Расписание — это функция αˉ(t)\bar\alpha(t), и сравнивать расписания нужно в log-SNR.
  • Линейное тратит треть шагов на режим SNR <0.01< 0.01, косинусное — шесть процентов. В этом весь эффект, и он про распределение бюджета, а не про форму кривой.
  • Число шагов при обучении и при сэмплировании — разные величины; вторая меняется после обучения.
  • Пропуск шагов законен, потому что детерминированная формулировка снимает требование малого шага.
  • При росте разрешения то же αˉ\bar\alpha означает меньше разрушения, и расписание приходится сдвигать.

Источники

Проверки

0 из 2
  1. Бюджет шагов и его распределение

    Отметьте все верные утверждения о расписаниях шума.

  2. Куда уходит бюджет расписания

    Реализуйте schedule_budget(kind, t, total_steps, every). Строка kind задаёт расписание:

    • "cosine": αˉ(t)=f(t)f(0)\bar\alpha(t) = \dfrac{f(t)}{f(0)}, где f(u)=cos2 ⁣(u+s1+sπ2)f(u) = \cos^2\!\left(\dfrac{u + s}{1 + s}\cdot\dfrac{\pi}{2}\right) и s=0.008s = 0.008;
    • "quadratic": αˉ(t)=max(1t2, 0)\bar\alpha(t) = \max(1 - t^2,\ 0);
    • "linear": непрерывный вариант DDPM, logαˉ(t)=10000tlog(1β(u))du\log\bar\alpha(t) = 1000 \displaystyle\int_0^t \log\big(1 - \beta(u)\big)\,du с β(u)=104+(0.02104)u\beta(u) = 10^{-4} + (0.02 - 10^{-4})\,u. Интеграл возьмите суммой Римана по средним точкам с двумястами подынтервалами — так эталон и посчитан.

    Верните [alpha_bar, log_snr, hard_fraction, sampler_calls]:

    • alpha_bar — значение αˉ\bar\alpha в точке t;
    • log_snr = logαˉ1αˉ\log\dfrac{\bar\alpha}{1-\bar\alpha};
    • hard_fraction — доля узлов равномерной сетки ti=i/total_stepst_i = i/\texttt{total\_steps} при i=1total_stepsi = 1 \dots \texttt{total\_steps}, у которых SNR <1< 1;
    • sampler_calls = total_steps/every\lfloor \texttt{total\_steps} / \texttt{every} \rfloor — сколько прогонов сети останется, если брать каждый every-й шаг.

    Проверить себя можно так: у квадратичного расписания SNR равна единице ровно при t=1/2t = 1/\sqrt{2}, поэтому log_snr там обязан быть нулём.

    функция schedule_budget

    Загрузка редактора…

    Ctrl/⌘ + Enter