Исходники | Документация | Модели
Diffusers — это библиотека от Hugging Face, которая позволяет работать с сотнями предобученных моделей класса Stable Diffusion для создания изображений и аудио.
Какие задачи можно решать с помощью Stable Diffusion (из библиотеки Diffusers):
Text-To-Image (txt2img)
Самое популярное использование: вы описываете словами что хотите получить, а нейронная сеть выдает вам картинку.
Image-To-Image (img2img)
Генерация новых изображения на основе существующих. Вы предоставляете входное изображение в качестве отправной точки (заготовки) и описываете текстом, что хотите с ним сделать. Диффузионная модель сгенерирует новое изображение с тем же цветом и композицией, что и входное изображение, но с описанными изменениями.
Inpaint
На вход подается изображение, маска и текст. Нейросеть заменит изображение внутри маски тем, что написано в запросе.
Depth-to-image (depth2img)
Эта модель так же используется для создания новых изображений из существующих. Но при этом модель будет учитывать форму и глубину объектов исходного изображения, указанного с помощью карты глубины.
Помимо Stable Diffusion есть еще две сравнимые по качеству модели: Dall-E 2 и MidJerney. Но они закрытые, их весов в открытом доступе нету, а получить доступ к ним можно только за денюжку.
Теория
Прежде чем переходить к практике кратко рассмотрим теорию…
Что такое диффузия
Stable Diffusion принадлежит к классу диффузионных моделей. Их идея состоит в том, чтобы смешать картинку и случайный шум, а далее научить нейросеть восстанавливать оригиналы из зашумленных изображений.
Процесс диффузии состоит из двух частей: прямой и обратный. Прямой процесс портит изображение, обратный восстанавливает.
Прямой процесс состоит из множества шагов (например, 100). Начинается он с исходной картинки. Далее на каждом шаге мы берем картинку с предыдущего шага и добавляем к ней немного шума (1%). И так до последнего шага, на котором исходное изображение превращается в случайный шум.
Теперь у нас на каждый из 100 шагов есть пара изображений: изображение с шумом и сам шум. И мы будем обучать модель предсказывать этот шум, подавая ей на вход изображение с шумом.
Можно управлять тем, сколько шума добавляется к изображению на каждом шаге. Поэтому можно распределить процесс и на 10 шагов и на 100 шагов.
Обратный процесс тоже итерационный: на каждом шаге мы будем просить модель предсказать шум и будем вычитать из изображения этот шум. И так пока не пройдем все шаги и не удалим из изображения весь шум.
Т.к. каждый раз картинка восстанавливается из случайного шума, то каждый раз будет получаться новая картинка.
Содержимое моделей
Stable Diffusion — это не единая модель, а целая система, состоящая из нескольких компонентов и моделей. На этапе инференса, т.е. когда модель уже обучена, мы имеем следующую схему работы:
CLIP
CLIP (текстовый кодировщик модели) — это специальная языковая модель типа Transformer. CLIP получает на входе текст и выдает на выходе вектор чисел, описывающий каждое слово в тексте.
Модель CLIP обучалась на изображениях и описаниях к ним. Основная ее фишка в том, что при обучении она формирует два вектора: для картинки и для текста. И модель учится минимизировать косинусное расстояние между этими векторами. Т.о. выдаваемые ею эмбединги одновременно описывают и текст и картинку.
UNet + Scheduler
UNet — это рабочая лошадка под капотом Stable Diffusion. Именно UNet предсказывает шум. А управляет процессом Scheduler — это некий алгоритм, которые не содержит в себе обучаемых параметров. Он отвечает за то, как именно мы зашумляем изображение. Вместе они поэтапно очищают изображение от шума.
Но мало сгенерировать случайную картинку из шума. Нам еще нужно, чтобы модель обращала внимание на текстовое описание, которое мы ей подаем. Для этого в UNet используются CrossAttention блоки. Они позволяют в процессе очистки смотреть в CLIP, чтобы результат генерации соответствовал текстовому описанию.
Decoder
В процессе работы UNet'а и Scheduler'а от шума очищается не само изображение, а т.н. латентное пространство. Это своего рода сжатое представление изображения. Перед началом работы в этом латентном пространстве генерируется шум и уже он непосредственно очищается. А когда латентное пространство очищено от шума, оно обратно разжимается до нормальной картинки с помощью декодера.
Декодер это часть другой модели — автоэнкодера (Variational Autoencoder, VAE). Автоэнкордер это нейронная сеть из двух подсеток. Энкодер — сжимает изображение, а декодер, учится разжимать его до исходного состояния с максимальной точностью. Собственно, сжатое таким образом изображение и будет нашим латентным представлением. В обученной модели Stable Diffusion присутствует только декодер. При обучении используются обе части.
Цветная картинка с разрешением 512х512 состоит из 262 144 пикселей в каждом из 3 каналов. Если применять к ней процесс напрямую, то это потребует много времени и памяти. Мы же хотим генерировать картинки быстро и на относительно простых видеокартах. Поэтому, автокодер, используемый в Stable Diffusion, имеет коэффициент редукции (уменьшения) 8. Это означает, что изображение формы (3, 512, 512) становится (3, 64, 64) в латентном пространстве, что требует в 8 × 8 = 64 раза меньше памяти.
Установка
Для начала установим библиотеки:
pip install --upgrade diffusers accelerate transformers
После этого нужно найти модель. Где именно их искать рассмотрим ниже. А пока будем использовать модель Stable Diffusion XL с платформы Hugging Face.
Установленная и импортированная библиотека accelerate сильно ускоряет загрузку моделей в память.
Использование
Самый простой способ использовать предварительно обученную модель диффузии для инференса — использовать класс DiffusionPipeline. Он содержит все, что необходимо для диффузионной модели (токенайзер, Unet, декодер и т.д.).
Для экспериментов будем учится преобразовывать текст в изображение.
from diffusers import DiffusionPipeline
pipeline = DiffusionPipeline.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0")
promt = "An image of a squirrel in Picasso style"
image = pipeline(promt=promt).images[0]
image.save("image_of_squirrel_painting.png")
Здесь мы:
Создали экземплар DiffusionPipeline и загрузили предобученную модель с Hugging Face Hub.
Сформировали текстовый запрос и передали его в пайплайн. На выходе из пайплайна получили картинку.
Сохранили изображение.
На что тут следует обратить внимание…
Кэш и локальное использование
DiffusionPipeline загружает и кэширует все компоненты которые входят в пайплайн: модели, токенайзеры, планеровщики и т.д.
Найти их локально вы сможете по следующему пути:
C:\Users\<user_name>\.cache\huggingface\hub
Либо вы можете сразу скачать нужную вам модель локально:
!git lfs install
!git clone https://huggingface.co/runwayml/stable-diffusion-v1-5
А затем указать путь до нее в конвейере:
pipeline = DiffusionPipeline.from_pretrained("./stable-diffusion-v1-5")
Содержимое пайплайна
Как уже говорилось, Stable Diffusion это не монолитная модель, а сложная модульная структура. Что конкретно в нее входит можно посмотреть просто распечатав пайплайн:
StableDiffusionPipeline {
"_class_name": "StableDiffusionPipeline",
"_diffusers_version": "0.19.1",
"_name_or_path": "/root/work/text_to_image/stable_diffusion_v1_5/snapshots/c9ab35ff5f2c362e9e22fbafe278077e196057f0",
"feature_extractor": [
"transformers",
"CLIPImageProcessor"
],
"requires_safety_checker": true,
"safety_checker": [
"stable_diffusion",
"StableDiffusionSafetyChecker"
],
"scheduler": [
"diffusers",
"PNDMScheduler"
],
"text_encoder": [
"transformers",
"CLIPTextModel"
],
"tokenizer": [
"transformers",
"CLIPTokenizer"
],
"unet": [
"diffusers",
"UNet2DConditionModel"
],
"vae": [
"diffusers",
"AutoencoderKL"
]
}
Воспроизводимость
Stable Diffusion создает изображения из случайного шума. Из-за этого при каждом запуске вы будете видеть новую картинку. Это во-первых само по себе может быть не нужно, а во-вторых может помешать вам улучшать картинку посредством изменения параметров (о них ниже). Поэтому вы можете зафиксировать выдаваемое изображение с помощью сида:
import torch
generator = torch.Generator("cuda").manual_seed(0)
image = pipeline(prompt, generator=generator).images[0]
image
Больше про воспроизводимость можно почитать тут.
Скорость и память
По умолчанию DiffusionPipeline выполняет расчеты с точностью float32. Вы можете ускорить процесс инференса (и уменьшить потребляемую память) переключившись на меньшую точность — float16.
pipeline = DiffusionPipeline.from_pretrained(model_name, torch_dtype=torch.float16)
Основные параметры
Рассмотрим некоторые параметры пайплайна…
num_inference_steps
Один из важнейших параметров. Количество шагов шумоподавления. Увеличение num_inference_steps обычно приводит к более качественному изображению, но за счет более длительного расчета. По умолчанию = 50.
image = pipeline(prompt, num_inference_steps=10).images[0]
Соотношение между качеством изображения и количество пройденных шагов сильно зависят используемого от шедулера. Некоторые современные шедулеры могут выдавать картинки хорошего качества и за небольшое количество шагов.
height и width
Задают размеры выходного изображения (в пикселях). Но учтите, что модель обучалась на изображениях определенного размера и их же заточена выдавать. Изменение этих размеров скорее всего ухудшит качество выдаваемого изображения.
image = pipeline(prompt, height=256, width=256).images[0]
guidance_scale
Отвечает за то, насколько точно изображение соответствовать запросу. Более высокое значения побуждает модель генерировать изображения, тесно связанные с текстовым запросом, за счет более низкого качества изображения. Значение по умолчанию — 7,5.
image = pipeline(prompt, guidance_scale=10).images[0]
Массовая генерация
Подбирать картинки поштучно можно довольно долго. Поэтому можно задействовать массовую генерацию.
Например, можно за раз сгенерировать картинки по разным запросам:
# Функция выводит несколько изображений
def image_grid(imgs, rows=2, cols=2):
w, h = imgs[0].size
grid = Image.new("RGB", size=(cols * w, rows * h))
for i, img in enumerate(imgs):
grid.paste(img, box=(i % cols * w, i // cols * h))
return grid
# Запросы
prompts = [
'red car at night on a racing track',
'yellow car at night on a racing track',
'blue car at night on a racing track'
]
# Формируем пайплайн
pipeline = DiffusionPipeline.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0")
images = pipeline(prompts).images
# Сохраняем картинки
#for i in range(len(images)):
# images[i].save(f'{i}.png')
image_grid(images, rows=1, cols=3)
Или можно один и тот же запрос сгенерировать с разными сидами:
prompts = 3 * ['red car at night on a racing track']
pipeline = DiffusionPipeline.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0")
images = pipeline(prompts).images
image_grid(images, rows=1, cols=3)
Сколько картинок влезет в память за раз зависит от используемой модели и вашей карточки. Пробуйте увеличивать количество пока не получите ошибку OutOfMemoryError.
Замена компонентов
Как уже было сказано, Stable Diffusion состоит из различных модулей. И вы можете заменять их по своему усмотрению...
Планировщик
Планировщик задает правила обработки шума. Различные планировщики имеют разные скорости шумоподавления и компромиссы в отношении качества. В библиотеки Diffusers их представлено множество. При этом нельзя сказать, что какой-то из них лучше других. Каждый из них имеет свои плюсы и минусы.
Лучший способ узнать, какой из них лучше всего подходит для вас — попробовать их. Переключаться между планировщиками довольно просто:
from diffusers import EulerDiscreteScheduler
pipeline = StableDiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5")
# Меняем шедулер
pipeline.scheduler = EulerDiscreteScheduler.from_config(pipeline.scheduler.config)
Вы можете узнать, какие планировщики совместимы с текущей моделью, вызвав метод compatibles:
pipeline.scheduler.compatibles
[diffusers.schedulers.scheduling_ddim.DDIMScheduler,
diffusers.utils.dummy_torch_and_torchsde_objects.DPMSolverSDEScheduler,
diffusers.schedulers.scheduling_heun_discrete.HeunDiscreteScheduler,
diffusers.schedulers.scheduling_dpmsolver_singlestep.DPMSolverSinglestepScheduler,
diffusers.schedulers.scheduling_pndm.PNDMScheduler,
diffusers.schedulers.scheduling_unipc_multistep.UniPCMultistepScheduler,
diffusers.schedulers.scheduling_euler_ancestral_discrete.EulerAncestralDiscreteScheduler,
diffusers.schedulers.scheduling_k_dpm_2_ancestral_discrete.KDPM2AncestralDiscreteScheduler,
diffusers.schedulers.scheduling_lms_discrete.LMSDiscreteScheduler,
diffusers.schedulers.scheduling_euler_discrete.EulerDiscreteScheduler,
diffusers.schedulers.scheduling_k_dpm_2_discrete.KDPM2DiscreteScheduler,
diffusers.schedulers.scheduling_deis_multistep.DEISMultistepScheduler,
diffusers.schedulers.scheduling_ddpm.DDPMScheduler,
diffusers.schedulers.scheduling_dpmsolver_multistep.DPMSolverMultistepScheduler]
Автоэнкодер
Также можно поменять и автоэнкодер:
from diffusers import AutoencoderKL
pipeline = StableDiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5")
vae = AutoencoderKL.from_pretrained(
"stabilityai/sd-vae-ft-mse",
torch_dtype=torch.float16
).to("cuda")
pipeline.vae = vae
image = pipeline(prompt).images[0]
Hand made
Для любителей хардкора. Вы можете собрать весь пайплайн из отдельных компонентов. И даже вручную очищать изображение от шума: https://huggingface.co/docs/diffusers/main/en/using-diffusers/write_own_pipeline
Инженерия запросов
Важнейшая часть. Важнее любых других параметров — умение составлять правильные запросы.
Технически запрос состоит из двух частей и должен укладываться в 75 слов:
Prompt — положительный запрос. Указываем то, что хотим видеть.
Negative Prompt — отрицательный запрос. Указываем, чего не должно быть на изображении.
pipeline = DiffusionPipeline.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0")
promt = "An image of a squirrel in Picasso style"
negative_prompt = "ugly, bad anatomy"
image = pipeline(promt=promt, negative_prompt=negative_prompt).images[0]
image
Положительный запрос рассмотрим далее. Что касается отрицательного: помимо логической части (чего вы конкретно не хотите видеть на картинке) есть на просторах интернета много универсальных отрицательных подсказок, которые помогают улучшить изображение. Например такая:
ugly, tiling, poorly drawn hands, poorly drawn feet, poorly drawn face, out of frame, extra limbs, disfigured, deformed, body out of frame, blurry, bad anatomy, blurred, watermark, grainy, signature, cut off, draft
Анатомия запроса
За время существования Stable Diffusion авторы и сообщество выработало общие правила и рекомендации по составлению запроса, которые могут сильно повлиять на выдаваемое качество. Начнем с...
Общие советы:
-
Будьте максимально конкретны. Чем точнее вы опишите чего вы хотите, тем быстрее вы получите нужный результат.
Неправильно: Милый котенок
Правильно: Милый серый котенок с голубыми глазами, в галстуке-бабочке сидит на диване
-
Объясняйте простыми слова, как для ребенка. Не используйте метафор, эвфемизмов, словесных каламбуров, избегайте двусмысленных слов и фраз, которые могут иметь несколько интерпретаций.
Неправильно: Обезьяны занимаются бизнесом
Правильно: Обезьяны сидят за столом в деловых костюмах
Используйте синонимы для описания одного и того же явления. Так модель попытается понять, к чему вы больше всего стремитесь. Например, если вы хотите передать мрачное настроение, то одновременно используйте слова «темно», «мрачно», «плохо освещено», «страшно», «хоррор».
Следите за порядком слов, он имеет значение. Stable Diffusion придает больший вес первым словам, но может пропустить то, что вы указали в конце. Вы можете перемещать части своего запроса и получать кардинально разные результаты. Например, если вас не устраивает качество человеческого лица, перенесите его описание ближе к началу.
Материал
Укажите материал, используемый для создания художественных работ. Вот некоторые примеры:
portrait
digital painting
concept art
ultra realistic illustration
underwater portrait
photograph
oil painting
Здесь и далее будем добавлять к предложению "fat cat lies on the couch, <var>" одно из значений.
Стиль
Под стилем понимается художественный стиль изображения. Например:
hyperrealistic
pop-art
modernist
art nouveau
fantasy
surrealist
steampunk
photorealisic
sci-fi
Автор
Укажите автора произведения и модель постарается изобразить картинку в его фирменном стиле. Примеры:
Stanley Artgerm Lau
John Singer Sargen
John Collier
Frida Kahlo
Alphonse Mucha
Van Gogh
Веб-сайт
Есть сайты на просторах интернета, который собирают изображения в определенных стилях и жанрах (и на которых училась модель). Использование их в запросе поможет направить модель в сторону этих стилей.
pixiv — стиль японских аниме
pixabay — коммерческие стоковые фотографий
artstation — современная иллюстрация, фэнтези
deviant art — сообщество художников общего типа
Разрешение
Разрешение указывает насколько четким и подробным должно быть изображение.
unreal engine
sharp focus
8k
vray
highly detailed
sharp focus
Цвет
Вы можете контролировать цветовую гамму изображения. Указанные цвета могут присутствовать либо в виде общего тона изображения, либо отражаться в отдельных объектах.
iridescent gold
silver
vintage
rust
Свет
Свет – одна из ключевых составляющих хорошей композиции.
cinematic lighting
dark
atmospheric illumination
god rays
sunlight
backlight
Это далеко не полный список категорий (и уж точно не полный список значений). Вам не обязательно включать все эти компоненты в каждый свой запрос. Относитесь к ним как к контрольному списку, чтобы помнить, что можно использовать.
Вы можете пробовать комбинировать значения из одной категории. Например, указать одновременно двух художников: Stanley Artgerm Lau and Alphonse Mucha. И модель попытается смешать два их стиля.
Вспомогательные сервисы
Т.к. составление запросов это целое искусство, то в интернете появились сервисы, которые облегчают вам составление запросов:
Генераторы запросов
Write-Ai-Art-Prompts — ИИ-помощник, который предлагает дополнительные слова к уже введенному запросу.
PromptoMania — самый подробный промптер с кучей настроек, каждая из них проиллюстрирована.
Сборники примеров запросов
Public Prompts — бесплатные запросы, разделенные по тематикам;
Stable Diffusion Modifier Studies — стили с примерами рассортированы в алфавитном порядке.
Lexica Art — крупнейшая галерея с бесплатными картинками и их описаниями, откуда можно копировать отдельные части запроса.
OpenArt — галерея, аналогичная Lexica Art, картинки можно смешивать или добавлять в закладки.
Веса
Вы можете придавать отдельным словам в запросе разные веса, тем самым заставляя модель обращать на них больше внимания.
Делается это с помощью сторонней библиотеки Compel и специальных символов. Символ "+" увеличивает вес слова, а символ "-" уменьшает. Примерно так:
from diffusers import StableDiffusionPipeline
from compel import Compel
pipeline = StableDiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5")
compel = Compel(tokenizer=pipeline.tokenizer, text_encoder=pipeline.text_encoder)
prompt = "sky image, sun++ clouds--"
conditioning = compel([prompt])
image = pipeline(prompt_embeds=conditioning).images[0]
image
Заметьте, что количеством символов вы можете регулировать силу веса: чем больше символов — тем больше вес (и наоборот).
Есть и другие техники задания весов отдельным словам и целым предложениями. Более подробно здесь.
Прочие задачи
Вкратце рассмотрим как Stable Diffusion решает другие задачи...
image2image
image2image позволяет подавать на вход изображение и изменять его с помощью запроса.
Благодаря Stable Diffusion мем «Как нарисовать сову?» обретает новую жизнь:
pipeline = StableDiffusionImg2ImgPipeline.from_pretrained(
sd_15,
torch_dtype=torch.float16,
use_safetensors=True
).to("cuda")
init_image = Image.open('owl_2circle.png').convert("RGB").thumbnail((768, 768))
prompt = "forest owl, pencil drawing"
image = pipeline(prompt=prompt, image=init_image).images[0]
image
Здесь мы подали на вход изображение из двух кружочков и попросили Stable Diffusion нарисовать лесную сову.
Inpainting
Inpainting позволяет изменять изображение в определенной области, которую вы укажите посредством маски:
from diffusers import StableDiffusionInpaintPipeline
from PIL import Image
pipeline = StableDiffusionInpaintPipeline.from_pretrained(
"stabilityai/stable-diffusion-2-1-base",
torch_dtype=torch.float16,
use_safetensors=True,
variant="fp16",
).to("cuda")
init_image = Image.open("overture-creations-5sI6fQgYIuo.png").convert("RGB").resize((512, 512))
mask_image = Image.open("overture-creations-5sI6fQgYIuo_mask.png").convert("RGB").resize((512, 512))
prompt = "teddy bear, sitting on a park bench"
image = pipeline(
prompt=prompt,
image=init_image,
mask_image=mask_image
).images[0]
image
depth2image
depth2image это улучшенная версия image2image. Она не просто изменяет изображение, но и еще учитывает при этом 3D структуру изображения, которая подается на вход в виде маски.
pipe = StableDiffusionDepth2ImgPipeline.from_pretrained(
"stabilityai/stable-diffusion-2-depth",
torch_dtype=torch.float16,
use_safetensors=True,
).to("cuda")
init_image = Image.open("000000039769.jpg")
prompt = "two tigers"
n_prompt = "bad, deformed, ugly, bad anatomy"
image = pipe
prompt=prompt,
image=init_image,
negative_prompt=n_prompt,
strength=0.7).images[0]
image
Как вы могли заметить в данном коде мы не подавали никакой маски на вход. Это потому что модель сама генерирует эту маску (один из ее модулей) и ее же использует при изменении изображения. Но вы можете сгенерировать маску сторонними сервисами или даже моделями и подать ее на вход.
Обучение
Как и любую другую модель Stable Diffusion можно обучать и дообучать. Объяснение этого выходит за рамки статьи, но вы можете ознакомится с процессом самостоятельно:
А еще Stable Diffusion обладает рядом интересных техник, которые позволяют изучать новые концепции буквально на нескольких изображениях, а также изменять сгенерированные изображения:
https://huggingface.co/docs/diffusers/training/text_inversion
https://huggingface.co/docs/diffusers/using-diffusers/controlnet
Экосистема
С момента первой Stable Diffusion было выпущено много улучшенных версий:
Более новые версии не обязательно означают лучшее качество при тех же параметрах.
Некоторые пользователи (на просторах интернета) жаловались, что версия 2.0 немного хуже, чем 1.5 для некоторых запросов. Но при правильном построении запроса (с использованием отрицательных запросов) 2.0 и 2.1 кажутся лучше. Вообще "качество" довольно субъективная вещь. Самый лучший способ узнать, что для вас лучше — попробовать разные модели :)
Все выше приведенные модели можно считать официальными. Но благодаря открытому исходному коду экосистема Stable Diffusion сильно разрослась. Появились различные плагины, лоры, дообученные энтузиастами модели.
Начать поиск моделей можно с Hugging Face.
Существуют целые галереи с примерами картинок и ссылкой на модель, с помощью которой они сгенерированы:
https://huggingface.co/spaces/huggingface-projects/diffusers-gallery
https://huggingface.co/spaces/sd-concepts-library/stable-diffusion-conceptualizer
Помимо этого есть сторонние крупные сайты, на которых собраны дифузионный модели. Например: https://civitai.com
Веб-интерфейсы
Если работать через код вам не удобно, то можете попробовать один из веб-интерфейсов, специально разработанных для работы со Stable Diffusion.
З.Ы. Некоторые из них обладают гораздо большими возможностями по настройке параметров модели, чем программный интерфейс.
Вместо заключения
Хотя Stable Diffusion позволяет создавать невероятные картины, все же он не идеален. До сих пор можно столкнутся со следующими проблемами:
Stable Diffusion очень плохо работает с текстом.
Генерирует достаточно много артефактов.
Плохо работают с лицами.
С этими проблемами пытаются бороться некоторые дообученные модели.
И зачастую, чтобы добиться приемлемого результата, необходимо сгенерировать картинку множество раз.
---------------------