# Философия

🧨 Diffusers предоставляет **передовые** предобученные диффузионные модели для различных модальностей. Его цель — служить **модульным инструментарием** как для инференса, так и для обучения.

Мы стремимся создать библиотеку, которая выдержит испытание временем, и поэтому очень серьёзно относимся к проектированию API.

Если коротко, Diffusers создан как естественное расширение PyTorch. Поэтому большинство наших проектных решений основаны на [принципах проектирования PyTorch](https://pytorch.org/docs/stable/community/design.html#pytorch-design-philosophy). Рассмотрим самые важные из них:

## Удобство в ущерб производительности

- Хотя Diffusers содержит множество встроенных функций для повышения производительности (см. [Память и скорость](https://huggingface.co/docs/diffusers/optimization/fp16)), модели всегда загружаются с наивысшей точностью и минимальной оптимизацией. Поэтому по умолчанию диффузионные пайплайны всегда создаются на CPU с точностью float32, если пользователь не определил иное. Это обеспечивает совместимость с различными платформами и ускорителями и означает, что для запуска библиотеки не требуется сложная установка.
- Diffusers стремится быть **лёгким** пакетом и поэтому имеет очень мало обязательных зависимостей, но много необязательных, которые могут улучшить производительность (например, `accelerate`, `safetensors` и т. д.). Мы стараемся сделать библиотеку максимально лёгкой, чтобы её можно было добавлять в зависимости других пакетов без лишних забот.
- Diffusers предпочитает простой, самодостаточный код сжатому «магическому» коду. Это означает, что сокращённый синтаксис, такой как lambda-функции, и продвинутые операторы PyTorch часто нежелательны.

## Просто и легко

Как утверждает PyTorch, **явное лучше неявного** и **простое лучше сложного**. Эта философия дизайна отражена в нескольких частях библиотеки:
- Мы следуем API PyTorch, предоставляя методы вроде [`DiffusionPipeline.to`](https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.to), чтобы пользователь мог самостоятельно управлять устройствами.
- Предпочтительнее вызывать краткие сообщения об ошибках, чем молча исправлять неверный ввод. Diffusers стремится обучать пользователя, а не делать библиотеку максимально простой в использовании.
- Сложная логика взаимодействия модели и планировщика явно вынесена наружу, а не обрабатывается «магическим» образом внутри. Планировщики/сэмплеры отделены от диффузионных моделей и имеют минимальные взаимные зависимости. Это требует от пользователя самостоятельно написать развёрнутый цикл денойзинга. Однако такое разделение упрощает отладку и предоставляет пользователю больше контроля над адаптацией процесса денойзинга или заменой диффузионных моделей и планировщиков.
- Отдельно обученные компоненты конвейера диффузии, *например* текстовый энкодер, unet и вариационный автоэнкодер, имеют собственные классы моделей. Это требует от пользователя самостоятельно управлять взаимодействием между различными компонентами модели, а формат сериализации разделяет компоненты модели на разные файлы. Однако это упрощает отладку и кастомизацию. Обучение DreamBooth или Textual Inversion
очень проста благодаря возможности Diffusers разделять отдельные компоненты диффузионного пайплайна.

## Гибкая абстракция, удобная для контрибьюторов

Во многих частях библиотеки Diffusers придерживается важного принципа проектирования, заимствованного из [библиотеки Transformers](https://github.com/huggingface/transformers): предпочтение отдаётся коду, скопированному напрямую, а не поспешным абстракциям. Этот принцип очень категоричен и резко контрастирует с популярными подходами, такими как [«Не повторяйся» (DRY)](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). Коротко говоря, так же как Transformers делает это для файлов моделирования, Diffusers стремится поддерживать крайне низкий уровень абстракции и максимально самодостаточный код для пайплайнов и планировщиков. Функции, длинные блоки кода и даже классы могут дублироваться в нескольких файлах, что на первый взгляд может показаться неудачным и небрежным решением, делающим библиотеку непригодной для поддержки. **Однако** этот подход доказал свою высокую эффективность в Transformers и имеет большой смысл для открытых библиотек машинного обучения, развиваемых сообществом, потому что:
- Machine Learning — это область, которая развивается чрезвычайно быстро: парадигмы, архитектуры моделей и алгоритмы постоянно меняются, что делает крайне сложным создание долговременных абстракций в коде.
- Практики машинного обучения ценят возможность быстро вносить изменения в существующий код для генерации идей и исследований, поэтому предпочитают самодостаточный код коду с множеством абстракций.
- Открытые библиотеки опираются на вклад сообщества, поэтому им необходимо создавать библиотеки, к которым легко вносить изменения. Чем абстрактнее код и чем больше зависимостей, тем сложнее его читать и тем труднее вносить в него изменения. Вкладчики просто перестают работать с чрезмерно абстрактными библиотеками из-за страха сломать критически важные функции. Если внесение изменений в библиотеку не может нарушить работу других фундаментальных частей кода, это не только привлекает новых потенциальных вкладчиков, но и упрощает ревью и параллельную работу над несколькими частями.

В Hugging Face мы называем такой подход **политикой одного файла**, что означает: почти весь код определённого класса должен быть написан в одном самодостаточном файле. Чтобы узнать больше о философии этого подхода, посмотрите [этот пост в блоге](https://huggingface.co/blog/transformers-design-philosophy).

В Diffusers мы придерживаемся этой философии для пайплайнов, планировщиков и моделей. Некоторые старые модели предшествуют этому соглашению и сохраняются без изменений; все новые архитектуры моделей находятся в собственных самодостаточных файлах. Подробности см. в разделе [Models](#models) ниже.

Отлично, теперь вы в целом понимаете, почему 🧨 Diffusers устроен именно так 🤗. Мы стараемся последовательно применять эти принципы проектирования во всей библиотеке. Тем не менее, существуют некоторые незначительные отклонения от философии или неудачные решения в дизайне. Если у вас есть отзывы о проектировании, мы будем ❤️ рады услышать их [напрямую на GitHub](https://github.com/huggingface/diffusers/issues/new?assignees=&labels=&template=feedback.md&title=).

## Детали философии проектирования

Теперь перейдём к деталям философии проектирования. Diffusers предлагает два способа компоновки [моделей](https://github.com/huggingface/diffusers/tree/main/src/diffusers/models) и [планировщиков](https://github.com/huggingface/diffusers/tree/main/src/diffusers/schedulers) в рабочий процесс: стандартные [пайплайны](https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines), которые являются монолитными и выполняют одну задачу на класс пайплайна, и Modular Diffusers, который является модульным и основан на блоках. В разделах ниже последовательно рассматриваются пайплайны, модульные пайплайны, модели и планировщики.

### Пайплайны

Пайплайны, стандартные или модульные, предназначены только для инференса. Они созданы для простоты использования (поэтому не следуют принципу [Simple over easy](#simple-over-easy) на 100%): читаемы, самоочевидны, легко настраиваются и лучше всего воспринимаются как примеры использования [моделей](#models) и [планировщиков](#schedulers). Они не обладают полным набором функций. Чтобы создать полнофункциональные пользовательские интерфейсы на базе Diffusers, используйте [Modular Diffusers](../modular_diffusers/overview).

Следуются следующие принципы проектирования:
- Pipelines следуют политике одного файла. Все pipelines находятся в отдельных каталогах под src/diffusers/pipelines. Одна папка pipeline соответствует одной статье/проекту/релизу по диффузии. Несколько файлов pipeline могут быть собраны в одной папке pipeline, как это сделано для [`src/diffusers/pipelines/stable-diffusion`](https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines/stable_diffusion). Если pipelines имеют схожую функциональность, можно воспользоваться механизмом [# Copied from](https://github.com/huggingface/diffusers/blob/125d783076e5bd9785beb05367a2d2566843a271/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion_img2img.py#L251).
- Все конвейеры наследуются от [DiffusionPipeline](/docs/diffusers/v0.43.0/ru/api/pipelines/overview#diffusers.DiffusionPipeline).
- Каждый конвейер состоит из различных компонентов модели и планировщика, которые задокументированы в файле [`model_index.json`](https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5/blob/main/model_index.json), доступны как атрибуты конвейера под тем же именем и могут передаваться между конвейерами с помощью функции [`DiffusionPipeline.components`](https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.components).
- Каждый пайплайн должен быть загружаемым с помощью функции [`DiffusionPipeline.from_pretrained`](https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.from_pretrained).
- Каждый пайплайн должен иметь один и только один способ запуска через метод `__call__`. Именование аргументов `__call__` должно быть единообразным во всех пайплайнах.
- Pipelines следует называть по задаче, которую они предназначены для решения.

### Модульные Diffusers

Modular Diffusers — это модульная альтернатива стандартным пайплайнам. Вы собираете рабочий процесс из переиспользуемых *блоков пайплайна*, которые можно комбинировать, заменять и передавать другим. Стандартные пайплайны служат примерами того, как использовать модели и планировщики. Modular Diffusers — рекомендуемый способ создания полнофункциональных пользовательских интерфейсов на базе Diffusers, а также децентрализованного создания и обмена новыми пайплайнами сообществом.

Следуются следующие принципы проектирования:
- Модульные пайплайны следуют политике одного файла. Каждый из них находится в собственной папке под [src/diffusers/modular_pipelines/](https://github.com/huggingface/diffusers/tree/main/src/diffusers/modular_pipelines), а внутри папки рабочий процесс разделён на один файл на каждый этап: `encoders.py`, `before_denoise.py`, `denoise.py` и `decoders.py`. Ещё два файла завершают пайплайн. `modular_blocks_<model>.py` собирает этапы, а `modular_pipeline.py` определяет подкласс [ModularPipeline](/docs/diffusers/v0.43.0/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline) для каждой модели. Модульные пайплайны не импортируют друг друга.
- Каждый модульный конвейер определяется как набор [ModularPipelineBlocks](/docs/diffusers/v0.43.0/ru/api/modular_diffusers/pipeline_blocks#diffusers.ModularPipelineBlocks). Блоки-листья находятся в файлах стадий (`encoders.py`, `before_denoise.py`, `denoise.py`, `decoders.py`), а `modular_blocks_<model>.py` собирает их в полный рабочий процесс с помощью контейнерных классов, таких как [SequentialPipelineBlocks](/docs/diffusers/v0.43.0/ru/api/modular_diffusers/pipeline_blocks#diffusers.SequentialPipelineBlocks) и [AutoPipelineBlocks](/docs/diffusers/v0.43.0/ru/api/modular_diffusers/pipeline_blocks#diffusers.AutoPipelineBlocks). Это разделяет два понятия, которые `DiffusionPipeline combines`. Блок — это чистое определение. Он объявляет входные и выходные данные, а также зависимости от компонентов, но не содержит весов и не может выполняться. [ModularPipeline](/docs/diffusers/v0.43.0/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline), создаваемый с помощью `.init_pipeline(repo_id)`, является исполняемой частью. То, что блоки не имеют состояния и весов, делает их свободно комбинируемыми, заменяемыми и переносимыми между рабочими процессами.
- Для поддержки новой задачи напишите специфические для неё блоки, скомбинируйте их с существующими и зарегистрируйте рабочий процесс в `_workflow_map` на верхнем уровне сборки блоков. Один `ModularPipeline` может поддерживать множество рабочих процессов, таких как генерация изображений по тексту, преобразование изображения в изображение и inpainting, тогда как `DiffusionPipeline` выполняет только один.

См. [документацию Modular Diffusers](../modular_diffusers/overview) для полного руководства по проектированию и использованию.

### Модели

Модели спроектированы как настраиваемые наборы инструментов, являющиеся естественным расширением класса [Module в PyTorch](https://pytorch.org/docs/stable/generated/torch.nn.Module.html). Они должны следовать *политике одного файла*. Некоторые старые модели предшествуют этому соглашению и сохраняются в неизменном виде. Рассматривайте их как устаревшие исключения, а не как образец для новых моделей. Например, исходный класс [UNet2DConditionModel](/docs/diffusers/v0.43.0/ru/api/models/unet2d-cond#diffusers.UNet2DConditionModel) использовался для нескольких вариантов UNet.

Следуются следующие принципы проектирования:
- Каждый тип архитектуры модели находится в собственной папке под [src/diffusers/models](https://github.com/huggingface/diffusers/tree/main/src/diffusers/models), например [transformers/](https://github.com/huggingface/diffusers/tree/main/src/diffusers/models/transformers), [autoencoders/](https://github.com/huggingface/diffusers/tree/main/src/diffusers/models/autoencoders) или [unets/](https://github.com/huggingface/diffusers/tree/main/src/diffusers/models/unets). Каждая модельная семья имеет собственный файл в этой папке, например [transformer_flux.py](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/transformers/transformer_flux.py) и [transformer_wan.py](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/transformers/transformer_wan.py).
- Модели следуют политике одного файла. Каждый файл модели должен быть самодостаточным, за исключением небольшого числа стандартных модулей, которые каждая модель использует идентичным образом, таких как эмбеддинги временных шагов и слои нормализации. Импортируйте их из [embeddings.py](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/embeddings.py) и [normalization.py](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/normalization.py).
- Модели призваны раскрывать сложность, подобно классу `Module` в PyTorch, и выдавать понятные сообщения об ошибках.
- Все модели наследуются от `ModelMixin` и `ConfigMixin`.
- Модели можно оптимизировать для повышения производительности, если это не требует существенных изменений в коде, сохраняет обратную совместимость и обеспечивает значительную экономию памяти или вычислительных ресурсов.
- По умолчанию модели должны иметь наивысшую точность и наименьшую производительность.
- Чтобы интегрировать новую архитектуру модели, похожую на существующую, скопируйте существующий файл в качестве отправной точки и адаптируйте его. Используйте аннотации [`# Copied from`](./contribution#copied-from-mechanism) для слоёв, которые остаются идентичными, чтобы `make fix-copies` поддерживал их синхронизацию.

### Планировщики

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

Следуются следующие принципы проектирования:
- Все планировщики находятся в [`src/diffusers/schedulers`](https://github.com/huggingface/diffusers/tree/main/src/diffusers/schedulers).
- Планировщикам **запрещено** импортировать из крупных файлов утилит; они должны быть максимально самодостаточными.
- Один файл планировщика на Python соответствует одному алгоритму планировщика (как он может быть описан в научной статье).
- Если планировщики имеют схожие функции, мы можем воспользоваться механизмом `# Copied from`.
- Все планировщики наследуются от `SchedulerMixin` и `ConfigMixin`.
- Планировщики можно легко заменить с помощью метода [`ConfigMixin.from_config`](https://huggingface.co/docs/diffusers/main/en/api/configuration#diffusers.ConfigMixin.from_config), как подробно описано [здесь](../using-diffusers/schedulers).
- Каждый планировщик должен иметь `set_num_inference_steps` и функцию `step`. `set_num_inference_steps(...)` необходимо вызывать перед каждым процессом удаления шума, *то есть* перед вызовом `step(...)`.
- Каждый планировщик предоставляет шаги, по которым необходимо «итерироваться», через атрибут `timesteps`, представляющий собой массив шагов, для которых будет вызываться модель.
- Функция `step(...)` принимает предсказанный вывод модели и «текущий» образец (x_t) и возвращает «предыдущий», слегка менее зашумленный образец (x_t-1).
- Учитывая сложность диффузионных планировщиков, функция `step` не раскрывает всю эту сложность и может восприниматься как своего рода «чёрный ящик».
- В подавляющем большинстве случаев новые планировщики следует реализовывать в отдельном файле планирования.

