Фаза 04 · урок 11
Stable Diffusion — архитектура и тонкая настройка
Цель урока: Обучать DDPM непосредственно на RGB-изображениях 512x512 дорого. На каждом шаге обучения обратное распространение проходит через U-Net, которая видит 3x512x512 = 786,432 входных значения, а сэмплирование требует 50+ прямых проходов…
Текущий релиз AlexBred.com: первые 100 уроков русскоязычной программы.
Содержание урока
- Цели обучения
- Проблема
- Концепция
- Пайплайн
- Classifier-free guidance (CFG)
- Геометрия латентного пространства
- Архитектура U-Net
- Тонкая настройка LoRA
- Schedulers, которые вам встретятся
- Соберите
- Шаг 1: Text-to-image
- Шаг 2: Замена scheduler
- Шаг 3: Image-to-image
- Шаг 4: Inpainting
- Шаг 5: Загрузка LoRA
- Шаг 6: Обучение LoRA (набросок)
- Используйте
- Поставьте
- Упражнения
- Ключевые термины
- Дополнительные материалы
Stable Diffusion — это DDPM, работающая в латентном пространстве предобученного VAE, обусловленная текстом через cross-attention, сэмплируемая быстрым детерминированным ODE-решателем и направляемая classifier-free guidance.
Тип: Изучить + использовать Языки: Python Предварительные требования: Фаза 4, урок 10 (диффузия), фаза 7, урок 02 (самовнимание) Время: ~75 минут
Цели обучения
- Проследить пять частей пайплайна Stable Diffusion: VAE, текстовый кодировщик, U-Net, scheduler, safety checker — и понять, что именно делает каждая из них
- Объяснить латентную диффузию и почему обучение в латентном пространстве 4x64x64 (вместо изображения 3x512x512) снижает вычисления в 48 раз без потери качества
- Использовать
diffusersдля генерации изображений, image-to-image, inpainting и генерации с направлением ControlNet - Выполнить тонкую настройку Stable Diffusion с LoRA на небольшом пользовательском наборе данных и загрузить адаптер LoRA при инференсе
Проблема
Обучать DDPM непосредственно на RGB-изображениях 512x512 дорого. На каждом шаге обучения обратное распространение проходит через U-Net, которая видит 3x512x512 = 786,432 входных значения, а сэмплирование требует 50+ прямых проходов через ту же U-Net. На уровне качества Stable Diffusion 1.5 (выпущенной в 2022 году) диффузии в пиксельном пространстве потребовалось бы примерно 256 GPU-месяцев обучения и 10–30 секунд на изображение на потребительском GPU.
Приём, сделавший практичной text-to-image модель с открытыми весами, — латентная диффузия (latent diffusion; Rombach и др., CVPR 2022). Обучите VAE, отображающий изображение 3x512x512 в латентный тензор 4x64x64 и обратно, а затем выполняйте диффузию в этом латентном пространстве. Вычисления уменьшаются в (3*512*512)/(4*64*64) = 48x. На том же GPU сэмплирование сокращается с десятков секунд до менее чем двух секунд.
Почти каждая современная модель генерации изображений — SDXL, SD3, FLUX, HunyuanDiT, Wan-Video — является моделью латентной диффузии с вариациями автоэнкодера, денойзера (U-Net или DiT) и текстового обусловливания. Освоив Stable Diffusion, вы освоите этот шаблон.
Концепция
Пайплайн
- VAE — замороженный автоэнкодер. Кодировщик превращает изображение в латенты (используется для img2img и обучения). Декодировщик превращает латенты обратно в изображение.
- Текстовый кодировщик — текстовый кодировщик CLIP (SD 1.x/2.x), CLIP-L + CLIP-G (SDXL) или T5-XXL (SD3/FLUX). Создаёт последовательность встраиваний токенов.
- U-Net — денойзер. Имеет слои cross-attention, которые на каждом уровне разрешения сопоставляют латенты с текстовым встраиванием.
- Scheduler — алгоритм сэмплирования (DDIM, Euler, DPM-Solver++). Выбирает sigma и смешивает предсказанный шум обратно в латент.
- Safety checker — необязательный фильтр NSFW / незаконного контента для выходного изображения.
Classifier-free guidance (CFG)
Обычное текстовое обусловливание обучает epsilon_theta(x_t, t, c) для каждого промпта c. CFG обучает ту же сеть с отброшенным c в 10% случаев (заменённым пустым встраиванием), что даёт одну модель, предсказывающую и условный, и безусловный шум. При инференсе:
eps = eps_uncond + w * (eps_cond - eps_uncond)
w — масштаб guidance. При w=0 модель безусловна, при w=1 используется обычное условие, при w>1 результат сильнее подталкивается к тому, чтобы быть «более обусловленным промптом», ценой разнообразия. По умолчанию в SD используется w=7.5.
Именно CFG позволяет text-to-image работать с качеством, пригодным для продакшена. Без неё промпты слабо смещают выход; с ней промпты доминируют.
Геометрия латентного пространства
Четырёхканальный латент VAE — не просто сжатое изображение. Это многообразие, в котором арифметика приблизительно соответствует семантическим редактированиям (здесь живут и prompt engineering, и интерполяция), и в котором диффузионная U-Net обучена тратить весь свой бюджет моделирования. Декодирование случайного латента 4x64x64 не создаёт случайно выглядящее изображение — оно создаёт мусор, поскольку лишь определённое подмногообразие латентов декодируется в допустимые изображения.
Два следствия:
- Img2img = закодировать изображение в латент, добавить частичный шум, запустить денойзер, декодировать. Структура изображения сохраняется, потому что кодирование почти обратимо; содержание меняется в соответствии с промптом.
- Inpainting = то же, что img2img, но денойзер обновляет только замаскированные области; незамаскированные области сохраняются в закодированном латенте.
Архитектура U-Net
U-Net SD — крупная версия TinyUNet из урока 10 с тремя дополнениями:
- Блоки Transformer при каждом пространственном разрешении, содержащие self-attention + cross-attention к текстовому встраиванию.
- Встраивание времени через MLP над синусоидальным кодированием.
- Skip connections между кодировщиком и декодировщиком на соответствующих разрешениях.
Всего параметров в SD 1.5: ~860M. В SDXL: ~2.6B. В FLUX: ~12B. Рост числа параметров в основном приходится на слои внимания.
Тонкая настройка LoRA
Полная тонкая настройка Stable Diffusion требует 20+ GB VRAM и обновляет 860M параметров. LoRA (Low-Rank Adaptation) оставляет базовую модель замороженной и внедряет небольшие матрицы рангового разложения в слои внимания. Адаптер LoRA для SD обычно занимает 10–50 MB, обучается за 10–60 минут на одном потребительском GPU и загружается при инференсе как модификация без замены модели.
Original: W_q : (d_in, d_out) frozen
LoRA: W_q + alpha * (A @ B) where A : (d_in, r), B : (r, d_out)
r is typically 4-32.
Именно так распространяется почти каждая community-тонкая настройка. CivitAI и Hugging Face размещают их миллионы.
Schedulers, которые вам встретятся
- DDIM — детерминированный, ~50 шагов, простой.
- Euler ancestral — стохастический, 30–50 шагов, даёт немного более творческие образцы.
- DPM-Solver++ 2M Karras — детерминированный, 20–30 шагов, стандарт для продакшена.
- LCM / TCD / Turbo — модели согласованности и дистиллированные варианты; 1–4 шага ценой некоторого качества.
Замена scheduler — это однострочное изменение в diffusers, которое иногда исправляет проблемы образцов без какого-либо переобучения.
Соберите
В этом уроке diffusers используется сквозным образом, а не для пересборки Stable Diffusion с нуля. Компоненты, которые потребовались бы для такой пересборки (VAE, текстовый кодировщик, U-Net, scheduler), — темы отдельных уроков; здесь цель — свободно владеть production API.
Шаг 1: Text-to-image
import torch
from diffusers import StableDiffusionPipeline
pipe = StableDiffusionPipeline.from_pretrained(
"runwayml/stable-diffusion-v1-5",
torch_dtype=torch.float16,
).to("cuda")
image = pipe(
prompt="a dog riding a skateboard in tokyo, studio ghibli style",
guidance_scale=7.5,
num_inference_steps=25,
generator=torch.Generator("cuda").manual_seed(42),
).images[0]
image.save("dog.png")
float16 вдвое сокращает VRAM без видимой потери качества. num_inference_steps=25 со стандартным DPM-Solver++ соответствует num_inference_steps=50 с DDIM.
Шаг 2: Замена scheduler
from diffusers import DPMSolverMultistepScheduler, EulerAncestralDiscreteScheduler
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
Состояние scheduler отделено от весов U-Net. Вы можете обучать на DDPM и сэмплировать любым scheduler.
Шаг 3: Image-to-image
from diffusers import StableDiffusionImg2ImgPipeline
from PIL import Image
img2img = StableDiffusionImg2ImgPipeline.from_pretrained(
"runwayml/stable-diffusion-v1-5",
torch_dtype=torch.float16,
).to("cuda")
init_image = Image.open("dog.png").convert("RGB").resize((512, 512))
out = img2img(
prompt="a dog riding a skateboard, oil painting",
image=init_image,
strength=0.6,
guidance_scale=7.5,
).images[0]
strength задаёт, сколько шума добавить перед денойзингом (0.0 = без изменений, 1.0 = полная регенерация). Стандартный диапазон для переноса стиля — 0.5–0.7.
Шаг 4: Inpainting
from diffusers import StableDiffusionInpaintPipeline
inpaint = StableDiffusionInpaintPipeline.from_pretrained(
"runwayml/stable-diffusion-inpainting",
torch_dtype=torch.float16,
).to("cuda")
image = Image.open("dog.png").convert("RGB").resize((512, 512))
mask = Image.open("dog_mask.png").convert("L").resize((512, 512))
out = inpaint(
prompt="a cat",
image=image,
mask_image=mask,
guidance_scale=7.5,
).images[0]
Белые пиксели маски — область для регенерации. Чёрные пиксели сохраняются.
Шаг 5: Загрузка LoRA
pipe.load_lora_weights("sayakpaul/sd-lora-ghibli")
pipe.fuse_lora(lora_scale=0.8)
image = pipe(prompt="a village square in ghibli style").images[0]
lora_scale управляет силой: 0.0 = нет эффекта, 1.0 = полный эффект. fuse_lora для скорости встраивает адаптер в веса на месте, но не позволяет менять адаптеры. Вызовите pipe.unfuse_lora() до загрузки другого адаптера.
Шаг 6: Обучение LoRA (набросок)
Реальное обучение LoRA находится в peft или diffusers.training. Схема:
# Pseudocode
for step, batch in enumerate(dataloader):
images, prompts = batch
latents = vae.encode(images).latent_dist.sample() * 0.18215
t = torch.randint(0, num_train_timesteps, (batch_size,))
noise = torch.randn_like(latents)
noisy_latents = scheduler.add_noise(latents, noise, t)
text_emb = text_encoder(tokenizer(prompts))
pred_noise = unet(noisy_latents, t, text_emb) # LoRA weights injected here
loss = F.mse_loss(pred_noise, noise)
loss.backward()
optimizer.step()
Градиент получают только матрицы LoRA; базовые U-Net, VAE и текстовый кодировщик заморожены. При batch size 1 и gradient checkpointing это помещается в 8 GB VRAM.
Используйте
В продакшене решения, которые вы действительно принимаете:
- Семейство модели: SD 1.5 для community-тонких настроек с открытым исходным кодом, SDXL для более высокой точности, SD3 / FLUX для передового уровня и строгих лицензионных требований.
- Scheduler: DPM-Solver++ 2M Karras для 20–30 шагов, LCM-LoRA, когда задержка должна быть менее 1 секунды.
- Точность:
float16на 4080/4090,bfloat16на A100 и новее,int8(черезbitsandbytesилиcompel) при дефиците VRAM. - Обусловливание: простой текст работает; для более строгого контроля добавьте ControlNet (canny, depth, pose) поверх базового пайплайна.
Для пакетной генерации AUTO1111 / ComfyUI — инструменты сообщества; для production API используйте diffusers + accelerate или optimum-nvidia с компиляцией TensorRT.
Поставьте
Этот урок создаёт:
outputs/prompt-sd-pipeline-planner.md— промпт, выбирающий SD 1.5 / SDXL / SD3 / FLUX, а также scheduler и точность по бюджету задержки, целевому качеству и лицензионному ограничению.outputs/skill-lora-training-setup.md— навык, записывающий полную конфигурацию обучения LoRA для пользовательского набора данных, включая подписи, ранг, batch size и learning rate.
Упражнения
- (Легко) Сгенерируйте один и тот же промпт с
guidance_scaleв[1, 3, 5, 7.5, 10, 15]. Опишите, как меняется изображение. При каком значении guidance появляются артефакты? - (Средне) Возьмите любую настоящую фотографию, пропустите её через
StableDiffusionImg2ImgPipelineсо значениемstrengthв[0.2, 0.4, 0.6, 0.8, 1.0]. Какое значение сохраняет композицию, меняя стиль? Почему при 1.0 ввод полностью игнорируется? - (Сложно) Обучите LoRA на 10–20 изображениях одного объекта (питомца, логотипа, персонажа) и сгенерируйте новые сцены с этим объектом. Сообщите ранг LoRA и число шагов обучения, давшие лучшее сохранение идентичности без переобучения на входных изображениях.
Ключевые термины
| Термин | Как обычно говорят | Что это на самом деле означает |
|---|---|---|
| Latent diffusion | «Диффузия в латентах» | Запускает весь DDPM в латентном пространстве VAE (4x64x64), а не в пиксельном пространстве (3x512x512); экономия вычислений в 48 раз |
| VAE scale factor | «0.18215» | Константа, масштабирующая необработанный латент VAE примерно до единичной дисперсии; жёстко задана в каждом SD-пайплайне |
| Classifier-free guidance | «CFG» | Смешивает условное и безусловное предсказания шума; самый значимый параметр инференса |
| Scheduler | «Sampler» | Алгоритм, превращающий шум + предсказания модели в траекторию денойзинга латента |
| LoRA | «Low-rank adapter» | Небольшие матрицы рангового разложения, тонко настраивающие слои внимания без изменения базовых весов |
| Cross-attention | «Text-image attention» | Внимание от латентных токенов к текстовым токенам; внедряет информацию промпта на каждом уровне U-Net |
| ControlNet | «Structure conditioning» | Отдельно обученный адаптер, направляющий SD дополнительным входом (canny, depth, pose, segmentation) |
| DPM-Solver++ | «Стандартный scheduler» | Детерминированный ODE-решатель второго порядка; лучшее качество при малом числе шагов (20–30) в 2026 году |
Дополнительные материалы
- High-Resolution Image Synthesis with Latent Diffusion (Rombach et al., 2022) — статья о Stable Diffusion; включает каждую абляцию, обосновывающую дизайн
- Classifier-Free Diffusion Guidance (Ho & Salimans, 2022) — статья о CFG
- LoRA: Low-Rank Adaptation of Large Language Models (Hu et al., 2021) — сначала LoRA появилась в NLP; в SD она перешла почти без изменений
- документация diffusers — справочник по каждому пайплайну SD / SDXL / SD3 / FLUX
Источник: Stable Diffusion — Architecture & Fine-Tuning 04.10 — Image Generation — Diffusion Models · Фаза 04 — Компьютерное зрение · 04.12 — Video Understanding — Temporal Modeling · Полный каталог