Начало работы с Hugging Face Diffusers
Настройте Hugging Face Diffusers и SDXL, генерируйте изображения, меняйте параметры, экономьте память GPU и подключите API FastAPI к приложению на JavaScript.
Hugging Face Diffusers — это Python-библиотека для запуска диффузионных моделей генерации изображений, видео и аудио на собственном оборудовании. Она объединяет текстовый энкодер, планировщик (scheduler), сеть шумоподавления и VAE в единый объект пайплайна, который вызывается с промптом.
Начать работу с облачными API для генерации изображений несложно. Но затем приходят счета за каждое изображение, лимиты запросов тормозят прототип, а вам хочется понять, что на самом деле делает модель. Локальный запуск решает все три проблемы, однако большинство руководств предполагает, что вы уже знаете PyTorch.
Это руководство написано для JavaScript-разработчиков. Оно проведёт вас от установки до первого изображения, объяснит ключевые настройки, покажет, как уместить модель на GPU с небольшим объёмом памяти, а в конце — как создать эндпоинт на FastAPI, к которому фронтенд обращается через fetch(). Примеры рассчитаны на Diffusers v0.42.1.
Ключевые выводы
- Загружайте
stabilityai/stable-diffusion-xl-base-1.0черезDiffusionPipeline.from_pretrained(..., dtype=torch.float16)и переносите на"cuda"на GPU NVIDIA или на"mps"на Apple Silicon. - SDXL base 1.0 распространяется под лицензией CreativeML Open RAIL++-M License. Ознакомьтесь с ней, прежде чем использовать сгенерированные изображения в продукте.
torch.Generatorс заданным сидом делает результаты воспроизводимыми, поэтому можно менять по одной настройке за раз и видеть её эффект.- Если модель не помещается в память GPU, сначала вызовите
enable_model_cpu_offload(). В документации Diffusers последовательная выгрузка на CPU (sequential CPU offload) описана как крайне медленная. - Загружайте пайплайн один раз при старте сервера и переиспользуйте его во всех запросах. Никогда не загружайте его внутри обработчика запроса.
Что такое Hugging Face Diffusers?
Diffusers — библиотека Hugging Face для работы с диффузионными моделями. Её центральная абстракция — DiffusionPipeline, которая объединяет компоненты, необходимые модели:
- один или несколько текстовых энкодеров, преобразующих промпт в эмбеддинги;
- планировщик, управляющий алгоритмом шумоподавления;
- UNet или диффузионный трансформер, предсказывающий шум на каждом шаге;
- VAE, декодирующий результат в пиксели.
Если вы впервые сталкиваетесь с Hub, Inference Providers или Spaces, сначала прочитайте A Quick Guide to Hugging Face for Developers. Если вам нужен инференс вообще без Python-бэкенда, Transformers.js запускает модели прямо в браузере, хотя поддерживает другой набор моделей.
Какое оборудование нужно для Diffusers?
Diffusers работает на трёх типах машин. GPU NVIDIA — со значительным отрывом самый быстрый вариант, Mac на Apple Silicon работает через бэкенд PyTorch "mps", а обычный CPU тоже подойдёт, но работает очень медленно.
| Оборудование | Строка устройства | dtype | Чего ожидать |
|---|---|---|---|
| GPU NVIDIA | "cuda" | torch.float16 | Быстро |
| Mac на Apple Silicon | "mps" | torch.float16 | Работает, медленнее дискретного GPU NVIDIA |
| Только CPU | "cpu" | torch.float32 | Работает, очень медленно |
В Quickstart в качестве целевых устройств указаны "cuda", "mps", "xpu" и "cpu". Для Diffusers также есть отдельное руководство по Apple Silicon (MPS). Этот вспомогательный код выбирает подходящее устройство и dtype для текущей машины:
import torch
if torch.cuda.is_available():
device, dtype = "cuda", torch.float16
elif torch.backends.mps.is_available():
device, dtype = "mps", torch.float16
else:
device, dtype = "cpu", torch.float32
Установка Diffusers и генерация первого изображения
Генерация первого изображения с помощью Hugging Face Diffusers состоит из трёх шагов: установите Diffusers вместе с PyTorch, загрузите SDXL, затем вызовите пайплайн с промптом и сохраните .images[0].
Начиная с v0.42.0, Diffusers требует Python 3.11 или новее и тестируется с PyTorch 2.6 или новее. Следуйте руководству по установке, чтобы настроить виртуальное окружение, а затем установите пакеты. Текстовые энкодеры SDXL — это модели CLIP из Transformers, поэтому установите и эту библиотеку:
uv pip install "diffusers[torch]>=0.42.1" transformers
# or: pip install "diffusers[torch]>=0.42.1" transformers
Используя device и dtype из кода выше, сгенерируйте изображение:
import torch
from diffusers import DiffusionPipeline
pipeline = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
dtype=dtype,
).to(device)
prompt = "isometric pixel-art coffee shop at dusk, warm window light, rain on the street"
image = pipeline(prompt).images[0]
image.save("first.png")
image — это изображение PIL, поэтому .save() записывает PNG на диск. Идентификатор модели — это полное имя чекпоинта на Hub, включая префикс организации stabilityai/.
Лицензия: SDXL base 1.0 распространяется под лицензией CreativeML Open RAIL++-M License. Ознакомьтесь с ней, прежде чем использовать сгенерированные изображения в продукте, и проверяйте лицензию любой другой модели, на которую вы её замените.
Какие настройки SDXL важнее всего?
Четыре аргумента пайплайна SDXL определяют большую часть того, что вы увидите на выходе: num_inference_steps, guidance_scale, generator и height / width. Сначала зафиксируйте сид, затем меняйте по одному аргументу за раз.
generator = torch.Generator().manual_seed(42)
image = pipeline(
prompt,
num_inference_steps=30,
guidance_scale=7.0,
height=1024,
width=1024,
generator=generator,
).images[0]
num_inference_stepsзадаёт количество проходов шумоподавления. Чем больше шагов, тем дольше генерация и, как правило, выше детализация — пока прирост не сойдёт на нет.guidance_scaleопределяет, насколько точно модель следует промпту. Более высокие значения сильнее привязывают результат к тексту, а слишком высокие могут дать «пережаренную» картинку.generatorфиксирует начальный шум. При той же модели, настройках и оборудовании генератор с заданным сидом снова выдаст то же изображение.height/widthзадают размер выходного изображения в пикселях. Для больших изображений требуется больше памяти и времени.
В справочнике по пайплайну SDXL перечислены все аргументы с их значениями по умолчанию.
Замена планировщика
Планировщик определяет, как удаляется шум на каждом шаге, и разные планировщики по-разному балансируют между скоростью и качеством. Заменить его в загруженном пайплайне можно через from_config(), передав существующую конфигурацию, чтобы параметры, соответствующие чекпоинту, остались согласованными:
from diffusers import DPMSolverMultistepScheduler
pipeline.scheduler = DPMSolverMultistepScheduler.from_config(pipeline.scheduler.config)
generator = torch.Generator().manual_seed(42)
image = pipeline(prompt, num_inference_steps=20, generator=generator).images[0]
В руководстве по планировщикам используется именно этот паттерн с SDXL. В справочнике по DPMSolverMultistepScheduler указано, что он даёт качественные результаты примерно за 20 шагов. При сравнении планировщиков держите сид фиксированным, чтобы единственным отличием между двумя изображениями был именно планировщик.
Как уместить SDXL на GPU с небольшим объёмом памяти?
Если SDXL падает с ошибками нехватки памяти (out-of-memory), попробуйте по порядку четыре способа экономии памяти в Diffusers: выгрузку модели на CPU, тайлинг VAE, слайсинг VAE и 4-битное квантование. Каждый следующий шаг экономит больше памяти, но сильнее снижает скорость.
- Выгрузка модели на CPU. Не вызывайте
.to(device), а вместо этого вызовитеpipeline.enable_model_cpu_offload(). Каждая модель целиком находится на GPU только во время своей работы. - Тайлинг VAE.
pipeline.vae.enable_tiling()декодирует большие изображения по тайлам. В руководстве по памяти отмечается, что это может вызвать небольшие различия в тонах между тайлами. - Слайсинг VAE.
pipeline.vae.enable_slicing()декодирует батч по одному изображению за раз. Это помогает только при генерации нескольких изображений за один вызов. - 4-битное квантование. Установите
bitsandbytesи добавьте короткую конфигурацию:
from diffusers.quantizers import PipelineQuantizationConfig
quant_config = PipelineQuantizationConfig(
quant_backend="bitsandbytes_4bit",
quant_kwargs={"load_in_4bit": True, "bnb_4bit_quant_type": "nf4", "bnb_4bit_compute_dtype": torch.float16},
components_to_quantize=["unet"],
)
pipeline = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
dtype=torch.float16,
quantization_config=quant_config,
)
pipeline.enable_model_cpu_offload()
PipelineQuantizationConfig и его бэкенды описаны в обзоре квантования. В v0.39.0 в Diffusers добавили поддержку квантования bitsandbytes на MPS, поэтому этот подход не ограничен CUDA. На Apple Silicon для bitsandbytes требуется PyTorch 2.9 или новее — это выше базового требования PyTorch 2.6, упомянутого ранее.
Избегайте enable_sequential_cpu_offload(), если только ничто другое не помогает. Этот метод экономит больше всего памяти, но в руководстве по памяти он назван крайне медленным. Кроме того, для пайплайна, загруженного с device_map, необходимо вызвать reset_device_map(), прежде чем использовать .to() или любой из методов выгрузки.
Вызов Hugging Face Diffusers из веб-приложения
Чтобы использовать Diffusers из фронтенда, оберните пайплайн в небольшой HTTP-сервер. Сервер должен загружать модель один раз при запуске и возвращать байты PNG. Установите fastapi и uvicorn, затем сохраните этот код как main.py:
import io, threading
from contextlib import asynccontextmanager
import torch
from diffusers import DiffusionPipeline
from fastapi import FastAPI, Response
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
state = {}
lock = threading.Lock()
@asynccontextmanager
async def lifespan(app: FastAPI):
state["pipe"] = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.float16
).to("cuda") # or "mps"
yield
state.clear()
app = FastAPI(lifespan=lifespan)
app.add_middleware(CORSMiddleware, allow_origins=["http://localhost:5173"],
allow_methods=["POST"], allow_headers=["*"])
class GenerateRequest(BaseModel):
prompt: str
seed: int | None = None
@app.post("/generate")
def generate(req: GenerateRequest):
gen = torch.Generator().manual_seed(req.seed) if req.seed is not None else None
with lock:
image = state["pipe"](req.prompt, generator=gen).images[0]
buf = io.BytesIO()
image.save(buf, format="PNG")
return Response(content=buf.getvalue(), media_type="image/png")
Запустите его командой uvicorn main:app. Код построен на трёх возможностях FastAPI:
- Обработчик lifespan загружает модель один раз, до того как сервер начнёт принимать запросы.
from_pretrained()работает слишком медленно, чтобы вызывать его на каждый запрос. - Поскольку
generateобъявлена через обычныйdef, FastAPI выполняет её в пуле потоков, не блокируя цикл событий. - Блокировка (lock) гарантирует, что запросы используют GPU по очереди.
CORSMiddleware разрешает обращаться к эндпоинту с origin вашего фронтенда в режиме разработки.
На фронтенде используйте fetch(), чтобы запросить изображение, прочитайте тело ответа как Blob и отобразите его с помощью URL.createObjectURL():
async function generate(prompt, seed, img) {
const res = await fetch("http://localhost:8000/generate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ prompt, seed }),
});
if (!res.ok) throw new Error(`Generation failed: ${res.status}`);
if (img.src.startsWith("blob:")) URL.revokeObjectURL(img.src);
img.src = URL.createObjectURL(await res.blob());
}
Генерация занимает секунды, а не миллисекунды. Показывайте индикатор загрузки, блокируйте кнопку отправки, пока запрос выполняется, и передавайте сигнал AbortController, если пользователи могут отменить генерацию. Прежде чем отдавать сгенерированные PNG реальным посетителям, сожмите их с помощью OpenReplay image compressor.
Дальнейшие шаги
Когда пайплайн загружен на нужное устройство, Diffusers требует совсем немного кода: идентификатор модели, dtype, промпт и сид. Сгенерируйте первое изображение с SDXL, зафиксируйте сид и меняйте по одной настройке за раз. Если не хватает памяти, по порядку пройдите шаги по выгрузке и квантованию. Затем подключите эндпоинт FastAPI к своему фронтенду. Дальше руководство по памяти и руководство по планировщикам помогут разобраться с компромиссами, которые возникают при настройке для продакшена.
Часто задаваемые вопросы
Где Diffusers хранит загруженные веса моделей?
from_pretrained() сохраняет файлы модели в кэше Hugging Face Hub и переиспользует их при последующих запусках, не скачивая повторно. По умолчанию кэш находится в ~/.cache/huggingface/hub. Задайте переменную окружения HF_HUB_CACHE, чтобы перенести только кэш моделей. Задайте HF_HOME, чтобы перенести все данные Hugging Face — тогда кэш будет располагаться в HF_HOME/hub. Также можно передать аргумент cache_dir, чтобы выбрать расположение для отдельной загрузки.
Почему один и тот же сид даёт другое изображение на другой машине?
Сид полностью воспроизводит изображение только на том же оборудовании и с той же версией PyTorch. GPU с CUDA используют иной генератор случайных чисел, чем CPU, а матричное умножение на GPU менее детерминировано. Руководство Diffusers по воспроизводимости рекомендует передавать генератор на CPU, когда результаты должны совпадать, — именно его по умолчанию создаёт torch.Generator(). Но даже в этом случае идентичный результат на разных моделях GPU или версиях PyTorch не гарантируется.
Работает ли 4-битное квантование bitsandbytes на Mac с Apple Silicon?
Да, в свежих версиях. В Diffusers добавили поддержку квантования bitsandbytes на MPS в v0.39.0, а в bitsandbytes 0.50.0 все 4-битные конфигурации и LLM.int8() заработали на MPS. Для работы bitsandbytes на MPS требуется PyTorch 2.9 или новее — это выше минимального требования Diffusers (PyTorch 2.6). На macOS 26 и новее установка пакета kernels включает более быстрые ядра Metal.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.
Star on GitHub12k