Как нарисовать картину, если ты нейросеть

Как нарисовать картину, если ты нейросеть

В качестве вступления будет маленький персональный бомбёж. Мы живём в эпоху, когда информационное пространство засирают всякие журнашлюхи, маркетолухи и прочие владеющие отборной информацией и праведными целями люди. Поэтому нынче куда не плюнь, попадёшь в ИИ. Когда я учился в университете, нам давали эдак с пяток определений ИИ, и ни в одном из них не было ничего похожего на "штука, которая рисует картинки, чтобы отобрать работу у художников". Во всём этом современном вале вестей об ИИ нет никакого уважения к интеллекту. В связи с этим предлагаю мысленно заменять словосочетание "искусственный интеллект" на "перемножение матриц" в любом месте, в котором вы его встретите. Так будет и забавнее, и правдивее. Конец бомбежа.

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

Раньше, в прошлой жизни, для рисования картинок люди использовали подход, называемый GAN. Вы наверняка слышали про какой-нибудь StyleGAN, VQGAN или ещё что-то такое. GAN на нашенском означает генеративно-состязательная сеть, и в этом заключается принцип её обучения.

Берём генератор, кормим ему гауссов шум, он делает из этого картинку. Затем дискриминатор пытается понять, настоящая ли картинка или сгенерированная. Затем дискриминатор мы выкидываем, оставляем генератор и идём улучшать женщин, делая из них 2D.

Вы, наверное, хотите спросить у меня нечто вроде "ДА КАК ТЫ ИЗ ШУМА КАРТИНКУ СДЕЛАЕШЬ-ТО, ТЫ ДУРАК ТАМ СОВСЕМ?" А я вам отвечу, что шум - это всего лишь циферки, а нейросеть их перемножает. Ещё можете глянуть на иллюстрацию и сделать вид, что всё поняли.

Так все и жили, ковыряли эти свои ганы, пробовали новые архитектуры, грели атмосферу видеокартами и горя не знали. Пока однажды один сумрачный гений не додумался: "А что если мы будем возьмём гауссов шум и будем потихоньку итеративно фильтровать его. Когда мы очистим весь шум, мы обнаружим под ним... КАРТИНКУ!"

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

  1. Представляем, что у нас автокодировщик. Это значит, что модель сначала будет чистую картинку полностью зашумлять, а затем этот шум очищать обратно до исходной картинки.

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

  3. Во время обучения берём случайную итерацию t, добавляем то количество шума, которое соответствует этому состоянию, получая картинку x(t). Модель тем временем очищает только от этой итерации шума, получая картинку в предыдущем состоянии x(t-1). Благодаря всё тому же расписанию шума мы знаем, какая на самом деле должна быть картинка в x(t-1), поэтому вычисляем функцию потерь между тем, что должно быть и тем, что выдала модель.

  4. Гоняем пункт 3 до тех пор, пока ошибка не перестанет уменьшаться.

В тексте выше t и t-1 должны быть подстрочными, но как это сделать тут, я не понял, так что засунул в скобки, хотя это и неверно. Просто представьте их подстрочными, как на картинке ниже

Обученная модель берёт белый шум и итеративно его очищает, добиваясь в итоге изображения.

Допустим, вы поняли всё, что написано сверху. Тогда у вас возникает вопрос: "Если мы рисуем что-то из шума, то как нам добиться того, чего мы хотим, а не какой-то усреднённой картинки из данных, бывших в обучении? Мы же тут собрались, чтобы рисовать аниме-тян с огромными сиськами, а не лица каких-то мужиков." Вопрос вполне логичный.

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

Это была безусловная генерация изображений. При работе модель начинает гулять по латентному пространству и тянет оттуда случайное изображение. Нам это не очень подходит.

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

Например, способ под названием guidance. Я переведу это как "направление модели", а вы можете переводить термин, как вам больше нравится.

Рассмотрим так называемый CLIP guidance. Перед началом создания изображения прогоняете текст в его векторное представление через CLIP, на каждой итерации прогоняете получившуюся картинку через CLIP, сравниваете с текстом специальной метрикой, считаете функцию потерь, обновляете градиенты, с помощью этих градиентов обновляете картинку перед следующей итерацией. Повторить, пока не надоест.

Ща расскажу, придётся отправиться немного в прошлое.

Есть такая замечательная контора OpenAI, которая славится тем, что тратит кучу денег на обучение моделек, а потом никому не показывает их код и веса, а только хвастается, что они у них самые лучшие. Именно они сделали GPT, GPT-2, GPT-3, DALL-E и такдале (фьють, ха!) Однажды эти ребята подумали: "А чоб нам не сделать классификатор картинок, который без обучения будет их различать?"

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

Примерно такое было бы ваше самое первое упражнение, если б вы попробовали поковырять картиночные нейросети.

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

Чёрт его знает, использует ли это кто-нибудь вообще, картинки не моя специальность, сорян. Но вот текстовая часть модели оказалась удивительно удачной, и её пользуют в хвост и в гриву.

В путанном объяснении выше с градиентами и прочим было беглое описание подхода, когда у вас есть какая-то там диффузионная модель, и вы хотите заставить её генерить аниме. У этого способа два больших недостатка - вычислительная сложность и потребление памяти. Понятно, что это будет долго, и что вам нужно много видеокарты, чтобы такие штуки делать. В стрельнувших современных моделях, например, в Stable Diffusion это зашито прям в архитектуру, и никаких дополнительных усилий не требуется.

Верхнеуровне Stable Diffusion состоит как бы из двух больших блоков:

  1. Текстовая моделька из CLIP, которая переводит текстовый запрос в некий вектор.

  2. Диффузионная модель, которая постепенно в несколько итераций превращает шум в картинку.

На каждой итерации во второй блок поступает текстовый вектор, который подправляет генерацию. Поверх SD часто пихают дополнительные сетки, которые подправляют лица, увеличивают разрешение итоговой картинки и всякое такое.

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

Почему же диффузионные модели так превзошли ганы, что теперь художники паникуют?

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

  2. Во-вторых, диффузионные модели оказались в целом более стабильны, лучше отображают распределение реальных данных и гораздо менее склонны ко всяким схлопываниям (mode collapse), когда генератор создаёт изображения только из некоторой области распределения, полностью игнорируя все остальные.

  3. В-третьих, в ганы так и не смогли нормально запихнуть хорошие текстовые вектора (например, из CLIP), а применяли костыль с добавлением CLIP поверх сгенерированного изображения и муторным поиском соответствующей тексту картинки в пространстве генератора.

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

Если кому-то интересно более глубокий или более связный, чем у меня, рассказ, то направляю вас к:

  1. Джею Аламмару, у которого я украл несколько иллюстраций

  2. Видосику на ютубе с математикой

  3. Блогпосту про диффузионные модели, написанному до создания SD, с математикой и кодом

  4. Оригинальной статье про SD, если вы совсем упоролись

Все материалы по ссылкам на английском, эсли што.

30
561

Комментарии

Лапка Ёлка за статью

А не пробовал ли кто-то использовать диффузионые модели в качестве генеративной части GAN? Пускай другая сетка оценивает, что там нагенерировалось, чем нам 50 раз перезапускать генерацию.

UPD: нашёл - пробовали

Лапка Ёлка за статьюА не пробовал ли кто-то использовать диффузионые модели в качестве генеративной части GAN? Пускай другая сетка оценивает, что там…
Blazar
Лапка Ёлка за статьюА не пробовал ли кто-то использовать диффузионые модели в качестве генеративной части GAN? Пускай другая сетка оценивает, что там…

Blazar, генерацию не перезапускают 50 раз, она постепенно за 50 шагов превращает шум в изображение. Добавление дискриминатора в обучение может только на латентное пространство повлиять, но не факт, что в хорошую сторону

Blazar, генерацию не перезапускают 50 раз, она постепенно за 50 шагов превращает шум в изображение. Добавление дискриминатора в обучение может только…
Жук
Blazar, генерацию не перезапускают 50 раз, она постепенно за 50 шагов превращает шум в изображение. Добавление дискриминатора в обучение может только…

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

То есть вместо человека, перезапускающего сеть для получения нужного результата, заставить это делать ИИ

Жук, я имею в виду дообучение после обучения на готовом наборе данных. То есть скармливать дискриминатору уже готовое изображение, после чего, скажем…
Blazar
Жук, я имею в виду дообучение после обучения на готовом наборе данных. То есть скармливать дискриминатору уже готовое изображение, после чего, скажем…

Blazar, звучит похоже на то, как прикручивали CLIP. В случае с ганами изображения генерили, пока CLIP не выдаст достаточное соответствие входящему тексту, а в диффузионках на каждой итерации несколько изображений делали, а потом через CLIP гоняли.
Можно и дискриминатор сюда подсунуть, но только так долго считать и памяти много потребуется. Всё таки стараются идти к меньшему потреблению видеопамяти и более быстрым генерациям

Лапка вертухой с ноги с разворота! Годный пост!

Отличная статья, спасибо!