Это регургитация предыдущего Оригинальное название: Stable Diffusion from ScratchМне требуется более 1 дня, чтобы понять и реализовать код в этой статье. Случайно оригинальные статьи были удалены из Интернета - настолько, насколько сомнительно, что я копировал чужую работу - ну, оригинал больше не доступен, поэтому мой может стать авторитетной копией. И чтобы было ясно: я глубоко восхищаюсь авторами, которых я копирую. Я завидую их умственной ловкости и силе (а также времени и ресурсам, которые у них есть, чтобы продолжить эти исследования). Я восхищаюсь людьми, которые изобрели стабильную диффузию и другие алгоритмы в ее окрестностях. Ниже приведена моя скромная попытка реализовать код из этой статьи и получить стабильный экземпляр диффузии, написанный с нуля.
Кроме того, я буду в основном сосредотачиваться на вещах, с которыми я борюсь, так что это может быть не полный и полный учебник. Кроме того, для читателя я настоятельно рекомендую эту книгу: Погружение в глубокое обучениеОчень четко написано, это мне очень помогло. Их объяснение более яростного кодирования времени сделало концепцию кристально ясной для меня. Итак, краткая таблица содержания для этой статьи:
Таблица содержимого
- 1D передняя/обратная диффузия
- U-Net архитектура для работы с изображениями
- потеря, связанная с изучением функции оценки
- Модель внимания для условной генерации
- автокодер

Базовая передняя и обратная диффузия
Для архитектуры стабильной диффузии. Допустим, у нас есть данные (изображения или одна точка данных), и мы добавляем к ним шум. Затем мы тренируем нейронную сеть, чтобы обезвредить наши данные. Так что мы будем иметь переднюю диффузию (добавляющий шум) и обратную диффузию (удаляющий шум). Для первого шага мы используем очень простую одномерную точку (y), которая дифференцируется как функция времени (x).
$$ x(t + \Delta t) = x(t) + \sigma(t) \sqrt{ \Delta t} \; r $$
Где \( \sigma(t) > 0 \) - сила шума, \( \Delta t \) - размер шага, а \( r \sim \mathcal{N} (0, 1) \) - стандартная нормальная случайная величина. Мы неоднократно добавляем нормально распределенный шум в наш образец. Часто сила шума \(\sigma(t) > 0 \) выбирается в зависимости от времени и становится выше по мере увеличения t. Это передний проход, поэтому \(\sigma(t)\) увеличивается со временем при добавлении шума и уменьшается со временем при его удалении.
Давайте реализуем передний проход в питоне.
## Simulate forward diffusion for N steps.
def forward_diffusion_1d(x0, noise_strength_fn, t0, nsteps, dt):
"""x0: initial sample value, scalar
noise_strength_fn: function of time, outputs scalar noise strength
t0: initial time
nsteps: number of diffusion steps
dt: time step size
"""
# Initialize trajectory
x = np.zeros(nsteps + 1); x[0] = x0
t = t0 + np.arange(nsteps + 1)*dt
# Perform many Euler-Maruyama time steps
for i in range(nsteps):
noise_strength = noise_strength_fn(t[i])
random_normal = np.random.randn()
x[i+1] = x[i] + random_normal
return x, t
## Example noise strength function: always equal to 1
def noise_strength_constant(t):
return 1
Давайте запустим его и визуализируем:
nsteps = 100
t0 = 0
dt = 0.1
noise_strength_fn = noise_strength_constant
x0 = 0
num_tries = 5
for i in range(num_tries):
x, t = forward_diffusion_1d(x0, noise_strength_fn, t0, nsteps, dt)
plt.plot(t, x)
plt.xlabel('time', fontsize=20)
plt.ylabel('$x$', fontsize=20)
plt.title('Forward diffusion visualized', fontsize=20)
plt.show()

Мы можем изменить этот процесс диффузии с помощью аналогичного правила обновления:
$$ x(t + \Delta t) = x(t) + \sigma(T - t)^2 \frac{d}{dx}\left[ \log p(x, T-t) \right] \Delta t + \sigma(T-t) \sqrt{\Delta t} \ r $$
Где?
$$ s(x, t) := \frac{d}{dx} \log p(x, t) $$
- x = шумное изображение в момент t
- \(p(x, t)\) = плотность вероятности x в момент времени t
- \(\frac{d}{dx} \) = градиент относительно x
- \(s(x, t)\) = функция оценки
Это ключевой концептуальный момент в диффузионных моделях и стабильной диффузии. Цель модели состоит в том, чтобы научиться обезвреживать x, перемещая его в области с более высокой вероятностью распределения данных. Почему log(p) вместо p? Это имеет численные преимущества. Вероятности p(x) часто очень малы, особенно в больших размерах.

Производная от бревна небольшого значения довольно велика. Кроме того, бревно превращает продукты в суммы, делая градиенты более стабильными. Градиент журнала дает направление, а не величину, которая зависит от абсолютного значения p(x). log p(x) и p(x) равнозначны по оптимизации, но первые гораздо более стабильны в численном отношении. Многие алгоритмы обучения (MLE, score matching) естественно используют log.
Что значит иметь вероятностное распределение изображения? Для этого я отношу вас к вариационным автокодерам (VAE), которые кодируют вход (изображение) как распределение вероятностей (1) центр и (2) стандартное отклонение в скрытом пространстве. Скрытое пространственное представление изображения в VAE - это именно то, что является изученным распределением вероятностей изображения.
На практике мы еще не знаем функцию оценки; вместо этого мы должны ее изучить. Один из способов научиться этому — обучить нейронную сеть «денозировать» образцы с помощью очерняющей цели.
$$ J := \mathbb{E} {t\in (0, T), x 0 \sim p 0(x 0)}\left[ \ \Vert s(x {noised}, t) \sigma^2(t) + (x {noised} - x 0) \Vert^2 2 \ \right] $$
где \(p 0(x 0)\) является нашим целевым распределением (например, фотографии кошек и собак), и где \(x {noised}\) является выборкой целевого распределения (x 0\) после одного шага диффузии, т.е.\(x {noised} - x 0\) Это просто нормально распределенная случайная переменная.
Вот еще один способ написания того же самого, который ближе к фактической реализации. путем замены \[\begin{equation} x {noised} = x 0 + \sigma(t) \epsilon, \; \epsilon\sim \mathcal N(0,I) \end{equation}\] Мы получили эту объективную функцию. \[\begin{equation} J := \mathbb{E} {t\in (0, T), x 0 \sim p 0(x 0), \epsilon \sim \mathcal N(0,I)}\left[ \\Vert s(x 0 + \sigma(t) \epsilon, t) \sigma(t) + \epsilon \Vert^2 2 \ \right] \end{equation}\
символзначение\(J\)Функция потерь для обучения сети оценок \(s \theta \)\(\mathbb{E} {t \in (0,T), x 0 \sim p 0(x 0)}[\cdot] \)Ожидание с течением времени \(t\) и образцы данных \(x 0\)\( x 0 \sim p 0(x 0)\)\(x 0\) извлекается из распределения данных (например, изображений)\( x {\text{noised}} \)Шумная версия \(x 0\) в момент времени (t)\(s(x {\text{noised}}, t)\)Вывод сети с оценкой, приближение \(\nabla x \log p t(x {\text{noized}})\)\(\sigma(t)\)Функция шумового расписания (стандартное отклонение гауссовского шума в момент времени t)\(\Vert \cdot \Vert 2^2 \)Квадратная норма L2 (евклидово расстояние в квадрате)Мы учимся предсказывать сколько шума было добавлено к каждой части образцаМы должны быть в состоянии сделать это хорошо в любое время. \(t\) в процессе диффузии и для каждого (x 0\) в нашем оригинальном (собаки/кошки/и т.д.) распределении.
Весь термин по существу: прогнозируемый шум - фактический шум, поэтому его квадратизация и ожидание дают среднюю квадратную потерю ошибок для обучения сети оценок.
Зачем умножать на \(\sigma^2(t)\)? В выводах, соответствующих баллам, оптимальный балл связан с шумом, добавленным по \(\sigma^2(t)\):
\[ s \theta(x {\rm noised}, t) \approx - \frac{x {\rm noised} - x 0}{\sigma^2(t)} \.
Умножение на \(\sigma^2(t)\) удаляет масштабирование, поэтому сеть учится предсказывать фактический шум. Интуитивное объяснение:
- Возьмите чистую точку данных \( x {0} \)
- Добавьте гауссов шум, чтобы получить \( x {\text{noised}} \)
- Перейдите \( x {\text{noised}} \) в сеть \(s \theta \)
- Вычислите, насколько близко \(s \theta(x {\rm noised}, t) \, \sigma^2(t)\) к фактическому шуму \((x {\rm noized} - x 0)\)
- Среднее значение для всех точек данных и всех времен t
По сути, вы учите сеть, как «обезвредить» шумный образец в любое время.