Назад
775

Ускорение диффузионных моделей за счёт кэширования

775

Введение

За последние пару лет диффузионные модели прочно закрепились в мире генеративных (и не только) задач.

С их помощью мы получаем качественный и разнообразный материал при генерации:

  • изображений;
Рисунок 1. Генерация изображений с помощью модели Midjourney
  • видео;
Рисунок 2. Пример генерации видео [источник: Ray 2]
  • музыки.

И это ещё не весь список 🙂

Ускорение диффузионных моделей

К сожалению, этот процесс остаётся достаточно медленным. В предыдущих постах мы рассмотрели различные способы ускорения диффузионных моделей за счёт уменьшения количества шагов, включая consistency models и diffusion distillation (part 1, part 2).

А сегодня мы познакомимся с ещё одной парадигмой ускорения диффузионных моделей — ускорения самого шага сэмплирования.

В нашем обзоре мы рассмотрим две работы, которые вышли примерно в одно время. Их основная идея — кэширование информации при сэмплировании для свёрточных диффузионных моделей.

DeepCache (arxiv)

Мотивация

Как было сказано ранее, ускорять диффузионные модели можно не только за счёт уменьшения количества шагов, но и с помощью ускорения каждого шага. А как можно ускорить шаг? Например, переиспользовать что-то с предыдущих шагов.

Сначала авторы выдвигают предположение о том, что на соседних шагах расшумления генерации имеют схожие высокоуровневые фичи. В этом можно убедиться, например, посмотрев на рисунок ниже. Мы видим, что на соседних шагах извлекаемые из up-sampling block U2 Stable diffusion фичи очень похожи.

Рисунок 3. Feature maps in up-sampling block U2 in Stable diffusion [источник]

Помимо визуального сходства, авторы анализируют три различные диффузионные модели и обнаруживают следующее: независимо от модели хотя бы 1% соседних шагов имеет очень высокое сходство (> 0.95).

А в некоторых случаях (например, DDPM for LSUN-Church и LSUN-bedroom) одни шаги демонстрируют высокую степень сходства с 80% других шагов, как указано на рисунке ниже (с).

Рисунок 4. Heatmap сходства между фичами U2 на всех слоях для 3-х различных видов диффузионных моделей и процент шагов со сходством более 95% с текущим шагом [источник]

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

Основной пайплайн

Архитектура UNet для диффузионной модели состоит из Down, Mid и Up блоков и, соответственно, skip-connections, на которых и сфокусировались авторы.

Рисунок 5. Основной вид латентной диффузионной модели с архитектурой UNeT; в красном круге — skip-connections [источник]

Пусть у нас есть два соседних шага: \(t\) и \(t-1\). Сгенерируем сначала \(x_t\), затем для генерации \(x_{t-1}\) извлечем high-level features, полученные на предыдущем шаге. Это значит, что мы кэшируем фичи с up-sampling blocks прошлого шага для каждой skip-branch (обведены красным цветом на рисунке 5) ветки \(m\):

\(F_{\text{cache}}^t \leftarrow U_{m+1}^t(\cdot)\)

Далее на шаге \(t-1\) мы должны вычислить только Down-блоки \(D_m^{t-1}\), тогда на Up-блоках получится следующий результат:

\(U_m^{t-1} \leftarrow \text{Concat}(D_m^{t-1}(\cdot), F_{\text{cache}}^t)\)

Этот процесс проиллюстрирован на рисунке ниже:

Рисунок 6. Основная идея кэширования в свёрточных диффузионных моделях [источник]

Мы видим, как после кэширования на первом шаге мы затем можем захватывать вычисления одного down-sampling блока \(D_1^{t-1}\).

Кэширование для всего процесса инференса

Описанный выше алгоритм теперь надо каким-то образом применить не только к двум соседним шагам, но и ко всему процессу инференса. Как мы уже видели выше, сходство фичей заметно на нескольких последовательных шагах денойзинга. Поэтому самый простой способ применить этот алгоритм — сделать кэширование просто один раз в \(N\) шагов. То есть полный инференс мы будем проводить на шагах \(\text{Steps} = \{\text{step } \in \mathbb{N}| \text{x} = iN, 0 \leq i \leq k\}\) , \(k = \lceil T/N \rceil\).

Non-uniform caching

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

\(\mathcal{L} = \{l_i| l_i \in \text{linear space } \Big( (-c)^{\frac{1}{p}}, (T-c)^{\frac{1}{p}}, k\Big)\}\)

\(\text{Steps} = \text{unique int} (\{i_k |i_k = (l_k)^p + c, \text{where } l_k \in \mathcal{L} \})\)

где \(\text{linear space}(s, e, n)\) — равномерное распределение \(n\) чисел на отрезке от \(s\) до \(e\), а \(\text{unique int}(\cdot)\) — преобразование числа в int, которое следит, чтобы шаги не повторялись. Гиперпараметр \(c\) выбирает «центаральный» шаг для заданного linear space.

В этом уравнении частота изменения индексов изменяется квадратично по мере удаления от центрального временного шага \(c.\) Важно отметить: описанная стратегия представляет собой лишь один из возможных подходов. Альтернативные последовательности, особенно — сосредоточенные на конкретном временном шаге, также могут приводить к улучшению качества изображения.

Авторы дополнительно проводят исследования, выбирая различные значения параметра \(c\):

Рисунок 7. Сравнение основных метрик на датасете ImageNet \(256\times256\) для различных значений центральной точки с при фиксированном \(p=1.2\); интервал кэширования равен 20 [источник]

И параметра \(p\):

Рисунок 8. Сравнение основных метрик на датасете ImageNet \(256\times256\) для различных значений центральной точки p при фиксированном \(с=120\); интервал кэширования равен 20 [источник]

Резюме

Таким образом, можно записать основной алгоритм предложенного метода:

Рисунок 9. Основной алгоритм кэширования в DeepCache [источник]

Результаты

Рисунок 10. Результаты кэширования для различных интервалов [источник]

Сравнение с бейзлайнами

Авторы протестировали свою гипотезу на нескольких диффузионных моделях:

  • DDPM;
  • LDM;
  • Stable Diffusion.

А также для различных сэмплеров шагов:

  • 100-step DDIM для DDPM;
  • 250-step DDIM для LDM;
  • 50-step и 25-step PLMS для Stable Diffusion.

Также авторы для сравнения берут BK-SDM — модель, где они убирают часть слоёв архитектуры для ускорения.

Unconditional generation

Рисунок 11. Метрики unconditional генерации для различных диффузионных моделей [источник]

Class-conditioning results

Рисунок 12. Метрики class-conditional генерации [источник]

Stable Diffusion test

Рисунок 13. Примеры различных генераций для Stable diffusion [источник]
Рисунок 14. Основные метрики для обычной диффузионной модели и кэшированной [источник]
Рисунок 15. Метрики для обычной диффузионной модели SDv1.5 c сэмплером PLMS с различным числом шагов, а также метода DeepCache c различным количеством шагов кэширования для uniform и non-uniform стратегий [источник]

Выводы

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

Ещё картинки
Рисунок 16. Stable Diffusion v1.5: Samples with 50 PLMS steps (upper line) and 50 PLMS steps + DeepCache with N=5 (lower line). The speedup Ratio here is 2.15×. Here we select prompts from the MS-COCO 2017 validation set [источник]
Рисунок 17. Stable Diffusion v1.5: Samples with 50 PLMS steps (upper line) and 50 PLMS steps + DeepCache with N=5 (lower line). The speedup Ratio here is 2.15×. Here we select prompts from the MS-COCO 2017 validation set [источник]
Рисунок 18. LDM-4-G for ImageNet: Samples with DDIM-250 steps (upper line) and DDIM-250 steps + DeepCache with N=10 (lower line). The speedup Ratio here is 6.96× [источник]
Рисунок 19. DDPM for LSUN-Bedroom: Samples with DDIM-100 steps (upper line) and DDIM-100 steps + DeepCache with N=5 (lower line). The speedup Ratio here is 1.48× [источник]
Рисунок 20. DDPM for LSUN-Churches: Samples with DDIM-100 steps (upper line) and DDIM-100 steps + DeepCache with N=5 (lower line). The speedup Ratio here is 1.48× [источник]

Cache Me if You Can (arxiv)

Однако у предыдущего метода имеется ряд недостатков. Например, шаги для кэширования одинаковые и предопределённые для всех картинок. Более полной и подробной работой со схожей идеей является статья «Cache me if you can» (правда, у неё нет кода 😞).

Давайте разберём её подробнее.

Мотивация

Мотивация здесь такая же, как и в предыдущем методе.

Мы также рассматриваем UNet-архитектуру, состоящую из блоков \(B_i(x_i, s_i)\), \(i \in [0, N-1]\), N — число блоков, s — дополнительная информация.

В предложенных архитектурах диффузионных моделей имеются skip-connection блоки. Таким образом, \(B_i(x,s)\) записывается следующим образом:

\(B_i(x,s) = C_i(x, s) + \text{concat}(x, s)\)

\(C_i(x, s) = \text{layers}_i(\text{concat}(x, s))\)

Авторы проводят аналогичное исследование и смотрят, как сильно изменяется результат в зависимости от шага. Для этого они вводят метрику (обратите внимание, что метрика является относительной величиной!):

\(L1_{rel}(i, t) = \frac{\|C_i(x_t, s_t) — C_i(x_{t-1}, s_{t-1})\|_1}{\|C_i(x_t, s_t)\|_1}\)

Далее авторы анализируют изменение фичей.

Во-первых, они визуализируют результат:

Рисунок 21. Визуализация фичей двух блоков диффузионной модели с помощью PCA на разных шагах расшумления [источник]

А также считают метрику на 32-х различных картинках с 2-мя разными сидами, и при этом учитывают стандартное отклонение.

Рисунок 22. L1 relative метрика для различных блоков с разным разрешением для шагов денойзинга [источник]

Авторы делают следующие выводы:

  1. Плавное изменение результатов по шагам.
  2. Разные блоки ведут себя по-разному.

В целом очень похоже на то, что утверждают авторы DeepCache.

Однако авторы улучшают предложенную выше идею, добавляя некоторые модификации:

  • динамическое кэширование: автоматический выбор, когда и что кэшировать.
  • Shift and Scale: дополнительный скейлинг для избавления от артефактов кэширования.

Давайте разберёмся с этими пунктами подробнее.

Основной пайплайн

Рисунок 23. Основная идея кэширования с учётом shift and scale [источник]

Предполагается, что нам не надо вычислять все блоки на всех шагах с учётом слабого изменения, что приводит к идее кэширования.

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

\(L1_{rel}(i, t) = \frac{\|C_i(x_t, s_t) — C_i(x_{t-1}, s_{t-1})\|_1}{\|C_i(x_t, s_t)\|_1}\)

А затем сравнивают её с определённым трешхолдом. То есть интуиция следующая: для блока \(i\) мы сохраняем кэшированное значение, посчитанное на шаге \(t_a\), пока накопленное изменение не достигнет трешхолда \(\delta\). Как только мы доходим до трешхолда — значение пересчитывается:

\(\sum_{t=t_a}^{t_{b-1}} \text{L1}_{\text{rel}}(i,t) \leq \delta < \sum_{t=t_a}^{t_b} \text{L1}_{\text{rel}}(i,t)\)

Трешхолд подбирается имперически, ниже приведены результаты для различных значений \(\delta\):

Рисунок 24. Влияние значения трешхолда на качество и скорость генерации [источник]

Увеличение трешхолда действительно ведёт к большему ускорению, однако в то же время может заметно снизить качество генерации, добавляя артефактов и в целом ухудшая генерацию.

Авторы приводят пример расписания кэширования для LDM-512 на 20 шагах DPM.

Рисунок 25. Расписание кэширования модели LDM-512 на 20 шагах DPM [источник]

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

Scale-shift

Этот алгоритм уже работает неплохо, но при таком кэшировании могут возникать артефакты на итоговых картинках.

Для избавления от подобных артефактов авторы предлагают scale-shift adjustment mechanism. Он включает в себя дополнительные, зависящие от шага времени \(t\) scale и shift параметры для каждого слоя, который принимает кэш.

Такие величины получаются при оптимизации на выбранном трейн-сете. Алгоритм проиллюстрирован на рисунке ниже. Модель, работающая с кэшированием, представлена как студент, в то время как обычная модель — учитель. Сначала мы запускаем процесс расшумления студентом из шума, а затем аналогично учителем. Обратите внимание: учитель стартует каждый раз из точки траектории ученика. Затем считается лосс между двумя полученными траекториями (предсказаниями ученика и учителя на каждом шаге).

Рисунок 26. Основной пайплайн обучения shift и scale величин [источник]

Результаты

Основные эксперименты авторы проводят с моделями LDM-512 и EMU-768. Все эксперименты проводятся на A100 GPU.

Сравнивают они с двух точек зрения:

  • с фиксированным количеством шагов можно провести ускорение, не потеряв в качестве;
  • имея фиксированные вычислительные ресурсы можно проделать больше шагов и получить лучшее качество.

Ниже приведены результаты для кэширования с / без shift-scale техникой:

Рисунок 27. Визуализация результатов генерации бейзлайнов с разным количеством шагов, а также моделей с кэшированием [источник]

Также ниже приведено сравнение базовых моделей, моделей с кэшированием и с дополнительной shift-scale корректировкой. Отметим, что shift-scale корректировка помогает улучшить метрику FID, однако совсем немного даёт проседание в скорости:

Рисунок 28. Метрики качества и скорости для диффузионных моделей и применённого к ним алгоритма кэширования с / без использования shift-scale поправки [источник]

Также авторы приводят анализ картинок людьми, где картинки с / без кэширования занимают одинаковое время. Ниже приведены результаты.

Рисунок 29. Оценка изображений человеком для моделей с / без кэширования для разного количества шагов [источник]

Выводы

В отличие от DeepCache, эта работа предлагает более сложный подход к решению задачи ускорения за счёт кэширования. Авторы предлагают делать динамический кэшинг и дополнительную корректировку на shift и scale. В целом, выглядит более интересно, однако без кода, что усложняет возможность тестирования данной модели.

Еще немного картинок
Рисунок 30. Результаты LDM-512 — DDIM 50 Steps [источник]
Рисунок 31. Результаты LDM-512 — DDIM 50 Steps [источник]
Рисунок 32. Результаты EMU-768 — DPM 20 Steps [источник]

Общие выводы

Итак, сегодня мы познакомились с двумя интересными статьями, которые предлагают ускорять свёрточные диффузионные модели за счёт кэширования результатов на разных шагах, но не уменьшать количество шагов. Это альтернативный метод ускорения диффузионных моделей.

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

Для трансформерной диффузии всё может выглядеть немного иначе, хотя некоторые идеи применимы и там 🙂

Generative Computer Vision

Для тех, кто хочет разобраться в области генеративного CV. Узнаете, как устроены SOTA модели и подходы, научитесь тюнить генеративные модели, освоите super-resolution, face restoration, text2image, и другие задачи

Телеграм-канал

DeepSchool

Короткие посты по теории ML/DL, полезные
библиотеки и фреймворки, вопросы с собеседований
и советы, которые помогут в работе

Открыть Телеграм

Увидели ошибку?

Напишите нам в Telegram!