Compare commits
98 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 54234652cb | |||
| 2e2684f014 | |||
| 31adeb41cd | |||
| a7b9634e95 | |||
| 6b6b4bcffe | |||
| beb1c017ad | |||
| 06ee4db3e7 | |||
| 96880d498d | |||
| 991174e704 | |||
| 8502ea8ae1 | |||
| 12d0258c96 | |||
| 84bbd2f4ce | |||
| 600ef8a4dc | |||
| 984d340534 | |||
| a2071a1837 | |||
| d9f71ab3c3 | |||
| dd4b731e68 | |||
| 31b211bfe3 | |||
| 610a71d7d4 | |||
| c104482b9c | |||
| c7a84ba2f4 | |||
| 8b1e3ec93e | |||
| 4e57aeff1f | |||
| af92869d9b | |||
| 0bae6e447c | |||
| 0368483b61 | |||
| ddb9d8548c | |||
| 49979753e1 | |||
| a3904d7e34 | |||
| 7bfc1ee1b2 | |||
| 71c046102b | |||
| 83b112a145 | |||
| 8690e8b9d6 | |||
| 7db8c3ec40 | |||
| 9b7acc7cf2 | |||
| a216b0bb7f | |||
| 150142c537 | |||
| 35f45ecd71 | |||
| d5dd8df3b4 | |||
| 3e0d128da7 | |||
| a536e775fb | |||
| 3b01d72a64 | |||
| e2a4a46e99 | |||
| eda560d34c | |||
| adbb04864d | |||
| effe4b9784 | |||
| 5b51ad0052 | |||
| 10b4e354b6 | |||
| ea6938aea5 | |||
| 8ef0d9deff | |||
| fa2abfdb03 | |||
| 1d3ef67b09 | |||
| 0f0b531827 | |||
| e8284281c1 | |||
| 715a7da1b2 | |||
| 14d224d4e6 | |||
| 540399f540 | |||
| f088027e93 | |||
| c6e08ecd46 | |||
| 4ad7a1f5fd | |||
| 1f81fbe274 | |||
| 589931ca79 | |||
| 675be88f00 | |||
| df4ad6f4ac | |||
| bc90c28bc9 | |||
| f040c27d4c | |||
| 138fac703a | |||
| 468ae09ed8 | |||
| 3fca52022f | |||
| c375903db5 | |||
| b9d52fca1d | |||
| 2ada094bff | |||
| f1f542bdd4 | |||
| a9c403c001 | |||
| e7b9a0762b | |||
| 8eb17315c8 | |||
| c71c19c5e6 | |||
| adc31940a9 | |||
| 963ee05d16 | |||
| 668e34c6e0 | |||
| 25d7bb3ea6 | |||
| 394b8fb996 | |||
| a1d55e14ba | |||
| e5564d45bf | |||
| 2921a20194 | |||
| 3376252d71 | |||
| 16170c69ae | |||
| 4408047ac5 | |||
| 34fab8b511 | |||
| 298ce67999 | |||
| d2e7a19fd5 | |||
| cd3082008e | |||
| f3209b5b55 | |||
| 96399c3ec6 | |||
| 10d3220abe | |||
| f69511ecc6 | |||
| d2b10b1f4f | |||
| 23a2cd3337 |
@@ -63,11 +63,12 @@ body:
|
|||||||
|
|
||||||
Please tag a maximum of 2 people.
|
Please tag a maximum of 2 people.
|
||||||
|
|
||||||
Questions on DiffusionPipeline (Saving, Loading, From pretrained, ...):
|
Questions on DiffusionPipeline (Saving, Loading, From pretrained, ...): @sayakpaul @DN6
|
||||||
|
|
||||||
Questions on pipelines:
|
Questions on pipelines:
|
||||||
- Stable Diffusion @yiyixuxu @DN6 @sayakpaul
|
- Stable Diffusion @yiyixuxu @asomoza
|
||||||
- Stable Diffusion XL @yiyixuxu @sayakpaul @DN6
|
- Stable Diffusion XL @yiyixuxu @sayakpaul @DN6
|
||||||
|
- Stable Diffusion 3: @yiyixuxu @sayakpaul @DN6 @asomoza
|
||||||
- Kandinsky @yiyixuxu
|
- Kandinsky @yiyixuxu
|
||||||
- ControlNet @sayakpaul @yiyixuxu @DN6
|
- ControlNet @sayakpaul @yiyixuxu @DN6
|
||||||
- T2I Adapter @sayakpaul @yiyixuxu @DN6
|
- T2I Adapter @sayakpaul @yiyixuxu @DN6
|
||||||
@@ -75,11 +76,14 @@ body:
|
|||||||
- Text-to-Video / Video-to-Video @DN6 @sayakpaul
|
- Text-to-Video / Video-to-Video @DN6 @sayakpaul
|
||||||
- Wuerstchen @DN6
|
- Wuerstchen @DN6
|
||||||
- Other: @yiyixuxu @DN6
|
- Other: @yiyixuxu @DN6
|
||||||
|
- Improving generation quality: @asomoza
|
||||||
|
|
||||||
Questions on models:
|
Questions on models:
|
||||||
- UNet @DN6 @yiyixuxu @sayakpaul
|
- UNet @DN6 @yiyixuxu @sayakpaul
|
||||||
- VAE @sayakpaul @DN6 @yiyixuxu
|
- VAE @sayakpaul @DN6 @yiyixuxu
|
||||||
- Transformers/Attention @DN6 @yiyixuxu @sayakpaul @DN6
|
- Transformers/Attention @DN6 @yiyixuxu @sayakpaul
|
||||||
|
|
||||||
|
Questions on single file checkpoints: @DN6
|
||||||
|
|
||||||
Questions on Schedulers: @yiyixuxu
|
Questions on Schedulers: @yiyixuxu
|
||||||
|
|
||||||
@@ -99,7 +103,7 @@ body:
|
|||||||
|
|
||||||
Questions on JAX- and MPS-related things: @pcuenca
|
Questions on JAX- and MPS-related things: @pcuenca
|
||||||
|
|
||||||
Questions on audio pipelines: @DN6
|
Questions on audio pipelines: @sanchit-gandhi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ members/contributors who may be interested in your PR.
|
|||||||
Core library:
|
Core library:
|
||||||
|
|
||||||
- Schedulers: @yiyixuxu
|
- Schedulers: @yiyixuxu
|
||||||
- Pipelines: @sayakpaul @yiyixuxu @DN6
|
- Pipelines and pipeline callbacks: @yiyixuxu and @asomoza
|
||||||
- Training examples: @sayakpaul
|
- Training examples: @sayakpaul
|
||||||
- Docs: @stevhliu and @sayakpaul
|
- Docs: @stevhliu and @sayakpaul
|
||||||
- JAX and MPS: @pcuenca
|
- JAX and MPS: @pcuenca
|
||||||
@@ -48,7 +48,7 @@ Core library:
|
|||||||
|
|
||||||
Integrations:
|
Integrations:
|
||||||
|
|
||||||
- deepspeed: HF Trainer/Accelerate: @pacman100
|
- deepspeed: HF Trainer/Accelerate: @SunMarc
|
||||||
|
|
||||||
HF projects:
|
HF projects:
|
||||||
|
|
||||||
|
|||||||
@@ -13,13 +13,15 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
torch_pipelines_cuda_benchmark_tests:
|
torch_pipelines_cuda_benchmark_tests:
|
||||||
|
env:
|
||||||
|
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_BENCHMARK }}
|
||||||
name: Torch Core Pipelines CUDA Benchmarking Tests
|
name: Torch Core Pipelines CUDA Benchmarking Tests
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
max-parallel: 1
|
max-parallel: 1
|
||||||
runs-on: [single-gpu, nvidia-gpu, a10, ci]
|
runs-on: [single-gpu, nvidia-gpu, a10, ci]
|
||||||
container:
|
container:
|
||||||
image: diffusers/diffusers-pytorch-cuda
|
image: diffusers/diffusers-pytorch-compile-cuda
|
||||||
options: --shm-size "16gb" --ipc host -v /mnt/hf_cache:/mnt/cache/ --gpus 0
|
options: --shm-size "16gb" --ipc host -v /mnt/hf_cache:/mnt/cache/ --gpus 0
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout diffusers
|
- name: Checkout diffusers
|
||||||
@@ -51,3 +53,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: benchmark_test_reports
|
name: benchmark_test_reports
|
||||||
path: benchmarks/benchmark_outputs
|
path: benchmarks/benchmark_outputs
|
||||||
|
|
||||||
|
- name: Report success status
|
||||||
|
if: ${{ success() }}
|
||||||
|
run: |
|
||||||
|
pip install requests && python utils/notify_benchmarking_status.py --status=success
|
||||||
|
|
||||||
|
- name: Report failure status
|
||||||
|
if: ${{ failure() }}
|
||||||
|
run: |
|
||||||
|
pip install requests && python utils/notify_benchmarking_status.py --status=failure
|
||||||
@@ -22,6 +22,9 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
mirror_community_pipeline:
|
mirror_community_pipeline:
|
||||||
|
env:
|
||||||
|
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_COMMUNITY_MIRROR }}
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
# Checkout to correct ref
|
# Checkout to correct ref
|
||||||
@@ -87,3 +90,13 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
PATH_IN_REPO: ${{ env.PATH_IN_REPO }}
|
PATH_IN_REPO: ${{ env.PATH_IN_REPO }}
|
||||||
HF_TOKEN: ${{ secrets.HF_TOKEN_MIRROR_COMMUNITY_PIPELINES }}
|
HF_TOKEN: ${{ secrets.HF_TOKEN_MIRROR_COMMUNITY_PIPELINES }}
|
||||||
|
|
||||||
|
- name: Report success status
|
||||||
|
if: ${{ success() }}
|
||||||
|
run: |
|
||||||
|
pip install requests && python utils/notify_community_pipelines_mirror.py --status=success
|
||||||
|
|
||||||
|
- name: Report failure status
|
||||||
|
if: ${{ failure() }}
|
||||||
|
run: |
|
||||||
|
pip install requests && python utils/notify_community_pipelines_mirror.py --status=failure
|
||||||
@@ -33,4 +33,3 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
python -m venv /opt/venv && export PATH="/opt/venv/bin:$PATH"
|
python -m venv /opt/venv && export PATH="/opt/venv/bin:$PATH"
|
||||||
pytest tests/others/test_dependencies.py
|
pytest tests/others/test_dependencies.py
|
||||||
|
|
||||||
@@ -330,6 +330,7 @@ jobs:
|
|||||||
- name: Run example tests on GPU
|
- name: Run example tests on GPU
|
||||||
env:
|
env:
|
||||||
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
||||||
|
RUN_COMPILE: yes
|
||||||
run: |
|
run: |
|
||||||
python -m pytest -n 1 --max-worker-restart=0 --dist=loadfile -s -v -k "compile" --make-reports=tests_torch_compile_cuda tests/
|
python -m pytest -n 1 --max-worker-restart=0 --dist=loadfile -s -v -k "compile" --make-reports=tests_torch_compile_cuda tests/
|
||||||
- name: Failure short reports
|
- name: Failure short reports
|
||||||
|
|||||||
+3
-2
@@ -245,7 +245,7 @@ The official training examples are maintained by the Diffusers' core maintainers
|
|||||||
This is because of the same reasons put forward in [6. Contribute a community pipeline](#6-contribute-a-community-pipeline) for official pipelines vs. community pipelines: It is not feasible for the core maintainers to maintain all possible training methods for diffusion models.
|
This is because of the same reasons put forward in [6. Contribute a community pipeline](#6-contribute-a-community-pipeline) for official pipelines vs. community pipelines: It is not feasible for the core maintainers to maintain all possible training methods for diffusion models.
|
||||||
If the Diffusers core maintainers and the community consider a certain training paradigm to be too experimental or not popular enough, the corresponding training code should be put in the `research_projects` folder and maintained by the author.
|
If the Diffusers core maintainers and the community consider a certain training paradigm to be too experimental or not popular enough, the corresponding training code should be put in the `research_projects` folder and maintained by the author.
|
||||||
|
|
||||||
Both official training and research examples consist of a directory that contains one or more training scripts, a requirements.txt file, and a README.md file. In order for the user to make use of the
|
Both official training and research examples consist of a directory that contains one or more training scripts, a `requirements.txt` file, and a `README.md` file. In order for the user to make use of the
|
||||||
training examples, it is required to clone the repository:
|
training examples, it is required to clone the repository:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -255,7 +255,8 @@ git clone https://github.com/huggingface/diffusers
|
|||||||
as well as to install all additional dependencies required for training:
|
as well as to install all additional dependencies required for training:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install -r /examples/<your-example-folder>/requirements.txt
|
cd diffusers
|
||||||
|
pip install -r examples/<your-example-folder>/requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
Therefore when adding an example, the `requirements.txt` file shall define all pip dependencies required for your training example so that once all those are installed, the user can run the example's training script. See, for example, the [DreamBooth `requirements.txt` file](https://github.com/huggingface/diffusers/blob/main/examples/dreambooth/requirements.txt).
|
Therefore when adding an example, the `requirements.txt` file shall define all pip dependencies required for your training example so that once all those are installed, the user can run the example's training script. See, for example, the [DreamBooth `requirements.txt` file](https://github.com/huggingface/diffusers/blob/main/examples/dreambooth/requirements.txt).
|
||||||
|
|||||||
+5
-5
@@ -63,14 +63,14 @@ Let's walk through more detailed design decisions for each class.
|
|||||||
Pipelines are designed to be easy to use (therefore do not follow [*Simple over easy*](#simple-over-easy) 100%), are not feature complete, and should loosely be seen as examples of how to use [models](#models) and [schedulers](#schedulers) for inference.
|
Pipelines are designed to be easy to use (therefore do not follow [*Simple over easy*](#simple-over-easy) 100%), are not feature complete, and should loosely be seen as examples of how to use [models](#models) and [schedulers](#schedulers) for inference.
|
||||||
|
|
||||||
The following design principles are followed:
|
The following design principles are followed:
|
||||||
- Pipelines follow the single-file policy. All pipelines can be found in individual directories under src/diffusers/pipelines. One pipeline folder corresponds to one diffusion paper/project/release. Multiple pipeline files can be gathered in one pipeline folder, as it’s done for [`src/diffusers/pipelines/stable-diffusion`](https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines/stable_diffusion). If pipelines share similar functionality, one can make use of the [#Copied from mechanism](https://github.com/huggingface/diffusers/blob/125d783076e5bd9785beb05367a2d2566843a271/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion_img2img.py#L251).
|
- Pipelines follow the single-file policy. All pipelines can be found in individual directories under src/diffusers/pipelines. One pipeline folder corresponds to one diffusion paper/project/release. Multiple pipeline files can be gathered in one pipeline folder, as it’s done for [`src/diffusers/pipelines/stable-diffusion`](https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines/stable_diffusion). If pipelines share similar functionality, one can make use of the [# Copied from mechanism](https://github.com/huggingface/diffusers/blob/125d783076e5bd9785beb05367a2d2566843a271/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion_img2img.py#L251).
|
||||||
- Pipelines all inherit from [`DiffusionPipeline`].
|
- Pipelines all inherit from [`DiffusionPipeline`].
|
||||||
- Every pipeline consists of different model and scheduler components, that are documented in the [`model_index.json` file](https://huggingface.co/runwayml/stable-diffusion-v1-5/blob/main/model_index.json), are accessible under the same name as attributes of the pipeline and can be shared between pipelines with [`DiffusionPipeline.components`](https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.components) function.
|
- Every pipeline consists of different model and scheduler components, that are documented in the [`model_index.json` file](https://huggingface.co/runwayml/stable-diffusion-v1-5/blob/main/model_index.json), are accessible under the same name as attributes of the pipeline and can be shared between pipelines with [`DiffusionPipeline.components`](https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.components) function.
|
||||||
- Every pipeline should be loadable via the [`DiffusionPipeline.from_pretrained`](https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.from_pretrained) function.
|
- Every pipeline should be loadable via the [`DiffusionPipeline.from_pretrained`](https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.from_pretrained) function.
|
||||||
- Pipelines should be used **only** for inference.
|
- Pipelines should be used **only** for inference.
|
||||||
- Pipelines should be very readable, self-explanatory, and easy to tweak.
|
- Pipelines should be very readable, self-explanatory, and easy to tweak.
|
||||||
- Pipelines should be designed to build on top of each other and be easy to integrate into higher-level APIs.
|
- Pipelines should be designed to build on top of each other and be easy to integrate into higher-level APIs.
|
||||||
- Pipelines are **not** intended to be feature-complete user interfaces. For future complete user interfaces one should rather have a look at [InvokeAI](https://github.com/invoke-ai/InvokeAI), [Diffuzers](https://github.com/abhishekkrthakur/diffuzers), and [lama-cleaner](https://github.com/Sanster/lama-cleaner).
|
- Pipelines are **not** intended to be feature-complete user interfaces. For feature-complete user interfaces one should rather have a look at [InvokeAI](https://github.com/invoke-ai/InvokeAI), [Diffuzers](https://github.com/abhishekkrthakur/diffuzers), and [lama-cleaner](https://github.com/Sanster/lama-cleaner).
|
||||||
- Every pipeline should have one and only one way to run it via a `__call__` method. The naming of the `__call__` arguments should be shared across all pipelines.
|
- Every pipeline should have one and only one way to run it via a `__call__` method. The naming of the `__call__` arguments should be shared across all pipelines.
|
||||||
- Pipelines should be named after the task they are intended to solve.
|
- Pipelines should be named after the task they are intended to solve.
|
||||||
- In almost all cases, novel diffusion pipelines shall be implemented in a new pipeline folder/file.
|
- In almost all cases, novel diffusion pipelines shall be implemented in a new pipeline folder/file.
|
||||||
@@ -81,7 +81,7 @@ Models are designed as configurable toolboxes that are natural extensions of [Py
|
|||||||
|
|
||||||
The following design principles are followed:
|
The following design principles are followed:
|
||||||
- Models correspond to **a type of model architecture**. *E.g.* the [`UNet2DConditionModel`] class is used for all UNet variations that expect 2D image inputs and are conditioned on some context.
|
- Models correspond to **a type of model architecture**. *E.g.* the [`UNet2DConditionModel`] class is used for all UNet variations that expect 2D image inputs and are conditioned on some context.
|
||||||
- All models can be found in [`src/diffusers/models`](https://github.com/huggingface/diffusers/tree/main/src/diffusers/models) and every model architecture shall be defined in its file, e.g. [`unet_2d_condition.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/unet_2d_condition.py), [`transformer_2d.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/transformer_2d.py), etc...
|
- All models can be found in [`src/diffusers/models`](https://github.com/huggingface/diffusers/tree/main/src/diffusers/models) and every model architecture shall be defined in its file, e.g. [`unets/unet_2d_condition.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/unets/unet_2d_condition.py), [`transformers/transformer_2d.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/transformers/transformer_2d.py), etc...
|
||||||
- Models **do not** follow the single-file policy and should make use of smaller model building blocks, such as [`attention.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention.py), [`resnet.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/resnet.py), [`embeddings.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/embeddings.py), etc... **Note**: This is in stark contrast to Transformers' modeling files and shows that models do not really follow the single-file policy.
|
- Models **do not** follow the single-file policy and should make use of smaller model building blocks, such as [`attention.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention.py), [`resnet.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/resnet.py), [`embeddings.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/embeddings.py), etc... **Note**: This is in stark contrast to Transformers' modeling files and shows that models do not really follow the single-file policy.
|
||||||
- Models intend to expose complexity, just like PyTorch's `Module` class, and give clear error messages.
|
- Models intend to expose complexity, just like PyTorch's `Module` class, and give clear error messages.
|
||||||
- Models all inherit from `ModelMixin` and `ConfigMixin`.
|
- Models all inherit from `ModelMixin` and `ConfigMixin`.
|
||||||
@@ -90,7 +90,7 @@ The following design principles are followed:
|
|||||||
- To integrate new model checkpoints whose general architecture can be classified as an architecture that already exists in Diffusers, the existing model architecture shall be adapted to make it work with the new checkpoint. One should only create a new file if the model architecture is fundamentally different.
|
- To integrate new model checkpoints whose general architecture can be classified as an architecture that already exists in Diffusers, the existing model architecture shall be adapted to make it work with the new checkpoint. One should only create a new file if the model architecture is fundamentally different.
|
||||||
- Models should be designed to be easily extendable to future changes. This can be achieved by limiting public function arguments, configuration arguments, and "foreseeing" future changes, *e.g.* it is usually better to add `string` "...type" arguments that can easily be extended to new future types instead of boolean `is_..._type` arguments. Only the minimum amount of changes shall be made to existing architectures to make a new model checkpoint work.
|
- Models should be designed to be easily extendable to future changes. This can be achieved by limiting public function arguments, configuration arguments, and "foreseeing" future changes, *e.g.* it is usually better to add `string` "...type" arguments that can easily be extended to new future types instead of boolean `is_..._type` arguments. Only the minimum amount of changes shall be made to existing architectures to make a new model checkpoint work.
|
||||||
- The model design is a difficult trade-off between keeping code readable and concise and supporting many model checkpoints. For most parts of the modeling code, classes shall be adapted for new model checkpoints, while there are some exceptions where it is preferred to add new classes to make sure the code is kept concise and
|
- The model design is a difficult trade-off between keeping code readable and concise and supporting many model checkpoints. For most parts of the modeling code, classes shall be adapted for new model checkpoints, while there are some exceptions where it is preferred to add new classes to make sure the code is kept concise and
|
||||||
readable long-term, such as [UNet blocks](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/unet_2d_blocks.py) and [Attention processors](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
|
readable long-term, such as [UNet blocks](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/unets/unet_2d_blocks.py) and [Attention processors](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
|
||||||
|
|
||||||
### Schedulers
|
### Schedulers
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ The following design principles are followed:
|
|||||||
- All schedulers are found in [`src/diffusers/schedulers`](https://github.com/huggingface/diffusers/tree/main/src/diffusers/schedulers).
|
- All schedulers are found in [`src/diffusers/schedulers`](https://github.com/huggingface/diffusers/tree/main/src/diffusers/schedulers).
|
||||||
- Schedulers are **not** allowed to import from large utils files and shall be kept very self-contained.
|
- Schedulers are **not** allowed to import from large utils files and shall be kept very self-contained.
|
||||||
- One scheduler Python file corresponds to one scheduler algorithm (as might be defined in a paper).
|
- One scheduler Python file corresponds to one scheduler algorithm (as might be defined in a paper).
|
||||||
- If schedulers share similar functionalities, we can make use of the `#Copied from` mechanism.
|
- If schedulers share similar functionalities, we can make use of the `# Copied from` mechanism.
|
||||||
- Schedulers all inherit from `SchedulerMixin` and `ConfigMixin`.
|
- Schedulers all inherit from `SchedulerMixin` and `ConfigMixin`.
|
||||||
- Schedulers can be easily swapped out with the [`ConfigMixin.from_config`](https://huggingface.co/docs/diffusers/main/en/api/configuration#diffusers.ConfigMixin.from_config) method as explained in detail [here](./docs/source/en/using-diffusers/schedulers.md).
|
- Schedulers can be easily swapped out with the [`ConfigMixin.from_config`](https://huggingface.co/docs/diffusers/main/en/api/configuration#diffusers.ConfigMixin.from_config) method as explained in detail [here](./docs/source/en/using-diffusers/schedulers.md).
|
||||||
- Every scheduler has to have a `set_num_inference_steps`, and a `step` function. `set_num_inference_steps(...)` has to be called before every denoising process, *i.e.* before `step(...)` is called.
|
- Every scheduler has to have a `set_num_inference_steps`, and a `step` function. `set_num_inference_steps(...)` has to be called before every denoising process, *i.e.* before `step(...)` is called.
|
||||||
|
|||||||
@@ -20,21 +20,11 @@ limitations under the License.
|
|||||||
<br>
|
<br>
|
||||||
<p>
|
<p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/huggingface/diffusers/blob/main/LICENSE">
|
<a href="https://github.com/huggingface/diffusers/blob/main/LICENSE"><img alt="GitHub" src="https://img.shields.io/github/license/huggingface/datasets.svg?color=blue"></a>
|
||||||
<img alt="GitHub" src="https://img.shields.io/github/license/huggingface/datasets.svg?color=blue">
|
<a href="https://github.com/huggingface/diffusers/releases"><img alt="GitHub release" src="https://img.shields.io/github/release/huggingface/diffusers.svg"></a>
|
||||||
</a>
|
<a href="https://pepy.tech/project/diffusers"><img alt="GitHub release" src="https://static.pepy.tech/badge/diffusers/month"></a>
|
||||||
<a href="https://github.com/huggingface/diffusers/releases">
|
<a href="CODE_OF_CONDUCT.md"><img alt="Contributor Covenant" src="https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg"></a>
|
||||||
<img alt="GitHub release" src="https://img.shields.io/github/release/huggingface/diffusers.svg">
|
<a href="https://twitter.com/diffuserslib"><img alt="X account" src="https://img.shields.io/twitter/url/https/twitter.com/diffuserslib.svg?style=social&label=Follow%20%40diffuserslib"></a>
|
||||||
</a>
|
|
||||||
<a href="https://pepy.tech/project/diffusers">
|
|
||||||
<img alt="GitHub release" src="https://static.pepy.tech/badge/diffusers/month">
|
|
||||||
</a>
|
|
||||||
<a href="CODE_OF_CONDUCT.md">
|
|
||||||
<img alt="Contributor Covenant" src="https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg">
|
|
||||||
</a>
|
|
||||||
<a href="https://twitter.com/diffuserslib">
|
|
||||||
<img alt="X account" src="https://img.shields.io/twitter/url/https/twitter.com/diffuserslib.svg?style=social&label=Follow%20%40diffuserslib">
|
|
||||||
</a>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
🤗 Diffusers is the go-to library for state-of-the-art pretrained diffusion models for generating images, audio, and even 3D structures of molecules. Whether you're looking for a simple inference solution or training your own diffusion models, 🤗 Diffusers is a modular toolbox that supports both. Our library is designed with a focus on [usability over performance](https://huggingface.co/docs/diffusers/conceptual/philosophy#usability-over-performance), [simple over easy](https://huggingface.co/docs/diffusers/conceptual/philosophy#simple-over-easy), and [customizability over abstractions](https://huggingface.co/docs/diffusers/conceptual/philosophy#tweakable-contributorfriendly-over-abstraction).
|
🤗 Diffusers is the go-to library for state-of-the-art pretrained diffusion models for generating images, audio, and even 3D structures of molecules. Whether you're looking for a simple inference solution or training your own diffusion models, 🤗 Diffusers is a modular toolbox that supports both. Our library is designed with a focus on [usability over performance](https://huggingface.co/docs/diffusers/conceptual/philosophy#usability-over-performance), [simple over easy](https://huggingface.co/docs/diffusers/conceptual/philosophy#simple-over-easy), and [customizability over abstractions](https://huggingface.co/docs/diffusers/conceptual/philosophy#tweakable-contributorfriendly-over-abstraction).
|
||||||
@@ -77,7 +67,7 @@ Please refer to the [How to use Stable Diffusion in Apple Silicon](https://huggi
|
|||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
Generating outputs is super easy with 🤗 Diffusers. To generate an image from text, use the `from_pretrained` method to load any pretrained diffusion model (browse the [Hub](https://huggingface.co/models?library=diffusers&sort=downloads) for 25.000+ checkpoints):
|
Generating outputs is super easy with 🤗 Diffusers. To generate an image from text, use the `from_pretrained` method to load any pretrained diffusion model (browse the [Hub](https://huggingface.co/models?library=diffusers&sort=downloads) for 27.000+ checkpoints):
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from diffusers import DiffusionPipeline
|
from diffusers import DiffusionPipeline
|
||||||
@@ -219,7 +209,7 @@ Also, say 👋 in our public Discord channel <a href="https://discord.gg/G7tWnz9
|
|||||||
- https://github.com/deep-floyd/IF
|
- https://github.com/deep-floyd/IF
|
||||||
- https://github.com/bentoml/BentoML
|
- https://github.com/bentoml/BentoML
|
||||||
- https://github.com/bmaltais/kohya_ss
|
- https://github.com/bmaltais/kohya_ss
|
||||||
- +11.000 other amazing GitHub repositories 💪
|
- +12.000 other amazing GitHub repositories 💪
|
||||||
|
|
||||||
Thank you for using us ❤️.
|
Thank you for using us ❤️.
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ def main():
|
|||||||
print(f"****** Running file: {file} ******")
|
print(f"****** Running file: {file} ******")
|
||||||
|
|
||||||
# Run with canonical settings.
|
# Run with canonical settings.
|
||||||
if file != "benchmark_text_to_image.py":
|
if file != "benchmark_text_to_image.py" and file != "benchmark_ip_adapters.py":
|
||||||
command = f"python {file}"
|
command = f"python {file}"
|
||||||
run_command(command.split())
|
run_command(command.split())
|
||||||
|
|
||||||
@@ -49,6 +49,10 @@ def main():
|
|||||||
|
|
||||||
# Run variants.
|
# Run variants.
|
||||||
for file in python_files:
|
for file in python_files:
|
||||||
|
# See: https://github.com/pytorch/pytorch/issues/129637
|
||||||
|
if file == "benchmark_ip_adapters.py":
|
||||||
|
continue
|
||||||
|
|
||||||
if file == "benchmark_text_to_image.py":
|
if file == "benchmark_text_to_image.py":
|
||||||
for ckpt in ALL_T2I_CKPTS:
|
for ckpt in ALL_T2I_CKPTS:
|
||||||
command = f"python {file} --ckpt {ckpt}"
|
command = f"python {file} --ckpt {ckpt}"
|
||||||
|
|||||||
@@ -16,23 +16,24 @@ RUN apt install -y bash \
|
|||||||
ca-certificates \
|
ca-certificates \
|
||||||
libsndfile1-dev \
|
libsndfile1-dev \
|
||||||
libgl1 \
|
libgl1 \
|
||||||
python3.10 \
|
python3.9 \
|
||||||
|
python3.9-dev \
|
||||||
python3-pip \
|
python3-pip \
|
||||||
python3.10-venv && \
|
python3.9-venv && \
|
||||||
rm -rf /var/lib/apt/lists
|
rm -rf /var/lib/apt/lists
|
||||||
|
|
||||||
# make sure to use venv
|
# make sure to use venv
|
||||||
RUN python3.10 -m venv /opt/venv
|
RUN python3.9 -m venv /opt/venv
|
||||||
ENV PATH="/opt/venv/bin:$PATH"
|
ENV PATH="/opt/venv/bin:$PATH"
|
||||||
|
|
||||||
# pre-install the heavy dependencies (these can later be overridden by the deps from setup.py)
|
# pre-install the heavy dependencies (these can later be overridden by the deps from setup.py)
|
||||||
RUN python3.10 -m pip install --no-cache-dir --upgrade pip uv==0.1.11 && \
|
RUN python3.9 -m pip install --no-cache-dir --upgrade pip uv==0.1.11 && \
|
||||||
python3.10 -m uv pip install --no-cache-dir \
|
python3.9 -m uv pip install --no-cache-dir \
|
||||||
torch \
|
torch \
|
||||||
torchvision \
|
torchvision \
|
||||||
torchaudio \
|
torchaudio \
|
||||||
invisible_watermark && \
|
invisible_watermark && \
|
||||||
python3.10 -m pip install --no-cache-dir \
|
python3.9 -m pip install --no-cache-dir \
|
||||||
accelerate \
|
accelerate \
|
||||||
datasets \
|
datasets \
|
||||||
hf-doc-builder \
|
hf-doc-builder \
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
title: Load LoRAs for inference
|
title: Load LoRAs for inference
|
||||||
- local: tutorials/fast_diffusion
|
- local: tutorials/fast_diffusion
|
||||||
title: Accelerate inference of text-to-image diffusion models
|
title: Accelerate inference of text-to-image diffusion models
|
||||||
|
- local: tutorials/inference_with_big_models
|
||||||
|
title: Working with big models
|
||||||
title: Tutorials
|
title: Tutorials
|
||||||
- sections:
|
- sections:
|
||||||
- local: using-diffusers/loading
|
- local: using-diffusers/loading
|
||||||
@@ -81,6 +83,8 @@
|
|||||||
title: Kandinsky
|
title: Kandinsky
|
||||||
- local: using-diffusers/ip_adapter
|
- local: using-diffusers/ip_adapter
|
||||||
title: IP-Adapter
|
title: IP-Adapter
|
||||||
|
- local: using-diffusers/pag
|
||||||
|
title: PAG
|
||||||
- local: using-diffusers/controlnet
|
- local: using-diffusers/controlnet
|
||||||
title: ControlNet
|
title: ControlNet
|
||||||
- local: using-diffusers/t2i_adapter
|
- local: using-diffusers/t2i_adapter
|
||||||
@@ -253,6 +257,10 @@
|
|||||||
title: PriorTransformer
|
title: PriorTransformer
|
||||||
- local: api/models/controlnet
|
- local: api/models/controlnet
|
||||||
title: ControlNetModel
|
title: ControlNetModel
|
||||||
|
- local: api/models/controlnet_hunyuandit
|
||||||
|
title: HunyuanDiT2DControlNetModel
|
||||||
|
- local: api/models/controlnet_sd3
|
||||||
|
title: SD3ControlNetModel
|
||||||
title: Models
|
title: Models
|
||||||
- isExpanded: false
|
- isExpanded: false
|
||||||
sections:
|
sections:
|
||||||
@@ -276,6 +284,10 @@
|
|||||||
title: Consistency Models
|
title: Consistency Models
|
||||||
- local: api/pipelines/controlnet
|
- local: api/pipelines/controlnet
|
||||||
title: ControlNet
|
title: ControlNet
|
||||||
|
- local: api/pipelines/controlnet_hunyuandit
|
||||||
|
title: ControlNet with Hunyuan-DiT
|
||||||
|
- local: api/pipelines/controlnet_sd3
|
||||||
|
title: ControlNet with Stable Diffusion 3
|
||||||
- local: api/pipelines/controlnet_sdxl
|
- local: api/pipelines/controlnet_sdxl
|
||||||
title: ControlNet with Stable Diffusion XL
|
title: ControlNet with Stable Diffusion XL
|
||||||
- local: api/pipelines/controlnetxs
|
- local: api/pipelines/controlnetxs
|
||||||
@@ -318,6 +330,8 @@
|
|||||||
title: MultiDiffusion
|
title: MultiDiffusion
|
||||||
- local: api/pipelines/musicldm
|
- local: api/pipelines/musicldm
|
||||||
title: MusicLDM
|
title: MusicLDM
|
||||||
|
- local: api/pipelines/pag
|
||||||
|
title: PAG
|
||||||
- local: api/pipelines/paint_by_example
|
- local: api/pipelines/paint_by_example
|
||||||
title: Paint by Example
|
title: Paint by Example
|
||||||
- local: api/pipelines/pia
|
- local: api/pipelines/pia
|
||||||
|
|||||||
@@ -41,12 +41,6 @@ An attention processor is a class for applying different types of attention mech
|
|||||||
## FusedAttnProcessor2_0
|
## FusedAttnProcessor2_0
|
||||||
[[autodoc]] models.attention_processor.FusedAttnProcessor2_0
|
[[autodoc]] models.attention_processor.FusedAttnProcessor2_0
|
||||||
|
|
||||||
## LoRAAttnAddedKVProcessor
|
|
||||||
[[autodoc]] models.attention_processor.LoRAAttnAddedKVProcessor
|
|
||||||
|
|
||||||
## LoRAXFormersAttnProcessor
|
|
||||||
[[autodoc]] models.attention_processor.LoRAXFormersAttnProcessor
|
|
||||||
|
|
||||||
## SlicedAttnProcessor
|
## SlicedAttnProcessor
|
||||||
[[autodoc]] models.attention_processor.SlicedAttnProcessor
|
[[autodoc]] models.attention_processor.SlicedAttnProcessor
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ The abstract from the paper is:
|
|||||||
## Loading from the original format
|
## Loading from the original format
|
||||||
|
|
||||||
By default the [`AutoencoderKL`] should be loaded with [`~ModelMixin.from_pretrained`], but it can also be loaded
|
By default the [`AutoencoderKL`] should be loaded with [`~ModelMixin.from_pretrained`], but it can also be loaded
|
||||||
from the original format using [`FromOriginalVAEMixin.from_single_file`] as follows:
|
from the original format using [`FromOriginalModelMixin.from_single_file`] as follows:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from diffusers import AutoencoderKL
|
from diffusers import AutoencoderKL
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ The abstract from the paper is:
|
|||||||
## Loading from the original format
|
## Loading from the original format
|
||||||
|
|
||||||
By default the [`ControlNetModel`] should be loaded with [`~ModelMixin.from_pretrained`], but it can also be loaded
|
By default the [`ControlNetModel`] should be loaded with [`~ModelMixin.from_pretrained`], but it can also be loaded
|
||||||
from the original format using [`FromOriginalControlnetMixin.from_single_file`] as follows:
|
from the original format using [`FromOriginalModelMixin.from_single_file`] as follows:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
|
from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<!--Copyright 2024 The HuggingFace Team and Tencent Hunyuan Team. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
# HunyuanDiT2DControlNetModel
|
||||||
|
|
||||||
|
HunyuanDiT2DControlNetModel is an implementation of ControlNet for [Hunyuan-DiT](https://arxiv.org/abs/2405.08748).
|
||||||
|
|
||||||
|
ControlNet was introduced in [Adding Conditional Control to Text-to-Image Diffusion Models](https://huggingface.co/papers/2302.05543) by Lvmin Zhang, Anyi Rao, and Maneesh Agrawala.
|
||||||
|
|
||||||
|
With a ControlNet model, you can provide an additional control image to condition and control Hunyuan-DiT generation. For example, if you provide a depth map, the ControlNet model generates an image that'll preserve the spatial information from the depth map. It is a more flexible and accurate way to control the image generation process.
|
||||||
|
|
||||||
|
The abstract from the paper is:
|
||||||
|
|
||||||
|
*We present ControlNet, a neural network architecture to add spatial conditioning controls to large, pretrained text-to-image diffusion models. ControlNet locks the production-ready large diffusion models, and reuses their deep and robust encoding layers pretrained with billions of images as a strong backbone to learn a diverse set of conditional controls. The neural architecture is connected with "zero convolutions" (zero-initialized convolution layers) that progressively grow the parameters from zero and ensure that no harmful noise could affect the finetuning. We test various conditioning controls, eg, edges, depth, segmentation, human pose, etc, with Stable Diffusion, using single or multiple conditions, with or without prompts. We show that the training of ControlNets is robust with small (<50k) and large (>1m) datasets. Extensive results show that ControlNet may facilitate wider applications to control image diffusion models.*
|
||||||
|
|
||||||
|
This code is implemented by Tencent Hunyuan Team. You can find pre-trained checkpoints for Hunyuan-DiT ControlNets on [Tencent Hunyuan](https://huggingface.co/Tencent-Hunyuan).
|
||||||
|
|
||||||
|
## Example For Loading HunyuanDiT2DControlNetModel
|
||||||
|
|
||||||
|
```py
|
||||||
|
from diffusers import HunyuanDiT2DControlNetModel
|
||||||
|
import torch
|
||||||
|
controlnet = HunyuanDiT2DControlNetModel.from_pretrained("Tencent-Hunyuan/HunyuanDiT-v1.1-ControlNet-Diffusers-Pose", torch_dtype=torch.float16)
|
||||||
|
```
|
||||||
|
|
||||||
|
## HunyuanDiT2DControlNetModel
|
||||||
|
|
||||||
|
[[autodoc]] HunyuanDiT2DControlNetModel
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<!--Copyright 2024 The HuggingFace Team and The InstantX Team. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
# SD3ControlNetModel
|
||||||
|
|
||||||
|
SD3ControlNetModel is an implementation of ControlNet for Stable Diffusion 3.
|
||||||
|
|
||||||
|
The ControlNet model was introduced in [Adding Conditional Control to Text-to-Image Diffusion Models](https://huggingface.co/papers/2302.05543) by Lvmin Zhang, Anyi Rao, Maneesh Agrawala. It provides a greater degree of control over text-to-image generation by conditioning the model on additional inputs such as edge maps, depth maps, segmentation maps, and keypoints for pose detection.
|
||||||
|
|
||||||
|
The abstract from the paper is:
|
||||||
|
|
||||||
|
*We present ControlNet, a neural network architecture to add spatial conditioning controls to large, pretrained text-to-image diffusion models. ControlNet locks the production-ready large diffusion models, and reuses their deep and robust encoding layers pretrained with billions of images as a strong backbone to learn a diverse set of conditional controls. The neural architecture is connected with "zero convolutions" (zero-initialized convolution layers) that progressively grow the parameters from zero and ensure that no harmful noise could affect the finetuning. We test various conditioning controls, eg, edges, depth, segmentation, human pose, etc, with Stable Diffusion, using single or multiple conditions, with or without prompts. We show that the training of ControlNets is robust with small (<50k) and large (>1m) datasets. Extensive results show that ControlNet may facilitate wider applications to control image diffusion models.*
|
||||||
|
|
||||||
|
## Loading from the original format
|
||||||
|
|
||||||
|
By default the [`SD3ControlNetModel`] should be loaded with [`~ModelMixin.from_pretrained`].
|
||||||
|
|
||||||
|
```py
|
||||||
|
from diffusers import StableDiffusion3ControlNetPipeline
|
||||||
|
from diffusers.models import SD3ControlNetModel, SD3MultiControlNetModel
|
||||||
|
|
||||||
|
controlnet = SD3ControlNetModel.from_pretrained("InstantX/SD3-Controlnet-Canny")
|
||||||
|
pipe = StableDiffusion3ControlNetPipeline.from_pretrained("stabilityai/stable-diffusion-3-medium-diffusers", controlnet=controlnet)
|
||||||
|
```
|
||||||
|
|
||||||
|
## SD3ControlNetModel
|
||||||
|
|
||||||
|
[[autodoc]] SD3ControlNetModel
|
||||||
|
|
||||||
|
## SD3ControlNetOutput
|
||||||
|
|
||||||
|
[[autodoc]] models.controlnet_sd3.SD3ControlNetOutput
|
||||||
|
|
||||||
@@ -38,4 +38,4 @@ It is assumed one of the input classes is the masked latent pixel. The predicted
|
|||||||
|
|
||||||
## Transformer2DModelOutput
|
## Transformer2DModelOutput
|
||||||
|
|
||||||
[[autodoc]] models.transformers.transformer_2d.Transformer2DModelOutput
|
[[autodoc]] models.modeling_outputs.Transformer2DModelOutput
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ output = pipe(
|
|||||||
)
|
)
|
||||||
frames = output.frames[0]
|
frames = output.frames[0]
|
||||||
export_to_gif(frames, "animation.gif")
|
export_to_gif(frames, "animation.gif")
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Here are some sample outputs:
|
Here are some sample outputs:
|
||||||
@@ -303,7 +302,6 @@ output = pipe(
|
|||||||
)
|
)
|
||||||
frames = output.frames[0]
|
frames = output.frames[0]
|
||||||
export_to_gif(frames, "animation.gif")
|
export_to_gif(frames, "animation.gif")
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
@@ -378,7 +376,6 @@ output = pipe(
|
|||||||
)
|
)
|
||||||
frames = output.frames[0]
|
frames = output.frames[0]
|
||||||
export_to_gif(frames, "animation.gif")
|
export_to_gif(frames, "animation.gif")
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<!--Copyright 2024 The HuggingFace Team and Tencent Hunyuan Team. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
# ControlNet with Hunyuan-DiT
|
||||||
|
|
||||||
|
HunyuanDiTControlNetPipeline is an implementation of ControlNet for [Hunyuan-DiT](https://arxiv.org/abs/2405.08748).
|
||||||
|
|
||||||
|
ControlNet was introduced in [Adding Conditional Control to Text-to-Image Diffusion Models](https://huggingface.co/papers/2302.05543) by Lvmin Zhang, Anyi Rao, and Maneesh Agrawala.
|
||||||
|
|
||||||
|
With a ControlNet model, you can provide an additional control image to condition and control Hunyuan-DiT generation. For example, if you provide a depth map, the ControlNet model generates an image that'll preserve the spatial information from the depth map. It is a more flexible and accurate way to control the image generation process.
|
||||||
|
|
||||||
|
The abstract from the paper is:
|
||||||
|
|
||||||
|
*We present ControlNet, a neural network architecture to add spatial conditioning controls to large, pretrained text-to-image diffusion models. ControlNet locks the production-ready large diffusion models, and reuses their deep and robust encoding layers pretrained with billions of images as a strong backbone to learn a diverse set of conditional controls. The neural architecture is connected with "zero convolutions" (zero-initialized convolution layers) that progressively grow the parameters from zero and ensure that no harmful noise could affect the finetuning. We test various conditioning controls, eg, edges, depth, segmentation, human pose, etc, with Stable Diffusion, using single or multiple conditions, with or without prompts. We show that the training of ControlNets is robust with small (<50k) and large (>1m) datasets. Extensive results show that ControlNet may facilitate wider applications to control image diffusion models.*
|
||||||
|
|
||||||
|
This code is implemented by Tencent Hunyuan Team. You can find pre-trained checkpoints for Hunyuan-DiT ControlNets on [Tencent Hunyuan](https://huggingface.co/Tencent-Hunyuan).
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
|
||||||
|
Make sure to check out the Schedulers [guide](../../using-diffusers/schedulers) to learn how to explore the tradeoff between scheduler speed and quality, and see the [reuse components across pipelines](../../using-diffusers/loading#reuse-components-across-pipelines) section to learn how to efficiently load the same components into multiple pipelines.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
## HunyuanDiTControlNetPipeline
|
||||||
|
[[autodoc]] HunyuanDiTControlNetPipeline
|
||||||
|
- all
|
||||||
|
- __call__
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<!--Copyright 2023 The HuggingFace Team and The InstantX Team. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
# ControlNet with Stable Diffusion 3
|
||||||
|
|
||||||
|
StableDiffusion3ControlNetPipeline is an implementation of ControlNet for Stable Diffusion 3.
|
||||||
|
|
||||||
|
ControlNet was introduced in [Adding Conditional Control to Text-to-Image Diffusion Models](https://huggingface.co/papers/2302.05543) by Lvmin Zhang, Anyi Rao, and Maneesh Agrawala.
|
||||||
|
|
||||||
|
With a ControlNet model, you can provide an additional control image to condition and control Stable Diffusion generation. For example, if you provide a depth map, the ControlNet model generates an image that'll preserve the spatial information from the depth map. It is a more flexible and accurate way to control the image generation process.
|
||||||
|
|
||||||
|
The abstract from the paper is:
|
||||||
|
|
||||||
|
*We present ControlNet, a neural network architecture to add spatial conditioning controls to large, pretrained text-to-image diffusion models. ControlNet locks the production-ready large diffusion models, and reuses their deep and robust encoding layers pretrained with billions of images as a strong backbone to learn a diverse set of conditional controls. The neural architecture is connected with "zero convolutions" (zero-initialized convolution layers) that progressively grow the parameters from zero and ensure that no harmful noise could affect the finetuning. We test various conditioning controls, eg, edges, depth, segmentation, human pose, etc, with Stable Diffusion, using single or multiple conditions, with or without prompts. We show that the training of ControlNets is robust with small (<50k) and large (>1m) datasets. Extensive results show that ControlNet may facilitate wider applications to control image diffusion models.*
|
||||||
|
|
||||||
|
This code is implemented by [The InstantX Team](https://huggingface.co/InstantX). You can find pre-trained checkpoints for SD3-ControlNet on [The InstantX Team](https://huggingface.co/InstantX) Hub profile.
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
|
||||||
|
Make sure to check out the Schedulers [guide](../../using-diffusers/schedulers) to learn how to explore the tradeoff between scheduler speed and quality, and see the [reuse components across pipelines](../../using-diffusers/loading#reuse-components-across-pipelines) section to learn how to efficiently load the same components into multiple pipelines.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
## StableDiffusion3ControlNetPipeline
|
||||||
|
[[autodoc]] StableDiffusion3ControlNetPipeline
|
||||||
|
- all
|
||||||
|
- __call__
|
||||||
|
|
||||||
|
## StableDiffusion3PipelineOutput
|
||||||
|
[[autodoc]] pipelines.stable_diffusion_3.pipeline_output.StableDiffusion3PipelineOutput
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<!--Copyright 2024 The HuggingFace Team. All rights reserved.
|
<!--Copyright 2024 The HuggingFace Team and Tencent Hunyuan Team. All rights reserved.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
the License. You may obtain a copy of the License at
|
the License. You may obtain a copy of the License at
|
||||||
@@ -34,6 +34,12 @@ Make sure to check out the Schedulers [guide](../../using-diffusers/schedulers.m
|
|||||||
|
|
||||||
</Tip>
|
</Tip>
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
|
||||||
|
You can further improve generation quality by passing the generated image from [`HungyuanDiTPipeline`] to the [SDXL refiner](../../using-diffusers/sdxl#base-to-refiner-model) model.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
## Optimization
|
## Optimization
|
||||||
|
|
||||||
You can optimize the pipeline's runtime and memory consumption with torch.compile and feed-forward chunking. To learn about other optimization methods, check out the [Speed up inference](../../optimization/fp16) and [Reduce memory usage](../../optimization/memory) guides.
|
You can optimize the pipeline's runtime and memory consumption with torch.compile and feed-forward chunking. To learn about other optimization methods, check out the [Speed up inference](../../optimization/fp16) and [Reduce memory usage](../../optimization/memory) guides.
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ specific language governing permissions and limitations under the License.
|
|||||||
|
|
||||||
Kandinsky 3 is created by [Vladimir Arkhipkin](https://github.com/oriBetelgeuse),[Anastasia Maltseva](https://github.com/NastyaMittseva),[Igor Pavlov](https://github.com/boomb0om),[Andrei Filatov](https://github.com/anvilarth),[Arseniy Shakhmatov](https://github.com/cene555),[Andrey Kuznetsov](https://github.com/kuznetsoffandrey),[Denis Dimitrov](https://github.com/denndimitrov), [Zein Shaheen](https://github.com/zeinsh)
|
Kandinsky 3 is created by [Vladimir Arkhipkin](https://github.com/oriBetelgeuse),[Anastasia Maltseva](https://github.com/NastyaMittseva),[Igor Pavlov](https://github.com/boomb0om),[Andrei Filatov](https://github.com/anvilarth),[Arseniy Shakhmatov](https://github.com/cene555),[Andrey Kuznetsov](https://github.com/kuznetsoffandrey),[Denis Dimitrov](https://github.com/denndimitrov), [Zein Shaheen](https://github.com/zeinsh)
|
||||||
|
|
||||||
The description from it's Github page:
|
The description from it's GitHub page:
|
||||||
|
|
||||||
*Kandinsky 3.0 is an open-source text-to-image diffusion model built upon the Kandinsky2-x model family. In comparison to its predecessors, enhancements have been made to the text understanding and visual quality of the model, achieved by increasing the size of the text encoder and Diffusion U-Net models, respectively.*
|
*Kandinsky 3.0 is an open-source text-to-image diffusion model built upon the Kandinsky2-x model family. In comparison to its predecessors, enhancements have been made to the text understanding and visual quality of the model, achieved by increasing the size of the text encoder and Diffusion U-Net models, respectively.*
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<!--Copyright 2024 The HuggingFace Team. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Perturbed-Attention Guidance
|
||||||
|
|
||||||
|
[Perturbed-Attention Guidance (PAG)](https://ku-cvlab.github.io/Perturbed-Attention-Guidance/) is a new diffusion sampling guidance that improves sample quality across both unconditional and conditional settings, achieving this without requiring further training or the integration of external modules.
|
||||||
|
|
||||||
|
PAG was introduced in [Self-Rectifying Diffusion Sampling with Perturbed-Attention Guidance](https://huggingface.co/papers/2403.17377) by Donghoon Ahn, Hyoungwon Cho, Jaewon Min, Wooseok Jang, Jungwoo Kim, SeonHwa Kim, Hyun Hee Park, Kyong Hwan Jin and Seungryong Kim.
|
||||||
|
|
||||||
|
The abstract from the paper is:
|
||||||
|
|
||||||
|
*Recent studies have demonstrated that diffusion models are capable of generating high-quality samples, but their quality heavily depends on sampling guidance techniques, such as classifier guidance (CG) and classifier-free guidance (CFG). These techniques are often not applicable in unconditional generation or in various downstream tasks such as image restoration. In this paper, we propose a novel sampling guidance, called Perturbed-Attention Guidance (PAG), which improves diffusion sample quality across both unconditional and conditional settings, achieving this without requiring additional training or the integration of external modules. PAG is designed to progressively enhance the structure of samples throughout the denoising process. It involves generating intermediate samples with degraded structure by substituting selected self-attention maps in diffusion U-Net with an identity matrix, by considering the self-attention mechanisms' ability to capture structural information, and guiding the denoising process away from these degraded samples. In both ADM and Stable Diffusion, PAG surprisingly improves sample quality in conditional and even unconditional scenarios. Moreover, PAG significantly improves the baseline performance in various downstream tasks where existing guidances such as CG or CFG cannot be fully utilized, including ControlNet with empty prompts and image restoration such as inpainting and deblurring.*
|
||||||
|
|
||||||
|
## StableDiffusionPAGPipeline
|
||||||
|
[[autodoc]] StableDiffusionPAGPipeline
|
||||||
|
- all
|
||||||
|
- __call__
|
||||||
|
|
||||||
|
## StableDiffusionXLPAGPipeline
|
||||||
|
[[autodoc]] StableDiffusionXLPAGPipeline
|
||||||
|
- all
|
||||||
|
- __call__
|
||||||
|
|
||||||
|
## StableDiffusionXLPAGImg2ImgPipeline
|
||||||
|
[[autodoc]] StableDiffusionXLPAGImg2ImgPipeline
|
||||||
|
- all
|
||||||
|
- __call__
|
||||||
|
|
||||||
|
## StableDiffusionXLPAGInpaintPipeline
|
||||||
|
[[autodoc]] StableDiffusionXLPAGInpaintPipeline
|
||||||
|
- all
|
||||||
|
- __call__
|
||||||
|
|
||||||
|
## StableDiffusionXLControlNetPAGPipeline
|
||||||
|
[[autodoc]] StableDiffusionXLControlNetPAGPipeline
|
||||||
|
- all
|
||||||
|
- __call__
|
||||||
@@ -37,6 +37,12 @@ Make sure to check out the Schedulers [guide](../../using-diffusers/schedulers)
|
|||||||
|
|
||||||
</Tip>
|
</Tip>
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
|
||||||
|
You can further improve generation quality by passing the generated image from [`PixArtSigmaPipeline`] to the [SDXL refiner](../../using-diffusers/sdxl#base-to-refiner-model) model.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
## Inference with under 8GB GPU VRAM
|
## Inference with under 8GB GPU VRAM
|
||||||
|
|
||||||
Run the [`PixArtSigmaPipeline`] with under 8GB GPU VRAM by loading the text encoder in 8-bit precision. Let's walk through a full-fledged example.
|
Run the [`PixArtSigmaPipeline`] with under 8GB GPU VRAM by loading the text encoder in 8-bit precision. Let's walk through a full-fledged example.
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ from diffusers import DiffusionPipeline, DPMSolverMultistepScheduler
|
|||||||
import torch
|
import torch
|
||||||
|
|
||||||
repo_id = "stabilityai/stable-diffusion-2-base"
|
repo_id = "stabilityai/stable-diffusion-2-base"
|
||||||
pipe = DiffusionPipeline.from_pretrained(repo_id, torch_dtype=torch.float16, revision="fp16")
|
pipe = DiffusionPipeline.from_pretrained(repo_id, torch_dtype=torch.float16, variant="fp16")
|
||||||
|
|
||||||
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
|
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
|
||||||
pipe = pipe.to("cuda")
|
pipe = pipe.to("cuda")
|
||||||
@@ -72,7 +72,7 @@ init_image = load_image(img_url).resize((512, 512))
|
|||||||
mask_image = load_image(mask_url).resize((512, 512))
|
mask_image = load_image(mask_url).resize((512, 512))
|
||||||
|
|
||||||
repo_id = "stabilityai/stable-diffusion-2-inpainting"
|
repo_id = "stabilityai/stable-diffusion-2-inpainting"
|
||||||
pipe = DiffusionPipeline.from_pretrained(repo_id, torch_dtype=torch.float16, revision="fp16")
|
pipe = DiffusionPipeline.from_pretrained(repo_id, torch_dtype=torch.float16, variant="fp16")
|
||||||
|
|
||||||
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
|
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
|
||||||
pipe = pipe.to("cuda")
|
pipe = pipe.to("cuda")
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ The SD3 pipeline uses three text encoders to generate an image. Model offloading
|
|||||||
|
|
||||||
</Tip>
|
</Tip>
|
||||||
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import torch
|
import torch
|
||||||
from diffusers import StableDiffusion3Pipeline
|
from diffusers import StableDiffusion3Pipeline
|
||||||
@@ -197,6 +196,68 @@ image.save("sd3_hello_world.png")
|
|||||||
|
|
||||||
Check out the full script [here](https://gist.github.com/sayakpaul/508d89d7aad4f454900813da5d42ca97).
|
Check out the full script [here](https://gist.github.com/sayakpaul/508d89d7aad4f454900813da5d42ca97).
|
||||||
|
|
||||||
|
## Using Long Prompts with the T5 Text Encoder
|
||||||
|
|
||||||
|
By default, the T5 Text Encoder prompt uses a maximum sequence length of `256`. This can be adjusted by setting the `max_sequence_length` to accept fewer or more tokens. Keep in mind that longer sequences require additional resources and result in longer generation times, such as during batch inference.
|
||||||
|
|
||||||
|
```python
|
||||||
|
prompt = "A whimsical and creative image depicting a hybrid creature that is a mix of a waffle and a hippopotamus, basking in a river of melted butter amidst a breakfast-themed landscape. It features the distinctive, bulky body shape of a hippo. However, instead of the usual grey skin, the creature’s body resembles a golden-brown, crispy waffle fresh off the griddle. The skin is textured with the familiar grid pattern of a waffle, each square filled with a glistening sheen of syrup. The environment combines the natural habitat of a hippo with elements of a breakfast table setting, a river of warm, melted butter, with oversized utensils or plates peeking out from the lush, pancake-like foliage in the background, a towering pepper mill standing in for a tree. As the sun rises in this fantastical world, it casts a warm, buttery glow over the scene. The creature, content in its butter river, lets out a yawn. Nearby, a flock of birds take flight"
|
||||||
|
|
||||||
|
image = pipe(
|
||||||
|
prompt=prompt,
|
||||||
|
negative_prompt="",
|
||||||
|
num_inference_steps=28,
|
||||||
|
guidance_scale=4.5,
|
||||||
|
max_sequence_length=512,
|
||||||
|
).images[0]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sending a different prompt to the T5 Text Encoder
|
||||||
|
|
||||||
|
You can send a different prompt to the CLIP Text Encoders and the T5 Text Encoder to prevent the prompt from being truncated by the CLIP Text Encoders and to improve generation.
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
|
||||||
|
The prompt with the CLIP Text Encoders is still truncated to the 77 token limit.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
```python
|
||||||
|
prompt = "A whimsical and creative image depicting a hybrid creature that is a mix of a waffle and a hippopotamus, basking in a river of melted butter amidst a breakfast-themed landscape. A river of warm, melted butter, pancake-like foliage in the background, a towering pepper mill standing in for a tree."
|
||||||
|
|
||||||
|
prompt_3 = "A whimsical and creative image depicting a hybrid creature that is a mix of a waffle and a hippopotamus, basking in a river of melted butter amidst a breakfast-themed landscape. It features the distinctive, bulky body shape of a hippo. However, instead of the usual grey skin, the creature’s body resembles a golden-brown, crispy waffle fresh off the griddle. The skin is textured with the familiar grid pattern of a waffle, each square filled with a glistening sheen of syrup. The environment combines the natural habitat of a hippo with elements of a breakfast table setting, a river of warm, melted butter, with oversized utensils or plates peeking out from the lush, pancake-like foliage in the background, a towering pepper mill standing in for a tree. As the sun rises in this fantastical world, it casts a warm, buttery glow over the scene. The creature, content in its butter river, lets out a yawn. Nearby, a flock of birds take flight"
|
||||||
|
|
||||||
|
image = pipe(
|
||||||
|
prompt=prompt,
|
||||||
|
prompt_3=prompt_3,
|
||||||
|
negative_prompt="",
|
||||||
|
num_inference_steps=28,
|
||||||
|
guidance_scale=4.5,
|
||||||
|
max_sequence_length=512,
|
||||||
|
).images[0]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tiny AutoEncoder for Stable Diffusion 3
|
||||||
|
|
||||||
|
Tiny AutoEncoder for Stable Diffusion (TAESD3) is a tiny distilled version of Stable Diffusion 3's VAE by [Ollin Boer Bohan](https://github.com/madebyollin/taesd) that can decode [`StableDiffusion3Pipeline`] latents almost instantly.
|
||||||
|
|
||||||
|
To use with Stable Diffusion 3:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import torch
|
||||||
|
from diffusers import StableDiffusion3Pipeline, AutoencoderTiny
|
||||||
|
|
||||||
|
pipe = StableDiffusion3Pipeline.from_pretrained(
|
||||||
|
"stabilityai/stable-diffusion-3-medium-diffusers", torch_dtype=torch.float16
|
||||||
|
)
|
||||||
|
pipe.vae = AutoencoderTiny.from_pretrained("madebyollin/taesd3", torch_dtype=torch.float16)
|
||||||
|
pipe = pipe.to("cuda")
|
||||||
|
|
||||||
|
prompt = "slice of delicious New York-style berry cheesecake"
|
||||||
|
image = pipe(prompt, num_inference_steps=25).images[0]
|
||||||
|
image.save("cheesecake.png")
|
||||||
|
```
|
||||||
|
|
||||||
## Loading the original checkpoints via `from_single_file`
|
## Loading the original checkpoints via `from_single_file`
|
||||||
|
|
||||||
The `SD3Transformer2DModel` and `StableDiffusion3Pipeline` classes support loading the original checkpoints via the `from_single_file` method. This method allows you to load the original checkpoint files that were used to train the models.
|
The `SD3Transformer2DModel` and `StableDiffusion3Pipeline` classes support loading the original checkpoints via the `from_single_file` method. This method allows you to load the original checkpoint files that were used to train the models.
|
||||||
@@ -228,7 +289,10 @@ image = pipe("a picture of a cat holding a sign that says hello world").images[0
|
|||||||
image.save('sd3-single-file.png')
|
image.save('sd3-single-file.png')
|
||||||
```
|
```
|
||||||
|
|
||||||
### Loading the single file checkpoint without T5
|
### Loading the single file checkpoint with T5
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> The following example loads a checkpoint stored in a 8-bit floating point format which requires PyTorch 2.3 or later.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import torch
|
import torch
|
||||||
|
|||||||
@@ -22,14 +22,13 @@ We enormously value feedback from the community, so please do not be afraid to s
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
You can contribute in many ways ranging from answering questions on issues to adding new diffusion models to
|
You can contribute in many ways ranging from answering questions on issues and discussions to adding new diffusion models to the core library.
|
||||||
the core library.
|
|
||||||
|
|
||||||
In the following, we give an overview of different ways to contribute, ranked by difficulty in ascending order. All of them are valuable to the community.
|
In the following, we give an overview of different ways to contribute, ranked by difficulty in ascending order. All of them are valuable to the community.
|
||||||
|
|
||||||
* 1. Asking and answering questions on [the Diffusers discussion forum](https://discuss.huggingface.co/c/discussion-related-to-httpsgithubcomhuggingfacediffusers) or on [Discord](https://discord.gg/G7tWnz98XR).
|
* 1. Asking and answering questions on [the Diffusers discussion forum](https://discuss.huggingface.co/c/discussion-related-to-httpsgithubcomhuggingfacediffusers) or on [Discord](https://discord.gg/G7tWnz98XR).
|
||||||
* 2. Opening new issues on [the GitHub Issues tab](https://github.com/huggingface/diffusers/issues/new/choose).
|
* 2. Opening new issues on [the GitHub Issues tab](https://github.com/huggingface/diffusers/issues/new/choose) or new discussions on [the GitHub Discussions tab](https://github.com/huggingface/diffusers/discussions/new/choose).
|
||||||
* 3. Answering issues on [the GitHub Issues tab](https://github.com/huggingface/diffusers/issues).
|
* 3. Answering issues on [the GitHub Issues tab](https://github.com/huggingface/diffusers/issues) or discussions on [the GitHub Discussions tab](https://github.com/huggingface/diffusers/discussions).
|
||||||
* 4. Fix a simple issue, marked by the "Good first issue" label, see [here](https://github.com/huggingface/diffusers/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).
|
* 4. Fix a simple issue, marked by the "Good first issue" label, see [here](https://github.com/huggingface/diffusers/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).
|
||||||
* 5. Contribute to the [documentation](https://github.com/huggingface/diffusers/tree/main/docs/source).
|
* 5. Contribute to the [documentation](https://github.com/huggingface/diffusers/tree/main/docs/source).
|
||||||
* 6. Contribute a [Community Pipeline](https://github.com/huggingface/diffusers/issues?q=is%3Aopen+is%3Aissue+label%3Acommunity-examples).
|
* 6. Contribute a [Community Pipeline](https://github.com/huggingface/diffusers/issues?q=is%3Aopen+is%3Aissue+label%3Acommunity-examples).
|
||||||
@@ -63,7 +62,7 @@ In the same spirit, you are of immense help to the community by answering such q
|
|||||||
|
|
||||||
**Please** keep in mind that the more effort you put into asking or answering a question, the higher
|
**Please** keep in mind that the more effort you put into asking or answering a question, the higher
|
||||||
the quality of the publicly documented knowledge. In the same way, well-posed and well-answered questions create a high-quality knowledge database accessible to everybody, while badly posed questions or answers reduce the overall quality of the public knowledge database.
|
the quality of the publicly documented knowledge. In the same way, well-posed and well-answered questions create a high-quality knowledge database accessible to everybody, while badly posed questions or answers reduce the overall quality of the public knowledge database.
|
||||||
In short, a high quality question or answer is *precise*, *concise*, *relevant*, *easy-to-understand*, *accessible*, and *well-formated/well-posed*. For more information, please have a look through the [How to write a good issue](#how-to-write-a-good-issue) section.
|
In short, a high quality question or answer is *precise*, *concise*, *relevant*, *easy-to-understand*, *accessible*, and *well-formatted/well-posed*. For more information, please have a look through the [How to write a good issue](#how-to-write-a-good-issue) section.
|
||||||
|
|
||||||
**NOTE about channels**:
|
**NOTE about channels**:
|
||||||
[*The forum*](https://discuss.huggingface.co/c/discussion-related-to-httpsgithubcomhuggingfacediffusers/63) is much better indexed by search engines, such as Google. Posts are ranked by popularity rather than chronologically. Hence, it's easier to look up questions and answers that we posted some time ago.
|
[*The forum*](https://discuss.huggingface.co/c/discussion-related-to-httpsgithubcomhuggingfacediffusers/63) is much better indexed by search engines, such as Google. Posts are ranked by popularity rather than chronologically. Hence, it's easier to look up questions and answers that we posted some time ago.
|
||||||
@@ -99,7 +98,7 @@ This means in more detail:
|
|||||||
- Format your code.
|
- Format your code.
|
||||||
- Do not include any external libraries except for Diffusers depending on them.
|
- Do not include any external libraries except for Diffusers depending on them.
|
||||||
- **Always** provide all necessary information about your environment; for this, you can run: `diffusers-cli env` in your shell and copy-paste the displayed information to the issue.
|
- **Always** provide all necessary information about your environment; for this, you can run: `diffusers-cli env` in your shell and copy-paste the displayed information to the issue.
|
||||||
- Explain the issue. If the reader doesn't know what the issue is and why it is an issue, she cannot solve it.
|
- Explain the issue. If the reader doesn't know what the issue is and why it is an issue, (s)he cannot solve it.
|
||||||
- **Always** make sure the reader can reproduce your issue with as little effort as possible. If your code snippet cannot be run because of missing libraries or undefined variables, the reader cannot help you. Make sure your reproducible code snippet is as minimal as possible and can be copy-pasted into a simple Python shell.
|
- **Always** make sure the reader can reproduce your issue with as little effort as possible. If your code snippet cannot be run because of missing libraries or undefined variables, the reader cannot help you. Make sure your reproducible code snippet is as minimal as possible and can be copy-pasted into a simple Python shell.
|
||||||
- If in order to reproduce your issue a model and/or dataset is required, make sure the reader has access to that model or dataset. You can always upload your model or dataset to the [Hub](https://huggingface.co) to make it easily downloadable. Try to keep your model and dataset as small as possible, to make the reproduction of your issue as effortless as possible.
|
- If in order to reproduce your issue a model and/or dataset is required, make sure the reader has access to that model or dataset. You can always upload your model or dataset to the [Hub](https://huggingface.co) to make it easily downloadable. Try to keep your model and dataset as small as possible, to make the reproduction of your issue as effortless as possible.
|
||||||
|
|
||||||
@@ -288,7 +287,7 @@ The official training examples are maintained by the Diffusers' core maintainers
|
|||||||
This is because of the same reasons put forward in [6. Contribute a community pipeline](#6-contribute-a-community-pipeline) for official pipelines vs. community pipelines: It is not feasible for the core maintainers to maintain all possible training methods for diffusion models.
|
This is because of the same reasons put forward in [6. Contribute a community pipeline](#6-contribute-a-community-pipeline) for official pipelines vs. community pipelines: It is not feasible for the core maintainers to maintain all possible training methods for diffusion models.
|
||||||
If the Diffusers core maintainers and the community consider a certain training paradigm to be too experimental or not popular enough, the corresponding training code should be put in the `research_projects` folder and maintained by the author.
|
If the Diffusers core maintainers and the community consider a certain training paradigm to be too experimental or not popular enough, the corresponding training code should be put in the `research_projects` folder and maintained by the author.
|
||||||
|
|
||||||
Both official training and research examples consist of a directory that contains one or more training scripts, a requirements.txt file, and a README.md file. In order for the user to make use of the
|
Both official training and research examples consist of a directory that contains one or more training scripts, a `requirements.txt` file, and a `README.md` file. In order for the user to make use of the
|
||||||
training examples, it is required to clone the repository:
|
training examples, it is required to clone the repository:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -298,7 +297,8 @@ git clone https://github.com/huggingface/diffusers
|
|||||||
as well as to install all additional dependencies required for training:
|
as well as to install all additional dependencies required for training:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install -r /examples/<your-example-folder>/requirements.txt
|
cd diffusers
|
||||||
|
pip install -r examples/<your-example-folder>/requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
Therefore when adding an example, the `requirements.txt` file shall define all pip dependencies required for your training example so that once all those are installed, the user can run the example's training script. See, for example, the [DreamBooth `requirements.txt` file](https://github.com/huggingface/diffusers/blob/main/examples/dreambooth/requirements.txt).
|
Therefore when adding an example, the `requirements.txt` file shall define all pip dependencies required for your training example so that once all those are installed, the user can run the example's training script. See, for example, the [DreamBooth `requirements.txt` file](https://github.com/huggingface/diffusers/blob/main/examples/dreambooth/requirements.txt).
|
||||||
@@ -316,7 +316,7 @@ Once an example script works, please make sure to add a comprehensive `README.md
|
|||||||
- A link to some training results (logs, models, etc.) that show what the user can expect as shown [here](https://api.wandb.ai/report/patrickvonplaten/xm6cd5q5).
|
- A link to some training results (logs, models, etc.) that show what the user can expect as shown [here](https://api.wandb.ai/report/patrickvonplaten/xm6cd5q5).
|
||||||
- If you are adding a non-official/research training example, **please don't forget** to add a sentence that you are maintaining this training example which includes your git handle as shown [here](https://github.com/huggingface/diffusers/tree/main/examples/research_projects/intel_opts#diffusers-examples-with-intel-optimizations).
|
- If you are adding a non-official/research training example, **please don't forget** to add a sentence that you are maintaining this training example which includes your git handle as shown [here](https://github.com/huggingface/diffusers/tree/main/examples/research_projects/intel_opts#diffusers-examples-with-intel-optimizations).
|
||||||
|
|
||||||
If you are contributing to the official training examples, please also make sure to add a test to [examples/test_examples.py](https://github.com/huggingface/diffusers/blob/main/examples/test_examples.py). This is not necessary for non-official training examples.
|
If you are contributing to the official training examples, please also make sure to add a test to its folder such as [examples/dreambooth/test_dreambooth.py](https://github.com/huggingface/diffusers/blob/main/examples/dreambooth/test_dreambooth.py). This is not necessary for non-official training examples.
|
||||||
|
|
||||||
### 8. Fixing a "Good second issue"
|
### 8. Fixing a "Good second issue"
|
||||||
|
|
||||||
@@ -418,7 +418,7 @@ You will need basic `git` proficiency to be able to contribute to
|
|||||||
manual. Type `git --help` in a shell and enjoy. If you prefer books, [Pro
|
manual. Type `git --help` in a shell and enjoy. If you prefer books, [Pro
|
||||||
Git](https://git-scm.com/book/en/v2) is a very good reference.
|
Git](https://git-scm.com/book/en/v2) is a very good reference.
|
||||||
|
|
||||||
Follow these steps to start contributing ([supported Python versions](https://github.com/huggingface/diffusers/blob/main/setup.py#L244)):
|
Follow these steps to start contributing ([supported Python versions](https://github.com/huggingface/diffusers/blob/83bc6c94eaeb6f7704a2a428931cf2d9ad973ae9/setup.py#L270)):
|
||||||
|
|
||||||
1. Fork the [repository](https://github.com/huggingface/diffusers) by
|
1. Fork the [repository](https://github.com/huggingface/diffusers) by
|
||||||
clicking on the 'Fork' button on the repository's page. This creates a copy of the code
|
clicking on the 'Fork' button on the repository's page. This creates a copy of the code
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ Let's walk through more in-detail design decisions for each class.
|
|||||||
Pipelines are designed to be easy to use (therefore do not follow [*Simple over easy*](#simple-over-easy) 100%), are not feature complete, and should loosely be seen as examples of how to use [models](#models) and [schedulers](#schedulers) for inference.
|
Pipelines are designed to be easy to use (therefore do not follow [*Simple over easy*](#simple-over-easy) 100%), are not feature complete, and should loosely be seen as examples of how to use [models](#models) and [schedulers](#schedulers) for inference.
|
||||||
|
|
||||||
The following design principles are followed:
|
The following design principles are followed:
|
||||||
- Pipelines follow the single-file policy. All pipelines can be found in individual directories under src/diffusers/pipelines. One pipeline folder corresponds to one diffusion paper/project/release. Multiple pipeline files can be gathered in one pipeline folder, as it’s done for [`src/diffusers/pipelines/stable-diffusion`](https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines/stable_diffusion). If pipelines share similar functionality, one can make use of the [#Copied from mechanism](https://github.com/huggingface/diffusers/blob/125d783076e5bd9785beb05367a2d2566843a271/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion_img2img.py#L251).
|
- Pipelines follow the single-file policy. All pipelines can be found in individual directories under src/diffusers/pipelines. One pipeline folder corresponds to one diffusion paper/project/release. Multiple pipeline files can be gathered in one pipeline folder, as it’s done for [`src/diffusers/pipelines/stable-diffusion`](https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines/stable_diffusion). If pipelines share similar functionality, one can make use of the [# Copied from mechanism](https://github.com/huggingface/diffusers/blob/125d783076e5bd9785beb05367a2d2566843a271/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion_img2img.py#L251).
|
||||||
- Pipelines all inherit from [`DiffusionPipeline`].
|
- Pipelines all inherit from [`DiffusionPipeline`].
|
||||||
- Every pipeline consists of different model and scheduler components, that are documented in the [`model_index.json` file](https://huggingface.co/runwayml/stable-diffusion-v1-5/blob/main/model_index.json), are accessible under the same name as attributes of the pipeline and can be shared between pipelines with [`DiffusionPipeline.components`](https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.components) function.
|
- Every pipeline consists of different model and scheduler components, that are documented in the [`model_index.json` file](https://huggingface.co/runwayml/stable-diffusion-v1-5/blob/main/model_index.json), are accessible under the same name as attributes of the pipeline and can be shared between pipelines with [`DiffusionPipeline.components`](https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.components) function.
|
||||||
- Every pipeline should be loadable via the [`DiffusionPipeline.from_pretrained`](https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.from_pretrained) function.
|
- Every pipeline should be loadable via the [`DiffusionPipeline.from_pretrained`](https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.from_pretrained) function.
|
||||||
@@ -81,7 +81,7 @@ Models are designed as configurable toolboxes that are natural extensions of [Py
|
|||||||
|
|
||||||
The following design principles are followed:
|
The following design principles are followed:
|
||||||
- Models correspond to **a type of model architecture**. *E.g.* the [`UNet2DConditionModel`] class is used for all UNet variations that expect 2D image inputs and are conditioned on some context.
|
- Models correspond to **a type of model architecture**. *E.g.* the [`UNet2DConditionModel`] class is used for all UNet variations that expect 2D image inputs and are conditioned on some context.
|
||||||
- All models can be found in [`src/diffusers/models`](https://github.com/huggingface/diffusers/tree/main/src/diffusers/models) and every model architecture shall be defined in its file, e.g. [`unet_2d_condition.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/unet_2d_condition.py), [`transformer_2d.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/transformer_2d.py), etc...
|
- All models can be found in [`src/diffusers/models`](https://github.com/huggingface/diffusers/tree/main/src/diffusers/models) and every model architecture shall be defined in its file, e.g. [`unets/unet_2d_condition.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/unets/unet_2d_condition.py), [`transformers/transformer_2d.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/transformers/transformer_2d.py), etc...
|
||||||
- Models **do not** follow the single-file policy and should make use of smaller model building blocks, such as [`attention.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention.py), [`resnet.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/resnet.py), [`embeddings.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/embeddings.py), etc... **Note**: This is in stark contrast to Transformers' modeling files and shows that models do not really follow the single-file policy.
|
- Models **do not** follow the single-file policy and should make use of smaller model building blocks, such as [`attention.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention.py), [`resnet.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/resnet.py), [`embeddings.py`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/embeddings.py), etc... **Note**: This is in stark contrast to Transformers' modeling files and shows that models do not really follow the single-file policy.
|
||||||
- Models intend to expose complexity, just like PyTorch's `Module` class, and give clear error messages.
|
- Models intend to expose complexity, just like PyTorch's `Module` class, and give clear error messages.
|
||||||
- Models all inherit from `ModelMixin` and `ConfigMixin`.
|
- Models all inherit from `ModelMixin` and `ConfigMixin`.
|
||||||
@@ -90,7 +90,7 @@ The following design principles are followed:
|
|||||||
- To integrate new model checkpoints whose general architecture can be classified as an architecture that already exists in Diffusers, the existing model architecture shall be adapted to make it work with the new checkpoint. One should only create a new file if the model architecture is fundamentally different.
|
- To integrate new model checkpoints whose general architecture can be classified as an architecture that already exists in Diffusers, the existing model architecture shall be adapted to make it work with the new checkpoint. One should only create a new file if the model architecture is fundamentally different.
|
||||||
- Models should be designed to be easily extendable to future changes. This can be achieved by limiting public function arguments, configuration arguments, and "foreseeing" future changes, *e.g.* it is usually better to add `string` "...type" arguments that can easily be extended to new future types instead of boolean `is_..._type` arguments. Only the minimum amount of changes shall be made to existing architectures to make a new model checkpoint work.
|
- Models should be designed to be easily extendable to future changes. This can be achieved by limiting public function arguments, configuration arguments, and "foreseeing" future changes, *e.g.* it is usually better to add `string` "...type" arguments that can easily be extended to new future types instead of boolean `is_..._type` arguments. Only the minimum amount of changes shall be made to existing architectures to make a new model checkpoint work.
|
||||||
- The model design is a difficult trade-off between keeping code readable and concise and supporting many model checkpoints. For most parts of the modeling code, classes shall be adapted for new model checkpoints, while there are some exceptions where it is preferred to add new classes to make sure the code is kept concise and
|
- The model design is a difficult trade-off between keeping code readable and concise and supporting many model checkpoints. For most parts of the modeling code, classes shall be adapted for new model checkpoints, while there are some exceptions where it is preferred to add new classes to make sure the code is kept concise and
|
||||||
readable long-term, such as [UNet blocks](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/unet_2d_blocks.py) and [Attention processors](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
|
readable long-term, such as [UNet blocks](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/unets/unet_2d_blocks.py) and [Attention processors](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
|
||||||
|
|
||||||
### Schedulers
|
### Schedulers
|
||||||
|
|
||||||
@@ -100,9 +100,9 @@ The following design principles are followed:
|
|||||||
- All schedulers are found in [`src/diffusers/schedulers`](https://github.com/huggingface/diffusers/tree/main/src/diffusers/schedulers).
|
- All schedulers are found in [`src/diffusers/schedulers`](https://github.com/huggingface/diffusers/tree/main/src/diffusers/schedulers).
|
||||||
- Schedulers are **not** allowed to import from large utils files and shall be kept very self-contained.
|
- Schedulers are **not** allowed to import from large utils files and shall be kept very self-contained.
|
||||||
- One scheduler Python file corresponds to one scheduler algorithm (as might be defined in a paper).
|
- One scheduler Python file corresponds to one scheduler algorithm (as might be defined in a paper).
|
||||||
- If schedulers share similar functionalities, we can make use of the `#Copied from` mechanism.
|
- If schedulers share similar functionalities, we can make use of the `# Copied from` mechanism.
|
||||||
- Schedulers all inherit from `SchedulerMixin` and `ConfigMixin`.
|
- Schedulers all inherit from `SchedulerMixin` and `ConfigMixin`.
|
||||||
- Schedulers can be easily swapped out with the [`ConfigMixin.from_config`](https://huggingface.co/docs/diffusers/main/en/api/configuration#diffusers.ConfigMixin.from_config) method as explained in detail [here](../using-diffusers/schedulers.md).
|
- Schedulers can be easily swapped out with the [`ConfigMixin.from_config`](https://huggingface.co/docs/diffusers/main/en/api/configuration#diffusers.ConfigMixin.from_config) method as explained in detail [here](../using-diffusers/schedulers).
|
||||||
- Every scheduler has to have a `set_num_inference_steps`, and a `step` function. `set_num_inference_steps(...)` has to be called before every denoising process, *i.e.* before `step(...)` is called.
|
- Every scheduler has to have a `set_num_inference_steps`, and a `step` function. `set_num_inference_steps(...)` has to be called before every denoising process, *i.e.* before `step(...)` is called.
|
||||||
- Every scheduler exposes the timesteps to be "looped over" via a `timesteps` attribute, which is an array of timesteps the model will be called upon.
|
- Every scheduler exposes the timesteps to be "looped over" via a `timesteps` attribute, which is an array of timesteps the model will be called upon.
|
||||||
- The `step(...)` function takes a predicted model output and the "current" sample (x_t) and returns the "previous", slightly more denoised sample (x_t-1).
|
- The `step(...)` function takes a predicted model output and the "current" sample (x_t) and returns the "previous", slightly more denoised sample (x_t-1).
|
||||||
|
|||||||
@@ -52,76 +52,6 @@ To learn more, take a look at the [Distributed Inference with 🤗 Accelerate](h
|
|||||||
|
|
||||||
</Tip>
|
</Tip>
|
||||||
|
|
||||||
### Device placement
|
|
||||||
|
|
||||||
> [!WARNING]
|
|
||||||
> This feature is experimental and its APIs might change in the future.
|
|
||||||
|
|
||||||
With Accelerate, you can use the `device_map` to determine how to distribute the models of a pipeline across multiple devices. This is useful in situations where you have more than one GPU.
|
|
||||||
|
|
||||||
For example, if you have two 8GB GPUs, then using [`~DiffusionPipeline.enable_model_cpu_offload`] may not work so well because:
|
|
||||||
|
|
||||||
* it only works on a single GPU
|
|
||||||
* a single model might not fit on a single GPU ([`~DiffusionPipeline.enable_sequential_cpu_offload`] might work but it will be extremely slow and it is also limited to a single GPU)
|
|
||||||
|
|
||||||
To make use of both GPUs, you can use the "balanced" device placement strategy which splits the models across all available GPUs.
|
|
||||||
|
|
||||||
> [!WARNING]
|
|
||||||
> Only the "balanced" strategy is supported at the moment, and we plan to support additional mapping strategies in the future.
|
|
||||||
|
|
||||||
```diff
|
|
||||||
from diffusers import DiffusionPipeline
|
|
||||||
import torch
|
|
||||||
|
|
||||||
pipeline = DiffusionPipeline.from_pretrained(
|
|
||||||
- "runwayml/stable-diffusion-v1-5", torch_dtype=torch.float16, use_safetensors=True,
|
|
||||||
+ "runwayml/stable-diffusion-v1-5", torch_dtype=torch.float16, use_safetensors=True, device_map="balanced"
|
|
||||||
)
|
|
||||||
image = pipeline("a dog").images[0]
|
|
||||||
image
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also pass a dictionary to enforce the maximum GPU memory that can be used on each device:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
from diffusers import DiffusionPipeline
|
|
||||||
import torch
|
|
||||||
|
|
||||||
max_memory = {0:"1GB", 1:"1GB"}
|
|
||||||
pipeline = DiffusionPipeline.from_pretrained(
|
|
||||||
"runwayml/stable-diffusion-v1-5",
|
|
||||||
torch_dtype=torch.float16,
|
|
||||||
use_safetensors=True,
|
|
||||||
device_map="balanced",
|
|
||||||
+ max_memory=max_memory
|
|
||||||
)
|
|
||||||
image = pipeline("a dog").images[0]
|
|
||||||
image
|
|
||||||
```
|
|
||||||
|
|
||||||
If a device is not present in `max_memory`, then it will be completely ignored and will not participate in the device placement.
|
|
||||||
|
|
||||||
By default, Diffusers uses the maximum memory of all devices. If the models don't fit on the GPUs, they are offloaded to the CPU. If the CPU doesn't have enough memory, then you might see an error. In that case, you could defer to using [`~DiffusionPipeline.enable_sequential_cpu_offload`] and [`~DiffusionPipeline.enable_model_cpu_offload`].
|
|
||||||
|
|
||||||
Call [`~DiffusionPipeline.reset_device_map`] to reset the `device_map` of a pipeline. This is also necessary if you want to use methods like `to()`, [`~DiffusionPipeline.enable_sequential_cpu_offload`], and [`~DiffusionPipeline.enable_model_cpu_offload`] on a pipeline that was device-mapped.
|
|
||||||
|
|
||||||
```py
|
|
||||||
pipeline.reset_device_map()
|
|
||||||
```
|
|
||||||
|
|
||||||
Once a pipeline has been device-mapped, you can also access its device map via `hf_device_map`:
|
|
||||||
|
|
||||||
```py
|
|
||||||
print(pipeline.hf_device_map)
|
|
||||||
```
|
|
||||||
|
|
||||||
An example device map would look like so:
|
|
||||||
|
|
||||||
|
|
||||||
```bash
|
|
||||||
{'unet': 1, 'vae': 1, 'safety_checker': 0, 'text_encoder': 0}
|
|
||||||
```
|
|
||||||
|
|
||||||
## PyTorch Distributed
|
## PyTorch Distributed
|
||||||
|
|
||||||
PyTorch supports [`DistributedDataParallel`](https://pytorch.org/docs/stable/generated/torch.nn.parallel.DistributedDataParallel.html) which enables data parallelism.
|
PyTorch supports [`DistributedDataParallel`](https://pytorch.org/docs/stable/generated/torch.nn.parallel.DistributedDataParallel.html) which enables data parallelism.
|
||||||
@@ -176,3 +106,6 @@ Once you've completed the inference script, use the `--nproc_per_node` argument
|
|||||||
```bash
|
```bash
|
||||||
torchrun run_distributed.py --nproc_per_node=2
|
torchrun run_distributed.py --nproc_per_node=2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> You can use `device_map` within a [`DiffusionPipeline`] to distribute its model-level components on multiple devices. Refer to the [Device placement](../tutorials/inference_with_big_models#device-placement) guide to learn more.
|
||||||
@@ -181,7 +181,7 @@ accelerate launch --mixed_precision="fp16" train_text_to_image.py \
|
|||||||
--max_train_steps=15000 \
|
--max_train_steps=15000 \
|
||||||
--learning_rate=1e-05 \
|
--learning_rate=1e-05 \
|
||||||
--max_grad_norm=1 \
|
--max_grad_norm=1 \
|
||||||
--enable_xformers_memory_efficient_attention
|
--enable_xformers_memory_efficient_attention \
|
||||||
--lr_scheduler="constant" --lr_warmup_steps=0 \
|
--lr_scheduler="constant" --lr_warmup_steps=0 \
|
||||||
--output_dir="sd-naruto-model" \
|
--output_dir="sd-naruto-model" \
|
||||||
--push_to_hub
|
--push_to_hub
|
||||||
|
|||||||
@@ -34,13 +34,10 @@ Install [PyTorch nightly](https://pytorch.org/) to benefit from the latest and f
|
|||||||
pip3 install --pre torch --index-url https://download.pytorch.org/whl/nightly/cu121
|
pip3 install --pre torch --index-url https://download.pytorch.org/whl/nightly/cu121
|
||||||
```
|
```
|
||||||
|
|
||||||
<Tip>
|
> [!TIP]
|
||||||
|
> The results reported below are from a 80GB 400W A100 with its clock rate set to the maximum.
|
||||||
|
> If you're interested in the full benchmarking code, take a look at [huggingface/diffusion-fast](https://github.com/huggingface/diffusion-fast).
|
||||||
|
|
||||||
The results reported below are from a 80GB 400W A100 with its clock rate set to the maximum. <br>
|
|
||||||
|
|
||||||
If you're interested in the full benchmarking code, take a look at [huggingface/diffusion-fast](https://github.com/huggingface/diffusion-fast).
|
|
||||||
|
|
||||||
</Tip>
|
|
||||||
|
|
||||||
## Baseline
|
## Baseline
|
||||||
|
|
||||||
@@ -170,6 +167,9 @@ Using SDPA attention and compiling both the UNet and VAE cuts the latency from 3
|
|||||||
<img src="https://huggingface.co/datasets/sayakpaul/sample-datasets/resolve/main/progressive-acceleration-sdxl/SDXL%2C_Batch_Size%3A_1%2C_Steps%3A_30_3.png" width=500>
|
<img src="https://huggingface.co/datasets/sayakpaul/sample-datasets/resolve/main/progressive-acceleration-sdxl/SDXL%2C_Batch_Size%3A_1%2C_Steps%3A_30_3.png" width=500>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> From PyTorch 2.3.1, you can control the caching behavior of `torch.compile()`. This is particularly beneficial for compilation modes like `"max-autotune"` which performs a grid-search over several compilation flags to find the optimal configuration. Learn more in the [Compile Time Caching in torch.compile](https://pytorch.org/tutorials/recipes/torch_compile_caching_tutorial.html) tutorial.
|
||||||
|
|
||||||
### Prevent graph breaks
|
### Prevent graph breaks
|
||||||
|
|
||||||
Specifying `fullgraph=True` ensures there are no graph breaks in the underlying model to take full advantage of `torch.compile` without any performance degradation. For the UNet and VAE, this means changing how you access the return variables.
|
Specifying `fullgraph=True` ensures there are no graph breaks in the underlying model to take full advantage of `torch.compile` without any performance degradation. For the UNet and VAE, this means changing how you access the return variables.
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
<!--Copyright 2024 The HuggingFace Team. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Working with big models
|
||||||
|
|
||||||
|
A modern diffusion model, like [Stable Diffusion XL (SDXL)](../using-diffusers/sdxl), is not just a single model, but a collection of multiple models. SDXL has four different model-level components:
|
||||||
|
|
||||||
|
* A variational autoencoder (VAE)
|
||||||
|
* Two text encoders
|
||||||
|
* A UNet for denoising
|
||||||
|
|
||||||
|
Usually, the text encoders and the denoiser are much larger compared to the VAE.
|
||||||
|
|
||||||
|
As models get bigger and better, it’s possible your model is so big that even a single copy won’t fit in memory. But that doesn’t mean it can’t be loaded. If you have more than one GPU, there is more memory available to store your model. In this case, it’s better to split your model checkpoint into several smaller *checkpoint shards*.
|
||||||
|
|
||||||
|
When a text encoder checkpoint has multiple shards, like [T5-xxl for SD3](https://huggingface.co/stabilityai/stable-diffusion-3-medium-diffusers/tree/main/text_encoder_3), it is automatically handled by the [Transformers](https://huggingface.co/docs/transformers/index) library as it is a required dependency of Diffusers when using the [`StableDiffusion3Pipeline`]. More specifically, Transformers will automatically handle the loading of multiple shards within the requested model class and get it ready so that inference can be performed.
|
||||||
|
|
||||||
|
The denoiser checkpoint can also have multiple shards and supports inference thanks to the [Accelerate](https://huggingface.co/docs/accelerate/index) library.
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> Refer to the [Handling big models for inference](https://huggingface.co/docs/accelerate/main/en/concept_guides/big_model_inference) guide for general guidance when working with big models that are hard to fit into memory.
|
||||||
|
|
||||||
|
For example, let's save a sharded checkpoint for the [SDXL UNet](https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/tree/main/unet):
|
||||||
|
|
||||||
|
```python
|
||||||
|
from diffusers import UNet2DConditionModel
|
||||||
|
|
||||||
|
unet = UNet2DConditionModel.from_pretrained(
|
||||||
|
"stabilityai/stable-diffusion-xl-base-1.0", subfolder="unet"
|
||||||
|
)
|
||||||
|
unet.save_pretrained("sdxl-unet-sharded", max_shard_size="5GB")
|
||||||
|
```
|
||||||
|
|
||||||
|
The size of the fp32 variant of the SDXL UNet checkpoint is ~10.4GB. Set the `max_shard_size` parameter to 5GB to create 3 shards. After saving, you can load them in [`StableDiffusionXLPipeline`]:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from diffusers import UNet2DConditionModel, StableDiffusionXLPipeline
|
||||||
|
import torch
|
||||||
|
|
||||||
|
unet = UNet2DConditionModel.from_pretrained(
|
||||||
|
"sayakpaul/sdxl-unet-sharded", torch_dtype=torch.float16
|
||||||
|
)
|
||||||
|
pipeline = StableDiffusionXLPipeline.from_pretrained(
|
||||||
|
"stabilityai/stable-diffusion-xl-base-1.0", unet=unet, torch_dtype=torch.float16
|
||||||
|
).to("cuda")
|
||||||
|
|
||||||
|
image = pipeline("a cute dog running on the grass", num_inference_steps=30).images[0]
|
||||||
|
image.save("dog.png")
|
||||||
|
```
|
||||||
|
|
||||||
|
If placing all the model-level components on the GPU at once is not feasible, use [`~DiffusionPipeline.enable_model_cpu_offload`] to help you:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- pipeline.to("cuda")
|
||||||
|
+ pipeline.enable_model_cpu_offload()
|
||||||
|
```
|
||||||
|
|
||||||
|
In general, we recommend sharding when a checkpoint is more than 5GB (in fp32).
|
||||||
|
|
||||||
|
## Device placement
|
||||||
|
|
||||||
|
On distributed setups, you can run inference across multiple GPUs with Accelerate.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> This feature is experimental and its APIs might change in the future.
|
||||||
|
|
||||||
|
With Accelerate, you can use the `device_map` to determine how to distribute the models of a pipeline across multiple devices. This is useful in situations where you have more than one GPU.
|
||||||
|
|
||||||
|
For example, if you have two 8GB GPUs, then using [`~DiffusionPipeline.enable_model_cpu_offload`] may not work so well because:
|
||||||
|
|
||||||
|
* it only works on a single GPU
|
||||||
|
* a single model might not fit on a single GPU ([`~DiffusionPipeline.enable_sequential_cpu_offload`] might work but it will be extremely slow and it is also limited to a single GPU)
|
||||||
|
|
||||||
|
To make use of both GPUs, you can use the "balanced" device placement strategy which splits the models across all available GPUs.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> Only the "balanced" strategy is supported at the moment, and we plan to support additional mapping strategies in the future.
|
||||||
|
|
||||||
|
```diff
|
||||||
|
from diffusers import DiffusionPipeline
|
||||||
|
import torch
|
||||||
|
|
||||||
|
pipeline = DiffusionPipeline.from_pretrained(
|
||||||
|
- "runwayml/stable-diffusion-v1-5", torch_dtype=torch.float16, use_safetensors=True,
|
||||||
|
+ "runwayml/stable-diffusion-v1-5", torch_dtype=torch.float16, use_safetensors=True, device_map="balanced"
|
||||||
|
)
|
||||||
|
image = pipeline("a dog").images[0]
|
||||||
|
image
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also pass a dictionary to enforce the maximum GPU memory that can be used on each device:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
from diffusers import DiffusionPipeline
|
||||||
|
import torch
|
||||||
|
|
||||||
|
max_memory = {0:"1GB", 1:"1GB"}
|
||||||
|
pipeline = DiffusionPipeline.from_pretrained(
|
||||||
|
"runwayml/stable-diffusion-v1-5",
|
||||||
|
torch_dtype=torch.float16,
|
||||||
|
use_safetensors=True,
|
||||||
|
device_map="balanced",
|
||||||
|
+ max_memory=max_memory
|
||||||
|
)
|
||||||
|
image = pipeline("a dog").images[0]
|
||||||
|
image
|
||||||
|
```
|
||||||
|
|
||||||
|
If a device is not present in `max_memory`, then it will be completely ignored and will not participate in the device placement.
|
||||||
|
|
||||||
|
By default, Diffusers uses the maximum memory of all devices. If the models don't fit on the GPUs, they are offloaded to the CPU. If the CPU doesn't have enough memory, then you might see an error. In that case, you could defer to using [`~DiffusionPipeline.enable_sequential_cpu_offload`] and [`~DiffusionPipeline.enable_model_cpu_offload`].
|
||||||
|
|
||||||
|
Call [`~DiffusionPipeline.reset_device_map`] to reset the `device_map` of a pipeline. This is also necessary if you want to use methods like `to()`, [`~DiffusionPipeline.enable_sequential_cpu_offload`], and [`~DiffusionPipeline.enable_model_cpu_offload`] on a pipeline that was device-mapped.
|
||||||
|
|
||||||
|
```py
|
||||||
|
pipeline.reset_device_map()
|
||||||
|
```
|
||||||
|
|
||||||
|
Once a pipeline has been device-mapped, you can also access its device map via `hf_device_map`:
|
||||||
|
|
||||||
|
```py
|
||||||
|
print(pipeline.hf_device_map)
|
||||||
|
```
|
||||||
|
|
||||||
|
An example device map would look like so:
|
||||||
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
{'unet': 1, 'vae': 1, 'safety_checker': 0, 'text_encoder': 0}
|
||||||
|
```
|
||||||
@@ -418,7 +418,7 @@ my_local_checkpoint_path = hf_hub_download(
|
|||||||
|
|
||||||
my_local_config_path = snapshot_download(
|
my_local_config_path = snapshot_download(
|
||||||
repo_id="segmind/SSD-1B",
|
repo_id="segmind/SSD-1B",
|
||||||
allowed_patterns=["*.json", "**/*.json", "*.txt", "**/*.txt"]
|
allow_patterns=["*.json", "**/*.json", "*.txt", "**/*.txt"]
|
||||||
)
|
)
|
||||||
|
|
||||||
pipeline = StableDiffusionXLPipeline.from_single_file(my_local_checkpoint_path, config=my_local_config_path, local_files_only=True)
|
pipeline = StableDiffusionXLPipeline.from_single_file(my_local_checkpoint_path, config=my_local_config_path, local_files_only=True)
|
||||||
@@ -438,7 +438,7 @@ my_local_checkpoint_path = hf_hub_download(
|
|||||||
|
|
||||||
my_local_config_path = snapshot_download(
|
my_local_config_path = snapshot_download(
|
||||||
repo_id="segmind/SSD-1B",
|
repo_id="segmind/SSD-1B",
|
||||||
allowed_patterns=["*.json", "**/*.json", "*.txt", "**/*.txt"]
|
allow_patterns=["*.json", "**/*.json", "*.txt", "**/*.txt"]
|
||||||
local_dir="my_local_config"
|
local_dir="my_local_config"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -468,11 +468,10 @@ print("My local checkpoint: ", my_local_checkpoint_path)
|
|||||||
|
|
||||||
my_local_config_path = snapshot_download(
|
my_local_config_path = snapshot_download(
|
||||||
repo_id="segmind/SSD-1B",
|
repo_id="segmind/SSD-1B",
|
||||||
allowed_patterns=["*.json", "**/*.json", "*.txt", "**/*.txt"]
|
allow_patterns=["*.json", "**/*.json", "*.txt", "**/*.txt"]
|
||||||
local_dir_use_symlinks=False,
|
local_dir_use_symlinks=False,
|
||||||
)
|
)
|
||||||
print("My local config: ", my_local_config_path)
|
print("My local config: ", my_local_config_path)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Then you can pass the local paths to the `pretrained_model_link_or_path` and `config` parameters.
|
Then you can pass the local paths to the `pretrained_model_link_or_path` and `config` parameters.
|
||||||
|
|||||||
@@ -0,0 +1,351 @@
|
|||||||
|
<!--Copyright 2024 The HuggingFace Team. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Perturbed-Attention Guidance
|
||||||
|
|
||||||
|
[Perturbed-Attention Guidance (PAG)](https://ku-cvlab.github.io/Perturbed-Attention-Guidance/) is a new diffusion sampling guidance that improves sample quality across both unconditional and conditional settings, achieving this without requiring further training or the integration of external modules. PAG is designed to progressively enhance the structure of synthesized samples throughout the denoising process by considering the self-attention mechanisms' ability to capture structural information. It involves generating intermediate samples with degraded structure by substituting selected self-attention maps in diffusion U-Net with an identity matrix, and guiding the denoising process away from these degraded samples.
|
||||||
|
|
||||||
|
This guide will show you how to use PAG for various tasks and use cases.
|
||||||
|
|
||||||
|
|
||||||
|
## General tasks
|
||||||
|
|
||||||
|
You can apply PAG to the [`StableDiffusionXLPipeline`] for tasks such as text-to-image, image-to-image, and inpainting. To enable PAG for a specific task, load the pipeline using the [AutoPipeline](../api/pipelines/auto_pipeline) API with the `enable_pag=True` flag and the `pag_applied_layers` argument.
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> 🤗 Diffusers currently only supports using PAG with selected SDXL pipelines, but feel free to open a [feature request](https://github.com/huggingface/diffusers/issues/new/choose) if you want to add PAG support to a new pipeline!
|
||||||
|
|
||||||
|
<hfoptions id="tasks">
|
||||||
|
<hfoption id="Text-to-image">
|
||||||
|
|
||||||
|
```py
|
||||||
|
from diffusers import AutoPipelineForText2Image
|
||||||
|
from diffusers.utils import load_image
|
||||||
|
import torch
|
||||||
|
|
||||||
|
pipeline = AutoPipelineForText2Image.from_pretrained(
|
||||||
|
"stabilityai/stable-diffusion-xl-base-1.0",
|
||||||
|
enable_pag=True,
|
||||||
|
pag_applied_layers=["mid"],
|
||||||
|
torch_dtype=torch.float16
|
||||||
|
)
|
||||||
|
pipeline.enable_model_cpu_offload()
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> The `pag_applied_layers` argument allows you to specify which layers PAG is applied to. Additionally, you can use `set_pag_applied_layers` method to update these layers after the pipeline has been created. Check out the [pag_applied_layers](#pag_applied_layers) section to learn more about applying PAG to other layers.
|
||||||
|
|
||||||
|
If you already have a pipeline created and loaded, you can enable PAG on it using the `from_pipe` API with the `enable_pag` flag. Internally, a PAG pipeline is created based on the pipeline and task you specified. In the example below, since we used `AutoPipelineForText2Image` and passed a `StableDiffusionXLPipeline`, a `StableDiffusionXLPAGPipeline` is created accordingly. Note that this does not require additional memory, and you will have both `StableDiffusionXLPipeline` and `StableDiffusionXLPAGPipeline` loaded and ready to use. You can read more about the `from_pipe` API and how to reuse pipelines in diffuser[here](https://huggingface.co/docs/diffusers/using-diffusers/loading#reuse-a-pipeline)
|
||||||
|
|
||||||
|
```py
|
||||||
|
pipeline_sdxl = AutoPipelineForText2Image.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0, torch_dtype=torch.float16")
|
||||||
|
pipeline = AutoPipelineForText2Image.from_pipe(pipeline_sdxl, enable_pag=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
To generate an image, you will also need to pass a `pag_scale`. When `pag_scale` increases, images gain more semantically coherent structures and exhibit fewer artifacts. However overly large guidance scale can lead to smoother textures and slight saturation in the images, similarly to CFG. `pag_scale=3.0` is used in the official demo and works well in most of the use cases, but feel free to experiment and select the appropriate value according to your needs! PAG is disabled when `pag_scale=0`.
|
||||||
|
|
||||||
|
```py
|
||||||
|
prompt = "an insect robot preparing a delicious meal, anime style"
|
||||||
|
|
||||||
|
for pag_scale in [0.0, 3.0]:
|
||||||
|
generator = torch.Generator(device="cpu").manual_seed(0)
|
||||||
|
images = pipeline(
|
||||||
|
prompt=prompt,
|
||||||
|
num_inference_steps=25,
|
||||||
|
guidance_scale=7.0,
|
||||||
|
generator=generator,
|
||||||
|
pag_scale=pag_scale,
|
||||||
|
).images
|
||||||
|
```
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-4">
|
||||||
|
<div class="flex-1">
|
||||||
|
<img class="rounded-xl" src="https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/pag_0.0_cfg_7.0_mid.png"/>
|
||||||
|
<figcaption class="mt-2 text-center text-sm text-gray-500">generated image without PAG</figcaption>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<img class="rounded-xl" src="https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/pag_3.0_cfg_7.0_mid.png"/>
|
||||||
|
<figcaption class="mt-2 text-center text-sm text-gray-500">generated image with PAG</figcaption>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</hfoption>
|
||||||
|
<hfoption id="Image-to-image">
|
||||||
|
|
||||||
|
You can use PAG with image-to-image pipelines.
|
||||||
|
|
||||||
|
```py
|
||||||
|
from diffusers import AutoPipelineForImage2Image
|
||||||
|
from diffusers.utils import load_image
|
||||||
|
import torch
|
||||||
|
|
||||||
|
pipeline = AutoPipelineForImage2Image.from_pretrained(
|
||||||
|
"stabilityai/stable-diffusion-xl-base-1.0",
|
||||||
|
enable_pag=True,
|
||||||
|
pag_applied_layers=["mid"],
|
||||||
|
torch_dtype=torch.float16
|
||||||
|
)
|
||||||
|
pipeline.enable_model_cpu_offload()
|
||||||
|
```
|
||||||
|
|
||||||
|
If you already have a image-to-image pipeline and would like enable PAG on it, you can run this
|
||||||
|
|
||||||
|
```py
|
||||||
|
pipeline_t2i = AutoPipelineForImage2Image.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", torch_dtype=torch.float16)
|
||||||
|
pipeline = AutoPipelineForImage2Image.from_pipe(pipeline_t2i, enable_pag=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
It is also very easy to directly switch from a text-to-image pipeline to PAG enabled image-to-image pipeline
|
||||||
|
|
||||||
|
```py
|
||||||
|
pipeline_pag = AutoPipelineForText2Image.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", torch_dtype=torch.float16)
|
||||||
|
pipeline = AutoPipelineForImage2Image.from_pipe(pipeline_t2i, enable_pag=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
If you have a PAG enabled text-to-image pipeline, you can directly switch to a image-to-image pipeline with PAG still enabled
|
||||||
|
|
||||||
|
```py
|
||||||
|
pipeline_pag = AutoPipelineForText2Image.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", enable_pag=True, torch_dtype=torch.float16)
|
||||||
|
pipeline = AutoPipelineForImage2Image.from_pipe(pipeline_t2i)
|
||||||
|
```
|
||||||
|
|
||||||
|
Now let's generate an image!
|
||||||
|
|
||||||
|
```py
|
||||||
|
pag_scales = 4.0
|
||||||
|
guidance_scales = 7.0
|
||||||
|
|
||||||
|
url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/sdxl-text2img.png"
|
||||||
|
init_image = load_image(url)
|
||||||
|
prompt = "a dog catching a frisbee in the jungle"
|
||||||
|
|
||||||
|
generator = torch.Generator(device="cpu").manual_seed(0)
|
||||||
|
image = pipeline(
|
||||||
|
prompt,
|
||||||
|
image=init_image,
|
||||||
|
strength=0.8,
|
||||||
|
guidance_scale=guidance_scale,
|
||||||
|
pag_scale=pag_scale,
|
||||||
|
generator=generator).images[0]
|
||||||
|
```
|
||||||
|
|
||||||
|
</hfoption>
|
||||||
|
<hfoption id="Inpainting">
|
||||||
|
|
||||||
|
```py
|
||||||
|
from diffusers import AutoPipelineForInpainting
|
||||||
|
from diffusers.utils import load_image
|
||||||
|
import torch
|
||||||
|
|
||||||
|
pipeline = AutoPipelineForInpainting.from_pretrained(
|
||||||
|
"stabilityai/stable-diffusion-xl-base-1.0",
|
||||||
|
enable_pag=True,
|
||||||
|
torch_dtype=torch.float16
|
||||||
|
)
|
||||||
|
pipeline.enable_model_cpu_offload()
|
||||||
|
```
|
||||||
|
|
||||||
|
You can enable PAG on an exisiting inpainting pipeline like this
|
||||||
|
|
||||||
|
```py
|
||||||
|
pipeline_inpaint = AutoPipelineForInpaiting.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", torch_dtype=torch.float16)
|
||||||
|
pipeline = AutoPipelineForInpaiting.from_pipe(pipeline_inpaint, enable_pag=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
This still works when your pipeline has a different task:
|
||||||
|
|
||||||
|
```py
|
||||||
|
pipeline_t2i = AutoPipelineForText2Image.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", torch_dtype=torch.float16)
|
||||||
|
pipeline = AutoPipelineForInpaiting.from_pipe(pipeline_t2i, enable_pag=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's generate an image!
|
||||||
|
|
||||||
|
```py
|
||||||
|
img_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo.png"
|
||||||
|
mask_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo_mask.png"
|
||||||
|
init_image = load_image(img_url).convert("RGB")
|
||||||
|
mask_image = load_image(mask_url).convert("RGB")
|
||||||
|
|
||||||
|
prompt = "A majestic tiger sitting on a bench"
|
||||||
|
|
||||||
|
pag_scales = 3.0
|
||||||
|
guidance_scales = 7.5
|
||||||
|
|
||||||
|
generator = torch.Generator(device="cpu").manual_seed(1)
|
||||||
|
images = pipeline(
|
||||||
|
prompt=prompt,
|
||||||
|
image=init_image,
|
||||||
|
mask_image=mask_image,
|
||||||
|
strength=0.8,
|
||||||
|
num_inference_steps=50,
|
||||||
|
guidance_scale=guidance_scale,
|
||||||
|
generator=generator,
|
||||||
|
pag_scale=pag_scale,
|
||||||
|
).images
|
||||||
|
images[0]
|
||||||
|
```
|
||||||
|
</hfoption>
|
||||||
|
</hfoptions>
|
||||||
|
|
||||||
|
## PAG with ControlNet
|
||||||
|
|
||||||
|
To use PAG with ControlNet, first create a `controlnet`. Then, pass the `controlnet` and other PAG arguments to the `from_pretrained` method of the AutoPipeline for the specified task.
|
||||||
|
|
||||||
|
```py
|
||||||
|
from diffusers import AutoPipelineForText2Image, ControlNetModel
|
||||||
|
import torch
|
||||||
|
|
||||||
|
controlnet = ControlNetModel.from_pretrained(
|
||||||
|
"diffusers/controlnet-canny-sdxl-1.0", torch_dtype=torch.float16
|
||||||
|
)
|
||||||
|
|
||||||
|
pipeline = AutoPipelineForText2Image.from_pretrained(
|
||||||
|
"stabilityai/stable-diffusion-xl-base-1.0",
|
||||||
|
controlnet=controlnet,
|
||||||
|
enable_pag=True,
|
||||||
|
pag_applied_layers="mid",
|
||||||
|
torch_dtype=torch.float16
|
||||||
|
)
|
||||||
|
pipeline.enable_model_cpu_offload()
|
||||||
|
```
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
|
||||||
|
If you already have a controlnet pipeline and want to enable PAG, you can use the `from_pipe` API: `AutoPipelineForText2Image.from_pipe(pipeline_controlnet, enable_pag=True)`
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
You can use the pipeline in the same way you normally use ControlNet pipelines, with the added option to specify a `pag_scale` parameter. Note that PAG works well for unconditional generation. In this example, we will generate an image without a prompt.
|
||||||
|
|
||||||
|
```py
|
||||||
|
from diffusers.utils import load_image
|
||||||
|
canny_image = load_image(
|
||||||
|
"https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/pag_control_input.png"
|
||||||
|
)
|
||||||
|
|
||||||
|
for pag_scale in [0.0, 3.0]:
|
||||||
|
generator = torch.Generator(device="cpu").manual_seed(1)
|
||||||
|
images = pipeline(
|
||||||
|
prompt="",
|
||||||
|
controlnet_conditioning_scale=controlnet_conditioning_scale,
|
||||||
|
image=canny_image,
|
||||||
|
num_inference_steps=50,
|
||||||
|
guidance_scale=0,
|
||||||
|
generator=generator,
|
||||||
|
pag_scale=pag_scale,
|
||||||
|
).images
|
||||||
|
images[0]
|
||||||
|
```
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-4">
|
||||||
|
<div class="flex-1">
|
||||||
|
<img class="rounded-xl" src="https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/pag_0.0_controlnet.png"/>
|
||||||
|
<figcaption class="mt-2 text-center text-sm text-gray-500">generated image without PAG</figcaption>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<img class="rounded-xl" src="https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/pag_3.0_controlnet.png"/>
|
||||||
|
<figcaption class="mt-2 text-center text-sm text-gray-500">generated image with PAG</figcaption>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## PAG with IP-Adapter
|
||||||
|
|
||||||
|
[IP-Adapter](https://hf.co/papers/2308.06721) is a popular model that can be plugged into diffusion models to enable image prompting without any changes to the underlying model. You can enable PAG on a pipeline with IP-Adapter loaded.
|
||||||
|
|
||||||
|
```py
|
||||||
|
from diffusers import AutoPipelineForText2Image
|
||||||
|
from diffusers.utils import load_image
|
||||||
|
from transformers import CLIPVisionModelWithProjection
|
||||||
|
import torch
|
||||||
|
|
||||||
|
image_encoder = CLIPVisionModelWithProjection.from_pretrained(
|
||||||
|
"h94/IP-Adapter",
|
||||||
|
subfolder="models/image_encoder",
|
||||||
|
torch_dtype=torch.float16
|
||||||
|
)
|
||||||
|
|
||||||
|
pipeline = AutoPipelineForText2Image.from_pretrained(
|
||||||
|
"stabilityai/stable-diffusion-xl-base-1.0",
|
||||||
|
image_encoder=image_encoder,
|
||||||
|
enable_pag=True,
|
||||||
|
torch_dtype=torch.float16
|
||||||
|
).to("cuda")
|
||||||
|
|
||||||
|
pipeline.load_ip_adapter("h94/IP-Adapter", subfolder="sdxl_models", weight_name="ip-adapter-plus_sdxl_vit-h.bin")
|
||||||
|
|
||||||
|
pag_scales = 5.0
|
||||||
|
ip_adapter_scales = 0.8
|
||||||
|
|
||||||
|
image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/ip_adapter_diner.png")
|
||||||
|
|
||||||
|
pipeline.set_ip_adapter_scale(ip_adapter_scale)
|
||||||
|
generator = torch.Generator(device="cpu").manual_seed(0)
|
||||||
|
images = pipeline(
|
||||||
|
prompt="a polar bear sitting in a chair drinking a milkshake",
|
||||||
|
ip_adapter_image=image,
|
||||||
|
negative_prompt="deformed, ugly, wrong proportion, low res, bad anatomy, worst quality, low quality",
|
||||||
|
num_inference_steps=25,
|
||||||
|
guidance_scale=3.0,
|
||||||
|
generator=generator,
|
||||||
|
pag_scale=pag_scale,
|
||||||
|
).images
|
||||||
|
images[0]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
PAG reduces artifacts and improves the overall compposition.
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-4">
|
||||||
|
<div class="flex-1">
|
||||||
|
<img class="rounded-xl" src="https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/pag_0.0_ipa_0.8.png"/>
|
||||||
|
<figcaption class="mt-2 text-center text-sm text-gray-500">generated image without PAG</figcaption>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<img class="rounded-xl" src="https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/pag_5.0_ipa_0.8.png"/>
|
||||||
|
<figcaption class="mt-2 text-center text-sm text-gray-500">generated image with PAG</figcaption>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
## Configure parameters
|
||||||
|
|
||||||
|
### pag_applied_layers
|
||||||
|
|
||||||
|
The `pag_applied_layers` argument allows you to specify which layers PAG is applied to. By default, it applies only to the mid blocks. Changing this setting will significantly impact the output. You can use the `set_pag_applied_layers` method to adjust the PAG layers after the pipeline is created, helping you find the optimal layers for your model.
|
||||||
|
|
||||||
|
As an example, here is the images generated with `pag_layers = ["down.block_2"]` and `pag_layers = ["down.block_2", "up.block_1.attentions_0"]`
|
||||||
|
|
||||||
|
```py
|
||||||
|
prompt = "an insect robot preparing a delicious meal, anime style"
|
||||||
|
pipeline.set_pag_applied_layers(pag_layers)
|
||||||
|
generator = torch.Generator(device="cpu").manual_seed(0)
|
||||||
|
images = pipeline(
|
||||||
|
prompt=prompt,
|
||||||
|
num_inference_steps=25,
|
||||||
|
guidance_scale=guidance_scale,
|
||||||
|
generator=generator,
|
||||||
|
pag_scale=pag_scale,
|
||||||
|
).images
|
||||||
|
images[0]
|
||||||
|
```
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-4">
|
||||||
|
<div class="flex-1">
|
||||||
|
<img class="rounded-xl" src="https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/pag_3.0_cfg_7.0_down2_up1a0.png"/>
|
||||||
|
<figcaption class="mt-2 text-center text-sm text-gray-500">down.block_2 + up.block1.attentions_0</figcaption>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<img class="rounded-xl" src="https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/pag_3.0_cfg_7.0_down2.png"/>
|
||||||
|
<figcaption class="mt-2 text-center text-sm text-gray-500">down.block_2</figcaption>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -186,7 +186,7 @@ scheduler, scheduler_state = FlaxDPMSolverMultistepScheduler.from_pretrained(
|
|||||||
pipeline, params = FlaxStableDiffusionPipeline.from_pretrained(
|
pipeline, params = FlaxStableDiffusionPipeline.from_pretrained(
|
||||||
"runwayml/stable-diffusion-v1-5",
|
"runwayml/stable-diffusion-v1-5",
|
||||||
scheduler=scheduler,
|
scheduler=scheduler,
|
||||||
revision="bf16",
|
variant="bf16",
|
||||||
dtype=jax.numpy.bfloat16,
|
dtype=jax.numpy.bfloat16,
|
||||||
)
|
)
|
||||||
params["scheduler"] = scheduler_state
|
params["scheduler"] = scheduler_state
|
||||||
|
|||||||
@@ -285,6 +285,12 @@ refiner = DiffusionPipeline.from_pretrained(
|
|||||||
).to("cuda")
|
).to("cuda")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<Tip warning={true}>
|
||||||
|
|
||||||
|
You can use SDXL refiner with a different base model. For example, you can use the [Hunyuan-DiT](../../api/pipelines/hunyuandit) or [PixArt-Sigma](../../api/pipelines/pixart_sigma) pipelines to generate images with better prompt adherence. Once you have generated an image, you can pass it to the SDXL refiner model to enhance final generation quality.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
Generate an image from the base model, and set the model output to **latent** space:
|
Generate an image from the base model, and set the model output to **latent** space:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ Flax is a functional framework, so models are stateless and parameters are store
|
|||||||
dtype = jnp.bfloat16
|
dtype = jnp.bfloat16
|
||||||
pipeline, params = FlaxStableDiffusionPipeline.from_pretrained(
|
pipeline, params = FlaxStableDiffusionPipeline.from_pretrained(
|
||||||
"CompVis/stable-diffusion-v1-4",
|
"CompVis/stable-diffusion-v1-4",
|
||||||
revision="bf16",
|
variant="bf16",
|
||||||
dtype=dtype,
|
dtype=dtype,
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -10,30 +10,30 @@ an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express o
|
|||||||
specific language governing permissions and limitations under the License.
|
specific language governing permissions and limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
# 철학
|
# 철학 [[philosophy]]
|
||||||
|
|
||||||
🧨 Diffusers는 다양한 모달리티에서 **최신의** 사전 훈련된 diffusion 모델을 제공합니다.
|
🧨 Diffusers는 다양한 모달리티에서 **최신의** 사전 훈련된 diffusion 모델을 제공합니다.
|
||||||
그 목적은 추론과 훈련을 위한 **모듈식 툴박스**로 사용되는 것입니다.
|
그 목적은 추론과 훈련을 위한 **모듈식 툴박스**로 사용되는 것입니다.
|
||||||
|
|
||||||
우리는 오랜 시간에 견딜 수 있는 라이브러리를 구축하는 것을 목표로 하고, 따라서 API 설계를 매우 중요시합니다.
|
저희는 시간이 지나도 변치 않는 라이브러리를 구축하는 것을 목표로 하기에 API 설계를 매우 중요하게 생각합니다.
|
||||||
|
|
||||||
간단히 말해서, Diffusers는 PyTorch의 자연스러운 확장이 되도록 구축되었습니다. 따라서 대부분의 설계 선택은 [PyTorch의 설계 원칙](https://pytorch.org/docs/stable/community/design.html#pytorch-design-philosophy)에 기반합니다. 이제 가장 중요한 것들을 살펴보겠습니다:
|
간단히 말해서, Diffusers는 PyTorch를 자연스럽게 확장할 수 있도록 만들어졌습니다. 따라서 대부분의 설계 선택은 [PyTorch의 설계 원칙](https://pytorch.org/docs/stable/community/design.html#pytorch-design-philosophy)에 기반합니다. 이제 가장 중요한 것들을 살펴보겠습니다:
|
||||||
|
|
||||||
## 성능보다는 사용성을
|
## 성능보다는 사용성을 [[usability-over-performance]]
|
||||||
|
|
||||||
- Diffusers는 많은 내장 성능 향상 기능을 갖고 있지만 (자세한 내용은 [메모리와 속도](https://huggingface.co/docs/diffusers/optimization/fp16) 참조), 모델은 항상 가장 높은 정밀도와 최소한의 최적화로 로드됩니다. 따라서 기본적인 diffusion 파이프라인은 따로 정의하지 않는다면 CPU에서 float32 정밀도로 인스턴스화됩니다. 이는 다양한 플랫폼과 가속기에서의 사용성을 보장하며, 라이브러리를 실행하기 위해 복잡한 설치가 필요하지 않음을 의미합니다.
|
- Diffusers는 다양한 성능 향상 기능이 내장되어 있지만 (자세한 내용은 [메모리와 속도](https://huggingface.co/docs/diffusers/optimization/fp16) 참조), 모델은 항상 가장 높은 정밀도와 최소한의 최적화로 로드됩니다. 따라서 사용자가 별도로 정의하지 않는 한 기본적으로 diffusion 파이프라인은 항상 float32 정밀도로 CPU에 인스턴스화됩니다. 이는 다양한 플랫폼과 가속기에서의 사용성을 보장하며, 라이브러리를 실행하기 위해 복잡한 설치가 필요하지 않다는 것을 의미합니다.
|
||||||
- Diffusers는 **가벼운** 패키지를 지향하기 때문에 필수 종속성은 거의 없지만 성능을 향상시킬 수 있는 많은 선택적 종속성이 있습니다 (`accelerate`, `safetensors`, `onnx` 등). 저희는 라이브러리를 가능한 한 가볍게 유지하여 다른 패키지에 대한 종속성 걱정이 없도록 노력하고 있습니다.
|
- Diffusers는 **가벼운** 패키지를 지향하기 때문에 필수 종속성은 거의 없지만 성능을 향상시킬 수 있는 많은 선택적 종속성이 있습니다 (`accelerate`, `safetensors`, `onnx` 등). 저희는 라이브러리를 가능한 한 가볍게 유지하여 다른 패키지에 대한 종속성 걱정이 없도록 노력하고 있습니다.
|
||||||
- Diffusers는 간결하고 이해하기 쉬운 코드를 선호합니다. 이는 람다 함수나 고급 PyTorch 연산자와 같은 압축된 코드 구문을 자주 사용하지 않는 것을 의미합니다.
|
- Diffusers는 간결하고 이해하기 쉬운 코드를 선호합니다. 이는 람다 함수나 고급 PyTorch 연산자와 같은 압축된 코드 구문을 자주 사용하지 않는 것을 의미합니다.
|
||||||
|
|
||||||
## 쉬움보다는 간단함을
|
## 쉬움보다는 간단함을 [[simple-over-easy]]
|
||||||
|
|
||||||
PyTorch에서는 **명시적인 것이 암시적인 것보다 낫다**와 **단순한 것이 복잡한 것보다 낫다**라고 말합니다. 이 설계 철학은 라이브러리의 여러 부분에 반영되어 있습니다:
|
PyTorch에서는 **명시적인 것이 암시적인 것보다 낫다**와 **단순한 것이 복잡한 것보다 낫다**라고 말합니다. 이 설계 철학은 라이브러리의 여러 부분에 반영되어 있습니다:
|
||||||
- [`DiffusionPipeline.to`](https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.to)와 같은 메서드를 사용하여 사용자가 장치 관리를 할 수 있도록 PyTorch의 API를 따릅니다.
|
- [`DiffusionPipeline.to`](https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.to)와 같은 메소드를 사용하여 사용자가 장치 관리를 할 수 있도록 PyTorch의 API를 따릅니다.
|
||||||
- 잘못된 입력을 조용히 수정하는 대신 간결한 오류 메시지를 발생시키는 것이 우선입니다. Diffusers는 라이브러리를 가능한 한 쉽게 사용할 수 있도록 하는 것보다 사용자를 가르치는 것을 목표로 합니다.
|
- 잘못된 입력을 조용히 수정하는 대신 간결한 오류 메시지를 발생시키는 것이 우선입니다. Diffusers는 라이브러리를 가능한 한 쉽게 사용할 수 있도록 하는 것보다 사용자를 가르치는 것을 목표로 합니다.
|
||||||
- 복잡한 모델과 스케줄러 로직이 내부에서 마법처럼 처리하는 대신 노출됩니다. 스케줄러/샘플러는 서로에게 최소한의 종속성을 가지고 분리되어 있습니다. 이로써 사용자는 언롤된 노이즈 제거 루프를 작성해야 합니다. 그러나 이 분리는 디버깅을 더 쉽게하고 노이즈 제거 과정을 조정하거나 diffusers 모델이나 스케줄러를 교체하는 데 사용자에게 더 많은 제어권을 제공합니다.
|
- 복잡한 모델과 스케줄러 로직이 내부에서 마법처럼 처리하는 대신 노출됩니다. 스케줄러/샘플러는 서로에게 최소한의 종속성을 가지고 분리되어 있습니다. 이로써 사용자는 언롤된 노이즈 제거 루프를 작성해야 합니다. 그러나 이 분리는 디버깅을 더 쉽게하고 노이즈 제거 과정을 조정하거나 diffusers 모델이나 스케줄러를 교체하는 데 사용자에게 더 많은 제어권을 제공합니다.
|
||||||
- diffusers 파이프라인의 따로 훈련된 구성 요소인 text encoder, unet 및 variational autoencoder는 각각 자체 모델 클래스를 갖습니다. 이로써 사용자는 서로 다른 모델의 구성 요소 간의 상호 작용을 처리해야 하며, 직렬화 형식은 모델 구성 요소를 다른 파일로 분리합니다. 그러나 이는 디버깅과 커스터마이징을 더 쉽게합니다. DreamBooth나 Textual Inversion 훈련은 Diffusers의 'diffusion 파이프라인의 단일 구성 요소들을 분리할 수 있는 능력' 덕분에 매우 간단합니다.
|
- diffusers 파이프라인의 따로 훈련된 구성 요소인 text encoder, unet 및 variational autoencoder는 각각 자체 모델 클래스를 갖습니다. 이로써 사용자는 서로 다른 모델의 구성 요소 간의 상호 작용을 처리해야 하며, 직렬화 형식은 모델 구성 요소를 다른 파일로 분리합니다. 그러나 이는 디버깅과 커스터마이징을 더 쉽게합니다. DreamBooth나 Textual Inversion 훈련은 Diffusers의 'diffusion 파이프라인의 단일 구성 요소들을 분리할 수 있는 능력' 덕분에 매우 간단합니다.
|
||||||
|
|
||||||
## 추상화보다는 수정 가능하고 기여하기 쉬움을
|
## 추상화보다는 수정 가능하고 기여하기 쉬움을 [[tweakable-contributor-friendly-over-abstraction]]
|
||||||
|
|
||||||
라이브러리의 대부분에 대해 Diffusers는 [Transformers 라이브러리](https://github.com/huggingface/transformers)의 중요한 설계 원칙을 채택합니다, 바로 성급한 추상화보다는 copy-pasted 코드를 선호한다는 것입니다. 이 설계 원칙은 [Don't repeat yourself (DRY)](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)와 같은 인기 있는 설계 원칙과는 대조적으로 매우 의견이 분분한데요.
|
라이브러리의 대부분에 대해 Diffusers는 [Transformers 라이브러리](https://github.com/huggingface/transformers)의 중요한 설계 원칙을 채택합니다, 바로 성급한 추상화보다는 copy-pasted 코드를 선호한다는 것입니다. 이 설계 원칙은 [Don't repeat yourself (DRY)](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)와 같은 인기 있는 설계 원칙과는 대조적으로 매우 의견이 분분한데요.
|
||||||
간단히 말해서, Transformers가 모델링 파일에 대해 수행하는 것처럼, Diffusers는 매우 낮은 수준의 추상화와 매우 독립적인 코드를 유지하는 것을 선호합니다. 함수, 긴 코드 블록, 심지어 클래스도 여러 파일에 복사할 수 있으며, 이는 처음에는 라이브러리를 유지할 수 없게 만드는 나쁜, 서투른 설계 선택으로 보일 수 있습니다. 하지만 이러한 설계는 매우 성공적이며, 커뮤니티 기반의 오픈 소스 기계 학습 라이브러리에 매우 적합합니다. 그 이유는 다음과 같습니다:
|
간단히 말해서, Transformers가 모델링 파일에 대해 수행하는 것처럼, Diffusers는 매우 낮은 수준의 추상화와 매우 독립적인 코드를 유지하는 것을 선호합니다. 함수, 긴 코드 블록, 심지어 클래스도 여러 파일에 복사할 수 있으며, 이는 처음에는 라이브러리를 유지할 수 없게 만드는 나쁜, 서투른 설계 선택으로 보일 수 있습니다. 하지만 이러한 설계는 매우 성공적이며, 커뮤니티 기반의 오픈 소스 기계 학습 라이브러리에 매우 적합합니다. 그 이유는 다음과 같습니다:
|
||||||
@@ -48,16 +48,16 @@ Diffusers에서는 이러한 철학을 파이프라인과 스케줄러에 모두
|
|||||||
좋아요, 이제 🧨 Diffusers가 설계된 방식을 대략적으로 이해했을 것입니다 🤗.
|
좋아요, 이제 🧨 Diffusers가 설계된 방식을 대략적으로 이해했을 것입니다 🤗.
|
||||||
우리는 이러한 설계 원칙을 일관되게 라이브러리 전체에 적용하려고 노력하고 있습니다. 그럼에도 불구하고 철학에 대한 일부 예외 사항이나 불행한 설계 선택이 있을 수 있습니다. 디자인에 대한 피드백이 있다면 [GitHub에서 직접](https://github.com/huggingface/diffusers/issues/new?assignees=&labels=&template=feedback.md&title=) 알려주시면 감사하겠습니다.
|
우리는 이러한 설계 원칙을 일관되게 라이브러리 전체에 적용하려고 노력하고 있습니다. 그럼에도 불구하고 철학에 대한 일부 예외 사항이나 불행한 설계 선택이 있을 수 있습니다. 디자인에 대한 피드백이 있다면 [GitHub에서 직접](https://github.com/huggingface/diffusers/issues/new?assignees=&labels=&template=feedback.md&title=) 알려주시면 감사하겠습니다.
|
||||||
|
|
||||||
## 디자인 철학 자세히 알아보기
|
## 디자인 철학 자세히 알아보기 [[design-philosophy-in-details]]
|
||||||
|
|
||||||
이제 디자인 철학의 세부 사항을 좀 더 자세히 살펴보겠습니다. Diffusers는 주로 세 가지 주요 클래스로 구성됩니다: [파이프라인](https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines), [모델](https://github.com/huggingface/diffusers/tree/main/src/diffusers/models), 그리고 [스케줄러](https://github.com/huggingface/diffusers/tree/main/src/diffusers/schedulers). 각 클래스에 대한 더 자세한 설계 결정 사항을 살펴보겠습니다.
|
이제 디자인 철학의 세부 사항을 좀 더 자세히 살펴보겠습니다. Diffusers는 주로 세 가지 주요 클래스로 구성됩니다: [파이프라인](https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines), [모델](https://github.com/huggingface/diffusers/tree/main/src/diffusers/models), 그리고 [스케줄러](https://github.com/huggingface/diffusers/tree/main/src/diffusers/schedulers). 각 클래스에 대한 더 자세한 설계 결정 사항을 살펴보겠습니다.
|
||||||
|
|
||||||
### 파이프라인
|
### 파이프라인 [[pipelines]]
|
||||||
|
|
||||||
파이프라인은 사용하기 쉽도록 설계되었으며 (따라서 [*쉬움보다는 간단함을*](#쉬움보다는-간단함을)을 100% 따르지는 않음), feature-complete하지 않으며, 추론을 위한 [모델](#모델)과 [스케줄러](#스케줄러)를 사용하는 방법의 예시로 간주될 수 있습니다.
|
파이프라인은 사용하기 쉽도록 설계되었으며 (따라서 [*쉬움보다는 간단함을*](#쉬움보다는-간단함을)을 100% 따르지는 않음), feature-complete하지 않으며, 추론을 위한 [모델](#모델)과 [스케줄러](#스케줄러)를 사용하는 방법의 예시로 간주될 수 있습니다.
|
||||||
|
|
||||||
다음과 같은 설계 원칙을 따릅니다:
|
다음과 같은 설계 원칙을 따릅니다:
|
||||||
- 파이프라인은 단일 파일 정책을 따릅니다. 모든 파이프라인은 src/diffusers/pipelines의 개별 디렉토리에 있습니다. 하나의 파이프라인 폴더는 하나의 diffusion 논문/프로젝트/릴리스에 해당합니다. 여러 파이프라인 파일은 하나의 파이프라인 폴더에 모을 수 있습니다. 예를 들어 [`src/diffusers/pipelines/stable-diffusion`](https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines/stable_diffusion)에서 그렇게 하고 있습니다. 파이프라인이 유사한 기능을 공유하는 경우, [#Copied from mechanism](https://github.com/huggingface/diffusers/blob/125d783076e5bd9785beb05367a2d2566843a271/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion_img2img.py#L251)을 사용할 수 있습니다.
|
- 파이프라인은 단일 파일 정책을 따릅니다. 모든 파이프라인은 src/diffusers/pipelines의 개별 디렉토리에 있습니다. 하나의 파이프라인 폴더는 하나의 diffusion 논문/프로젝트/릴리스에 해당합니다. 여러 파이프라인 파일은 하나의 파이프라인 폴더에 모을 수 있습니다. 예를 들어 [`src/diffusers/pipelines/stable-diffusion`](https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines/stable_diffusion)에서 그렇게 하고 있습니다. 파이프라인이 유사한 기능을 공유하는 경우, [# Copied from mechanism](https://github.com/huggingface/diffusers/blob/125d783076e5bd9785beb05367a2d2566843a271/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion_img2img.py#L251)을 사용할 수 있습니다.
|
||||||
- 파이프라인은 모두 [`DiffusionPipeline`]을 상속합니다.
|
- 파이프라인은 모두 [`DiffusionPipeline`]을 상속합니다.
|
||||||
- 각 파이프라인은 서로 다른 모델 및 스케줄러 구성 요소로 구성되어 있으며, 이는 [`model_index.json` 파일](https://huggingface.co/runwayml/stable-diffusion-v1-5/blob/main/model_index.json)에 문서화되어 있으며, 파이프라인의 속성 이름과 동일한 이름으로 액세스할 수 있으며, [`DiffusionPipeline.components`](https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.components) 함수를 통해 파이프라인 간에 공유할 수 있습니다.
|
- 각 파이프라인은 서로 다른 모델 및 스케줄러 구성 요소로 구성되어 있으며, 이는 [`model_index.json` 파일](https://huggingface.co/runwayml/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) 함수를 통해 로드할 수 있어야 합니다.
|
- 각 파이프라인은 [`DiffusionPipeline.from_pretrained`](https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.from_pretrained) 함수를 통해 로드할 수 있어야 합니다.
|
||||||
@@ -65,11 +65,11 @@ Diffusers에서는 이러한 철학을 파이프라인과 스케줄러에 모두
|
|||||||
- 파이프라인은 매우 가독성이 좋고, 이해하기 쉽고, 쉽게 조정할 수 있도록 설계되어야 합니다.
|
- 파이프라인은 매우 가독성이 좋고, 이해하기 쉽고, 쉽게 조정할 수 있도록 설계되어야 합니다.
|
||||||
- 파이프라인은 서로 상호작용하고, 상위 수준 API에 쉽게 통합할 수 있도록 설계되어야 합니다.
|
- 파이프라인은 서로 상호작용하고, 상위 수준 API에 쉽게 통합할 수 있도록 설계되어야 합니다.
|
||||||
- 파이프라인은 사용자 인터페이스가 feature-complete하지 않게 하는 것을 목표로 합니다. future-complete한 사용자 인터페이스를 원한다면 [InvokeAI](https://github.com/invoke-ai/InvokeAI), [Diffuzers](https://github.com/abhishekkrthakur/diffuzers), [lama-cleaner](https://github.com/Sanster/lama-cleaner)를 참조해야 합니다.
|
- 파이프라인은 사용자 인터페이스가 feature-complete하지 않게 하는 것을 목표로 합니다. future-complete한 사용자 인터페이스를 원한다면 [InvokeAI](https://github.com/invoke-ai/InvokeAI), [Diffuzers](https://github.com/abhishekkrthakur/diffuzers), [lama-cleaner](https://github.com/Sanster/lama-cleaner)를 참조해야 합니다.
|
||||||
- 모든 파이프라인은 오로지 `__call__` 메서드를 통해 실행할 수 있어야 합니다. `__call__` 인자의 이름은 모든 파이프라인에서 공유되어야 합니다.
|
- 모든 파이프라인은 오로지 `__call__` 메소드를 통해 실행할 수 있어야 합니다. `__call__` 인자의 이름은 모든 파이프라인에서 공유되어야 합니다.
|
||||||
- 파이프라인은 해결하고자 하는 작업의 이름으로 지정되어야 합니다.
|
- 파이프라인은 해결하고자 하는 작업의 이름으로 지정되어야 합니다.
|
||||||
- 대부분의 경우에 새로운 diffusion 파이프라인은 새로운 파이프라인 폴더/파일에 구현되어야 합니다.
|
- 대부분의 경우에 새로운 diffusion 파이프라인은 새로운 파이프라인 폴더/파일에 구현되어야 합니다.
|
||||||
|
|
||||||
### 모델
|
### 모델 [[models]]
|
||||||
|
|
||||||
모델은 [PyTorch의 Module 클래스](https://pytorch.org/docs/stable/generated/torch.nn.Module.html)의 자연스러운 확장이 되도록, 구성 가능한 툴박스로 설계되었습니다. 그리고 모델은 **단일 파일 정책**을 일부만 따릅니다.
|
모델은 [PyTorch의 Module 클래스](https://pytorch.org/docs/stable/generated/torch.nn.Module.html)의 자연스러운 확장이 되도록, 구성 가능한 툴박스로 설계되었습니다. 그리고 모델은 **단일 파일 정책**을 일부만 따릅니다.
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ Diffusers에서는 이러한 철학을 파이프라인과 스케줄러에 모두
|
|||||||
- 모델은 미래의 변경 사항을 쉽게 확장할 수 있도록 설계되어야 합니다. 이는 공개 함수 인수들과 구성 인수들을 제한하고,미래의 변경 사항을 "예상"하는 것을 통해 달성할 수 있습니다. 예를 들어, 불리언 `is_..._type` 인수보다는 새로운 미래 유형에 쉽게 확장할 수 있는 문자열 "...type" 인수를 추가하는 것이 일반적으로 더 좋습니다. 새로운 모델 체크포인트가 작동하도록 하기 위해 기존 아키텍처에 최소한의 변경만을 가해야 합니다.
|
- 모델은 미래의 변경 사항을 쉽게 확장할 수 있도록 설계되어야 합니다. 이는 공개 함수 인수들과 구성 인수들을 제한하고,미래의 변경 사항을 "예상"하는 것을 통해 달성할 수 있습니다. 예를 들어, 불리언 `is_..._type` 인수보다는 새로운 미래 유형에 쉽게 확장할 수 있는 문자열 "...type" 인수를 추가하는 것이 일반적으로 더 좋습니다. 새로운 모델 체크포인트가 작동하도록 하기 위해 기존 아키텍처에 최소한의 변경만을 가해야 합니다.
|
||||||
- 모델 디자인은 코드의 가독성과 간결성을 유지하는 것과 많은 모델 체크포인트를 지원하는 것 사이의 어려운 균형 조절입니다. 모델링 코드의 대부분은 새로운 모델 체크포인트를 위해 클래스를 수정하는 것이 좋지만, [UNet 블록](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/unet_2d_blocks.py) 및 [Attention 프로세서](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py)와 같이 코드를 장기적으로 간결하고 읽기 쉽게 유지하기 위해 새로운 클래스를 추가하는 예외도 있습니다.
|
- 모델 디자인은 코드의 가독성과 간결성을 유지하는 것과 많은 모델 체크포인트를 지원하는 것 사이의 어려운 균형 조절입니다. 모델링 코드의 대부분은 새로운 모델 체크포인트를 위해 클래스를 수정하는 것이 좋지만, [UNet 블록](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/unet_2d_blocks.py) 및 [Attention 프로세서](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py)와 같이 코드를 장기적으로 간결하고 읽기 쉽게 유지하기 위해 새로운 클래스를 추가하는 예외도 있습니다.
|
||||||
|
|
||||||
### 스케줄러
|
### 스케줄러 [[schedulers]]
|
||||||
|
|
||||||
스케줄러는 추론을 위한 노이즈 제거 과정을 안내하고 훈련을 위한 노이즈 스케줄을 정의하는 역할을 합니다. 스케줄러는 개별 클래스로 설계되어 있으며, 로드 가능한 구성 파일과 **단일 파일 정책**을 엄격히 따릅니다.
|
스케줄러는 추론을 위한 노이즈 제거 과정을 안내하고 훈련을 위한 노이즈 스케줄을 정의하는 역할을 합니다. 스케줄러는 개별 클래스로 설계되어 있으며, 로드 가능한 구성 파일과 **단일 파일 정책**을 엄격히 따릅니다.
|
||||||
|
|
||||||
@@ -93,9 +93,9 @@ Diffusers에서는 이러한 철학을 파이프라인과 스케줄러에 모두
|
|||||||
- 모든 스케줄러는 [`src/diffusers/schedulers`](https://github.com/huggingface/diffusers/tree/main/src/diffusers/schedulers)에서 찾을 수 있습니다.
|
- 모든 스케줄러는 [`src/diffusers/schedulers`](https://github.com/huggingface/diffusers/tree/main/src/diffusers/schedulers)에서 찾을 수 있습니다.
|
||||||
- 스케줄러는 큰 유틸리티 파일에서 가져오지 **않아야** 하며, 자체 포함성을 유지해야 합니다.
|
- 스케줄러는 큰 유틸리티 파일에서 가져오지 **않아야** 하며, 자체 포함성을 유지해야 합니다.
|
||||||
- 하나의 스케줄러 Python 파일은 하나의 스케줄러 알고리즘(논문에서 정의된 것과 같은)에 해당합니다.
|
- 하나의 스케줄러 Python 파일은 하나의 스케줄러 알고리즘(논문에서 정의된 것과 같은)에 해당합니다.
|
||||||
- 스케줄러가 유사한 기능을 공유하는 경우, `#Copied from` 메커니즘을 사용할 수 있습니다.
|
- 스케줄러가 유사한 기능을 공유하는 경우, `# Copied from` 메커니즘을 사용할 수 있습니다.
|
||||||
- 모든 스케줄러는 `SchedulerMixin`과 `ConfigMixin`을 상속합니다.
|
- 모든 스케줄러는 `SchedulerMixin`과 `ConfigMixin`을 상속합니다.
|
||||||
- [`ConfigMixin.from_config`](https://huggingface.co/docs/diffusers/main/en/api/configuration#diffusers.ConfigMixin.from_config) 메서드를 사용하여 스케줄러를 쉽게 교체할 수 있습니다. 자세한 내용은 [여기](../using-diffusers/schedulers.md)에서 설명합니다.
|
- [`ConfigMixin.from_config`](https://huggingface.co/docs/diffusers/main/en/api/configuration#diffusers.ConfigMixin.from_config) 메소드를 사용하여 스케줄러를 쉽게 교체할 수 있습니다. 자세한 내용은 [여기](../using-diffusers/schedulers.md)에서 설명합니다.
|
||||||
- 모든 스케줄러는 `set_num_inference_steps`와 `step` 함수를 가져야 합니다. `set_num_inference_steps(...)`는 각 노이즈 제거 과정(즉, `step(...)`이 호출되기 전) 이전에 호출되어야 합니다.
|
- 모든 스케줄러는 `set_num_inference_steps`와 `step` 함수를 가져야 합니다. `set_num_inference_steps(...)`는 각 노이즈 제거 과정(즉, `step(...)`이 호출되기 전) 이전에 호출되어야 합니다.
|
||||||
- 각 스케줄러는 모델이 호출될 타임스텝의 배열인 `timesteps` 속성을 통해 루프를 돌 수 있는 타임스텝을 노출합니다.
|
- 각 스케줄러는 모델이 호출될 타임스텝의 배열인 `timesteps` 속성을 통해 루프를 돌 수 있는 타임스텝을 노출합니다.
|
||||||
- `step(...)` 함수는 예측된 모델 출력과 "현재" 샘플(x_t)을 입력으로 받고, "이전" 약간 더 노이즈가 제거된 샘플(x_t-1)을 반환합니다.
|
- `step(...)` 함수는 예측된 모델 출력과 "현재" 샘플(x_t)을 입력으로 받고, "이전" 약간 더 노이즈가 제거된 샘플(x_t-1)을 반환합니다.
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ outputs = pipeline(
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
더 많은 정보를 얻기 위해, Optimum Habana의 [문서](https://huggingface.co/docs/optimum/habana/usage_guides/stable_diffusion)와 공식 Github 저장소에 제공된 [예시](https://github.com/huggingface/optimum-habana/tree/main/examples/stable-diffusion)를 확인하세요.
|
더 많은 정보를 얻기 위해, Optimum Habana의 [문서](https://huggingface.co/docs/optimum/habana/usage_guides/stable_diffusion)와 공식 GitHub 저장소에 제공된 [예시](https://github.com/huggingface/optimum-habana/tree/main/examples/stable-diffusion)를 확인하세요.
|
||||||
|
|
||||||
|
|
||||||
## 벤치마크
|
## 벤치마크
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ scheduler, scheduler_state = FlaxDPMSolverMultistepScheduler.from_pretrained(
|
|||||||
pipeline, params = FlaxStableDiffusionPipeline.from_pretrained(
|
pipeline, params = FlaxStableDiffusionPipeline.from_pretrained(
|
||||||
model_id,
|
model_id,
|
||||||
scheduler=scheduler,
|
scheduler=scheduler,
|
||||||
revision="bf16",
|
variant="bf16",
|
||||||
dtype=jax.numpy.bfloat16,
|
dtype=jax.numpy.bfloat16,
|
||||||
)
|
)
|
||||||
params["scheduler"] = scheduler_state
|
params["scheduler"] = scheduler_state
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ Flax는 함수형 프레임워크이므로 모델은 무상태(stateless)형이
|
|||||||
```python
|
```python
|
||||||
pipeline, params = FlaxStableDiffusionPipeline.from_pretrained(
|
pipeline, params = FlaxStableDiffusionPipeline.from_pretrained(
|
||||||
"CompVis/stable-diffusion-v1-4",
|
"CompVis/stable-diffusion-v1-4",
|
||||||
revision="bf16",
|
variant="bf16",
|
||||||
dtype=dtype,
|
dtype=dtype,
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -436,7 +436,7 @@ lora_path = "lora-library/B-LoRA-pen_sketch"
|
|||||||
|
|
||||||
state_dict = lora_lora_unet_blocks(content_B_lora_path,alpha=1,target_blocks=["unet.up_blocks.0.attentions.0"])
|
state_dict = lora_lora_unet_blocks(content_B_lora_path,alpha=1,target_blocks=["unet.up_blocks.0.attentions.0"])
|
||||||
|
|
||||||
# Load traine dlora layers into the unet
|
# Load trained lora layers into the unet
|
||||||
pipeline.load_lora_into_unet(state_dict, None, pipeline.unet)
|
pipeline.load_lora_into_unet(state_dict, None, pipeline.unet)
|
||||||
|
|
||||||
#generate
|
#generate
|
||||||
|
|||||||
@@ -326,7 +326,7 @@ def parse_args(input_args=None):
|
|||||||
type=str,
|
type=str,
|
||||||
default="TOK",
|
default="TOK",
|
||||||
help="identifier specifying the instance(or instances) as used in instance_prompt, validation prompt, "
|
help="identifier specifying the instance(or instances) as used in instance_prompt, validation prompt, "
|
||||||
"captions - e.g. TOK. To use multiple identifiers, please specify them in a comma seperated string - e.g. "
|
"captions - e.g. TOK. To use multiple identifiers, please specify them in a comma separated string - e.g. "
|
||||||
"'TOK,TOK2,TOK3' etc.",
|
"'TOK,TOK2,TOK3' etc.",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -559,7 +559,7 @@ def parse_args(input_args=None):
|
|||||||
"--prodigy_beta3",
|
"--prodigy_beta3",
|
||||||
type=float,
|
type=float,
|
||||||
default=None,
|
default=None,
|
||||||
help="coefficients for computing the Prodidy stepsize using running averages. If set to None, "
|
help="coefficients for computing the Prodigy stepsize using running averages. If set to None, "
|
||||||
"uses the value of square root of beta2. Ignored if optimizer is adamW",
|
"uses the value of square root of beta2. Ignored if optimizer is adamW",
|
||||||
)
|
)
|
||||||
parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay")
|
parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay")
|
||||||
@@ -736,7 +736,7 @@ class TokenEmbeddingsHandler:
|
|||||||
# random initialization of new tokens
|
# random initialization of new tokens
|
||||||
std_token_embedding = text_encoder.text_model.embeddings.token_embedding.weight.data.std()
|
std_token_embedding = text_encoder.text_model.embeddings.token_embedding.weight.data.std()
|
||||||
|
|
||||||
print(f"{idx} text encodedr's std_token_embedding: {std_token_embedding}")
|
print(f"{idx} text encoder's std_token_embedding: {std_token_embedding}")
|
||||||
|
|
||||||
text_encoder.text_model.embeddings.token_embedding.weight.data[self.train_ids] = (
|
text_encoder.text_model.embeddings.token_embedding.weight.data[self.train_ids] = (
|
||||||
torch.randn(len(self.train_ids), text_encoder.text_model.config.hidden_size)
|
torch.randn(len(self.train_ids), text_encoder.text_model.config.hidden_size)
|
||||||
@@ -948,7 +948,7 @@ class DreamBoothDataset(Dataset):
|
|||||||
else:
|
else:
|
||||||
example["instance_prompt"] = self.instance_prompt
|
example["instance_prompt"] = self.instance_prompt
|
||||||
|
|
||||||
else: # costum prompts were provided, but length does not match size of image dataset
|
else: # custom prompts were provided, but length does not match size of image dataset
|
||||||
example["instance_prompt"] = self.instance_prompt
|
example["instance_prompt"] = self.instance_prompt
|
||||||
|
|
||||||
if self.class_data_root:
|
if self.class_data_root:
|
||||||
@@ -1524,17 +1524,22 @@ def main(args):
|
|||||||
torch.cuda.empty_cache()
|
torch.cuda.empty_cache()
|
||||||
|
|
||||||
# Scheduler and math around the number of training steps.
|
# Scheduler and math around the number of training steps.
|
||||||
overrode_max_train_steps = False
|
# Check the PR https://github.com/huggingface/diffusers/pull/8312 for detailed explanation.
|
||||||
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
|
num_warmup_steps_for_scheduler = args.lr_warmup_steps * accelerator.num_processes
|
||||||
if args.max_train_steps is None:
|
if args.max_train_steps is None:
|
||||||
args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch
|
len_train_dataloader_after_sharding = math.ceil(len(train_dataloader) / accelerator.num_processes)
|
||||||
overrode_max_train_steps = True
|
num_update_steps_per_epoch = math.ceil(len_train_dataloader_after_sharding / args.gradient_accumulation_steps)
|
||||||
|
num_training_steps_for_scheduler = (
|
||||||
|
args.num_train_epochs * num_update_steps_per_epoch * accelerator.num_processes
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
num_training_steps_for_scheduler = args.max_train_steps * accelerator.num_processes
|
||||||
|
|
||||||
lr_scheduler = get_scheduler(
|
lr_scheduler = get_scheduler(
|
||||||
args.lr_scheduler,
|
args.lr_scheduler,
|
||||||
optimizer=optimizer,
|
optimizer=optimizer,
|
||||||
num_warmup_steps=args.lr_warmup_steps * accelerator.num_processes,
|
num_warmup_steps=num_warmup_steps_for_scheduler,
|
||||||
num_training_steps=args.max_train_steps * accelerator.num_processes,
|
num_training_steps=num_training_steps_for_scheduler,
|
||||||
num_cycles=args.lr_num_cycles,
|
num_cycles=args.lr_num_cycles,
|
||||||
power=args.lr_power,
|
power=args.lr_power,
|
||||||
)
|
)
|
||||||
@@ -1551,8 +1556,14 @@ def main(args):
|
|||||||
|
|
||||||
# We need to recalculate our total training steps as the size of the training dataloader may have changed.
|
# We need to recalculate our total training steps as the size of the training dataloader may have changed.
|
||||||
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
|
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
|
||||||
if overrode_max_train_steps:
|
if args.max_train_steps is None:
|
||||||
args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch
|
args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch
|
||||||
|
if num_training_steps_for_scheduler != args.max_train_steps * accelerator.num_processes:
|
||||||
|
logger.warning(
|
||||||
|
f"The length of the 'train_dataloader' after 'accelerator.prepare' ({len(train_dataloader)}) does not match "
|
||||||
|
f"the expected length ({len_train_dataloader_after_sharding}) when the learning rate scheduler was created. "
|
||||||
|
f"This inconsistency may result in the learning rate scheduler not functioning properly."
|
||||||
|
)
|
||||||
# Afterwards we recalculate our number of training epochs
|
# Afterwards we recalculate our number of training epochs
|
||||||
args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch)
|
args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch)
|
||||||
|
|
||||||
@@ -1845,10 +1856,10 @@ def main(args):
|
|||||||
generator = torch.Generator(device=accelerator.device).manual_seed(args.seed) if args.seed else None
|
generator = torch.Generator(device=accelerator.device).manual_seed(args.seed) if args.seed else None
|
||||||
pipeline_args = {"prompt": args.validation_prompt}
|
pipeline_args = {"prompt": args.validation_prompt}
|
||||||
|
|
||||||
if torch.backends.mps.is_available():
|
if torch.backends.mps.is_available():
|
||||||
autocast_ctx = nullcontext()
|
autocast_ctx = nullcontext()
|
||||||
else:
|
else:
|
||||||
autocast_ctx = torch.autocast(accelerator.device.type)
|
autocast_ctx = torch.autocast(accelerator.device.type)
|
||||||
|
|
||||||
with autocast_ctx:
|
with autocast_ctx:
|
||||||
images = [
|
images = [
|
||||||
@@ -1869,7 +1880,6 @@ def main(args):
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
del pipeline
|
del pipeline
|
||||||
torch.cuda.empty_cache()
|
torch.cuda.empty_cache()
|
||||||
|
|
||||||
@@ -1967,7 +1977,7 @@ def main(args):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Conver to WebUI format
|
# Convert to WebUI format
|
||||||
lora_state_dict = load_file(f"{args.output_dir}/pytorch_lora_weights.safetensors")
|
lora_state_dict = load_file(f"{args.output_dir}/pytorch_lora_weights.safetensors")
|
||||||
peft_state_dict = convert_all_state_dict_to_peft(lora_state_dict)
|
peft_state_dict = convert_all_state_dict_to_peft(lora_state_dict)
|
||||||
kohya_state_dict = convert_state_dict_to_kohya(peft_state_dict)
|
kohya_state_dict = convert_state_dict_to_kohya(peft_state_dict)
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ from typing import List, Optional
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import torch
|
import torch
|
||||||
import torch.nn.functional as F
|
import torch.nn.functional as F
|
||||||
|
|
||||||
# imports of the TokenEmbeddingsHandler class
|
|
||||||
import torch.utils.checkpoint
|
import torch.utils.checkpoint
|
||||||
import transformers
|
import transformers
|
||||||
from accelerate import Accelerator
|
from accelerate import Accelerator
|
||||||
@@ -77,6 +75,9 @@ from diffusers.utils.import_utils import is_xformers_available
|
|||||||
from diffusers.utils.torch_utils import is_compiled_module
|
from diffusers.utils.torch_utils import is_compiled_module
|
||||||
|
|
||||||
|
|
||||||
|
if is_wandb_available():
|
||||||
|
import wandb
|
||||||
|
|
||||||
# Will error if the minimal version of diffusers is not installed. Remove at your own risks.
|
# Will error if the minimal version of diffusers is not installed. Remove at your own risks.
|
||||||
check_min_version("0.30.0.dev0")
|
check_min_version("0.30.0.dev0")
|
||||||
|
|
||||||
@@ -101,12 +102,12 @@ def save_model_card(
|
|||||||
repo_id: str,
|
repo_id: str,
|
||||||
use_dora: bool,
|
use_dora: bool,
|
||||||
images=None,
|
images=None,
|
||||||
base_model=str,
|
base_model: str = None,
|
||||||
train_text_encoder=False,
|
train_text_encoder=False,
|
||||||
train_text_encoder_ti=False,
|
train_text_encoder_ti=False,
|
||||||
token_abstraction_dict=None,
|
token_abstraction_dict=None,
|
||||||
instance_prompt=str,
|
instance_prompt: str = None,
|
||||||
validation_prompt=str,
|
validation_prompt: str = None,
|
||||||
repo_folder=None,
|
repo_folder=None,
|
||||||
vae_path=None,
|
vae_path=None,
|
||||||
):
|
):
|
||||||
@@ -135,6 +136,14 @@ def save_model_card(
|
|||||||
diffusers_imports_pivotal = ""
|
diffusers_imports_pivotal = ""
|
||||||
diffusers_example_pivotal = ""
|
diffusers_example_pivotal = ""
|
||||||
webui_example_pivotal = ""
|
webui_example_pivotal = ""
|
||||||
|
license = ""
|
||||||
|
if "playground" in base_model:
|
||||||
|
license = """\n
|
||||||
|
## License
|
||||||
|
|
||||||
|
Please adhere to the licensing terms as described [here](https://huggingface.co/playgroundai/playground-v2.5-1024px-aesthetic/blob/main/LICENSE.md).
|
||||||
|
"""
|
||||||
|
|
||||||
if train_text_encoder_ti:
|
if train_text_encoder_ti:
|
||||||
trigger_str = (
|
trigger_str = (
|
||||||
"To trigger image generation of trained concept(or concepts) replace each concept identifier "
|
"To trigger image generation of trained concept(or concepts) replace each concept identifier "
|
||||||
@@ -223,11 +232,75 @@ Pivotal tuning was enabled: {train_text_encoder_ti}.
|
|||||||
|
|
||||||
Special VAE used for training: {vae_path}.
|
Special VAE used for training: {vae_path}.
|
||||||
|
|
||||||
|
{license}
|
||||||
"""
|
"""
|
||||||
with open(os.path.join(repo_folder, "README.md"), "w") as f:
|
with open(os.path.join(repo_folder, "README.md"), "w") as f:
|
||||||
f.write(yaml + model_card)
|
f.write(yaml + model_card)
|
||||||
|
|
||||||
|
|
||||||
|
def log_validation(
|
||||||
|
pipeline,
|
||||||
|
args,
|
||||||
|
accelerator,
|
||||||
|
pipeline_args,
|
||||||
|
epoch,
|
||||||
|
is_final_validation=False,
|
||||||
|
):
|
||||||
|
logger.info(
|
||||||
|
f"Running validation... \n Generating {args.num_validation_images} images with prompt:"
|
||||||
|
f" {args.validation_prompt}."
|
||||||
|
)
|
||||||
|
|
||||||
|
# We train on the simplified learning objective. If we were previously predicting a variance, we need the scheduler to ignore it
|
||||||
|
scheduler_args = {}
|
||||||
|
|
||||||
|
if not args.do_edm_style_training:
|
||||||
|
if "variance_type" in pipeline.scheduler.config:
|
||||||
|
variance_type = pipeline.scheduler.config.variance_type
|
||||||
|
|
||||||
|
if variance_type in ["learned", "learned_range"]:
|
||||||
|
variance_type = "fixed_small"
|
||||||
|
|
||||||
|
scheduler_args["variance_type"] = variance_type
|
||||||
|
|
||||||
|
pipeline.scheduler = DPMSolverMultistepScheduler.from_config(pipeline.scheduler.config, **scheduler_args)
|
||||||
|
|
||||||
|
pipeline = pipeline.to(accelerator.device)
|
||||||
|
pipeline.set_progress_bar_config(disable=True)
|
||||||
|
|
||||||
|
# run inference
|
||||||
|
generator = torch.Generator(device=accelerator.device).manual_seed(args.seed) if args.seed else None
|
||||||
|
# Currently the context determination is a bit hand-wavy. We can improve it in the future if there's a better
|
||||||
|
# way to condition it. Reference: https://github.com/huggingface/diffusers/pull/7126#issuecomment-1968523051
|
||||||
|
if torch.backends.mps.is_available() or "playground" in args.pretrained_model_name_or_path:
|
||||||
|
autocast_ctx = nullcontext()
|
||||||
|
else:
|
||||||
|
autocast_ctx = torch.autocast(accelerator.device.type)
|
||||||
|
|
||||||
|
with autocast_ctx:
|
||||||
|
images = [pipeline(**pipeline_args, generator=generator).images[0] for _ in range(args.num_validation_images)]
|
||||||
|
|
||||||
|
for tracker in accelerator.trackers:
|
||||||
|
phase_name = "test" if is_final_validation else "validation"
|
||||||
|
if tracker.name == "tensorboard":
|
||||||
|
np_images = np.stack([np.asarray(img) for img in images])
|
||||||
|
tracker.writer.add_images(phase_name, np_images, epoch, dataformats="NHWC")
|
||||||
|
if tracker.name == "wandb":
|
||||||
|
tracker.log(
|
||||||
|
{
|
||||||
|
phase_name: [
|
||||||
|
wandb.Image(image, caption=f"{i}: {args.validation_prompt}") for i, image in enumerate(images)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
del pipeline
|
||||||
|
if torch.cuda.is_available():
|
||||||
|
torch.cuda.empty_cache()
|
||||||
|
|
||||||
|
return images
|
||||||
|
|
||||||
|
|
||||||
def import_model_class_from_model_name_or_path(
|
def import_model_class_from_model_name_or_path(
|
||||||
pretrained_model_name_or_path: str, revision: str, subfolder: str = "text_encoder"
|
pretrained_model_name_or_path: str, revision: str, subfolder: str = "text_encoder"
|
||||||
):
|
):
|
||||||
@@ -348,7 +421,7 @@ def parse_args(input_args=None):
|
|||||||
type=str,
|
type=str,
|
||||||
default="TOK",
|
default="TOK",
|
||||||
help="identifier specifying the instance(or instances) as used in instance_prompt, validation prompt, "
|
help="identifier specifying the instance(or instances) as used in instance_prompt, validation prompt, "
|
||||||
"captions - e.g. TOK. To use multiple identifiers, please specify them in a comma seperated string - e.g. "
|
"captions - e.g. TOK. To use multiple identifiers, please specify them in a comma separated string - e.g. "
|
||||||
"'TOK,TOK2,TOK3' etc.",
|
"'TOK,TOK2,TOK3' etc.",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -390,6 +463,7 @@ def parse_args(input_args=None):
|
|||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--do_edm_style_training",
|
"--do_edm_style_training",
|
||||||
|
default=False,
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Flag to conduct training using the EDM formulation as introduced in https://arxiv.org/abs/2206.00364.",
|
help="Flag to conduct training using the EDM formulation as introduced in https://arxiv.org/abs/2206.00364.",
|
||||||
)
|
)
|
||||||
@@ -499,6 +573,13 @@ def parse_args(input_args=None):
|
|||||||
default=1e-4,
|
default=1e-4,
|
||||||
help="Initial learning rate (after the potential warmup period) to use.",
|
help="Initial learning rate (after the potential warmup period) to use.",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--clip_skip",
|
||||||
|
type=int,
|
||||||
|
default=None,
|
||||||
|
help="Number of layers to be skipped from CLIP while computing the prompt embeddings. A value of 1 means that "
|
||||||
|
"the output of the pre-final layer will be used for computing the prompt embeddings.",
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--text_encoder_lr",
|
"--text_encoder_lr",
|
||||||
@@ -571,7 +652,7 @@ def parse_args(input_args=None):
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--optimizer",
|
"--optimizer",
|
||||||
type=str,
|
type=str,
|
||||||
default="adamW",
|
default="AdamW",
|
||||||
help=('The optimizer type to use. Choose between ["AdamW", "prodigy"]'),
|
help=('The optimizer type to use. Choose between ["AdamW", "prodigy"]'),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -591,7 +672,7 @@ def parse_args(input_args=None):
|
|||||||
"--prodigy_beta3",
|
"--prodigy_beta3",
|
||||||
type=float,
|
type=float,
|
||||||
default=None,
|
default=None,
|
||||||
help="coefficients for computing the Prodidy stepsize using running averages. If set to None, "
|
help="coefficients for computing the Prodigy stepsize using running averages. If set to None, "
|
||||||
"uses the value of square root of beta2. Ignored if optimizer is adamW",
|
"uses the value of square root of beta2. Ignored if optimizer is adamW",
|
||||||
)
|
)
|
||||||
parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay")
|
parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay")
|
||||||
@@ -824,7 +905,7 @@ class TokenEmbeddingsHandler:
|
|||||||
# random initialization of new tokens
|
# random initialization of new tokens
|
||||||
std_token_embedding = text_encoder.text_model.embeddings.token_embedding.weight.data.std()
|
std_token_embedding = text_encoder.text_model.embeddings.token_embedding.weight.data.std()
|
||||||
|
|
||||||
print(f"{idx} text encodedr's std_token_embedding: {std_token_embedding}")
|
print(f"{idx} text encoder's std_token_embedding: {std_token_embedding}")
|
||||||
|
|
||||||
text_encoder.text_model.embeddings.token_embedding.weight.data[self.train_ids] = (
|
text_encoder.text_model.embeddings.token_embedding.weight.data[self.train_ids] = (
|
||||||
torch.randn(len(self.train_ids), text_encoder.text_model.config.hidden_size)
|
torch.randn(len(self.train_ids), text_encoder.text_model.config.hidden_size)
|
||||||
@@ -906,11 +987,6 @@ class DreamBoothDataset(Dataset):
|
|||||||
instance_data_root,
|
instance_data_root,
|
||||||
instance_prompt,
|
instance_prompt,
|
||||||
class_prompt,
|
class_prompt,
|
||||||
dataset_name,
|
|
||||||
dataset_config_name,
|
|
||||||
cache_dir,
|
|
||||||
image_column,
|
|
||||||
caption_column,
|
|
||||||
train_text_encoder_ti,
|
train_text_encoder_ti,
|
||||||
class_data_root=None,
|
class_data_root=None,
|
||||||
class_num=None,
|
class_num=None,
|
||||||
@@ -929,7 +1005,7 @@ class DreamBoothDataset(Dataset):
|
|||||||
self.train_text_encoder_ti = train_text_encoder_ti
|
self.train_text_encoder_ti = train_text_encoder_ti
|
||||||
# if --dataset_name is provided or a metadata jsonl file is provided in the local --instance_data directory,
|
# if --dataset_name is provided or a metadata jsonl file is provided in the local --instance_data directory,
|
||||||
# we load the training data using load_dataset
|
# we load the training data using load_dataset
|
||||||
if dataset_name is not None:
|
if args.dataset_name is not None:
|
||||||
try:
|
try:
|
||||||
from datasets import load_dataset
|
from datasets import load_dataset
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -942,25 +1018,26 @@ class DreamBoothDataset(Dataset):
|
|||||||
# See more about loading custom images at
|
# See more about loading custom images at
|
||||||
# https://huggingface.co/docs/datasets/v2.0.0/en/dataset_script
|
# https://huggingface.co/docs/datasets/v2.0.0/en/dataset_script
|
||||||
dataset = load_dataset(
|
dataset = load_dataset(
|
||||||
dataset_name,
|
args.dataset_name,
|
||||||
dataset_config_name,
|
args.dataset_config_name,
|
||||||
cache_dir=cache_dir,
|
cache_dir=args.cache_dir,
|
||||||
)
|
)
|
||||||
# Preprocessing the datasets.
|
# Preprocessing the datasets.
|
||||||
column_names = dataset["train"].column_names
|
column_names = dataset["train"].column_names
|
||||||
|
|
||||||
# 6. Get the column names for input/target.
|
# 6. Get the column names for input/target.
|
||||||
if image_column is None:
|
if args.image_column is None:
|
||||||
image_column = column_names[0]
|
image_column = column_names[0]
|
||||||
logger.info(f"image column defaulting to {image_column}")
|
logger.info(f"image column defaulting to {image_column}")
|
||||||
else:
|
else:
|
||||||
|
image_column = args.image_column
|
||||||
if image_column not in column_names:
|
if image_column not in column_names:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"`--image_column` value '{image_column}' not found in dataset columns. Dataset columns are: {', '.join(column_names)}"
|
f"`--image_column` value '{args.image_column}' not found in dataset columns. Dataset columns are: {', '.join(column_names)}"
|
||||||
)
|
)
|
||||||
instance_images = dataset["train"][image_column]
|
instance_images = dataset["train"][image_column]
|
||||||
|
|
||||||
if caption_column is None:
|
if args.caption_column is None:
|
||||||
logger.info(
|
logger.info(
|
||||||
"No caption column provided, defaulting to instance_prompt for all images. If your dataset "
|
"No caption column provided, defaulting to instance_prompt for all images. If your dataset "
|
||||||
"contains captions/prompts for the images, make sure to specify the "
|
"contains captions/prompts for the images, make sure to specify the "
|
||||||
@@ -968,11 +1045,11 @@ class DreamBoothDataset(Dataset):
|
|||||||
)
|
)
|
||||||
self.custom_instance_prompts = None
|
self.custom_instance_prompts = None
|
||||||
else:
|
else:
|
||||||
if caption_column not in column_names:
|
if args.caption_column not in column_names:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"`--caption_column` value '{caption_column}' not found in dataset columns. Dataset columns are: {', '.join(column_names)}"
|
f"`--caption_column` value '{args.caption_column}' not found in dataset columns. Dataset columns are: {', '.join(column_names)}"
|
||||||
)
|
)
|
||||||
custom_instance_prompts = dataset["train"][caption_column]
|
custom_instance_prompts = dataset["train"][args.caption_column]
|
||||||
# create final list of captions according to --repeats
|
# create final list of captions according to --repeats
|
||||||
self.custom_instance_prompts = []
|
self.custom_instance_prompts = []
|
||||||
for caption in custom_instance_prompts:
|
for caption in custom_instance_prompts:
|
||||||
@@ -1097,7 +1174,7 @@ class DreamBoothDataset(Dataset):
|
|||||||
else:
|
else:
|
||||||
example["instance_prompt"] = self.instance_prompt
|
example["instance_prompt"] = self.instance_prompt
|
||||||
|
|
||||||
else: # costum prompts were provided, but length does not match size of image dataset
|
else: # custom prompts were provided, but length does not match size of image dataset
|
||||||
example["instance_prompt"] = self.instance_prompt
|
example["instance_prompt"] = self.instance_prompt
|
||||||
|
|
||||||
if self.class_data_root:
|
if self.class_data_root:
|
||||||
@@ -1166,7 +1243,7 @@ def tokenize_prompt(tokenizer, prompt, add_special_tokens=False):
|
|||||||
|
|
||||||
|
|
||||||
# Adapted from pipelines.StableDiffusionXLPipeline.encode_prompt
|
# Adapted from pipelines.StableDiffusionXLPipeline.encode_prompt
|
||||||
def encode_prompt(text_encoders, tokenizers, prompt, text_input_ids_list=None):
|
def encode_prompt(text_encoders, tokenizers, prompt, text_input_ids_list=None, clip_skip=None):
|
||||||
prompt_embeds_list = []
|
prompt_embeds_list = []
|
||||||
|
|
||||||
for i, text_encoder in enumerate(text_encoders):
|
for i, text_encoder in enumerate(text_encoders):
|
||||||
@@ -1178,13 +1255,16 @@ def encode_prompt(text_encoders, tokenizers, prompt, text_input_ids_list=None):
|
|||||||
text_input_ids = text_input_ids_list[i]
|
text_input_ids = text_input_ids_list[i]
|
||||||
|
|
||||||
prompt_embeds = text_encoder(
|
prompt_embeds = text_encoder(
|
||||||
text_input_ids.to(text_encoder.device),
|
text_input_ids.to(text_encoder.device), output_hidden_states=True, return_dict=False
|
||||||
output_hidden_states=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# We are only ALWAYS interested in the pooled output of the final text encoder
|
# We are only ALWAYS interested in the pooled output of the final text encoder
|
||||||
pooled_prompt_embeds = prompt_embeds[0]
|
pooled_prompt_embeds = prompt_embeds[0]
|
||||||
prompt_embeds = prompt_embeds.hidden_states[-2]
|
if clip_skip is None:
|
||||||
|
prompt_embeds = prompt_embeds[-1][-2]
|
||||||
|
else:
|
||||||
|
# "2" because SDXL always indexes from the penultimate layer.
|
||||||
|
prompt_embeds = prompt_embeds[-1][-(clip_skip + 2)]
|
||||||
bs_embed, seq_len, _ = prompt_embeds.shape
|
bs_embed, seq_len, _ = prompt_embeds.shape
|
||||||
prompt_embeds = prompt_embeds.view(bs_embed, seq_len, -1)
|
prompt_embeds = prompt_embeds.view(bs_embed, seq_len, -1)
|
||||||
prompt_embeds_list.append(prompt_embeds)
|
prompt_embeds_list.append(prompt_embeds)
|
||||||
@@ -1200,9 +1280,16 @@ def main(args):
|
|||||||
"You cannot use both --report_to=wandb and --hub_token due to a security risk of exposing your token."
|
"You cannot use both --report_to=wandb and --hub_token due to a security risk of exposing your token."
|
||||||
" Please use `huggingface-cli login` to authenticate with the Hub."
|
" Please use `huggingface-cli login` to authenticate with the Hub."
|
||||||
)
|
)
|
||||||
|
|
||||||
if args.do_edm_style_training and args.snr_gamma is not None:
|
if args.do_edm_style_training and args.snr_gamma is not None:
|
||||||
raise ValueError("Min-SNR formulation is not supported when conducting EDM-style training.")
|
raise ValueError("Min-SNR formulation is not supported when conducting EDM-style training.")
|
||||||
|
|
||||||
|
if torch.backends.mps.is_available() and args.mixed_precision == "bf16":
|
||||||
|
# due to pytorch#99272, MPS does not yet support bfloat16.
|
||||||
|
raise ValueError(
|
||||||
|
"Mixed precision training with bfloat16 is not supported on MPS. Please use fp16 (recommended) or fp32 instead."
|
||||||
|
)
|
||||||
|
|
||||||
logging_dir = Path(args.output_dir, args.logging_dir)
|
logging_dir = Path(args.output_dir, args.logging_dir)
|
||||||
|
|
||||||
accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir)
|
accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir)
|
||||||
@@ -1215,10 +1302,13 @@ def main(args):
|
|||||||
kwargs_handlers=[kwargs],
|
kwargs_handlers=[kwargs],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Disable AMP for MPS.
|
||||||
|
if torch.backends.mps.is_available():
|
||||||
|
accelerator.native_amp = False
|
||||||
|
|
||||||
if args.report_to == "wandb":
|
if args.report_to == "wandb":
|
||||||
if not is_wandb_available():
|
if not is_wandb_available():
|
||||||
raise ImportError("Make sure to install wandb if you want to use it for logging during training.")
|
raise ImportError("Make sure to install wandb if you want to use it for logging during training.")
|
||||||
import wandb
|
|
||||||
|
|
||||||
# Make one log on every process with the configuration for debugging.
|
# Make one log on every process with the configuration for debugging.
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@@ -1246,7 +1336,8 @@ def main(args):
|
|||||||
cur_class_images = len(list(class_images_dir.iterdir()))
|
cur_class_images = len(list(class_images_dir.iterdir()))
|
||||||
|
|
||||||
if cur_class_images < args.num_class_images:
|
if cur_class_images < args.num_class_images:
|
||||||
torch_dtype = torch.float16 if accelerator.device.type == "cuda" else torch.float32
|
has_supported_fp16_accelerator = torch.cuda.is_available() or torch.backends.mps.is_available()
|
||||||
|
torch_dtype = torch.float16 if has_supported_fp16_accelerator else torch.float32
|
||||||
if args.prior_generation_precision == "fp32":
|
if args.prior_generation_precision == "fp32":
|
||||||
torch_dtype = torch.float32
|
torch_dtype = torch.float32
|
||||||
elif args.prior_generation_precision == "fp16":
|
elif args.prior_generation_precision == "fp16":
|
||||||
@@ -1404,6 +1495,12 @@ def main(args):
|
|||||||
elif accelerator.mixed_precision == "bf16":
|
elif accelerator.mixed_precision == "bf16":
|
||||||
weight_dtype = torch.bfloat16
|
weight_dtype = torch.bfloat16
|
||||||
|
|
||||||
|
if torch.backends.mps.is_available() and weight_dtype == torch.bfloat16:
|
||||||
|
# due to pytorch#99272, MPS does not yet support bfloat16.
|
||||||
|
raise ValueError(
|
||||||
|
"Mixed precision training with bfloat16 is not supported on MPS. Please use fp16 (recommended) or fp32 instead."
|
||||||
|
)
|
||||||
|
|
||||||
# Move unet, vae and text_encoder to device and cast to weight_dtype
|
# Move unet, vae and text_encoder to device and cast to weight_dtype
|
||||||
unet.to(accelerator.device, dtype=weight_dtype)
|
unet.to(accelerator.device, dtype=weight_dtype)
|
||||||
|
|
||||||
@@ -1508,15 +1605,13 @@ def main(args):
|
|||||||
if isinstance(model, type(unwrap_model(unet))):
|
if isinstance(model, type(unwrap_model(unet))):
|
||||||
unet_lora_layers_to_save = convert_state_dict_to_diffusers(get_peft_model_state_dict(model))
|
unet_lora_layers_to_save = convert_state_dict_to_diffusers(get_peft_model_state_dict(model))
|
||||||
elif isinstance(model, type(unwrap_model(text_encoder_one))):
|
elif isinstance(model, type(unwrap_model(text_encoder_one))):
|
||||||
if args.train_text_encoder:
|
text_encoder_one_lora_layers_to_save = convert_state_dict_to_diffusers(
|
||||||
text_encoder_one_lora_layers_to_save = convert_state_dict_to_diffusers(
|
get_peft_model_state_dict(model)
|
||||||
get_peft_model_state_dict(model)
|
)
|
||||||
)
|
|
||||||
elif isinstance(model, type(unwrap_model(text_encoder_two))):
|
elif isinstance(model, type(unwrap_model(text_encoder_two))):
|
||||||
if args.train_text_encoder:
|
text_encoder_two_lora_layers_to_save = convert_state_dict_to_diffusers(
|
||||||
text_encoder_two_lora_layers_to_save = convert_state_dict_to_diffusers(
|
get_peft_model_state_dict(model)
|
||||||
get_peft_model_state_dict(model)
|
)
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"unexpected save model: {model.__class__}")
|
raise ValueError(f"unexpected save model: {model.__class__}")
|
||||||
|
|
||||||
@@ -1564,6 +1659,7 @@ def main(args):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if args.train_text_encoder:
|
if args.train_text_encoder:
|
||||||
|
# Do we need to call `scale_lora_layers()` here?
|
||||||
_set_state_dict_into_text_encoder(lora_state_dict, prefix="text_encoder.", text_encoder=text_encoder_one_)
|
_set_state_dict_into_text_encoder(lora_state_dict, prefix="text_encoder.", text_encoder=text_encoder_one_)
|
||||||
|
|
||||||
_set_state_dict_into_text_encoder(
|
_set_state_dict_into_text_encoder(
|
||||||
@@ -1578,14 +1674,14 @@ def main(args):
|
|||||||
if args.train_text_encoder:
|
if args.train_text_encoder:
|
||||||
models.extend([text_encoder_one_, text_encoder_two_])
|
models.extend([text_encoder_one_, text_encoder_two_])
|
||||||
# only upcast trainable parameters (LoRA) into fp32
|
# only upcast trainable parameters (LoRA) into fp32
|
||||||
cast_training_params(models)
|
cast_training_params(models)
|
||||||
|
|
||||||
accelerator.register_save_state_pre_hook(save_model_hook)
|
accelerator.register_save_state_pre_hook(save_model_hook)
|
||||||
accelerator.register_load_state_pre_hook(load_model_hook)
|
accelerator.register_load_state_pre_hook(load_model_hook)
|
||||||
|
|
||||||
# Enable TF32 for faster training on Ampere GPUs,
|
# Enable TF32 for faster training on Ampere GPUs,
|
||||||
# cf https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices
|
# cf https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices
|
||||||
if args.allow_tf32:
|
if args.allow_tf32 and torch.cuda.is_available():
|
||||||
torch.backends.cuda.matmul.allow_tf32 = True
|
torch.backends.cuda.matmul.allow_tf32 = True
|
||||||
|
|
||||||
if args.scale_lr:
|
if args.scale_lr:
|
||||||
@@ -1711,12 +1807,7 @@ def main(args):
|
|||||||
instance_data_root=args.instance_data_dir,
|
instance_data_root=args.instance_data_dir,
|
||||||
instance_prompt=args.instance_prompt,
|
instance_prompt=args.instance_prompt,
|
||||||
class_prompt=args.class_prompt,
|
class_prompt=args.class_prompt,
|
||||||
dataset_name=args.dataset_name,
|
|
||||||
dataset_config_name=args.dataset_config_name,
|
|
||||||
cache_dir=args.cache_dir,
|
|
||||||
image_column=args.image_column,
|
|
||||||
train_text_encoder_ti=args.train_text_encoder_ti,
|
train_text_encoder_ti=args.train_text_encoder_ti,
|
||||||
caption_column=args.caption_column,
|
|
||||||
class_data_root=args.class_data_dir if args.with_prior_preservation else None,
|
class_data_root=args.class_data_dir if args.with_prior_preservation else None,
|
||||||
token_abstraction_dict=token_abstraction_dict if args.train_text_encoder_ti else None,
|
token_abstraction_dict=token_abstraction_dict if args.train_text_encoder_ti else None,
|
||||||
class_num=args.num_class_images,
|
class_num=args.num_class_images,
|
||||||
@@ -1740,8 +1831,6 @@ def main(args):
|
|||||||
|
|
||||||
def compute_time_ids(crops_coords_top_left, original_size=None):
|
def compute_time_ids(crops_coords_top_left, original_size=None):
|
||||||
# Adapted from pipeline.StableDiffusionXLPipeline._get_add_time_ids
|
# Adapted from pipeline.StableDiffusionXLPipeline._get_add_time_ids
|
||||||
if original_size is None:
|
|
||||||
original_size = (args.resolution, args.resolution)
|
|
||||||
target_size = (args.resolution, args.resolution)
|
target_size = (args.resolution, args.resolution)
|
||||||
add_time_ids = list(original_size + crops_coords_top_left + target_size)
|
add_time_ids = list(original_size + crops_coords_top_left + target_size)
|
||||||
add_time_ids = torch.tensor([add_time_ids])
|
add_time_ids = torch.tensor([add_time_ids])
|
||||||
@@ -1752,9 +1841,9 @@ def main(args):
|
|||||||
tokenizers = [tokenizer_one, tokenizer_two]
|
tokenizers = [tokenizer_one, tokenizer_two]
|
||||||
text_encoders = [text_encoder_one, text_encoder_two]
|
text_encoders = [text_encoder_one, text_encoder_two]
|
||||||
|
|
||||||
def compute_text_embeddings(prompt, text_encoders, tokenizers):
|
def compute_text_embeddings(prompt, text_encoders, tokenizers, clip_skip):
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
prompt_embeds, pooled_prompt_embeds = encode_prompt(text_encoders, tokenizers, prompt)
|
prompt_embeds, pooled_prompt_embeds = encode_prompt(text_encoders, tokenizers, prompt, clip_skip)
|
||||||
prompt_embeds = prompt_embeds.to(accelerator.device)
|
prompt_embeds = prompt_embeds.to(accelerator.device)
|
||||||
pooled_prompt_embeds = pooled_prompt_embeds.to(accelerator.device)
|
pooled_prompt_embeds = pooled_prompt_embeds.to(accelerator.device)
|
||||||
return prompt_embeds, pooled_prompt_embeds
|
return prompt_embeds, pooled_prompt_embeds
|
||||||
@@ -1764,7 +1853,7 @@ def main(args):
|
|||||||
# the redundant encoding.
|
# the redundant encoding.
|
||||||
if freeze_text_encoder and not train_dataset.custom_instance_prompts:
|
if freeze_text_encoder and not train_dataset.custom_instance_prompts:
|
||||||
instance_prompt_hidden_states, instance_pooled_prompt_embeds = compute_text_embeddings(
|
instance_prompt_hidden_states, instance_pooled_prompt_embeds = compute_text_embeddings(
|
||||||
args.instance_prompt, text_encoders, tokenizers
|
args.instance_prompt, text_encoders, tokenizers, args.clip_skip
|
||||||
)
|
)
|
||||||
|
|
||||||
# Handle class prompt for prior-preservation.
|
# Handle class prompt for prior-preservation.
|
||||||
@@ -1778,7 +1867,8 @@ def main(args):
|
|||||||
if freeze_text_encoder and not train_dataset.custom_instance_prompts:
|
if freeze_text_encoder and not train_dataset.custom_instance_prompts:
|
||||||
del tokenizers, text_encoders
|
del tokenizers, text_encoders
|
||||||
gc.collect()
|
gc.collect()
|
||||||
torch.cuda.empty_cache()
|
if torch.cuda.is_available():
|
||||||
|
torch.cuda.empty_cache()
|
||||||
|
|
||||||
# If custom instance prompts are NOT provided (i.e. the instance prompt is used for all images),
|
# If custom instance prompts are NOT provided (i.e. the instance prompt is used for all images),
|
||||||
# pack the statically computed variables appropriately here. This is so that we don't
|
# pack the statically computed variables appropriately here. This is so that we don't
|
||||||
@@ -1794,7 +1884,7 @@ def main(args):
|
|||||||
if args.with_prior_preservation:
|
if args.with_prior_preservation:
|
||||||
prompt_embeds = torch.cat([prompt_embeds, class_prompt_hidden_states], dim=0)
|
prompt_embeds = torch.cat([prompt_embeds, class_prompt_hidden_states], dim=0)
|
||||||
unet_add_text_embeds = torch.cat([unet_add_text_embeds, class_pooled_prompt_embeds], dim=0)
|
unet_add_text_embeds = torch.cat([unet_add_text_embeds, class_pooled_prompt_embeds], dim=0)
|
||||||
# if we're optmizing the text encoder (both if instance prompt is used for all images or custom prompts) we need to tokenize and encode the
|
# if we're optimizing the text encoder (both if instance prompt is used for all images or custom prompts) we need to tokenize and encode the
|
||||||
# batch prompts on all training steps
|
# batch prompts on all training steps
|
||||||
else:
|
else:
|
||||||
tokens_one = tokenize_prompt(tokenizer_one, args.instance_prompt, add_special_tokens)
|
tokens_one = tokenize_prompt(tokenizer_one, args.instance_prompt, add_special_tokens)
|
||||||
@@ -1820,17 +1910,22 @@ def main(args):
|
|||||||
torch.cuda.empty_cache()
|
torch.cuda.empty_cache()
|
||||||
|
|
||||||
# Scheduler and math around the number of training steps.
|
# Scheduler and math around the number of training steps.
|
||||||
overrode_max_train_steps = False
|
# Check the PR https://github.com/huggingface/diffusers/pull/8312 for detailed explanation.
|
||||||
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
|
num_warmup_steps_for_scheduler = args.lr_warmup_steps * accelerator.num_processes
|
||||||
if args.max_train_steps is None:
|
if args.max_train_steps is None:
|
||||||
args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch
|
len_train_dataloader_after_sharding = math.ceil(len(train_dataloader) / accelerator.num_processes)
|
||||||
overrode_max_train_steps = True
|
num_update_steps_per_epoch = math.ceil(len_train_dataloader_after_sharding / args.gradient_accumulation_steps)
|
||||||
|
num_training_steps_for_scheduler = (
|
||||||
|
args.num_train_epochs * num_update_steps_per_epoch * accelerator.num_processes
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
num_training_steps_for_scheduler = args.max_train_steps * accelerator.num_processes
|
||||||
|
|
||||||
lr_scheduler = get_scheduler(
|
lr_scheduler = get_scheduler(
|
||||||
args.lr_scheduler,
|
args.lr_scheduler,
|
||||||
optimizer=optimizer,
|
optimizer=optimizer,
|
||||||
num_warmup_steps=args.lr_warmup_steps * accelerator.num_processes,
|
num_warmup_steps=num_warmup_steps_for_scheduler,
|
||||||
num_training_steps=args.max_train_steps * accelerator.num_processes,
|
num_training_steps=num_training_steps_for_scheduler,
|
||||||
num_cycles=args.lr_num_cycles,
|
num_cycles=args.lr_num_cycles,
|
||||||
power=args.lr_power,
|
power=args.lr_power,
|
||||||
)
|
)
|
||||||
@@ -1847,8 +1942,14 @@ def main(args):
|
|||||||
|
|
||||||
# We need to recalculate our total training steps as the size of the training dataloader may have changed.
|
# We need to recalculate our total training steps as the size of the training dataloader may have changed.
|
||||||
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
|
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
|
||||||
if overrode_max_train_steps:
|
if args.max_train_steps is None:
|
||||||
args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch
|
args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch
|
||||||
|
if num_training_steps_for_scheduler != args.max_train_steps * accelerator.num_processes:
|
||||||
|
logger.warning(
|
||||||
|
f"The length of the 'train_dataloader' after 'accelerator.prepare' ({len(train_dataloader)}) does not match "
|
||||||
|
f"the expected length ({len_train_dataloader_after_sharding}) when the learning rate scheduler was created. "
|
||||||
|
f"This inconsistency may result in the learning rate scheduler not functioning properly."
|
||||||
|
)
|
||||||
# Afterwards we recalculate our number of training epochs
|
# Afterwards we recalculate our number of training epochs
|
||||||
args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch)
|
args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch)
|
||||||
|
|
||||||
@@ -1946,8 +2047,8 @@ def main(args):
|
|||||||
text_encoder_two.train()
|
text_encoder_two.train()
|
||||||
# set top parameter requires_grad = True for gradient checkpointing works
|
# set top parameter requires_grad = True for gradient checkpointing works
|
||||||
if args.train_text_encoder:
|
if args.train_text_encoder:
|
||||||
text_encoder_one.text_model.embeddings.requires_grad_(True)
|
accelerator.unwrap_model(text_encoder_one).text_model.embeddings.requires_grad_(True)
|
||||||
text_encoder_two.text_model.embeddings.requires_grad_(True)
|
accelerator.unwrap_model(text_encoder_two).text_model.embeddings.requires_grad_(True)
|
||||||
|
|
||||||
for step, batch in enumerate(train_dataloader):
|
for step, batch in enumerate(train_dataloader):
|
||||||
if pivoted:
|
if pivoted:
|
||||||
@@ -1962,7 +2063,7 @@ def main(args):
|
|||||||
if train_dataset.custom_instance_prompts:
|
if train_dataset.custom_instance_prompts:
|
||||||
if freeze_text_encoder:
|
if freeze_text_encoder:
|
||||||
prompt_embeds, unet_add_text_embeds = compute_text_embeddings(
|
prompt_embeds, unet_add_text_embeds = compute_text_embeddings(
|
||||||
prompts, text_encoders, tokenizers
|
prompts, text_encoders, tokenizers, args.clip_skip
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@@ -2040,7 +2141,6 @@ def main(args):
|
|||||||
if freeze_text_encoder:
|
if freeze_text_encoder:
|
||||||
unet_added_conditions = {
|
unet_added_conditions = {
|
||||||
"time_ids": add_time_ids,
|
"time_ids": add_time_ids,
|
||||||
# "time_ids": add_time_ids.repeat(elems_to_repeat_time_ids, 1),
|
|
||||||
"text_embeds": unet_add_text_embeds.repeat(elems_to_repeat_text_embeds, 1),
|
"text_embeds": unet_add_text_embeds.repeat(elems_to_repeat_text_embeds, 1),
|
||||||
}
|
}
|
||||||
prompt_embeds_input = prompt_embeds.repeat(elems_to_repeat_text_embeds, 1, 1)
|
prompt_embeds_input = prompt_embeds.repeat(elems_to_repeat_text_embeds, 1, 1)
|
||||||
@@ -2058,6 +2158,7 @@ def main(args):
|
|||||||
tokenizers=None,
|
tokenizers=None,
|
||||||
prompt=None,
|
prompt=None,
|
||||||
text_input_ids_list=[tokens_one, tokens_two],
|
text_input_ids_list=[tokens_one, tokens_two],
|
||||||
|
clip_skip=args.clip_skip,
|
||||||
)
|
)
|
||||||
unet_added_conditions.update(
|
unet_added_conditions.update(
|
||||||
{"text_embeds": pooled_prompt_embeds.repeat(elems_to_repeat_text_embeds, 1)}
|
{"text_embeds": pooled_prompt_embeds.repeat(elems_to_repeat_text_embeds, 1)}
|
||||||
@@ -2220,10 +2321,6 @@ def main(args):
|
|||||||
|
|
||||||
if accelerator.is_main_process:
|
if accelerator.is_main_process:
|
||||||
if args.validation_prompt is not None and epoch % args.validation_epochs == 0:
|
if args.validation_prompt is not None and epoch % args.validation_epochs == 0:
|
||||||
logger.info(
|
|
||||||
f"Running validation... \n Generating {args.num_validation_images} images with prompt:"
|
|
||||||
f" {args.validation_prompt}."
|
|
||||||
)
|
|
||||||
# create pipeline
|
# create pipeline
|
||||||
if freeze_text_encoder:
|
if freeze_text_encoder:
|
||||||
text_encoder_one = text_encoder_cls_one.from_pretrained(
|
text_encoder_one = text_encoder_cls_one.from_pretrained(
|
||||||
@@ -2250,70 +2347,29 @@ def main(args):
|
|||||||
variant=args.variant,
|
variant=args.variant,
|
||||||
torch_dtype=weight_dtype,
|
torch_dtype=weight_dtype,
|
||||||
)
|
)
|
||||||
|
|
||||||
# We train on the simplified learning objective. If we were previously predicting a variance, we need the scheduler to ignore it
|
|
||||||
scheduler_args = {}
|
|
||||||
|
|
||||||
if not args.do_edm_style_training:
|
|
||||||
if "variance_type" in pipeline.scheduler.config:
|
|
||||||
variance_type = pipeline.scheduler.config.variance_type
|
|
||||||
|
|
||||||
if variance_type in ["learned", "learned_range"]:
|
|
||||||
variance_type = "fixed_small"
|
|
||||||
|
|
||||||
scheduler_args["variance_type"] = variance_type
|
|
||||||
|
|
||||||
pipeline.scheduler = DPMSolverMultistepScheduler.from_config(
|
|
||||||
pipeline.scheduler.config, **scheduler_args
|
|
||||||
)
|
|
||||||
|
|
||||||
pipeline = pipeline.to(accelerator.device)
|
|
||||||
pipeline.set_progress_bar_config(disable=True)
|
|
||||||
|
|
||||||
# run inference
|
|
||||||
generator = torch.Generator(device=accelerator.device).manual_seed(args.seed) if args.seed else None
|
|
||||||
pipeline_args = {"prompt": args.validation_prompt}
|
pipeline_args = {"prompt": args.validation_prompt}
|
||||||
if torch.backends.mps.is_available() or "playground" in args.pretrained_model_name_or_path:
|
|
||||||
autocast_ctx = nullcontext()
|
|
||||||
else:
|
|
||||||
autocast_ctx = torch.autocast(accelerator.device.type)
|
|
||||||
|
|
||||||
with autocast_ctx:
|
images = log_validation(
|
||||||
images = [
|
pipeline,
|
||||||
pipeline(**pipeline_args, generator=generator).images[0]
|
args,
|
||||||
for _ in range(args.num_validation_images)
|
accelerator,
|
||||||
]
|
pipeline_args,
|
||||||
|
epoch,
|
||||||
for tracker in accelerator.trackers:
|
)
|
||||||
if tracker.name == "tensorboard":
|
|
||||||
np_images = np.stack([np.asarray(img) for img in images])
|
|
||||||
tracker.writer.add_images("validation", np_images, epoch, dataformats="NHWC")
|
|
||||||
if tracker.name == "wandb":
|
|
||||||
tracker.log(
|
|
||||||
{
|
|
||||||
"validation": [
|
|
||||||
wandb.Image(image, caption=f"{i}: {args.validation_prompt}")
|
|
||||||
for i, image in enumerate(images)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
del pipeline
|
|
||||||
torch.cuda.empty_cache()
|
|
||||||
|
|
||||||
# Save the lora layers
|
# Save the lora layers
|
||||||
accelerator.wait_for_everyone()
|
accelerator.wait_for_everyone()
|
||||||
if accelerator.is_main_process:
|
if accelerator.is_main_process:
|
||||||
unet = accelerator.unwrap_model(unet)
|
unet = unwrap_model(unet)
|
||||||
unet = unet.to(torch.float32)
|
unet = unet.to(torch.float32)
|
||||||
unet_lora_layers = convert_state_dict_to_diffusers(get_peft_model_state_dict(unet))
|
unet_lora_layers = convert_state_dict_to_diffusers(get_peft_model_state_dict(unet))
|
||||||
|
|
||||||
if args.train_text_encoder:
|
if args.train_text_encoder:
|
||||||
text_encoder_one = accelerator.unwrap_model(text_encoder_one)
|
text_encoder_one = unwrap_model(text_encoder_one)
|
||||||
text_encoder_lora_layers = convert_state_dict_to_diffusers(
|
text_encoder_lora_layers = convert_state_dict_to_diffusers(
|
||||||
get_peft_model_state_dict(text_encoder_one.to(torch.float32))
|
get_peft_model_state_dict(text_encoder_one.to(torch.float32))
|
||||||
)
|
)
|
||||||
text_encoder_two = accelerator.unwrap_model(text_encoder_two)
|
text_encoder_two = unwrap_model(text_encoder_two)
|
||||||
text_encoder_2_lora_layers = convert_state_dict_to_diffusers(
|
text_encoder_2_lora_layers = convert_state_dict_to_diffusers(
|
||||||
get_peft_model_state_dict(text_encoder_two.to(torch.float32))
|
get_peft_model_state_dict(text_encoder_two.to(torch.float32))
|
||||||
)
|
)
|
||||||
@@ -2332,86 +2388,40 @@ def main(args):
|
|||||||
embeddings_path = f"{args.output_dir}/{args.output_dir}_emb.safetensors"
|
embeddings_path = f"{args.output_dir}/{args.output_dir}_emb.safetensors"
|
||||||
embedding_handler.save_embeddings(embeddings_path)
|
embedding_handler.save_embeddings(embeddings_path)
|
||||||
|
|
||||||
|
# Final inference
|
||||||
|
# Load previous pipeline
|
||||||
|
vae = AutoencoderKL.from_pretrained(
|
||||||
|
vae_path,
|
||||||
|
subfolder="vae" if args.pretrained_vae_model_name_or_path is None else None,
|
||||||
|
revision=args.revision,
|
||||||
|
variant=args.variant,
|
||||||
|
torch_dtype=weight_dtype,
|
||||||
|
)
|
||||||
|
pipeline = StableDiffusionXLPipeline.from_pretrained(
|
||||||
|
args.pretrained_model_name_or_path,
|
||||||
|
vae=vae,
|
||||||
|
revision=args.revision,
|
||||||
|
variant=args.variant,
|
||||||
|
torch_dtype=weight_dtype,
|
||||||
|
)
|
||||||
|
|
||||||
|
# load attention processors
|
||||||
|
pipeline.load_lora_weights(args.output_dir)
|
||||||
|
|
||||||
|
# run inference
|
||||||
images = []
|
images = []
|
||||||
if args.validation_prompt and args.num_validation_images > 0:
|
if args.validation_prompt and args.num_validation_images > 0:
|
||||||
# Final inference
|
pipeline_args = {"prompt": args.validation_prompt, "num_inference_steps": 25}
|
||||||
# Load previous pipeline
|
images = log_validation(
|
||||||
vae = AutoencoderKL.from_pretrained(
|
pipeline,
|
||||||
vae_path,
|
args,
|
||||||
subfolder="vae" if args.pretrained_vae_model_name_or_path is None else None,
|
accelerator,
|
||||||
revision=args.revision,
|
pipeline_args,
|
||||||
variant=args.variant,
|
epoch,
|
||||||
torch_dtype=weight_dtype,
|
is_final_validation=True,
|
||||||
)
|
|
||||||
pipeline = StableDiffusionXLPipeline.from_pretrained(
|
|
||||||
args.pretrained_model_name_or_path,
|
|
||||||
vae=vae,
|
|
||||||
revision=args.revision,
|
|
||||||
variant=args.variant,
|
|
||||||
torch_dtype=weight_dtype,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# We train on the simplified learning objective. If we were previously predicting a variance, we need the scheduler to ignore it
|
# Convert to WebUI format
|
||||||
scheduler_args = {}
|
|
||||||
|
|
||||||
if not args.do_edm_style_training:
|
|
||||||
if "variance_type" in pipeline.scheduler.config:
|
|
||||||
variance_type = pipeline.scheduler.config.variance_type
|
|
||||||
|
|
||||||
if variance_type in ["learned", "learned_range"]:
|
|
||||||
variance_type = "fixed_small"
|
|
||||||
|
|
||||||
scheduler_args["variance_type"] = variance_type
|
|
||||||
|
|
||||||
pipeline.scheduler = DPMSolverMultistepScheduler.from_config(
|
|
||||||
pipeline.scheduler.config, **scheduler_args
|
|
||||||
)
|
|
||||||
|
|
||||||
# load attention processors
|
|
||||||
pipeline.load_lora_weights(args.output_dir)
|
|
||||||
|
|
||||||
# load new tokens
|
|
||||||
if args.train_text_encoder_ti:
|
|
||||||
state_dict = load_file(embeddings_path)
|
|
||||||
all_new_tokens = []
|
|
||||||
for key, value in token_abstraction_dict.items():
|
|
||||||
all_new_tokens.extend(value)
|
|
||||||
pipeline.load_textual_inversion(
|
|
||||||
state_dict["clip_l"],
|
|
||||||
token=all_new_tokens,
|
|
||||||
text_encoder=pipeline.text_encoder,
|
|
||||||
tokenizer=pipeline.tokenizer,
|
|
||||||
)
|
|
||||||
pipeline.load_textual_inversion(
|
|
||||||
state_dict["clip_g"],
|
|
||||||
token=all_new_tokens,
|
|
||||||
text_encoder=pipeline.text_encoder_2,
|
|
||||||
tokenizer=pipeline.tokenizer_2,
|
|
||||||
)
|
|
||||||
|
|
||||||
# run inference
|
|
||||||
pipeline = pipeline.to(accelerator.device)
|
|
||||||
generator = torch.Generator(device=accelerator.device).manual_seed(args.seed) if args.seed else None
|
|
||||||
images = [
|
|
||||||
pipeline(args.validation_prompt, num_inference_steps=25, generator=generator).images[0]
|
|
||||||
for _ in range(args.num_validation_images)
|
|
||||||
]
|
|
||||||
|
|
||||||
for tracker in accelerator.trackers:
|
|
||||||
if tracker.name == "tensorboard":
|
|
||||||
np_images = np.stack([np.asarray(img) for img in images])
|
|
||||||
tracker.writer.add_images("test", np_images, epoch, dataformats="NHWC")
|
|
||||||
if tracker.name == "wandb":
|
|
||||||
tracker.log(
|
|
||||||
{
|
|
||||||
"test": [
|
|
||||||
wandb.Image(image, caption=f"{i}: {args.validation_prompt}")
|
|
||||||
for i, image in enumerate(images)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Conver to WebUI format
|
|
||||||
lora_state_dict = load_file(f"{args.output_dir}/pytorch_lora_weights.safetensors")
|
lora_state_dict = load_file(f"{args.output_dir}/pytorch_lora_weights.safetensors")
|
||||||
peft_state_dict = convert_all_state_dict_to_peft(lora_state_dict)
|
peft_state_dict = convert_all_state_dict_to_peft(lora_state_dict)
|
||||||
kohya_state_dict = convert_state_dict_to_kohya(peft_state_dict)
|
kohya_state_dict = convert_state_dict_to_kohya(peft_state_dict)
|
||||||
@@ -2430,6 +2440,7 @@ def main(args):
|
|||||||
repo_folder=args.output_dir,
|
repo_folder=args.output_dir,
|
||||||
vae_path=args.pretrained_vae_model_name_or_path,
|
vae_path=args.pretrained_vae_model_name_or_path,
|
||||||
)
|
)
|
||||||
|
|
||||||
if args.push_to_hub:
|
if args.push_to_hub:
|
||||||
upload_folder(
|
upload_folder(
|
||||||
repo_id=repo_id,
|
repo_id=repo_id,
|
||||||
|
|||||||
+191
-215
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
|||||||
# A SDXL pipeline can take unlimited weighted prompt
|
# A SDXL pipeline can take unlimited weighted prompt
|
||||||
#
|
#
|
||||||
# Author: Andrew Zhu
|
# Author: Andrew Zhu
|
||||||
# Github: https://github.com/xhinker
|
# GitHub: https://github.com/xhinker
|
||||||
# Medium: https://medium.com/@xhinker
|
# Medium: https://medium.com/@xhinker
|
||||||
## -----------------------------------------------------------
|
## -----------------------------------------------------------
|
||||||
|
|
||||||
@@ -24,12 +24,7 @@ from diffusers import DiffusionPipeline, StableDiffusionXLPipeline
|
|||||||
from diffusers.image_processor import PipelineImageInput, VaeImageProcessor
|
from diffusers.image_processor import PipelineImageInput, VaeImageProcessor
|
||||||
from diffusers.loaders import FromSingleFileMixin, IPAdapterMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
from diffusers.loaders import FromSingleFileMixin, IPAdapterMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
||||||
from diffusers.models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
from diffusers.models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
||||||
from diffusers.models.attention_processor import (
|
from diffusers.models.attention_processor import AttnProcessor2_0, XFormersAttnProcessor
|
||||||
AttnProcessor2_0,
|
|
||||||
LoRAAttnProcessor2_0,
|
|
||||||
LoRAXFormersAttnProcessor,
|
|
||||||
XFormersAttnProcessor,
|
|
||||||
)
|
|
||||||
from diffusers.pipelines.pipeline_utils import StableDiffusionMixin
|
from diffusers.pipelines.pipeline_utils import StableDiffusionMixin
|
||||||
from diffusers.pipelines.stable_diffusion_xl.pipeline_output import StableDiffusionXLPipelineOutput
|
from diffusers.pipelines.stable_diffusion_xl.pipeline_output import StableDiffusionXLPipelineOutput
|
||||||
from diffusers.schedulers import KarrasDiffusionSchedulers
|
from diffusers.schedulers import KarrasDiffusionSchedulers
|
||||||
@@ -1292,12 +1287,7 @@ class SDXLLongPromptWeightingPipeline(
|
|||||||
self.vae.to(dtype=torch.float32)
|
self.vae.to(dtype=torch.float32)
|
||||||
use_torch_2_0_or_xformers = isinstance(
|
use_torch_2_0_or_xformers = isinstance(
|
||||||
self.vae.decoder.mid_block.attentions[0].processor,
|
self.vae.decoder.mid_block.attentions[0].processor,
|
||||||
(
|
(AttnProcessor2_0, XFormersAttnProcessor),
|
||||||
AttnProcessor2_0,
|
|
||||||
XFormersAttnProcessor,
|
|
||||||
LoRAXFormersAttnProcessor,
|
|
||||||
LoRAAttnProcessor2_0,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
# if xformers or torch_2_0 is used attention block does not need
|
# if xformers or torch_2_0 is used attention block does not need
|
||||||
# to be in float32 which can save lots of memory
|
# to be in float32 which can save lots of memory
|
||||||
@@ -2175,7 +2165,7 @@ class SDXLLongPromptWeightingPipeline(
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def save_lora_weights(
|
def save_lora_weights(
|
||||||
self,
|
cls,
|
||||||
save_directory: Union[str, os.PathLike],
|
save_directory: Union[str, os.PathLike],
|
||||||
unet_lora_layers: Dict[str, Union[torch.nn.Module, torch.Tensor]] = None,
|
unet_lora_layers: Dict[str, Union[torch.nn.Module, torch.Tensor]] = None,
|
||||||
text_encoder_lora_layers: Dict[str, Union[torch.nn.Module, torch.Tensor]] = None,
|
text_encoder_lora_layers: Dict[str, Union[torch.nn.Module, torch.Tensor]] = None,
|
||||||
@@ -2198,7 +2188,7 @@ class SDXLLongPromptWeightingPipeline(
|
|||||||
state_dict.update(pack_weights(text_encoder_lora_layers, "text_encoder"))
|
state_dict.update(pack_weights(text_encoder_lora_layers, "text_encoder"))
|
||||||
state_dict.update(pack_weights(text_encoder_2_lora_layers, "text_encoder_2"))
|
state_dict.update(pack_weights(text_encoder_2_lora_layers, "text_encoder_2"))
|
||||||
|
|
||||||
self.write_lora_layers(
|
cls.write_lora_layers(
|
||||||
state_dict=state_dict,
|
state_dict=state_dict,
|
||||||
save_directory=save_directory,
|
save_directory=save_directory,
|
||||||
is_main_process=is_main_process,
|
is_main_process=is_main_process,
|
||||||
|
|||||||
@@ -16,12 +16,7 @@ from diffusers.loaders import (
|
|||||||
TextualInversionLoaderMixin,
|
TextualInversionLoaderMixin,
|
||||||
)
|
)
|
||||||
from diffusers.models import AutoencoderKL, UNet2DConditionModel
|
from diffusers.models import AutoencoderKL, UNet2DConditionModel
|
||||||
from diffusers.models.attention_processor import (
|
from diffusers.models.attention_processor import AttnProcessor2_0, XFormersAttnProcessor
|
||||||
AttnProcessor2_0,
|
|
||||||
LoRAAttnProcessor2_0,
|
|
||||||
LoRAXFormersAttnProcessor,
|
|
||||||
XFormersAttnProcessor,
|
|
||||||
)
|
|
||||||
from diffusers.models.lora import adjust_lora_scale_text_encoder
|
from diffusers.models.lora import adjust_lora_scale_text_encoder
|
||||||
from diffusers.pipelines.pipeline_utils import DiffusionPipeline, StableDiffusionMixin
|
from diffusers.pipelines.pipeline_utils import DiffusionPipeline, StableDiffusionMixin
|
||||||
from diffusers.schedulers import KarrasDiffusionSchedulers
|
from diffusers.schedulers import KarrasDiffusionSchedulers
|
||||||
@@ -612,12 +607,7 @@ class DemoFusionSDXLPipeline(
|
|||||||
self.vae.to(dtype=torch.float32)
|
self.vae.to(dtype=torch.float32)
|
||||||
use_torch_2_0_or_xformers = isinstance(
|
use_torch_2_0_or_xformers = isinstance(
|
||||||
self.vae.decoder.mid_block.attentions[0].processor,
|
self.vae.decoder.mid_block.attentions[0].processor,
|
||||||
(
|
(AttnProcessor2_0, XFormersAttnProcessor),
|
||||||
AttnProcessor2_0,
|
|
||||||
XFormersAttnProcessor,
|
|
||||||
LoRAXFormersAttnProcessor,
|
|
||||||
LoRAAttnProcessor2_0,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
# if xformers or torch_2_0 is used attention block does not need
|
# if xformers or torch_2_0 is used attention block does not need
|
||||||
# to be in float32 which can save lots of memory
|
# to be in float32 which can save lots of memory
|
||||||
@@ -805,10 +795,10 @@ class DemoFusionSDXLPipeline(
|
|||||||
Control the strength of dilated sampling. For specific impacts, please refer to Appendix C
|
Control the strength of dilated sampling. For specific impacts, please refer to Appendix C
|
||||||
in the DemoFusion paper.
|
in the DemoFusion paper.
|
||||||
cosine_scale_3 (`float`, defaults to 1):
|
cosine_scale_3 (`float`, defaults to 1):
|
||||||
Control the strength of the gaussion filter. For specific impacts, please refer to Appendix C
|
Control the strength of the gaussian filter. For specific impacts, please refer to Appendix C
|
||||||
in the DemoFusion paper.
|
in the DemoFusion paper.
|
||||||
sigma (`float`, defaults to 1):
|
sigma (`float`, defaults to 1):
|
||||||
The standerd value of the gaussian filter.
|
The standard value of the gaussian filter.
|
||||||
show_image (`bool`, defaults to False):
|
show_image (`bool`, defaults to False):
|
||||||
Determine whether to show intermediate results during generation.
|
Determine whether to show intermediate results during generation.
|
||||||
|
|
||||||
@@ -1349,7 +1339,7 @@ class DemoFusionSDXLPipeline(
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def save_lora_weights(
|
def save_lora_weights(
|
||||||
self,
|
cls,
|
||||||
save_directory: Union[str, os.PathLike],
|
save_directory: Union[str, os.PathLike],
|
||||||
unet_lora_layers: Dict[str, Union[torch.nn.Module, torch.Tensor]] = None,
|
unet_lora_layers: Dict[str, Union[torch.nn.Module, torch.Tensor]] = None,
|
||||||
text_encoder_lora_layers: Dict[str, Union[torch.nn.Module, torch.Tensor]] = None,
|
text_encoder_lora_layers: Dict[str, Union[torch.nn.Module, torch.Tensor]] = None,
|
||||||
@@ -1378,7 +1368,7 @@ class DemoFusionSDXLPipeline(
|
|||||||
state_dict.update(pack_weights(text_encoder_lora_layers, "text_encoder"))
|
state_dict.update(pack_weights(text_encoder_lora_layers, "text_encoder"))
|
||||||
state_dict.update(pack_weights(text_encoder_2_lora_layers, "text_encoder_2"))
|
state_dict.update(pack_weights(text_encoder_2_lora_layers, "text_encoder_2"))
|
||||||
|
|
||||||
self.write_lora_layers(
|
cls.write_lora_layers(
|
||||||
state_dict=state_dict,
|
state_dict=state_dict,
|
||||||
save_directory=save_directory,
|
save_directory=save_directory,
|
||||||
is_main_process=is_main_process,
|
is_main_process=is_main_process,
|
||||||
|
|||||||
@@ -46,8 +46,6 @@ from diffusers.models.attention_processor import (
|
|||||||
Attention,
|
Attention,
|
||||||
AttnProcessor2_0,
|
AttnProcessor2_0,
|
||||||
FusedAttnProcessor2_0,
|
FusedAttnProcessor2_0,
|
||||||
LoRAAttnProcessor2_0,
|
|
||||||
LoRAXFormersAttnProcessor,
|
|
||||||
XFormersAttnProcessor,
|
XFormersAttnProcessor,
|
||||||
)
|
)
|
||||||
from diffusers.models.lora import adjust_lora_scale_text_encoder
|
from diffusers.models.lora import adjust_lora_scale_text_encoder
|
||||||
@@ -1153,8 +1151,6 @@ class StyleAlignedSDXLPipeline(
|
|||||||
(
|
(
|
||||||
AttnProcessor2_0,
|
AttnProcessor2_0,
|
||||||
XFormersAttnProcessor,
|
XFormersAttnProcessor,
|
||||||
LoRAXFormersAttnProcessor,
|
|
||||||
LoRAAttnProcessor2_0,
|
|
||||||
FusedAttnProcessor2_0,
|
FusedAttnProcessor2_0,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,981 @@
|
|||||||
|
# Copyright 2024 Stability AI and The HuggingFace Team. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
from typing import Callable, Dict, List, Optional, Union
|
||||||
|
|
||||||
|
import torch
|
||||||
|
from transformers import (
|
||||||
|
CLIPTextModelWithProjection,
|
||||||
|
CLIPTokenizer,
|
||||||
|
T5EncoderModel,
|
||||||
|
T5TokenizerFast,
|
||||||
|
)
|
||||||
|
|
||||||
|
from diffusers.image_processor import PipelineImageInput, VaeImageProcessor
|
||||||
|
from diffusers.models.autoencoders import AutoencoderKL
|
||||||
|
from diffusers.models.transformers import SD3Transformer2DModel
|
||||||
|
from diffusers.pipelines.pipeline_utils import DiffusionPipeline
|
||||||
|
from diffusers.pipelines.stable_diffusion_3.pipeline_output import StableDiffusion3PipelineOutput
|
||||||
|
from diffusers.schedulers import FlowMatchEulerDiscreteScheduler
|
||||||
|
from diffusers.utils import (
|
||||||
|
is_torch_xla_available,
|
||||||
|
logging,
|
||||||
|
replace_example_docstring,
|
||||||
|
)
|
||||||
|
from diffusers.utils.torch_utils import randn_tensor
|
||||||
|
|
||||||
|
|
||||||
|
if is_torch_xla_available():
|
||||||
|
import torch_xla.core.xla_model as xm
|
||||||
|
|
||||||
|
XLA_AVAILABLE = True
|
||||||
|
else:
|
||||||
|
XLA_AVAILABLE = False
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
||||||
|
|
||||||
|
EXAMPLE_DOC_STRING = """
|
||||||
|
Examples:
|
||||||
|
```py
|
||||||
|
>>> import torch
|
||||||
|
|
||||||
|
>>> from diffusers import AutoPipelineForImage2Image
|
||||||
|
>>> from diffusers.utils import load_image
|
||||||
|
|
||||||
|
>>> device = "cuda"
|
||||||
|
>>> model_id_or_path = "stabilityai/stable-diffusion-3-medium-diffusers"
|
||||||
|
>>> pipe = AutoPipelineForImage2Image.from_pretrained(model_id_or_path, torch_dtype=torch.float16)
|
||||||
|
>>> pipe = pipe.to(device)
|
||||||
|
|
||||||
|
>>> url = "https://raw.githubusercontent.com/CompVis/stable-diffusion/main/assets/stable-samples/img2img/sketch-mountains-input.jpg"
|
||||||
|
>>> init_image = load_image(url).resize((512, 512))
|
||||||
|
|
||||||
|
>>> prompt = "cat wizard, gandalf, lord of the rings, detailed, fantasy, cute, adorable, Pixar, Disney, 8k"
|
||||||
|
|
||||||
|
>>> images = pipe(prompt=prompt, image=init_image, strength=0.95, guidance_scale=7.5).images[0]
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.retrieve_latents
|
||||||
|
def retrieve_latents(
|
||||||
|
encoder_output: torch.Tensor, generator: Optional[torch.Generator] = None, sample_mode: str = "sample"
|
||||||
|
):
|
||||||
|
if hasattr(encoder_output, "latent_dist") and sample_mode == "sample":
|
||||||
|
return encoder_output.latent_dist.sample(generator)
|
||||||
|
elif hasattr(encoder_output, "latent_dist") and sample_mode == "argmax":
|
||||||
|
return encoder_output.latent_dist.mode()
|
||||||
|
elif hasattr(encoder_output, "latents"):
|
||||||
|
return encoder_output.latents
|
||||||
|
else:
|
||||||
|
raise AttributeError("Could not access latents of provided encoder_output")
|
||||||
|
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
|
||||||
|
def retrieve_timesteps(
|
||||||
|
scheduler,
|
||||||
|
num_inference_steps: Optional[int] = None,
|
||||||
|
device: Optional[Union[str, torch.device]] = None,
|
||||||
|
timesteps: Optional[List[int]] = None,
|
||||||
|
sigmas: Optional[List[float]] = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
|
||||||
|
custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
scheduler (`SchedulerMixin`):
|
||||||
|
The scheduler to get timesteps from.
|
||||||
|
num_inference_steps (`int`):
|
||||||
|
The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
|
||||||
|
must be `None`.
|
||||||
|
device (`str` or `torch.device`, *optional*):
|
||||||
|
The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
|
||||||
|
timesteps (`List[int]`, *optional*):
|
||||||
|
Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
|
||||||
|
`num_inference_steps` and `sigmas` must be `None`.
|
||||||
|
sigmas (`List[float]`, *optional*):
|
||||||
|
Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
|
||||||
|
`num_inference_steps` and `timesteps` must be `None`.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
`Tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
|
||||||
|
second element is the number of inference steps.
|
||||||
|
"""
|
||||||
|
if timesteps is not None and sigmas is not None:
|
||||||
|
raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
|
||||||
|
if timesteps is not None:
|
||||||
|
accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
|
||||||
|
if not accepts_timesteps:
|
||||||
|
raise ValueError(
|
||||||
|
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
||||||
|
f" timestep schedules. Please check whether you are using the correct scheduler."
|
||||||
|
)
|
||||||
|
scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
|
||||||
|
timesteps = scheduler.timesteps
|
||||||
|
num_inference_steps = len(timesteps)
|
||||||
|
elif sigmas is not None:
|
||||||
|
accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
|
||||||
|
if not accept_sigmas:
|
||||||
|
raise ValueError(
|
||||||
|
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
||||||
|
f" sigmas schedules. Please check whether you are using the correct scheduler."
|
||||||
|
)
|
||||||
|
scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
|
||||||
|
timesteps = scheduler.timesteps
|
||||||
|
num_inference_steps = len(timesteps)
|
||||||
|
else:
|
||||||
|
scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
|
||||||
|
timesteps = scheduler.timesteps
|
||||||
|
return timesteps, num_inference_steps
|
||||||
|
|
||||||
|
|
||||||
|
class StableDiffusion3DifferentialImg2ImgPipeline(DiffusionPipeline):
|
||||||
|
r"""
|
||||||
|
Args:
|
||||||
|
transformer ([`SD3Transformer2DModel`]):
|
||||||
|
Conditional Transformer (MMDiT) architecture to denoise the encoded image latents.
|
||||||
|
scheduler ([`FlowMatchEulerDiscreteScheduler`]):
|
||||||
|
A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
|
||||||
|
vae ([`AutoencoderKL`]):
|
||||||
|
Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representations.
|
||||||
|
text_encoder ([`CLIPTextModelWithProjection`]):
|
||||||
|
[CLIP](https://huggingface.co/docs/transformers/model_doc/clip#transformers.CLIPTextModelWithProjection),
|
||||||
|
specifically the [clip-vit-large-patch14](https://huggingface.co/openai/clip-vit-large-patch14) variant,
|
||||||
|
with an additional added projection layer that is initialized with a diagonal matrix with the `hidden_size`
|
||||||
|
as its dimension.
|
||||||
|
text_encoder_2 ([`CLIPTextModelWithProjection`]):
|
||||||
|
[CLIP](https://huggingface.co/docs/transformers/model_doc/clip#transformers.CLIPTextModelWithProjection),
|
||||||
|
specifically the
|
||||||
|
[laion/CLIP-ViT-bigG-14-laion2B-39B-b160k](https://huggingface.co/laion/CLIP-ViT-bigG-14-laion2B-39B-b160k)
|
||||||
|
variant.
|
||||||
|
text_encoder_3 ([`T5EncoderModel`]):
|
||||||
|
Frozen text-encoder. Stable Diffusion 3 uses
|
||||||
|
[T5](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5EncoderModel), specifically the
|
||||||
|
[t5-v1_1-xxl](https://huggingface.co/google/t5-v1_1-xxl) variant.
|
||||||
|
tokenizer (`CLIPTokenizer`):
|
||||||
|
Tokenizer of class
|
||||||
|
[CLIPTokenizer](https://huggingface.co/docs/transformers/v4.21.0/en/model_doc/clip#transformers.CLIPTokenizer).
|
||||||
|
tokenizer_2 (`CLIPTokenizer`):
|
||||||
|
Second Tokenizer of class
|
||||||
|
[CLIPTokenizer](https://huggingface.co/docs/transformers/v4.21.0/en/model_doc/clip#transformers.CLIPTokenizer).
|
||||||
|
tokenizer_3 (`T5TokenizerFast`):
|
||||||
|
Tokenizer of class
|
||||||
|
[T5Tokenizer](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5Tokenizer).
|
||||||
|
"""
|
||||||
|
|
||||||
|
model_cpu_offload_seq = "text_encoder->text_encoder_2->text_encoder_3->transformer->vae"
|
||||||
|
_optional_components = []
|
||||||
|
_callback_tensor_inputs = ["latents", "prompt_embeds", "negative_prompt_embeds", "negative_pooled_prompt_embeds"]
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
transformer: SD3Transformer2DModel,
|
||||||
|
scheduler: FlowMatchEulerDiscreteScheduler,
|
||||||
|
vae: AutoencoderKL,
|
||||||
|
text_encoder: CLIPTextModelWithProjection,
|
||||||
|
tokenizer: CLIPTokenizer,
|
||||||
|
text_encoder_2: CLIPTextModelWithProjection,
|
||||||
|
tokenizer_2: CLIPTokenizer,
|
||||||
|
text_encoder_3: T5EncoderModel,
|
||||||
|
tokenizer_3: T5TokenizerFast,
|
||||||
|
):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.register_modules(
|
||||||
|
vae=vae,
|
||||||
|
text_encoder=text_encoder,
|
||||||
|
text_encoder_2=text_encoder_2,
|
||||||
|
text_encoder_3=text_encoder_3,
|
||||||
|
tokenizer=tokenizer,
|
||||||
|
tokenizer_2=tokenizer_2,
|
||||||
|
tokenizer_3=tokenizer_3,
|
||||||
|
transformer=transformer,
|
||||||
|
scheduler=scheduler,
|
||||||
|
)
|
||||||
|
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
|
||||||
|
self.image_processor = VaeImageProcessor(
|
||||||
|
vae_scale_factor=self.vae_scale_factor, vae_latent_channels=self.vae.config.latent_channels
|
||||||
|
)
|
||||||
|
self.mask_processor = VaeImageProcessor(
|
||||||
|
vae_scale_factor=self.vae_scale_factor, do_normalize=False, do_convert_grayscale=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.tokenizer_max_length = self.tokenizer.model_max_length
|
||||||
|
self.default_sample_size = self.transformer.config.sample_size
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion_3.pipeline_stable_diffusion_3.StableDiffusion3Pipeline._get_t5_prompt_embeds
|
||||||
|
def _get_t5_prompt_embeds(
|
||||||
|
self,
|
||||||
|
prompt: Union[str, List[str]] = None,
|
||||||
|
num_images_per_prompt: int = 1,
|
||||||
|
max_sequence_length: int = 256,
|
||||||
|
device: Optional[torch.device] = None,
|
||||||
|
dtype: Optional[torch.dtype] = None,
|
||||||
|
):
|
||||||
|
device = device or self._execution_device
|
||||||
|
dtype = dtype or self.text_encoder.dtype
|
||||||
|
|
||||||
|
prompt = [prompt] if isinstance(prompt, str) else prompt
|
||||||
|
batch_size = len(prompt)
|
||||||
|
|
||||||
|
if self.text_encoder_3 is None:
|
||||||
|
return torch.zeros(
|
||||||
|
(
|
||||||
|
batch_size * num_images_per_prompt,
|
||||||
|
self.tokenizer_max_length,
|
||||||
|
self.transformer.config.joint_attention_dim,
|
||||||
|
),
|
||||||
|
device=device,
|
||||||
|
dtype=dtype,
|
||||||
|
)
|
||||||
|
|
||||||
|
text_inputs = self.tokenizer_3(
|
||||||
|
prompt,
|
||||||
|
padding="max_length",
|
||||||
|
max_length=max_sequence_length,
|
||||||
|
truncation=True,
|
||||||
|
add_special_tokens=True,
|
||||||
|
return_tensors="pt",
|
||||||
|
)
|
||||||
|
text_input_ids = text_inputs.input_ids
|
||||||
|
untruncated_ids = self.tokenizer_3(prompt, padding="longest", return_tensors="pt").input_ids
|
||||||
|
|
||||||
|
if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
|
||||||
|
removed_text = self.tokenizer_3.batch_decode(untruncated_ids[:, self.tokenizer_max_length - 1 : -1])
|
||||||
|
logger.warning(
|
||||||
|
"The following part of your input was truncated because `max_sequence_length` is set to "
|
||||||
|
f" {max_sequence_length} tokens: {removed_text}"
|
||||||
|
)
|
||||||
|
|
||||||
|
prompt_embeds = self.text_encoder_3(text_input_ids.to(device))[0]
|
||||||
|
|
||||||
|
dtype = self.text_encoder_3.dtype
|
||||||
|
prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
|
||||||
|
|
||||||
|
_, seq_len, _ = prompt_embeds.shape
|
||||||
|
|
||||||
|
# duplicate text embeddings and attention mask for each generation per prompt, using mps friendly method
|
||||||
|
prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
|
||||||
|
prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
|
||||||
|
|
||||||
|
return prompt_embeds
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion_3.pipeline_stable_diffusion_3.StableDiffusion3Pipeline._get_clip_prompt_embeds
|
||||||
|
def _get_clip_prompt_embeds(
|
||||||
|
self,
|
||||||
|
prompt: Union[str, List[str]],
|
||||||
|
num_images_per_prompt: int = 1,
|
||||||
|
device: Optional[torch.device] = None,
|
||||||
|
clip_skip: Optional[int] = None,
|
||||||
|
clip_model_index: int = 0,
|
||||||
|
):
|
||||||
|
device = device or self._execution_device
|
||||||
|
|
||||||
|
clip_tokenizers = [self.tokenizer, self.tokenizer_2]
|
||||||
|
clip_text_encoders = [self.text_encoder, self.text_encoder_2]
|
||||||
|
|
||||||
|
tokenizer = clip_tokenizers[clip_model_index]
|
||||||
|
text_encoder = clip_text_encoders[clip_model_index]
|
||||||
|
|
||||||
|
prompt = [prompt] if isinstance(prompt, str) else prompt
|
||||||
|
batch_size = len(prompt)
|
||||||
|
|
||||||
|
text_inputs = tokenizer(
|
||||||
|
prompt,
|
||||||
|
padding="max_length",
|
||||||
|
max_length=self.tokenizer_max_length,
|
||||||
|
truncation=True,
|
||||||
|
return_tensors="pt",
|
||||||
|
)
|
||||||
|
|
||||||
|
text_input_ids = text_inputs.input_ids
|
||||||
|
untruncated_ids = tokenizer(prompt, padding="longest", return_tensors="pt").input_ids
|
||||||
|
if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
|
||||||
|
removed_text = tokenizer.batch_decode(untruncated_ids[:, self.tokenizer_max_length - 1 : -1])
|
||||||
|
logger.warning(
|
||||||
|
"The following part of your input was truncated because CLIP can only handle sequences up to"
|
||||||
|
f" {self.tokenizer_max_length} tokens: {removed_text}"
|
||||||
|
)
|
||||||
|
prompt_embeds = text_encoder(text_input_ids.to(device), output_hidden_states=True)
|
||||||
|
pooled_prompt_embeds = prompt_embeds[0]
|
||||||
|
|
||||||
|
if clip_skip is None:
|
||||||
|
prompt_embeds = prompt_embeds.hidden_states[-2]
|
||||||
|
else:
|
||||||
|
prompt_embeds = prompt_embeds.hidden_states[-(clip_skip + 2)]
|
||||||
|
|
||||||
|
prompt_embeds = prompt_embeds.to(dtype=self.text_encoder.dtype, device=device)
|
||||||
|
|
||||||
|
_, seq_len, _ = prompt_embeds.shape
|
||||||
|
# duplicate text embeddings for each generation per prompt, using mps friendly method
|
||||||
|
prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
|
||||||
|
prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
|
||||||
|
|
||||||
|
pooled_prompt_embeds = pooled_prompt_embeds.repeat(1, num_images_per_prompt, 1)
|
||||||
|
pooled_prompt_embeds = pooled_prompt_embeds.view(batch_size * num_images_per_prompt, -1)
|
||||||
|
|
||||||
|
return prompt_embeds, pooled_prompt_embeds
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion_3.pipeline_stable_diffusion_3.StableDiffusion3Pipeline.encode_prompt
|
||||||
|
def encode_prompt(
|
||||||
|
self,
|
||||||
|
prompt: Union[str, List[str]],
|
||||||
|
prompt_2: Union[str, List[str]],
|
||||||
|
prompt_3: Union[str, List[str]],
|
||||||
|
device: Optional[torch.device] = None,
|
||||||
|
num_images_per_prompt: int = 1,
|
||||||
|
do_classifier_free_guidance: bool = True,
|
||||||
|
negative_prompt: Optional[Union[str, List[str]]] = None,
|
||||||
|
negative_prompt_2: Optional[Union[str, List[str]]] = None,
|
||||||
|
negative_prompt_3: Optional[Union[str, List[str]]] = None,
|
||||||
|
prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
|
negative_prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
|
pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
|
negative_pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
|
clip_skip: Optional[int] = None,
|
||||||
|
max_sequence_length: int = 256,
|
||||||
|
):
|
||||||
|
r"""
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prompt (`str` or `List[str]`, *optional*):
|
||||||
|
prompt to be encoded
|
||||||
|
prompt_2 (`str` or `List[str]`, *optional*):
|
||||||
|
The prompt or prompts to be sent to the `tokenizer_2` and `text_encoder_2`. If not defined, `prompt` is
|
||||||
|
used in all text-encoders
|
||||||
|
prompt_3 (`str` or `List[str]`, *optional*):
|
||||||
|
The prompt or prompts to be sent to the `tokenizer_3` and `text_encoder_3`. If not defined, `prompt` is
|
||||||
|
used in all text-encoders
|
||||||
|
device: (`torch.device`):
|
||||||
|
torch device
|
||||||
|
num_images_per_prompt (`int`):
|
||||||
|
number of images that should be generated per prompt
|
||||||
|
do_classifier_free_guidance (`bool`):
|
||||||
|
whether to use classifier free guidance or not
|
||||||
|
negative_prompt (`str` or `List[str]`, *optional*):
|
||||||
|
The prompt or prompts not to guide the image generation. If not defined, one has to pass
|
||||||
|
`negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
|
||||||
|
less than `1`).
|
||||||
|
negative_prompt_2 (`str` or `List[str]`, *optional*):
|
||||||
|
The prompt or prompts not to guide the image generation to be sent to `tokenizer_2` and
|
||||||
|
`text_encoder_2`. If not defined, `negative_prompt` is used in all the text-encoders.
|
||||||
|
negative_prompt_2 (`str` or `List[str]`, *optional*):
|
||||||
|
The prompt or prompts not to guide the image generation to be sent to `tokenizer_3` and
|
||||||
|
`text_encoder_3`. If not defined, `negative_prompt` is used in both text-encoders
|
||||||
|
prompt_embeds (`torch.FloatTensor`, *optional*):
|
||||||
|
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
|
||||||
|
provided, text embeddings will be generated from `prompt` input argument.
|
||||||
|
negative_prompt_embeds (`torch.FloatTensor`, *optional*):
|
||||||
|
Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
|
||||||
|
weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
|
||||||
|
argument.
|
||||||
|
pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
|
||||||
|
Pre-generated pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting.
|
||||||
|
If not provided, pooled text embeddings will be generated from `prompt` input argument.
|
||||||
|
negative_pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
|
||||||
|
Pre-generated negative pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
|
||||||
|
weighting. If not provided, pooled negative_prompt_embeds will be generated from `negative_prompt`
|
||||||
|
input argument.
|
||||||
|
clip_skip (`int`, *optional*):
|
||||||
|
Number of layers to be skipped from CLIP while computing the prompt embeddings. A value of 1 means that
|
||||||
|
the output of the pre-final layer will be used for computing the prompt embeddings.
|
||||||
|
"""
|
||||||
|
device = device or self._execution_device
|
||||||
|
|
||||||
|
prompt = [prompt] if isinstance(prompt, str) else prompt
|
||||||
|
if prompt is not None:
|
||||||
|
batch_size = len(prompt)
|
||||||
|
else:
|
||||||
|
batch_size = prompt_embeds.shape[0]
|
||||||
|
|
||||||
|
if prompt_embeds is None:
|
||||||
|
prompt_2 = prompt_2 or prompt
|
||||||
|
prompt_2 = [prompt_2] if isinstance(prompt_2, str) else prompt_2
|
||||||
|
|
||||||
|
prompt_3 = prompt_3 or prompt
|
||||||
|
prompt_3 = [prompt_3] if isinstance(prompt_3, str) else prompt_3
|
||||||
|
|
||||||
|
prompt_embed, pooled_prompt_embed = self._get_clip_prompt_embeds(
|
||||||
|
prompt=prompt,
|
||||||
|
device=device,
|
||||||
|
num_images_per_prompt=num_images_per_prompt,
|
||||||
|
clip_skip=clip_skip,
|
||||||
|
clip_model_index=0,
|
||||||
|
)
|
||||||
|
prompt_2_embed, pooled_prompt_2_embed = self._get_clip_prompt_embeds(
|
||||||
|
prompt=prompt_2,
|
||||||
|
device=device,
|
||||||
|
num_images_per_prompt=num_images_per_prompt,
|
||||||
|
clip_skip=clip_skip,
|
||||||
|
clip_model_index=1,
|
||||||
|
)
|
||||||
|
clip_prompt_embeds = torch.cat([prompt_embed, prompt_2_embed], dim=-1)
|
||||||
|
|
||||||
|
t5_prompt_embed = self._get_t5_prompt_embeds(
|
||||||
|
prompt=prompt_3,
|
||||||
|
num_images_per_prompt=num_images_per_prompt,
|
||||||
|
max_sequence_length=max_sequence_length,
|
||||||
|
device=device,
|
||||||
|
)
|
||||||
|
|
||||||
|
clip_prompt_embeds = torch.nn.functional.pad(
|
||||||
|
clip_prompt_embeds, (0, t5_prompt_embed.shape[-1] - clip_prompt_embeds.shape[-1])
|
||||||
|
)
|
||||||
|
|
||||||
|
prompt_embeds = torch.cat([clip_prompt_embeds, t5_prompt_embed], dim=-2)
|
||||||
|
pooled_prompt_embeds = torch.cat([pooled_prompt_embed, pooled_prompt_2_embed], dim=-1)
|
||||||
|
|
||||||
|
if do_classifier_free_guidance and negative_prompt_embeds is None:
|
||||||
|
negative_prompt = negative_prompt or ""
|
||||||
|
negative_prompt_2 = negative_prompt_2 or negative_prompt
|
||||||
|
negative_prompt_3 = negative_prompt_3 or negative_prompt
|
||||||
|
|
||||||
|
# normalize str to list
|
||||||
|
negative_prompt = batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
|
||||||
|
negative_prompt_2 = (
|
||||||
|
batch_size * [negative_prompt_2] if isinstance(negative_prompt_2, str) else negative_prompt_2
|
||||||
|
)
|
||||||
|
negative_prompt_3 = (
|
||||||
|
batch_size * [negative_prompt_3] if isinstance(negative_prompt_3, str) else negative_prompt_3
|
||||||
|
)
|
||||||
|
|
||||||
|
if prompt is not None and type(prompt) is not type(negative_prompt):
|
||||||
|
raise TypeError(
|
||||||
|
f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
|
||||||
|
f" {type(prompt)}."
|
||||||
|
)
|
||||||
|
elif batch_size != len(negative_prompt):
|
||||||
|
raise ValueError(
|
||||||
|
f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
|
||||||
|
f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
|
||||||
|
" the batch size of `prompt`."
|
||||||
|
)
|
||||||
|
|
||||||
|
negative_prompt_embed, negative_pooled_prompt_embed = self._get_clip_prompt_embeds(
|
||||||
|
negative_prompt,
|
||||||
|
device=device,
|
||||||
|
num_images_per_prompt=num_images_per_prompt,
|
||||||
|
clip_skip=None,
|
||||||
|
clip_model_index=0,
|
||||||
|
)
|
||||||
|
negative_prompt_2_embed, negative_pooled_prompt_2_embed = self._get_clip_prompt_embeds(
|
||||||
|
negative_prompt_2,
|
||||||
|
device=device,
|
||||||
|
num_images_per_prompt=num_images_per_prompt,
|
||||||
|
clip_skip=None,
|
||||||
|
clip_model_index=1,
|
||||||
|
)
|
||||||
|
negative_clip_prompt_embeds = torch.cat([negative_prompt_embed, negative_prompt_2_embed], dim=-1)
|
||||||
|
|
||||||
|
t5_negative_prompt_embed = self._get_t5_prompt_embeds(
|
||||||
|
prompt=negative_prompt_3,
|
||||||
|
num_images_per_prompt=num_images_per_prompt,
|
||||||
|
max_sequence_length=max_sequence_length,
|
||||||
|
device=device,
|
||||||
|
)
|
||||||
|
|
||||||
|
negative_clip_prompt_embeds = torch.nn.functional.pad(
|
||||||
|
negative_clip_prompt_embeds,
|
||||||
|
(0, t5_negative_prompt_embed.shape[-1] - negative_clip_prompt_embeds.shape[-1]),
|
||||||
|
)
|
||||||
|
|
||||||
|
negative_prompt_embeds = torch.cat([negative_clip_prompt_embeds, t5_negative_prompt_embed], dim=-2)
|
||||||
|
negative_pooled_prompt_embeds = torch.cat(
|
||||||
|
[negative_pooled_prompt_embed, negative_pooled_prompt_2_embed], dim=-1
|
||||||
|
)
|
||||||
|
|
||||||
|
return prompt_embeds, negative_prompt_embeds, pooled_prompt_embeds, negative_pooled_prompt_embeds
|
||||||
|
|
||||||
|
def check_inputs(
|
||||||
|
self,
|
||||||
|
prompt,
|
||||||
|
prompt_2,
|
||||||
|
prompt_3,
|
||||||
|
strength,
|
||||||
|
negative_prompt=None,
|
||||||
|
negative_prompt_2=None,
|
||||||
|
negative_prompt_3=None,
|
||||||
|
prompt_embeds=None,
|
||||||
|
negative_prompt_embeds=None,
|
||||||
|
pooled_prompt_embeds=None,
|
||||||
|
negative_pooled_prompt_embeds=None,
|
||||||
|
callback_on_step_end_tensor_inputs=None,
|
||||||
|
max_sequence_length=None,
|
||||||
|
):
|
||||||
|
if strength < 0 or strength > 1:
|
||||||
|
raise ValueError(f"The value of strength should in [0.0, 1.0] but is {strength}")
|
||||||
|
|
||||||
|
if callback_on_step_end_tensor_inputs is not None and not all(
|
||||||
|
k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
|
||||||
|
):
|
||||||
|
raise ValueError(
|
||||||
|
f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if prompt is not None and prompt_embeds is not None:
|
||||||
|
raise ValueError(
|
||||||
|
f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
|
||||||
|
" only forward one of the two."
|
||||||
|
)
|
||||||
|
elif prompt_2 is not None and prompt_embeds is not None:
|
||||||
|
raise ValueError(
|
||||||
|
f"Cannot forward both `prompt_2`: {prompt_2} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
|
||||||
|
" only forward one of the two."
|
||||||
|
)
|
||||||
|
elif prompt_3 is not None and prompt_embeds is not None:
|
||||||
|
raise ValueError(
|
||||||
|
f"Cannot forward both `prompt_3`: {prompt_2} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
|
||||||
|
" only forward one of the two."
|
||||||
|
)
|
||||||
|
elif prompt is None and prompt_embeds is None:
|
||||||
|
raise ValueError(
|
||||||
|
"Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
|
||||||
|
)
|
||||||
|
elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
|
||||||
|
raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
|
||||||
|
elif prompt_2 is not None and (not isinstance(prompt_2, str) and not isinstance(prompt_2, list)):
|
||||||
|
raise ValueError(f"`prompt_2` has to be of type `str` or `list` but is {type(prompt_2)}")
|
||||||
|
elif prompt_3 is not None and (not isinstance(prompt_3, str) and not isinstance(prompt_3, list)):
|
||||||
|
raise ValueError(f"`prompt_3` has to be of type `str` or `list` but is {type(prompt_3)}")
|
||||||
|
|
||||||
|
if negative_prompt is not None and negative_prompt_embeds is not None:
|
||||||
|
raise ValueError(
|
||||||
|
f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:"
|
||||||
|
f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
|
||||||
|
)
|
||||||
|
elif negative_prompt_2 is not None and negative_prompt_embeds is not None:
|
||||||
|
raise ValueError(
|
||||||
|
f"Cannot forward both `negative_prompt_2`: {negative_prompt_2} and `negative_prompt_embeds`:"
|
||||||
|
f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
|
||||||
|
)
|
||||||
|
elif negative_prompt_3 is not None and negative_prompt_embeds is not None:
|
||||||
|
raise ValueError(
|
||||||
|
f"Cannot forward both `negative_prompt_3`: {negative_prompt_3} and `negative_prompt_embeds`:"
|
||||||
|
f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
|
||||||
|
)
|
||||||
|
|
||||||
|
if prompt_embeds is not None and negative_prompt_embeds is not None:
|
||||||
|
if prompt_embeds.shape != negative_prompt_embeds.shape:
|
||||||
|
raise ValueError(
|
||||||
|
"`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but"
|
||||||
|
f" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`"
|
||||||
|
f" {negative_prompt_embeds.shape}."
|
||||||
|
)
|
||||||
|
|
||||||
|
if prompt_embeds is not None and pooled_prompt_embeds is None:
|
||||||
|
raise ValueError(
|
||||||
|
"If `prompt_embeds` are provided, `pooled_prompt_embeds` also have to be passed. Make sure to generate `pooled_prompt_embeds` from the same text encoder that was used to generate `prompt_embeds`."
|
||||||
|
)
|
||||||
|
|
||||||
|
if negative_prompt_embeds is not None and negative_pooled_prompt_embeds is None:
|
||||||
|
raise ValueError(
|
||||||
|
"If `negative_prompt_embeds` are provided, `negative_pooled_prompt_embeds` also have to be passed. Make sure to generate `negative_pooled_prompt_embeds` from the same text encoder that was used to generate `negative_prompt_embeds`."
|
||||||
|
)
|
||||||
|
|
||||||
|
if max_sequence_length is not None and max_sequence_length > 512:
|
||||||
|
raise ValueError(f"`max_sequence_length` cannot be greater than 512 but is {max_sequence_length}")
|
||||||
|
|
||||||
|
def get_timesteps(self, num_inference_steps, strength, device):
|
||||||
|
# get the original timestep using init_timestep
|
||||||
|
init_timestep = min(num_inference_steps * strength, num_inference_steps)
|
||||||
|
|
||||||
|
t_start = int(max(num_inference_steps - init_timestep, 0))
|
||||||
|
timesteps = self.scheduler.timesteps[t_start * self.scheduler.order :]
|
||||||
|
|
||||||
|
return timesteps, num_inference_steps - t_start
|
||||||
|
|
||||||
|
def prepare_latents(
|
||||||
|
self, batch_size, num_channels_latents, height, width, image, timestep, dtype, device, generator=None
|
||||||
|
):
|
||||||
|
shape = (
|
||||||
|
batch_size,
|
||||||
|
num_channels_latents,
|
||||||
|
int(height) // self.vae_scale_factor,
|
||||||
|
int(width) // self.vae_scale_factor,
|
||||||
|
)
|
||||||
|
|
||||||
|
image = image.to(device=device, dtype=dtype)
|
||||||
|
|
||||||
|
if isinstance(generator, list) and len(generator) != batch_size:
|
||||||
|
raise ValueError(
|
||||||
|
f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
|
||||||
|
f" size of {batch_size}. Make sure the batch size matches the length of the generators."
|
||||||
|
)
|
||||||
|
elif isinstance(generator, list):
|
||||||
|
init_latents = [
|
||||||
|
retrieve_latents(self.vae.encode(image[i : i + 1]), generator=generator[i]) for i in range(batch_size)
|
||||||
|
]
|
||||||
|
init_latents = torch.cat(init_latents, dim=0)
|
||||||
|
else:
|
||||||
|
init_latents = retrieve_latents(self.vae.encode(image), generator=generator)
|
||||||
|
|
||||||
|
init_latents = (init_latents - self.vae.config.shift_factor) * self.vae.config.scaling_factor
|
||||||
|
|
||||||
|
if batch_size > init_latents.shape[0] and batch_size % init_latents.shape[0] == 0:
|
||||||
|
# expand init_latents for batch_size
|
||||||
|
additional_image_per_prompt = batch_size // init_latents.shape[0]
|
||||||
|
init_latents = torch.cat([init_latents] * additional_image_per_prompt, dim=0)
|
||||||
|
elif batch_size > init_latents.shape[0] and batch_size % init_latents.shape[0] != 0:
|
||||||
|
raise ValueError(
|
||||||
|
f"Cannot duplicate `image` of batch size {init_latents.shape[0]} to {batch_size} text prompts."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
init_latents = torch.cat([init_latents], dim=0)
|
||||||
|
|
||||||
|
shape = init_latents.shape
|
||||||
|
noise = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
|
||||||
|
|
||||||
|
init_latents = self.scheduler.scale_noise(init_latents, timestep, noise)
|
||||||
|
latents = init_latents.to(device=device, dtype=dtype)
|
||||||
|
|
||||||
|
return latents
|
||||||
|
|
||||||
|
@property
|
||||||
|
def guidance_scale(self):
|
||||||
|
return self._guidance_scale
|
||||||
|
|
||||||
|
@property
|
||||||
|
def clip_skip(self):
|
||||||
|
return self._clip_skip
|
||||||
|
|
||||||
|
# here `guidance_scale` is defined analog to the guidance weight `w` of equation (2)
|
||||||
|
# of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1`
|
||||||
|
# corresponds to doing no classifier free guidance.
|
||||||
|
@property
|
||||||
|
def do_classifier_free_guidance(self):
|
||||||
|
return self._guidance_scale > 1
|
||||||
|
|
||||||
|
@property
|
||||||
|
def num_timesteps(self):
|
||||||
|
return self._num_timesteps
|
||||||
|
|
||||||
|
@property
|
||||||
|
def interrupt(self):
|
||||||
|
return self._interrupt
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
|
@replace_example_docstring(EXAMPLE_DOC_STRING)
|
||||||
|
def __call__(
|
||||||
|
self,
|
||||||
|
prompt: Union[str, List[str]] = None,
|
||||||
|
prompt_2: Optional[Union[str, List[str]]] = None,
|
||||||
|
prompt_3: Optional[Union[str, List[str]]] = None,
|
||||||
|
height: Optional[int] = None,
|
||||||
|
width: Optional[int] = None,
|
||||||
|
image: PipelineImageInput = None,
|
||||||
|
strength: float = 0.6,
|
||||||
|
num_inference_steps: int = 50,
|
||||||
|
timesteps: List[int] = None,
|
||||||
|
guidance_scale: float = 7.0,
|
||||||
|
negative_prompt: Optional[Union[str, List[str]]] = None,
|
||||||
|
negative_prompt_2: Optional[Union[str, List[str]]] = None,
|
||||||
|
negative_prompt_3: Optional[Union[str, List[str]]] = None,
|
||||||
|
num_images_per_prompt: Optional[int] = 1,
|
||||||
|
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
|
||||||
|
latents: Optional[torch.FloatTensor] = None,
|
||||||
|
prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
|
negative_prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
|
pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
|
negative_pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
|
output_type: Optional[str] = "pil",
|
||||||
|
return_dict: bool = True,
|
||||||
|
clip_skip: Optional[int] = None,
|
||||||
|
callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
|
||||||
|
callback_on_step_end_tensor_inputs: List[str] = ["latents"],
|
||||||
|
max_sequence_length: int = 256,
|
||||||
|
map: PipelineImageInput = None,
|
||||||
|
):
|
||||||
|
r"""
|
||||||
|
Function invoked when calling the pipeline for generation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prompt (`str` or `List[str]`, *optional*):
|
||||||
|
The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
|
||||||
|
instead.
|
||||||
|
prompt_2 (`str` or `List[str]`, *optional*):
|
||||||
|
The prompt or prompts to be sent to `tokenizer_2` and `text_encoder_2`. If not defined, `prompt` is
|
||||||
|
will be used instead
|
||||||
|
prompt_3 (`str` or `List[str]`, *optional*):
|
||||||
|
The prompt or prompts to be sent to `tokenizer_3` and `text_encoder_3`. If not defined, `prompt` is
|
||||||
|
will be used instead
|
||||||
|
height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
|
||||||
|
The height in pixels of the generated image. This is set to 1024 by default for the best results.
|
||||||
|
width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
|
||||||
|
The width in pixels of the generated image. This is set to 1024 by default for the best results.
|
||||||
|
num_inference_steps (`int`, *optional*, defaults to 50):
|
||||||
|
The number of denoising steps. More denoising steps usually lead to a higher quality image at the
|
||||||
|
expense of slower inference.
|
||||||
|
timesteps (`List[int]`, *optional*):
|
||||||
|
Custom timesteps to use for the denoising process with schedulers which support a `timesteps` argument
|
||||||
|
in their `set_timesteps` method. If not defined, the default behavior when `num_inference_steps` is
|
||||||
|
passed will be used. Must be in descending order.
|
||||||
|
guidance_scale (`float`, *optional*, defaults to 5.0):
|
||||||
|
Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://arxiv.org/abs/2207.12598).
|
||||||
|
`guidance_scale` is defined as `w` of equation 2. of [Imagen
|
||||||
|
Paper](https://arxiv.org/pdf/2205.11487.pdf). Guidance scale is enabled by setting `guidance_scale >
|
||||||
|
1`. Higher guidance scale encourages to generate images that are closely linked to the text `prompt`,
|
||||||
|
usually at the expense of lower image quality.
|
||||||
|
negative_prompt (`str` or `List[str]`, *optional*):
|
||||||
|
The prompt or prompts not to guide the image generation. If not defined, one has to pass
|
||||||
|
`negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
|
||||||
|
less than `1`).
|
||||||
|
negative_prompt_2 (`str` or `List[str]`, *optional*):
|
||||||
|
The prompt or prompts not to guide the image generation to be sent to `tokenizer_2` and
|
||||||
|
`text_encoder_2`. If not defined, `negative_prompt` is used instead
|
||||||
|
negative_prompt_3 (`str` or `List[str]`, *optional*):
|
||||||
|
The prompt or prompts not to guide the image generation to be sent to `tokenizer_3` and
|
||||||
|
`text_encoder_3`. If not defined, `negative_prompt` is used instead
|
||||||
|
num_images_per_prompt (`int`, *optional*, defaults to 1):
|
||||||
|
The number of images to generate per prompt.
|
||||||
|
generator (`torch.Generator` or `List[torch.Generator]`, *optional*):
|
||||||
|
One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)
|
||||||
|
to make generation deterministic.
|
||||||
|
latents (`torch.FloatTensor`, *optional*):
|
||||||
|
Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image
|
||||||
|
generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
|
||||||
|
tensor will ge generated by sampling using the supplied random `generator`.
|
||||||
|
prompt_embeds (`torch.FloatTensor`, *optional*):
|
||||||
|
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
|
||||||
|
provided, text embeddings will be generated from `prompt` input argument.
|
||||||
|
negative_prompt_embeds (`torch.FloatTensor`, *optional*):
|
||||||
|
Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
|
||||||
|
weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
|
||||||
|
argument.
|
||||||
|
pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
|
||||||
|
Pre-generated pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting.
|
||||||
|
If not provided, pooled text embeddings will be generated from `prompt` input argument.
|
||||||
|
negative_pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
|
||||||
|
Pre-generated negative pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
|
||||||
|
weighting. If not provided, pooled negative_prompt_embeds will be generated from `negative_prompt`
|
||||||
|
input argument.
|
||||||
|
output_type (`str`, *optional*, defaults to `"pil"`):
|
||||||
|
The output format of the generate image. Choose between
|
||||||
|
[PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`.
|
||||||
|
return_dict (`bool`, *optional*, defaults to `True`):
|
||||||
|
Whether or not to return a [`~pipelines.stable_diffusion_xl.StableDiffusionXLPipelineOutput`] instead
|
||||||
|
of a plain tuple.
|
||||||
|
callback_on_step_end (`Callable`, *optional*):
|
||||||
|
A function that calls at the end of each denoising steps during the inference. The function is called
|
||||||
|
with the following arguments: `callback_on_step_end(self: DiffusionPipeline, step: int, timestep: int,
|
||||||
|
callback_kwargs: Dict)`. `callback_kwargs` will include a list of all tensors as specified by
|
||||||
|
`callback_on_step_end_tensor_inputs`.
|
||||||
|
callback_on_step_end_tensor_inputs (`List`, *optional*):
|
||||||
|
The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
|
||||||
|
will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
|
||||||
|
`._callback_tensor_inputs` attribute of your pipeline class.
|
||||||
|
max_sequence_length (`int` defaults to 256): Maximum sequence length to use with the `prompt`.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
[`~pipelines.stable_diffusion_3.StableDiffusion3PipelineOutput`] or `tuple`:
|
||||||
|
[`~pipelines.stable_diffusion_3.StableDiffusion3PipelineOutput`] if `return_dict` is True, otherwise a
|
||||||
|
`tuple`. When returning a tuple, the first element is a list with the generated images.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 0. Default height and width
|
||||||
|
height = height or self.default_sample_size * self.vae_scale_factor
|
||||||
|
width = width or self.default_sample_size * self.vae_scale_factor
|
||||||
|
|
||||||
|
# 1. Check inputs. Raise error if not correct
|
||||||
|
self.check_inputs(
|
||||||
|
prompt,
|
||||||
|
prompt_2,
|
||||||
|
prompt_3,
|
||||||
|
strength,
|
||||||
|
negative_prompt=negative_prompt,
|
||||||
|
negative_prompt_2=negative_prompt_2,
|
||||||
|
negative_prompt_3=negative_prompt_3,
|
||||||
|
prompt_embeds=prompt_embeds,
|
||||||
|
negative_prompt_embeds=negative_prompt_embeds,
|
||||||
|
pooled_prompt_embeds=pooled_prompt_embeds,
|
||||||
|
negative_pooled_prompt_embeds=negative_pooled_prompt_embeds,
|
||||||
|
callback_on_step_end_tensor_inputs=callback_on_step_end_tensor_inputs,
|
||||||
|
max_sequence_length=max_sequence_length,
|
||||||
|
)
|
||||||
|
|
||||||
|
self._guidance_scale = guidance_scale
|
||||||
|
self._clip_skip = clip_skip
|
||||||
|
self._interrupt = False
|
||||||
|
|
||||||
|
# 2. Define call parameters
|
||||||
|
if prompt is not None and isinstance(prompt, str):
|
||||||
|
batch_size = 1
|
||||||
|
elif prompt is not None and isinstance(prompt, list):
|
||||||
|
batch_size = len(prompt)
|
||||||
|
else:
|
||||||
|
batch_size = prompt_embeds.shape[0]
|
||||||
|
|
||||||
|
device = self._execution_device
|
||||||
|
|
||||||
|
(
|
||||||
|
prompt_embeds,
|
||||||
|
negative_prompt_embeds,
|
||||||
|
pooled_prompt_embeds,
|
||||||
|
negative_pooled_prompt_embeds,
|
||||||
|
) = self.encode_prompt(
|
||||||
|
prompt=prompt,
|
||||||
|
prompt_2=prompt_2,
|
||||||
|
prompt_3=prompt_3,
|
||||||
|
negative_prompt=negative_prompt,
|
||||||
|
negative_prompt_2=negative_prompt_2,
|
||||||
|
negative_prompt_3=negative_prompt_3,
|
||||||
|
do_classifier_free_guidance=self.do_classifier_free_guidance,
|
||||||
|
prompt_embeds=prompt_embeds,
|
||||||
|
negative_prompt_embeds=negative_prompt_embeds,
|
||||||
|
pooled_prompt_embeds=pooled_prompt_embeds,
|
||||||
|
negative_pooled_prompt_embeds=negative_pooled_prompt_embeds,
|
||||||
|
device=device,
|
||||||
|
clip_skip=self.clip_skip,
|
||||||
|
num_images_per_prompt=num_images_per_prompt,
|
||||||
|
max_sequence_length=max_sequence_length,
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.do_classifier_free_guidance:
|
||||||
|
prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds], dim=0)
|
||||||
|
pooled_prompt_embeds = torch.cat([negative_pooled_prompt_embeds, pooled_prompt_embeds], dim=0)
|
||||||
|
|
||||||
|
# 3. Preprocess image
|
||||||
|
init_image = self.image_processor.preprocess(image, height=height, width=width).to(dtype=torch.float32)
|
||||||
|
|
||||||
|
map = self.mask_processor.preprocess(
|
||||||
|
map, height=height // self.vae_scale_factor, width=width // self.vae_scale_factor
|
||||||
|
).to(device)
|
||||||
|
|
||||||
|
# 4. Prepare timesteps
|
||||||
|
timesteps, num_inference_steps = retrieve_timesteps(self.scheduler, num_inference_steps, device, timesteps)
|
||||||
|
|
||||||
|
# begin diff diff change
|
||||||
|
total_time_steps = num_inference_steps
|
||||||
|
# end diff diff change
|
||||||
|
|
||||||
|
timesteps, num_inference_steps = self.get_timesteps(num_inference_steps, strength, device)
|
||||||
|
latent_timestep = timesteps[:1].repeat(batch_size * num_images_per_prompt)
|
||||||
|
|
||||||
|
# 5. Prepare latent variables
|
||||||
|
num_channels_latents = self.transformer.config.in_channels
|
||||||
|
if latents is None:
|
||||||
|
latents = self.prepare_latents(
|
||||||
|
batch_size * num_images_per_prompt,
|
||||||
|
num_channels_latents,
|
||||||
|
height,
|
||||||
|
width,
|
||||||
|
init_image,
|
||||||
|
latent_timestep,
|
||||||
|
prompt_embeds.dtype,
|
||||||
|
device,
|
||||||
|
generator,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 6. Denoising loop
|
||||||
|
num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
|
||||||
|
self._num_timesteps = len(timesteps)
|
||||||
|
|
||||||
|
# preparations for diff diff
|
||||||
|
original_with_noise = self.prepare_latents(
|
||||||
|
batch_size * num_images_per_prompt,
|
||||||
|
num_channels_latents,
|
||||||
|
height,
|
||||||
|
width,
|
||||||
|
init_image,
|
||||||
|
timesteps,
|
||||||
|
prompt_embeds.dtype,
|
||||||
|
device,
|
||||||
|
generator,
|
||||||
|
)
|
||||||
|
thresholds = torch.arange(total_time_steps, dtype=map.dtype) / total_time_steps
|
||||||
|
thresholds = thresholds.unsqueeze(1).unsqueeze(1).to(device)
|
||||||
|
masks = map.squeeze() > thresholds
|
||||||
|
# end diff diff preparations
|
||||||
|
|
||||||
|
with self.progress_bar(total=num_inference_steps) as progress_bar:
|
||||||
|
for i, t in enumerate(timesteps):
|
||||||
|
if self.interrupt:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# diff diff
|
||||||
|
if i == 0:
|
||||||
|
latents = original_with_noise[:1]
|
||||||
|
else:
|
||||||
|
mask = masks[i].unsqueeze(0).to(latents.dtype)
|
||||||
|
mask = mask.unsqueeze(1) # fit shape
|
||||||
|
latents = original_with_noise[i] * mask + latents * (1 - mask)
|
||||||
|
# end diff diff
|
||||||
|
|
||||||
|
# expand the latents if we are doing classifier free guidance
|
||||||
|
latent_model_input = torch.cat([latents] * 2) if self.do_classifier_free_guidance else latents
|
||||||
|
# broadcast to batch dimension in a way that's compatible with ONNX/Core ML
|
||||||
|
timestep = t.expand(latent_model_input.shape[0])
|
||||||
|
|
||||||
|
noise_pred = self.transformer(
|
||||||
|
hidden_states=latent_model_input,
|
||||||
|
timestep=timestep,
|
||||||
|
encoder_hidden_states=prompt_embeds,
|
||||||
|
pooled_projections=pooled_prompt_embeds,
|
||||||
|
return_dict=False,
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
# perform guidance
|
||||||
|
if self.do_classifier_free_guidance:
|
||||||
|
noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
|
||||||
|
noise_pred = noise_pred_uncond + self.guidance_scale * (noise_pred_text - noise_pred_uncond)
|
||||||
|
|
||||||
|
# compute the previous noisy sample x_t -> x_t-1
|
||||||
|
latents_dtype = latents.dtype
|
||||||
|
latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
|
||||||
|
|
||||||
|
if latents.dtype != latents_dtype:
|
||||||
|
if torch.backends.mps.is_available():
|
||||||
|
# some platforms (eg. apple mps) misbehave due to a pytorch bug: https://github.com/pytorch/pytorch/pull/99272
|
||||||
|
latents = latents.to(latents_dtype)
|
||||||
|
|
||||||
|
if callback_on_step_end is not None:
|
||||||
|
callback_kwargs = {}
|
||||||
|
for k in callback_on_step_end_tensor_inputs:
|
||||||
|
callback_kwargs[k] = locals()[k]
|
||||||
|
callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
|
||||||
|
|
||||||
|
latents = callback_outputs.pop("latents", latents)
|
||||||
|
prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
|
||||||
|
negative_prompt_embeds = callback_outputs.pop("negative_prompt_embeds", negative_prompt_embeds)
|
||||||
|
negative_pooled_prompt_embeds = callback_outputs.pop(
|
||||||
|
"negative_pooled_prompt_embeds", negative_pooled_prompt_embeds
|
||||||
|
)
|
||||||
|
|
||||||
|
if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
|
||||||
|
progress_bar.update()
|
||||||
|
|
||||||
|
if XLA_AVAILABLE:
|
||||||
|
xm.mark_step()
|
||||||
|
|
||||||
|
if output_type == "latent":
|
||||||
|
image = latents
|
||||||
|
|
||||||
|
else:
|
||||||
|
latents = (latents / self.vae.config.scaling_factor) + self.vae.config.shift_factor
|
||||||
|
|
||||||
|
image = self.vae.decode(latents, return_dict=False)[0]
|
||||||
|
image = self.image_processor.postprocess(image, output_type=output_type)
|
||||||
|
|
||||||
|
# Offload all models
|
||||||
|
self.maybe_free_model_hooks()
|
||||||
|
|
||||||
|
if not return_dict:
|
||||||
|
return (image,)
|
||||||
|
|
||||||
|
return StableDiffusion3PipelineOutput(images=image)
|
||||||
@@ -25,12 +25,7 @@ from transformers import CLIPTextModel, CLIPTextModelWithProjection, CLIPTokeniz
|
|||||||
from diffusers.image_processor import PipelineImageInput, VaeImageProcessor
|
from diffusers.image_processor import PipelineImageInput, VaeImageProcessor
|
||||||
from diffusers.loaders import FromSingleFileMixin, StableDiffusionXLLoraLoaderMixin, TextualInversionLoaderMixin
|
from diffusers.loaders import FromSingleFileMixin, StableDiffusionXLLoraLoaderMixin, TextualInversionLoaderMixin
|
||||||
from diffusers.models import AutoencoderKL, ControlNetModel, MultiAdapter, T2IAdapter, UNet2DConditionModel
|
from diffusers.models import AutoencoderKL, ControlNetModel, MultiAdapter, T2IAdapter, UNet2DConditionModel
|
||||||
from diffusers.models.attention_processor import (
|
from diffusers.models.attention_processor import AttnProcessor2_0, XFormersAttnProcessor
|
||||||
AttnProcessor2_0,
|
|
||||||
LoRAAttnProcessor2_0,
|
|
||||||
LoRAXFormersAttnProcessor,
|
|
||||||
XFormersAttnProcessor,
|
|
||||||
)
|
|
||||||
from diffusers.models.lora import adjust_lora_scale_text_encoder
|
from diffusers.models.lora import adjust_lora_scale_text_encoder
|
||||||
from diffusers.pipelines.controlnet.multicontrolnet import MultiControlNetModel
|
from diffusers.pipelines.controlnet.multicontrolnet import MultiControlNetModel
|
||||||
from diffusers.pipelines.pipeline_utils import DiffusionPipeline, StableDiffusionMixin
|
from diffusers.pipelines.pipeline_utils import DiffusionPipeline, StableDiffusionMixin
|
||||||
@@ -797,12 +792,7 @@ class StableDiffusionXLControlNetAdapterPipeline(
|
|||||||
self.vae.to(dtype=torch.float32)
|
self.vae.to(dtype=torch.float32)
|
||||||
use_torch_2_0_or_xformers = isinstance(
|
use_torch_2_0_or_xformers = isinstance(
|
||||||
self.vae.decoder.mid_block.attentions[0].processor,
|
self.vae.decoder.mid_block.attentions[0].processor,
|
||||||
(
|
(AttnProcessor2_0, XFormersAttnProcessor),
|
||||||
AttnProcessor2_0,
|
|
||||||
XFormersAttnProcessor,
|
|
||||||
LoRAXFormersAttnProcessor,
|
|
||||||
LoRAAttnProcessor2_0,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
# if xformers or torch_2_0 is used attention block does not need
|
# if xformers or torch_2_0 is used attention block does not need
|
||||||
# to be in float32 which can save lots of memory
|
# to be in float32 which can save lots of memory
|
||||||
|
|||||||
@@ -44,12 +44,7 @@ from diffusers.models import (
|
|||||||
T2IAdapter,
|
T2IAdapter,
|
||||||
UNet2DConditionModel,
|
UNet2DConditionModel,
|
||||||
)
|
)
|
||||||
from diffusers.models.attention_processor import (
|
from diffusers.models.attention_processor import AttnProcessor2_0, XFormersAttnProcessor
|
||||||
AttnProcessor2_0,
|
|
||||||
LoRAAttnProcessor2_0,
|
|
||||||
LoRAXFormersAttnProcessor,
|
|
||||||
XFormersAttnProcessor,
|
|
||||||
)
|
|
||||||
from diffusers.models.lora import adjust_lora_scale_text_encoder
|
from diffusers.models.lora import adjust_lora_scale_text_encoder
|
||||||
from diffusers.pipelines.controlnet.multicontrolnet import MultiControlNetModel
|
from diffusers.pipelines.controlnet.multicontrolnet import MultiControlNetModel
|
||||||
from diffusers.pipelines.pipeline_utils import StableDiffusionMixin
|
from diffusers.pipelines.pipeline_utils import StableDiffusionMixin
|
||||||
@@ -1135,12 +1130,7 @@ class StableDiffusionXLControlNetAdapterInpaintPipeline(
|
|||||||
self.vae.to(dtype=torch.float32)
|
self.vae.to(dtype=torch.float32)
|
||||||
use_torch_2_0_or_xformers = isinstance(
|
use_torch_2_0_or_xformers = isinstance(
|
||||||
self.vae.decoder.mid_block.attentions[0].processor,
|
self.vae.decoder.mid_block.attentions[0].processor,
|
||||||
(
|
(AttnProcessor2_0, XFormersAttnProcessor),
|
||||||
AttnProcessor2_0,
|
|
||||||
XFormersAttnProcessor,
|
|
||||||
LoRAXFormersAttnProcessor,
|
|
||||||
LoRAAttnProcessor2_0,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
# if xformers or torch_2_0 is used attention block does not need
|
# if xformers or torch_2_0 is used attention block does not need
|
||||||
# to be in float32 which can save lots of memory
|
# to be in float32 which can save lots of memory
|
||||||
|
|||||||
@@ -37,8 +37,6 @@ from diffusers.loaders import (
|
|||||||
from diffusers.models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
from diffusers.models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
||||||
from diffusers.models.attention_processor import (
|
from diffusers.models.attention_processor import (
|
||||||
AttnProcessor2_0,
|
AttnProcessor2_0,
|
||||||
LoRAAttnProcessor2_0,
|
|
||||||
LoRAXFormersAttnProcessor,
|
|
||||||
XFormersAttnProcessor,
|
XFormersAttnProcessor,
|
||||||
)
|
)
|
||||||
from diffusers.models.lora import adjust_lora_scale_text_encoder
|
from diffusers.models.lora import adjust_lora_scale_text_encoder
|
||||||
@@ -854,8 +852,6 @@ class StableDiffusionXLDifferentialImg2ImgPipeline(
|
|||||||
(
|
(
|
||||||
AttnProcessor2_0,
|
AttnProcessor2_0,
|
||||||
XFormersAttnProcessor,
|
XFormersAttnProcessor,
|
||||||
LoRAXFormersAttnProcessor,
|
|
||||||
LoRAAttnProcessor2_0,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
# if xformers or torch_2_0 is used attention block does not need
|
# if xformers or torch_2_0 is used attention block does not need
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ from diffusers.loaders import (
|
|||||||
from diffusers.models import AutoencoderKL, UNet2DConditionModel
|
from diffusers.models import AutoencoderKL, UNet2DConditionModel
|
||||||
from diffusers.models.attention_processor import (
|
from diffusers.models.attention_processor import (
|
||||||
AttnProcessor2_0,
|
AttnProcessor2_0,
|
||||||
LoRAAttnProcessor2_0,
|
|
||||||
LoRAXFormersAttnProcessor,
|
|
||||||
XFormersAttnProcessor,
|
XFormersAttnProcessor,
|
||||||
)
|
)
|
||||||
from diffusers.models.lora import adjust_lora_scale_text_encoder
|
from diffusers.models.lora import adjust_lora_scale_text_encoder
|
||||||
@@ -662,8 +660,6 @@ class StableDiffusionXLPipelineIpex(
|
|||||||
(
|
(
|
||||||
AttnProcessor2_0,
|
AttnProcessor2_0,
|
||||||
XFormersAttnProcessor,
|
XFormersAttnProcessor,
|
||||||
LoRAXFormersAttnProcessor,
|
|
||||||
LoRAAttnProcessor2_0,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
# if xformers or torch_2_0 is used attention block does not need
|
# if xformers or torch_2_0 is used attention block does not need
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ class StableDiffusionTiledUpscalePipeline(StableDiffusionUpscalePipeline):
|
|||||||
def main():
|
def main():
|
||||||
# Run a demo
|
# Run a demo
|
||||||
model_id = "stabilityai/stable-diffusion-x4-upscaler"
|
model_id = "stabilityai/stable-diffusion-x4-upscaler"
|
||||||
pipe = StableDiffusionTiledUpscalePipeline.from_pretrained(model_id, revision="fp16", torch_dtype=torch.float16)
|
pipe = StableDiffusionTiledUpscalePipeline.from_pretrained(model_id, variant="fp16", torch_dtype=torch.float16)
|
||||||
pipe = pipe.to("cuda")
|
pipe = pipe.to("cuda")
|
||||||
image = Image.open("../../docs/source/imgs/diffusers_library.jpg")
|
image = Image.open("../../docs/source/imgs/diffusers_library.jpg")
|
||||||
|
|
||||||
|
|||||||
@@ -1111,11 +1111,16 @@ def main(args):
|
|||||||
|
|
||||||
# 15. LR Scheduler creation
|
# 15. LR Scheduler creation
|
||||||
# Scheduler and math around the number of training steps.
|
# Scheduler and math around the number of training steps.
|
||||||
overrode_max_train_steps = False
|
# Check the PR https://github.com/huggingface/diffusers/pull/8312 for detailed explanation.
|
||||||
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
|
num_warmup_steps_for_scheduler = args.lr_warmup_steps * accelerator.num_processes
|
||||||
if args.max_train_steps is None:
|
if args.max_train_steps is None:
|
||||||
args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch
|
len_train_dataloader_after_sharding = math.ceil(len(train_dataloader) / accelerator.num_processes)
|
||||||
overrode_max_train_steps = True
|
num_update_steps_per_epoch = math.ceil(len_train_dataloader_after_sharding / args.gradient_accumulation_steps)
|
||||||
|
num_training_steps_for_scheduler = (
|
||||||
|
args.num_train_epochs * num_update_steps_per_epoch * accelerator.num_processes
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
num_training_steps_for_scheduler = args.max_train_steps * accelerator.num_processes
|
||||||
|
|
||||||
if args.scale_lr:
|
if args.scale_lr:
|
||||||
args.learning_rate = (
|
args.learning_rate = (
|
||||||
@@ -1130,8 +1135,8 @@ def main(args):
|
|||||||
lr_scheduler = get_scheduler(
|
lr_scheduler = get_scheduler(
|
||||||
args.lr_scheduler,
|
args.lr_scheduler,
|
||||||
optimizer=optimizer,
|
optimizer=optimizer,
|
||||||
num_warmup_steps=args.lr_warmup_steps * accelerator.num_processes,
|
num_warmup_steps=num_warmup_steps_for_scheduler,
|
||||||
num_training_steps=args.max_train_steps * accelerator.num_processes,
|
num_training_steps=num_training_steps_for_scheduler,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 16. Prepare for training
|
# 16. Prepare for training
|
||||||
@@ -1142,8 +1147,14 @@ def main(args):
|
|||||||
|
|
||||||
# We need to recalculate our total training steps as the size of the training dataloader may have changed.
|
# We need to recalculate our total training steps as the size of the training dataloader may have changed.
|
||||||
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
|
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
|
||||||
if overrode_max_train_steps:
|
if args.max_train_steps is None:
|
||||||
args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch
|
args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch
|
||||||
|
if num_training_steps_for_scheduler != args.max_train_steps * accelerator.num_processes:
|
||||||
|
logger.warning(
|
||||||
|
f"The length of the 'train_dataloader' after 'accelerator.prepare' ({len(train_dataloader)}) does not match "
|
||||||
|
f"the expected length ({len_train_dataloader_after_sharding}) when the learning rate scheduler was created. "
|
||||||
|
f"This inconsistency may result in the learning rate scheduler not functioning properly."
|
||||||
|
)
|
||||||
# Afterwards we recalculate our number of training epochs
|
# Afterwards we recalculate our number of training epochs
|
||||||
args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch)
|
args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch)
|
||||||
|
|
||||||
|
|||||||
@@ -1088,17 +1088,22 @@ def main(args):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Scheduler and math around the number of training steps.
|
# Scheduler and math around the number of training steps.
|
||||||
overrode_max_train_steps = False
|
# Check the PR https://github.com/huggingface/diffusers/pull/8312 for detailed explanation.
|
||||||
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
|
num_warmup_steps_for_scheduler = args.lr_warmup_steps * accelerator.num_processes
|
||||||
if args.max_train_steps is None:
|
if args.max_train_steps is None:
|
||||||
args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch
|
len_train_dataloader_after_sharding = math.ceil(len(train_dataloader) / accelerator.num_processes)
|
||||||
overrode_max_train_steps = True
|
num_update_steps_per_epoch = math.ceil(len_train_dataloader_after_sharding / args.gradient_accumulation_steps)
|
||||||
|
num_training_steps_for_scheduler = (
|
||||||
|
args.num_train_epochs * num_update_steps_per_epoch * accelerator.num_processes
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
num_training_steps_for_scheduler = args.max_train_steps * accelerator.num_processes
|
||||||
|
|
||||||
lr_scheduler = get_scheduler(
|
lr_scheduler = get_scheduler(
|
||||||
args.lr_scheduler,
|
args.lr_scheduler,
|
||||||
optimizer=optimizer,
|
optimizer=optimizer,
|
||||||
num_warmup_steps=args.lr_warmup_steps * accelerator.num_processes,
|
num_warmup_steps=num_warmup_steps_for_scheduler,
|
||||||
num_training_steps=args.max_train_steps * accelerator.num_processes,
|
num_training_steps=num_training_steps_for_scheduler,
|
||||||
num_cycles=args.lr_num_cycles,
|
num_cycles=args.lr_num_cycles,
|
||||||
power=args.lr_power,
|
power=args.lr_power,
|
||||||
)
|
)
|
||||||
@@ -1110,8 +1115,14 @@ def main(args):
|
|||||||
|
|
||||||
# We need to recalculate our total training steps as the size of the training dataloader may have changed.
|
# We need to recalculate our total training steps as the size of the training dataloader may have changed.
|
||||||
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
|
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
|
||||||
if overrode_max_train_steps:
|
if args.max_train_steps is None:
|
||||||
args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch
|
args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch
|
||||||
|
if num_training_steps_for_scheduler != args.max_train_steps * accelerator.num_processes:
|
||||||
|
logger.warning(
|
||||||
|
f"The length of the 'train_dataloader' after 'accelerator.prepare' ({len(train_dataloader)}) does not match "
|
||||||
|
f"the expected length ({len_train_dataloader_after_sharding}) when the learning rate scheduler was created. "
|
||||||
|
f"This inconsistency may result in the learning rate scheduler not functioning properly."
|
||||||
|
)
|
||||||
# Afterwards we recalculate our number of training epochs
|
# Afterwards we recalculate our number of training epochs
|
||||||
args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch)
|
args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch)
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ The `train_dreambooth_sd3.py` script shows how to implement the training procedu
|
|||||||
huggingface-cli login
|
huggingface-cli login
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This will also allow us to push the trained model parameters to the Hugging Face Hub platform.
|
||||||
|
|
||||||
## Running locally with PyTorch
|
## Running locally with PyTorch
|
||||||
|
|
||||||
### Installing the dependencies
|
### Installing the dependencies
|
||||||
@@ -116,6 +118,8 @@ To better track our training experiments, we're using the following flags in the
|
|||||||
|
|
||||||
[LoRA](https://huggingface.co/docs/peft/conceptual_guides/adapter#low-rank-adaptation-lora) is a popular parameter-efficient fine-tuning technique that allows you to achieve full-finetuning like performance but with a fraction of learnable parameters.
|
[LoRA](https://huggingface.co/docs/peft/conceptual_guides/adapter#low-rank-adaptation-lora) is a popular parameter-efficient fine-tuning technique that allows you to achieve full-finetuning like performance but with a fraction of learnable parameters.
|
||||||
|
|
||||||
|
Note also that we use PEFT library as backend for LoRA training, make sure to have `peft>=0.6.0` installed in your environment.
|
||||||
|
|
||||||
To perform DreamBooth with LoRA, run:
|
To perform DreamBooth with LoRA, run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -142,3 +146,41 @@ accelerate launch train_dreambooth_lora_sd3.py \
|
|||||||
--seed="0" \
|
--seed="0" \
|
||||||
--push_to_hub
|
--push_to_hub
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Text Encoder Training
|
||||||
|
Alongside the transformer, LoRA fine-tuning of the CLIP text encoders is now also supported.
|
||||||
|
To do so, just specify `--train_text_encoder` while launching training. Please keep the following points in mind:
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> SD3 has three text encoders (CLIP L/14, OpenCLIP bigG/14, and T5-v1.1-XXL).
|
||||||
|
By enabling `--train_text_encoder`, LoRA fine-tuning of both **CLIP encoders** is performed. At the moment, T5 fine-tuning is not supported and weights remain frozen when text encoder training is enabled.
|
||||||
|
|
||||||
|
To perform DreamBooth LoRA with text-encoder training, run:
|
||||||
|
```bash
|
||||||
|
export MODEL_NAME="stabilityai/stable-diffusion-3-medium-diffusers"
|
||||||
|
export OUTPUT_DIR="trained-sd3-lora"
|
||||||
|
|
||||||
|
accelerate launch train_dreambooth_lora_sd3.py \
|
||||||
|
--pretrained_model_name_or_path=$MODEL_NAME \
|
||||||
|
--output_dir=$OUTPUT_DIR \
|
||||||
|
--dataset_name="Norod78/Yarn-art-style" \
|
||||||
|
--instance_prompt="a photo of TOK yarn art dog" \
|
||||||
|
--resolution=1024 \
|
||||||
|
--train_batch_size=1 \
|
||||||
|
--train_text_encoder\
|
||||||
|
--gradient_accumulation_steps=1 \
|
||||||
|
--optimizer="prodigy"\
|
||||||
|
--learning_rate=1.0 \
|
||||||
|
--text_encoder_lr=1.0 \
|
||||||
|
--report_to="wandb" \
|
||||||
|
--lr_scheduler="constant" \
|
||||||
|
--lr_warmup_steps=0 \
|
||||||
|
--max_train_steps=1500 \
|
||||||
|
--rank=32 \
|
||||||
|
--seed="0" \
|
||||||
|
--push_to_hub
|
||||||
|
```
|
||||||
|
|
||||||
|
## Other notes
|
||||||
|
|
||||||
|
We default to the "logit_normal" weighting scheme for the loss following the SD3 paper. Thanks to @bghira for helping us discover that for other weighting schemes supported from the training script, training may incur numerical instabilities.
|
||||||
@@ -261,7 +261,7 @@ The authors found that by using DoRA, both the learning capacity and training st
|
|||||||
**Usage**
|
**Usage**
|
||||||
1. To use DoRA you need to upgrade the installation of `peft`:
|
1. To use DoRA you need to upgrade the installation of `peft`:
|
||||||
```bash
|
```bash
|
||||||
pip install-U peft
|
pip install -U peft
|
||||||
```
|
```
|
||||||
2. Enable DoRA training by adding this flag
|
2. Enable DoRA training by adding this flag
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -0,0 +1,165 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
# Copyright 2024 HuggingFace Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
import safetensors
|
||||||
|
|
||||||
|
|
||||||
|
sys.path.append("..")
|
||||||
|
from test_examples_utils import ExamplesTestsAccelerate, run_command # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
stream_handler = logging.StreamHandler(sys.stdout)
|
||||||
|
logger.addHandler(stream_handler)
|
||||||
|
|
||||||
|
|
||||||
|
class DreamBoothLoRASD3(ExamplesTestsAccelerate):
|
||||||
|
instance_data_dir = "docs/source/en/imgs"
|
||||||
|
instance_prompt = "photo"
|
||||||
|
pretrained_model_name_or_path = "hf-internal-testing/tiny-sd3-pipe"
|
||||||
|
script_path = "examples/dreambooth/train_dreambooth_lora_sd3.py"
|
||||||
|
|
||||||
|
def test_dreambooth_lora_sd3(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
test_args = f"""
|
||||||
|
{self.script_path}
|
||||||
|
--pretrained_model_name_or_path {self.pretrained_model_name_or_path}
|
||||||
|
--instance_data_dir {self.instance_data_dir}
|
||||||
|
--instance_prompt {self.instance_prompt}
|
||||||
|
--resolution 64
|
||||||
|
--train_batch_size 1
|
||||||
|
--gradient_accumulation_steps 1
|
||||||
|
--max_train_steps 2
|
||||||
|
--learning_rate 5.0e-04
|
||||||
|
--scale_lr
|
||||||
|
--lr_scheduler constant
|
||||||
|
--lr_warmup_steps 0
|
||||||
|
--output_dir {tmpdir}
|
||||||
|
""".split()
|
||||||
|
|
||||||
|
run_command(self._launch_args + test_args)
|
||||||
|
# save_pretrained smoke test
|
||||||
|
self.assertTrue(os.path.isfile(os.path.join(tmpdir, "pytorch_lora_weights.safetensors")))
|
||||||
|
|
||||||
|
# make sure the state_dict has the correct naming in the parameters.
|
||||||
|
lora_state_dict = safetensors.torch.load_file(os.path.join(tmpdir, "pytorch_lora_weights.safetensors"))
|
||||||
|
is_lora = all("lora" in k for k in lora_state_dict.keys())
|
||||||
|
self.assertTrue(is_lora)
|
||||||
|
|
||||||
|
# when not training the text encoder, all the parameters in the state dict should start
|
||||||
|
# with `"transformer"` in their names.
|
||||||
|
starts_with_transformer = all(key.startswith("transformer") for key in lora_state_dict.keys())
|
||||||
|
self.assertTrue(starts_with_transformer)
|
||||||
|
|
||||||
|
def test_dreambooth_lora_text_encoder_sd3(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
test_args = f"""
|
||||||
|
{self.script_path}
|
||||||
|
--pretrained_model_name_or_path {self.pretrained_model_name_or_path}
|
||||||
|
--instance_data_dir {self.instance_data_dir}
|
||||||
|
--instance_prompt {self.instance_prompt}
|
||||||
|
--resolution 64
|
||||||
|
--train_batch_size 1
|
||||||
|
--train_text_encoder
|
||||||
|
--gradient_accumulation_steps 1
|
||||||
|
--max_train_steps 2
|
||||||
|
--learning_rate 5.0e-04
|
||||||
|
--scale_lr
|
||||||
|
--lr_scheduler constant
|
||||||
|
--lr_warmup_steps 0
|
||||||
|
--output_dir {tmpdir}
|
||||||
|
""".split()
|
||||||
|
|
||||||
|
run_command(self._launch_args + test_args)
|
||||||
|
# save_pretrained smoke test
|
||||||
|
self.assertTrue(os.path.isfile(os.path.join(tmpdir, "pytorch_lora_weights.safetensors")))
|
||||||
|
|
||||||
|
# make sure the state_dict has the correct naming in the parameters.
|
||||||
|
lora_state_dict = safetensors.torch.load_file(os.path.join(tmpdir, "pytorch_lora_weights.safetensors"))
|
||||||
|
is_lora = all("lora" in k for k in lora_state_dict.keys())
|
||||||
|
self.assertTrue(is_lora)
|
||||||
|
|
||||||
|
starts_with_expected_prefix = all(
|
||||||
|
(key.startswith("transformer") or key.startswith("text_encoder")) for key in lora_state_dict.keys()
|
||||||
|
)
|
||||||
|
self.assertTrue(starts_with_expected_prefix)
|
||||||
|
|
||||||
|
def test_dreambooth_lora_sd3_checkpointing_checkpoints_total_limit(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
test_args = f"""
|
||||||
|
{self.script_path}
|
||||||
|
--pretrained_model_name_or_path={self.pretrained_model_name_or_path}
|
||||||
|
--instance_data_dir={self.instance_data_dir}
|
||||||
|
--output_dir={tmpdir}
|
||||||
|
--instance_prompt={self.instance_prompt}
|
||||||
|
--resolution=64
|
||||||
|
--train_batch_size=1
|
||||||
|
--gradient_accumulation_steps=1
|
||||||
|
--max_train_steps=6
|
||||||
|
--checkpoints_total_limit=2
|
||||||
|
--checkpointing_steps=2
|
||||||
|
""".split()
|
||||||
|
|
||||||
|
run_command(self._launch_args + test_args)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
{x for x in os.listdir(tmpdir) if "checkpoint" in x},
|
||||||
|
{"checkpoint-4", "checkpoint-6"},
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_dreambooth_lora_sd3_checkpointing_checkpoints_total_limit_removes_multiple_checkpoints(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
test_args = f"""
|
||||||
|
{self.script_path}
|
||||||
|
--pretrained_model_name_or_path={self.pretrained_model_name_or_path}
|
||||||
|
--instance_data_dir={self.instance_data_dir}
|
||||||
|
--output_dir={tmpdir}
|
||||||
|
--instance_prompt={self.instance_prompt}
|
||||||
|
--resolution=64
|
||||||
|
--train_batch_size=1
|
||||||
|
--gradient_accumulation_steps=1
|
||||||
|
--max_train_steps=4
|
||||||
|
--checkpointing_steps=2
|
||||||
|
""".split()
|
||||||
|
|
||||||
|
run_command(self._launch_args + test_args)
|
||||||
|
|
||||||
|
self.assertEqual({x for x in os.listdir(tmpdir) if "checkpoint" in x}, {"checkpoint-2", "checkpoint-4"})
|
||||||
|
|
||||||
|
resume_run_args = f"""
|
||||||
|
{self.script_path}
|
||||||
|
--pretrained_model_name_or_path={self.pretrained_model_name_or_path}
|
||||||
|
--instance_data_dir={self.instance_data_dir}
|
||||||
|
--output_dir={tmpdir}
|
||||||
|
--instance_prompt={self.instance_prompt}
|
||||||
|
--resolution=64
|
||||||
|
--train_batch_size=1
|
||||||
|
--gradient_accumulation_steps=1
|
||||||
|
--max_train_steps=8
|
||||||
|
--checkpointing_steps=2
|
||||||
|
--resume_from_checkpoint=checkpoint-4
|
||||||
|
--checkpoints_total_limit=2
|
||||||
|
""".split()
|
||||||
|
|
||||||
|
run_command(self._launch_args + resume_run_args)
|
||||||
|
|
||||||
|
self.assertEqual({x for x in os.listdir(tmpdir) if "checkpoint" in x}, {"checkpoint-6", "checkpoint-8"})
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
# Copyright 2024 HuggingFace Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
from diffusers import DiffusionPipeline, SD3Transformer2DModel
|
||||||
|
|
||||||
|
|
||||||
|
sys.path.append("..")
|
||||||
|
from test_examples_utils import ExamplesTestsAccelerate, run_command # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
stream_handler = logging.StreamHandler(sys.stdout)
|
||||||
|
logger.addHandler(stream_handler)
|
||||||
|
|
||||||
|
|
||||||
|
class DreamBoothSD3(ExamplesTestsAccelerate):
|
||||||
|
instance_data_dir = "docs/source/en/imgs"
|
||||||
|
instance_prompt = "photo"
|
||||||
|
pretrained_model_name_or_path = "hf-internal-testing/tiny-sd3-pipe"
|
||||||
|
script_path = "examples/dreambooth/train_dreambooth_sd3.py"
|
||||||
|
|
||||||
|
def test_dreambooth(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
test_args = f"""
|
||||||
|
{self.script_path}
|
||||||
|
--pretrained_model_name_or_path {self.pretrained_model_name_or_path}
|
||||||
|
--instance_data_dir {self.instance_data_dir}
|
||||||
|
--instance_prompt {self.instance_prompt}
|
||||||
|
--resolution 64
|
||||||
|
--train_batch_size 1
|
||||||
|
--gradient_accumulation_steps 1
|
||||||
|
--max_train_steps 2
|
||||||
|
--learning_rate 5.0e-04
|
||||||
|
--scale_lr
|
||||||
|
--lr_scheduler constant
|
||||||
|
--lr_warmup_steps 0
|
||||||
|
--output_dir {tmpdir}
|
||||||
|
""".split()
|
||||||
|
|
||||||
|
run_command(self._launch_args + test_args)
|
||||||
|
# save_pretrained smoke test
|
||||||
|
self.assertTrue(os.path.isfile(os.path.join(tmpdir, "transformer", "diffusion_pytorch_model.safetensors")))
|
||||||
|
self.assertTrue(os.path.isfile(os.path.join(tmpdir, "scheduler", "scheduler_config.json")))
|
||||||
|
|
||||||
|
def test_dreambooth_checkpointing(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
# Run training script with checkpointing
|
||||||
|
# max_train_steps == 4, checkpointing_steps == 2
|
||||||
|
# Should create checkpoints at steps 2, 4
|
||||||
|
|
||||||
|
initial_run_args = f"""
|
||||||
|
{self.script_path}
|
||||||
|
--pretrained_model_name_or_path {self.pretrained_model_name_or_path}
|
||||||
|
--instance_data_dir {self.instance_data_dir}
|
||||||
|
--instance_prompt {self.instance_prompt}
|
||||||
|
--resolution 64
|
||||||
|
--train_batch_size 1
|
||||||
|
--gradient_accumulation_steps 1
|
||||||
|
--max_train_steps 4
|
||||||
|
--learning_rate 5.0e-04
|
||||||
|
--scale_lr
|
||||||
|
--lr_scheduler constant
|
||||||
|
--lr_warmup_steps 0
|
||||||
|
--output_dir {tmpdir}
|
||||||
|
--checkpointing_steps=2
|
||||||
|
--seed=0
|
||||||
|
""".split()
|
||||||
|
|
||||||
|
run_command(self._launch_args + initial_run_args)
|
||||||
|
|
||||||
|
# check can run the original fully trained output pipeline
|
||||||
|
pipe = DiffusionPipeline.from_pretrained(tmpdir)
|
||||||
|
pipe(self.instance_prompt, num_inference_steps=1)
|
||||||
|
|
||||||
|
# check checkpoint directories exist
|
||||||
|
self.assertTrue(os.path.isdir(os.path.join(tmpdir, "checkpoint-2")))
|
||||||
|
self.assertTrue(os.path.isdir(os.path.join(tmpdir, "checkpoint-4")))
|
||||||
|
|
||||||
|
# check can run an intermediate checkpoint
|
||||||
|
transformer = SD3Transformer2DModel.from_pretrained(tmpdir, subfolder="checkpoint-2/transformer")
|
||||||
|
pipe = DiffusionPipeline.from_pretrained(self.pretrained_model_name_or_path, transformer=transformer)
|
||||||
|
pipe(self.instance_prompt, num_inference_steps=1)
|
||||||
|
|
||||||
|
# Remove checkpoint 2 so that we can check only later checkpoints exist after resuming
|
||||||
|
shutil.rmtree(os.path.join(tmpdir, "checkpoint-2"))
|
||||||
|
|
||||||
|
# Run training script for 7 total steps resuming from checkpoint 4
|
||||||
|
|
||||||
|
resume_run_args = f"""
|
||||||
|
{self.script_path}
|
||||||
|
--pretrained_model_name_or_path {self.pretrained_model_name_or_path}
|
||||||
|
--instance_data_dir {self.instance_data_dir}
|
||||||
|
--instance_prompt {self.instance_prompt}
|
||||||
|
--resolution 64
|
||||||
|
--train_batch_size 1
|
||||||
|
--gradient_accumulation_steps 1
|
||||||
|
--max_train_steps 6
|
||||||
|
--learning_rate 5.0e-04
|
||||||
|
--scale_lr
|
||||||
|
--lr_scheduler constant
|
||||||
|
--lr_warmup_steps 0
|
||||||
|
--output_dir {tmpdir}
|
||||||
|
--checkpointing_steps=2
|
||||||
|
--resume_from_checkpoint=checkpoint-4
|
||||||
|
--seed=0
|
||||||
|
""".split()
|
||||||
|
|
||||||
|
run_command(self._launch_args + resume_run_args)
|
||||||
|
|
||||||
|
# check can run new fully trained pipeline
|
||||||
|
pipe = DiffusionPipeline.from_pretrained(tmpdir)
|
||||||
|
pipe(self.instance_prompt, num_inference_steps=1)
|
||||||
|
|
||||||
|
# check old checkpoints do not exist
|
||||||
|
self.assertFalse(os.path.isdir(os.path.join(tmpdir, "checkpoint-2")))
|
||||||
|
|
||||||
|
# check new checkpoints exist
|
||||||
|
self.assertTrue(os.path.isdir(os.path.join(tmpdir, "checkpoint-4")))
|
||||||
|
self.assertTrue(os.path.isdir(os.path.join(tmpdir, "checkpoint-6")))
|
||||||
|
|
||||||
|
def test_dreambooth_checkpointing_checkpoints_total_limit(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
test_args = f"""
|
||||||
|
{self.script_path}
|
||||||
|
--pretrained_model_name_or_path={self.pretrained_model_name_or_path}
|
||||||
|
--instance_data_dir={self.instance_data_dir}
|
||||||
|
--output_dir={tmpdir}
|
||||||
|
--instance_prompt={self.instance_prompt}
|
||||||
|
--resolution=64
|
||||||
|
--train_batch_size=1
|
||||||
|
--gradient_accumulation_steps=1
|
||||||
|
--max_train_steps=6
|
||||||
|
--checkpoints_total_limit=2
|
||||||
|
--checkpointing_steps=2
|
||||||
|
""".split()
|
||||||
|
|
||||||
|
run_command(self._launch_args + test_args)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
{x for x in os.listdir(tmpdir) if "checkpoint" in x},
|
||||||
|
{"checkpoint-4", "checkpoint-6"},
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_dreambooth_checkpointing_checkpoints_total_limit_removes_multiple_checkpoints(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
test_args = f"""
|
||||||
|
{self.script_path}
|
||||||
|
--pretrained_model_name_or_path={self.pretrained_model_name_or_path}
|
||||||
|
--instance_data_dir={self.instance_data_dir}
|
||||||
|
--output_dir={tmpdir}
|
||||||
|
--instance_prompt={self.instance_prompt}
|
||||||
|
--resolution=64
|
||||||
|
--train_batch_size=1
|
||||||
|
--gradient_accumulation_steps=1
|
||||||
|
--max_train_steps=4
|
||||||
|
--checkpointing_steps=2
|
||||||
|
""".split()
|
||||||
|
|
||||||
|
run_command(self._launch_args + test_args)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
{x for x in os.listdir(tmpdir) if "checkpoint" in x},
|
||||||
|
{"checkpoint-2", "checkpoint-4"},
|
||||||
|
)
|
||||||
|
|
||||||
|
resume_run_args = f"""
|
||||||
|
{self.script_path}
|
||||||
|
--pretrained_model_name_or_path={self.pretrained_model_name_or_path}
|
||||||
|
--instance_data_dir={self.instance_data_dir}
|
||||||
|
--output_dir={tmpdir}
|
||||||
|
--instance_prompt={self.instance_prompt}
|
||||||
|
--resolution=64
|
||||||
|
--train_batch_size=1
|
||||||
|
--gradient_accumulation_steps=1
|
||||||
|
--max_train_steps=8
|
||||||
|
--checkpointing_steps=2
|
||||||
|
--resume_from_checkpoint=checkpoint-4
|
||||||
|
--checkpoints_total_limit=2
|
||||||
|
""".split()
|
||||||
|
|
||||||
|
run_command(self._launch_args + resume_run_args)
|
||||||
|
|
||||||
|
self.assertEqual({x for x in os.listdir(tmpdir) if "checkpoint" in x}, {"checkpoint-6", "checkpoint-8"})
|
||||||
@@ -54,6 +54,7 @@ from diffusers import (
|
|||||||
)
|
)
|
||||||
from diffusers.optimization import get_scheduler
|
from diffusers.optimization import get_scheduler
|
||||||
from diffusers.training_utils import (
|
from diffusers.training_utils import (
|
||||||
|
_set_state_dict_into_text_encoder,
|
||||||
cast_training_params,
|
cast_training_params,
|
||||||
compute_density_for_timestep_sampling,
|
compute_density_for_timestep_sampling,
|
||||||
compute_loss_weighting_for_sd3,
|
compute_loss_weighting_for_sd3,
|
||||||
@@ -80,6 +81,7 @@ def save_model_card(
|
|||||||
repo_id: str,
|
repo_id: str,
|
||||||
images=None,
|
images=None,
|
||||||
base_model: str = None,
|
base_model: str = None,
|
||||||
|
train_text_encoder=False,
|
||||||
instance_prompt=None,
|
instance_prompt=None,
|
||||||
validation_prompt=None,
|
validation_prompt=None,
|
||||||
repo_folder=None,
|
repo_folder=None,
|
||||||
@@ -103,6 +105,8 @@ These are {repo_id} DreamBooth weights for {base_model}.
|
|||||||
|
|
||||||
The weights were trained using [DreamBooth](https://dreambooth.github.io/).
|
The weights were trained using [DreamBooth](https://dreambooth.github.io/).
|
||||||
|
|
||||||
|
LoRA for the text encoder was enabled: {train_text_encoder}.
|
||||||
|
|
||||||
## Trigger words
|
## Trigger words
|
||||||
|
|
||||||
You should use {instance_prompt} to trigger the image generation.
|
You should use {instance_prompt} to trigger the image generation.
|
||||||
@@ -113,7 +117,7 @@ You should use {instance_prompt} to trigger the image generation.
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Please adhere to the licensing terms as described `[here](https://huggingface.co/stabilityai/stable-diffusion-3-medium/blob/main/LICENSE)`.
|
Please adhere to the licensing terms as described [here](https://huggingface.co/stabilityai/stable-diffusion-3-medium/blob/main/LICENSE).
|
||||||
"""
|
"""
|
||||||
model_card = load_or_create_model_card(
|
model_card = load_or_create_model_card(
|
||||||
repo_id_or_path=repo_id,
|
repo_id_or_path=repo_id,
|
||||||
@@ -128,6 +132,7 @@ Please adhere to the licensing terms as described `[here](https://huggingface.co
|
|||||||
"text-to-image",
|
"text-to-image",
|
||||||
"diffusers-training",
|
"diffusers-training",
|
||||||
"diffusers",
|
"diffusers",
|
||||||
|
"lora",
|
||||||
"sd3",
|
"sd3",
|
||||||
"sd3-diffusers",
|
"sd3-diffusers",
|
||||||
"template:sd-lora",
|
"template:sd-lora",
|
||||||
@@ -381,6 +386,12 @@ def parse_args(input_args=None):
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
help="whether to randomly flip images horizontally",
|
help="whether to randomly flip images horizontally",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--train_text_encoder",
|
||||||
|
action="store_true",
|
||||||
|
help="Whether to train the text encoder (clip text encoders only). If set, the text encoder should be float32 precision.",
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--train_batch_size", type=int, default=4, help="Batch size (per device) for the training dataloader."
|
"--train_batch_size", type=int, default=4, help="Batch size (per device) for the training dataloader."
|
||||||
)
|
)
|
||||||
@@ -477,7 +488,10 @@ def parse_args(input_args=None):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--weighting_scheme", type=str, default="sigma_sqrt", choices=["sigma_sqrt", "logit_normal", "mode", "cosmap"]
|
"--weighting_scheme",
|
||||||
|
type=str,
|
||||||
|
default="logit_normal",
|
||||||
|
choices=["sigma_sqrt", "logit_normal", "mode", "cosmap"],
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--logit_mean", type=float, default=0.0, help="mean to use when using the `'logit_normal'` weighting scheme."
|
"--logit_mean", type=float, default=0.0, help="mean to use when using the `'logit_normal'` weighting scheme."
|
||||||
@@ -514,7 +528,7 @@ def parse_args(input_args=None):
|
|||||||
"--prodigy_beta3",
|
"--prodigy_beta3",
|
||||||
type=float,
|
type=float,
|
||||||
default=None,
|
default=None,
|
||||||
help="coefficients for computing the Prodidy stepsize using running averages. If set to None, "
|
help="coefficients for computing the Prodigy stepsize using running averages. If set to None, "
|
||||||
"uses the value of square root of beta2. Ignored if optimizer is adamW",
|
"uses the value of square root of beta2. Ignored if optimizer is adamW",
|
||||||
)
|
)
|
||||||
parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay")
|
parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay")
|
||||||
@@ -785,7 +799,7 @@ class DreamBoothDataset(Dataset):
|
|||||||
else:
|
else:
|
||||||
example["instance_prompt"] = self.instance_prompt
|
example["instance_prompt"] = self.instance_prompt
|
||||||
|
|
||||||
else: # costum prompts were provided, but length does not match size of image dataset
|
else: # custom prompts were provided, but length does not match size of image dataset
|
||||||
example["instance_prompt"] = self.instance_prompt
|
example["instance_prompt"] = self.instance_prompt
|
||||||
|
|
||||||
if self.class_data_root:
|
if self.class_data_root:
|
||||||
@@ -853,19 +867,25 @@ def _encode_prompt_with_t5(
|
|||||||
prompt=None,
|
prompt=None,
|
||||||
num_images_per_prompt=1,
|
num_images_per_prompt=1,
|
||||||
device=None,
|
device=None,
|
||||||
|
text_input_ids=None,
|
||||||
):
|
):
|
||||||
prompt = [prompt] if isinstance(prompt, str) else prompt
|
prompt = [prompt] if isinstance(prompt, str) else prompt
|
||||||
batch_size = len(prompt)
|
batch_size = len(prompt)
|
||||||
|
|
||||||
text_inputs = tokenizer(
|
if tokenizer is not None:
|
||||||
prompt,
|
text_inputs = tokenizer(
|
||||||
padding="max_length",
|
prompt,
|
||||||
max_length=max_sequence_length,
|
padding="max_length",
|
||||||
truncation=True,
|
max_length=max_sequence_length,
|
||||||
add_special_tokens=True,
|
truncation=True,
|
||||||
return_tensors="pt",
|
add_special_tokens=True,
|
||||||
)
|
return_tensors="pt",
|
||||||
text_input_ids = text_inputs.input_ids
|
)
|
||||||
|
text_input_ids = text_inputs.input_ids
|
||||||
|
else:
|
||||||
|
if text_input_ids is None:
|
||||||
|
raise ValueError("text_input_ids must be provided when the tokenizer is not specified")
|
||||||
|
|
||||||
prompt_embeds = text_encoder(text_input_ids.to(device))[0]
|
prompt_embeds = text_encoder(text_input_ids.to(device))[0]
|
||||||
|
|
||||||
dtype = text_encoder.dtype
|
dtype = text_encoder.dtype
|
||||||
@@ -885,20 +905,26 @@ def _encode_prompt_with_clip(
|
|||||||
tokenizer,
|
tokenizer,
|
||||||
prompt: str,
|
prompt: str,
|
||||||
device=None,
|
device=None,
|
||||||
|
text_input_ids=None,
|
||||||
num_images_per_prompt: int = 1,
|
num_images_per_prompt: int = 1,
|
||||||
):
|
):
|
||||||
prompt = [prompt] if isinstance(prompt, str) else prompt
|
prompt = [prompt] if isinstance(prompt, str) else prompt
|
||||||
batch_size = len(prompt)
|
batch_size = len(prompt)
|
||||||
|
|
||||||
text_inputs = tokenizer(
|
if tokenizer is not None:
|
||||||
prompt,
|
text_inputs = tokenizer(
|
||||||
padding="max_length",
|
prompt,
|
||||||
max_length=77,
|
padding="max_length",
|
||||||
truncation=True,
|
max_length=77,
|
||||||
return_tensors="pt",
|
truncation=True,
|
||||||
)
|
return_tensors="pt",
|
||||||
|
)
|
||||||
|
|
||||||
|
text_input_ids = text_inputs.input_ids
|
||||||
|
else:
|
||||||
|
if text_input_ids is None:
|
||||||
|
raise ValueError("text_input_ids must be provided when the tokenizer is not specified")
|
||||||
|
|
||||||
text_input_ids = text_inputs.input_ids
|
|
||||||
prompt_embeds = text_encoder(text_input_ids.to(device), output_hidden_states=True)
|
prompt_embeds = text_encoder(text_input_ids.to(device), output_hidden_states=True)
|
||||||
|
|
||||||
pooled_prompt_embeds = prompt_embeds[0]
|
pooled_prompt_embeds = prompt_embeds[0]
|
||||||
@@ -920,6 +946,7 @@ def encode_prompt(
|
|||||||
max_sequence_length,
|
max_sequence_length,
|
||||||
device=None,
|
device=None,
|
||||||
num_images_per_prompt: int = 1,
|
num_images_per_prompt: int = 1,
|
||||||
|
text_input_ids_list=None,
|
||||||
):
|
):
|
||||||
prompt = [prompt] if isinstance(prompt, str) else prompt
|
prompt = [prompt] if isinstance(prompt, str) else prompt
|
||||||
|
|
||||||
@@ -928,13 +955,14 @@ def encode_prompt(
|
|||||||
|
|
||||||
clip_prompt_embeds_list = []
|
clip_prompt_embeds_list = []
|
||||||
clip_pooled_prompt_embeds_list = []
|
clip_pooled_prompt_embeds_list = []
|
||||||
for tokenizer, text_encoder in zip(clip_tokenizers, clip_text_encoders):
|
for i, (tokenizer, text_encoder) in enumerate(zip(clip_tokenizers, clip_text_encoders)):
|
||||||
prompt_embeds, pooled_prompt_embeds = _encode_prompt_with_clip(
|
prompt_embeds, pooled_prompt_embeds = _encode_prompt_with_clip(
|
||||||
text_encoder=text_encoder,
|
text_encoder=text_encoder,
|
||||||
tokenizer=tokenizer,
|
tokenizer=tokenizer,
|
||||||
prompt=prompt,
|
prompt=prompt,
|
||||||
device=device if device is not None else text_encoder.device,
|
device=device if device is not None else text_encoder.device,
|
||||||
num_images_per_prompt=num_images_per_prompt,
|
num_images_per_prompt=num_images_per_prompt,
|
||||||
|
text_input_ids=text_input_ids_list[i] if text_input_ids_list else None,
|
||||||
)
|
)
|
||||||
clip_prompt_embeds_list.append(prompt_embeds)
|
clip_prompt_embeds_list.append(prompt_embeds)
|
||||||
clip_pooled_prompt_embeds_list.append(pooled_prompt_embeds)
|
clip_pooled_prompt_embeds_list.append(pooled_prompt_embeds)
|
||||||
@@ -948,6 +976,7 @@ def encode_prompt(
|
|||||||
max_sequence_length,
|
max_sequence_length,
|
||||||
prompt=prompt,
|
prompt=prompt,
|
||||||
num_images_per_prompt=num_images_per_prompt,
|
num_images_per_prompt=num_images_per_prompt,
|
||||||
|
text_input_ids=text_input_ids_list[-1] if text_input_ids_list else None,
|
||||||
device=device if device is not None else text_encoders[-1].device,
|
device=device if device is not None else text_encoders[-1].device,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1142,6 +1171,9 @@ def main(args):
|
|||||||
|
|
||||||
if args.gradient_checkpointing:
|
if args.gradient_checkpointing:
|
||||||
transformer.enable_gradient_checkpointing()
|
transformer.enable_gradient_checkpointing()
|
||||||
|
if args.train_text_encoder:
|
||||||
|
text_encoder_one.gradient_checkpointing_enable()
|
||||||
|
text_encoder_two.gradient_checkpointing_enable()
|
||||||
|
|
||||||
# now we will add new LoRA weights to the attention layers
|
# now we will add new LoRA weights to the attention layers
|
||||||
transformer_lora_config = LoraConfig(
|
transformer_lora_config = LoraConfig(
|
||||||
@@ -1152,6 +1184,16 @@ def main(args):
|
|||||||
)
|
)
|
||||||
transformer.add_adapter(transformer_lora_config)
|
transformer.add_adapter(transformer_lora_config)
|
||||||
|
|
||||||
|
if args.train_text_encoder:
|
||||||
|
text_lora_config = LoraConfig(
|
||||||
|
r=args.rank,
|
||||||
|
lora_alpha=args.rank,
|
||||||
|
init_lora_weights="gaussian",
|
||||||
|
target_modules=["q_proj", "k_proj", "v_proj", "out_proj"],
|
||||||
|
)
|
||||||
|
text_encoder_one.add_adapter(text_lora_config)
|
||||||
|
text_encoder_two.add_adapter(text_lora_config)
|
||||||
|
|
||||||
def unwrap_model(model):
|
def unwrap_model(model):
|
||||||
model = accelerator.unwrap_model(model)
|
model = accelerator.unwrap_model(model)
|
||||||
model = model._orig_mod if is_compiled_module(model) else model
|
model = model._orig_mod if is_compiled_module(model) else model
|
||||||
@@ -1161,10 +1203,16 @@ def main(args):
|
|||||||
def save_model_hook(models, weights, output_dir):
|
def save_model_hook(models, weights, output_dir):
|
||||||
if accelerator.is_main_process:
|
if accelerator.is_main_process:
|
||||||
transformer_lora_layers_to_save = None
|
transformer_lora_layers_to_save = None
|
||||||
|
text_encoder_one_lora_layers_to_save = None
|
||||||
|
text_encoder_two_lora_layers_to_save = None
|
||||||
|
|
||||||
for model in models:
|
for model in models:
|
||||||
if isinstance(model, type(unwrap_model(transformer))):
|
if isinstance(model, type(unwrap_model(transformer))):
|
||||||
transformer_lora_layers_to_save = get_peft_model_state_dict(model)
|
transformer_lora_layers_to_save = get_peft_model_state_dict(model)
|
||||||
|
elif isinstance(model, type(unwrap_model(text_encoder_one))):
|
||||||
|
text_encoder_one_lora_layers_to_save = get_peft_model_state_dict(model)
|
||||||
|
elif isinstance(model, type(unwrap_model(text_encoder_two))):
|
||||||
|
text_encoder_two_lora_layers_to_save = get_peft_model_state_dict(model)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"unexpected save model: {model.__class__}")
|
raise ValueError(f"unexpected save model: {model.__class__}")
|
||||||
|
|
||||||
@@ -1172,17 +1220,26 @@ def main(args):
|
|||||||
weights.pop()
|
weights.pop()
|
||||||
|
|
||||||
StableDiffusion3Pipeline.save_lora_weights(
|
StableDiffusion3Pipeline.save_lora_weights(
|
||||||
output_dir, transformer_lora_layers=transformer_lora_layers_to_save
|
output_dir,
|
||||||
|
transformer_lora_layers=transformer_lora_layers_to_save,
|
||||||
|
text_encoder_lora_layers=text_encoder_one_lora_layers_to_save,
|
||||||
|
text_encoder_2_lora_layers=text_encoder_two_lora_layers_to_save,
|
||||||
)
|
)
|
||||||
|
|
||||||
def load_model_hook(models, input_dir):
|
def load_model_hook(models, input_dir):
|
||||||
transformer_ = None
|
transformer_ = None
|
||||||
|
text_encoder_one_ = None
|
||||||
|
text_encoder_two_ = None
|
||||||
|
|
||||||
while len(models) > 0:
|
while len(models) > 0:
|
||||||
model = models.pop()
|
model = models.pop()
|
||||||
|
|
||||||
if isinstance(model, type(unwrap_model(transformer))):
|
if isinstance(model, type(unwrap_model(transformer))):
|
||||||
transformer_ = model
|
transformer_ = model
|
||||||
|
elif isinstance(model, type(unwrap_model(text_encoder_one))):
|
||||||
|
text_encoder_one_ = model
|
||||||
|
elif isinstance(model, type(unwrap_model(text_encoder_two))):
|
||||||
|
text_encoder_two_ = model
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"unexpected save model: {model.__class__}")
|
raise ValueError(f"unexpected save model: {model.__class__}")
|
||||||
|
|
||||||
@@ -1201,12 +1258,21 @@ def main(args):
|
|||||||
f"Loading adapter weights from state_dict led to unexpected keys not found in the model: "
|
f"Loading adapter weights from state_dict led to unexpected keys not found in the model: "
|
||||||
f" {unexpected_keys}. "
|
f" {unexpected_keys}. "
|
||||||
)
|
)
|
||||||
|
if args.train_text_encoder:
|
||||||
|
# Do we need to call `scale_lora_layers()` here?
|
||||||
|
_set_state_dict_into_text_encoder(lora_state_dict, prefix="text_encoder.", text_encoder=text_encoder_one_)
|
||||||
|
|
||||||
|
_set_state_dict_into_text_encoder(
|
||||||
|
lora_state_dict, prefix="text_encoder_2.", text_encoder=text_encoder_two_
|
||||||
|
)
|
||||||
|
|
||||||
# Make sure the trainable params are in float32. This is again needed since the base models
|
# Make sure the trainable params are in float32. This is again needed since the base models
|
||||||
# are in `weight_dtype`. More details:
|
# are in `weight_dtype`. More details:
|
||||||
# https://github.com/huggingface/diffusers/pull/6514#discussion_r1449796804
|
# https://github.com/huggingface/diffusers/pull/6514#discussion_r1449796804
|
||||||
if args.mixed_precision == "fp16":
|
if args.mixed_precision == "fp16":
|
||||||
models = [transformer_]
|
models = [transformer_]
|
||||||
|
if args.train_text_encoder:
|
||||||
|
models.extend([text_encoder_one_, text_encoder_two_])
|
||||||
# only upcast trainable parameters (LoRA) into fp32
|
# only upcast trainable parameters (LoRA) into fp32
|
||||||
cast_training_params(models)
|
cast_training_params(models)
|
||||||
|
|
||||||
@@ -1226,14 +1292,37 @@ def main(args):
|
|||||||
# Make sure the trainable params are in float32.
|
# Make sure the trainable params are in float32.
|
||||||
if args.mixed_precision == "fp16":
|
if args.mixed_precision == "fp16":
|
||||||
models = [transformer]
|
models = [transformer]
|
||||||
|
if args.train_text_encoder:
|
||||||
|
models.extend([text_encoder_one, text_encoder_two])
|
||||||
# only upcast trainable parameters (LoRA) into fp32
|
# only upcast trainable parameters (LoRA) into fp32
|
||||||
cast_training_params(models, dtype=torch.float32)
|
cast_training_params(models, dtype=torch.float32)
|
||||||
|
|
||||||
transformer_lora_parameters = list(filter(lambda p: p.requires_grad, transformer.parameters()))
|
transformer_lora_parameters = list(filter(lambda p: p.requires_grad, transformer.parameters()))
|
||||||
|
if args.train_text_encoder:
|
||||||
|
text_lora_parameters_one = list(filter(lambda p: p.requires_grad, text_encoder_one.parameters()))
|
||||||
|
text_lora_parameters_two = list(filter(lambda p: p.requires_grad, text_encoder_two.parameters()))
|
||||||
|
|
||||||
# Optimization parameters
|
# Optimization parameters
|
||||||
transformer_parameters_with_lr = {"params": transformer_lora_parameters, "lr": args.learning_rate}
|
transformer_parameters_with_lr = {"params": transformer_lora_parameters, "lr": args.learning_rate}
|
||||||
params_to_optimize = [transformer_parameters_with_lr]
|
if args.train_text_encoder:
|
||||||
|
# different learning rate for text encoder and unet
|
||||||
|
text_lora_parameters_one_with_lr = {
|
||||||
|
"params": text_lora_parameters_one,
|
||||||
|
"weight_decay": args.adam_weight_decay_text_encoder,
|
||||||
|
"lr": args.text_encoder_lr if args.text_encoder_lr else args.learning_rate,
|
||||||
|
}
|
||||||
|
text_lora_parameters_two_with_lr = {
|
||||||
|
"params": text_lora_parameters_two,
|
||||||
|
"weight_decay": args.adam_weight_decay_text_encoder,
|
||||||
|
"lr": args.text_encoder_lr if args.text_encoder_lr else args.learning_rate,
|
||||||
|
}
|
||||||
|
params_to_optimize = [
|
||||||
|
transformer_parameters_with_lr,
|
||||||
|
text_lora_parameters_one_with_lr,
|
||||||
|
text_lora_parameters_two_with_lr,
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
params_to_optimize = [transformer_parameters_with_lr]
|
||||||
|
|
||||||
# Optimizer creation
|
# Optimizer creation
|
||||||
if not (args.optimizer.lower() == "prodigy" or args.optimizer.lower() == "adamw"):
|
if not (args.optimizer.lower() == "prodigy" or args.optimizer.lower() == "adamw"):
|
||||||
@@ -1314,34 +1403,35 @@ def main(args):
|
|||||||
num_workers=args.dataloader_num_workers,
|
num_workers=args.dataloader_num_workers,
|
||||||
)
|
)
|
||||||
|
|
||||||
tokenizers = [tokenizer_one, tokenizer_two, tokenizer_three]
|
if not args.train_text_encoder:
|
||||||
text_encoders = [text_encoder_one, text_encoder_two, text_encoder_three]
|
tokenizers = [tokenizer_one, tokenizer_two, tokenizer_three]
|
||||||
|
text_encoders = [text_encoder_one, text_encoder_two, text_encoder_three]
|
||||||
|
|
||||||
def compute_text_embeddings(prompt, text_encoders, tokenizers):
|
def compute_text_embeddings(prompt, text_encoders, tokenizers):
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
prompt_embeds, pooled_prompt_embeds = encode_prompt(
|
prompt_embeds, pooled_prompt_embeds = encode_prompt(
|
||||||
text_encoders, tokenizers, prompt, args.max_sequence_length
|
text_encoders, tokenizers, prompt, args.max_sequence_length
|
||||||
)
|
)
|
||||||
prompt_embeds = prompt_embeds.to(accelerator.device)
|
prompt_embeds = prompt_embeds.to(accelerator.device)
|
||||||
pooled_prompt_embeds = pooled_prompt_embeds.to(accelerator.device)
|
pooled_prompt_embeds = pooled_prompt_embeds.to(accelerator.device)
|
||||||
return prompt_embeds, pooled_prompt_embeds
|
return prompt_embeds, pooled_prompt_embeds
|
||||||
|
|
||||||
if not train_dataset.custom_instance_prompts:
|
if not args.train_text_encoder and not train_dataset.custom_instance_prompts:
|
||||||
instance_prompt_hidden_states, instance_pooled_prompt_embeds = compute_text_embeddings(
|
instance_prompt_hidden_states, instance_pooled_prompt_embeds = compute_text_embeddings(
|
||||||
args.instance_prompt, text_encoders, tokenizers
|
args.instance_prompt, text_encoders, tokenizers
|
||||||
)
|
)
|
||||||
|
|
||||||
# Handle class prompt for prior-preservation.
|
# Handle class prompt for prior-preservation.
|
||||||
if args.with_prior_preservation:
|
if args.with_prior_preservation:
|
||||||
class_prompt_hidden_states, class_pooled_prompt_embeds = compute_text_embeddings(
|
if not args.train_text_encoder:
|
||||||
args.class_prompt, text_encoders, tokenizers
|
class_prompt_hidden_states, class_pooled_prompt_embeds = compute_text_embeddings(
|
||||||
)
|
args.class_prompt, text_encoders, tokenizers
|
||||||
|
)
|
||||||
|
|
||||||
# Clear the memory here
|
# Clear the memory here
|
||||||
if not train_dataset.custom_instance_prompts:
|
if not args.train_text_encoder and train_dataset.custom_instance_prompts:
|
||||||
del tokenizers, text_encoders
|
del tokenizers, text_encoders
|
||||||
# Explicitly delete the objects as well, otherwise only the lists are deleted and the original references remain, preventing garbage collection
|
# Explicitly delete the objects as well, otherwise only the lists are deleted and the original references remain, preventing garbage collection
|
||||||
del tokenizer_one, tokenizer_two, tokenizer_three
|
|
||||||
del text_encoder_one, text_encoder_two, text_encoder_three
|
del text_encoder_one, text_encoder_two, text_encoder_three
|
||||||
gc.collect()
|
gc.collect()
|
||||||
if torch.cuda.is_available():
|
if torch.cuda.is_available():
|
||||||
@@ -1352,12 +1442,13 @@ def main(args):
|
|||||||
# have to pass them to the dataloader.
|
# have to pass them to the dataloader.
|
||||||
|
|
||||||
if not train_dataset.custom_instance_prompts:
|
if not train_dataset.custom_instance_prompts:
|
||||||
prompt_embeds = instance_prompt_hidden_states
|
if not args.train_text_encoder:
|
||||||
pooled_prompt_embeds = instance_pooled_prompt_embeds
|
prompt_embeds = instance_prompt_hidden_states
|
||||||
if args.with_prior_preservation:
|
pooled_prompt_embeds = instance_pooled_prompt_embeds
|
||||||
prompt_embeds = torch.cat([prompt_embeds, class_prompt_hidden_states], dim=0)
|
if args.with_prior_preservation:
|
||||||
pooled_prompt_embeds = torch.cat([pooled_prompt_embeds, class_pooled_prompt_embeds], dim=0)
|
prompt_embeds = torch.cat([prompt_embeds, class_prompt_hidden_states], dim=0)
|
||||||
# if we're optmizing the text encoder (both if instance prompt is used for all images or custom prompts) we need to tokenize and encode the
|
pooled_prompt_embeds = torch.cat([pooled_prompt_embeds, class_pooled_prompt_embeds], dim=0)
|
||||||
|
# if we're optimizing the text encoder (both if instance prompt is used for all images or custom prompts) we need to tokenize and encode the
|
||||||
# batch prompts on all training steps
|
# batch prompts on all training steps
|
||||||
else:
|
else:
|
||||||
tokens_one = tokenize_prompt(tokenizer_one, args.instance_prompt)
|
tokens_one = tokenize_prompt(tokenizer_one, args.instance_prompt)
|
||||||
@@ -1388,9 +1479,25 @@ def main(args):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Prepare everything with our `accelerator`.
|
# Prepare everything with our `accelerator`.
|
||||||
transformer, optimizer, train_dataloader, lr_scheduler = accelerator.prepare(
|
# Prepare everything with our `accelerator`.
|
||||||
transformer, optimizer, train_dataloader, lr_scheduler
|
if args.train_text_encoder:
|
||||||
)
|
(
|
||||||
|
transformer,
|
||||||
|
text_encoder_one,
|
||||||
|
text_encoder_two,
|
||||||
|
optimizer,
|
||||||
|
train_dataloader,
|
||||||
|
lr_scheduler,
|
||||||
|
) = accelerator.prepare(
|
||||||
|
transformer, text_encoder_one, text_encoder_two, optimizer, train_dataloader, lr_scheduler
|
||||||
|
)
|
||||||
|
assert text_encoder_one is not None
|
||||||
|
assert text_encoder_two is not None
|
||||||
|
assert text_encoder_three is not None
|
||||||
|
else:
|
||||||
|
transformer, optimizer, train_dataloader, lr_scheduler = accelerator.prepare(
|
||||||
|
transformer, optimizer, train_dataloader, lr_scheduler
|
||||||
|
)
|
||||||
|
|
||||||
# We need to recalculate our total training steps as the size of the training dataloader may have changed.
|
# We need to recalculate our total training steps as the size of the training dataloader may have changed.
|
||||||
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
|
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
|
||||||
@@ -1468,6 +1575,13 @@ def main(args):
|
|||||||
|
|
||||||
for epoch in range(first_epoch, args.num_train_epochs):
|
for epoch in range(first_epoch, args.num_train_epochs):
|
||||||
transformer.train()
|
transformer.train()
|
||||||
|
if args.train_text_encoder:
|
||||||
|
text_encoder_one.train()
|
||||||
|
text_encoder_two.train()
|
||||||
|
|
||||||
|
# set top parameter requires_grad = True for gradient checkpointing works
|
||||||
|
accelerator.unwrap_model(text_encoder_one).text_model.embeddings.requires_grad_(True)
|
||||||
|
accelerator.unwrap_model(text_encoder_two).text_model.embeddings.requires_grad_(True)
|
||||||
|
|
||||||
for step, batch in enumerate(train_dataloader):
|
for step, batch in enumerate(train_dataloader):
|
||||||
models_to_accumulate = [transformer]
|
models_to_accumulate = [transformer]
|
||||||
@@ -1477,7 +1591,30 @@ def main(args):
|
|||||||
|
|
||||||
# encode batch prompts when custom prompts are provided for each image -
|
# encode batch prompts when custom prompts are provided for each image -
|
||||||
if train_dataset.custom_instance_prompts:
|
if train_dataset.custom_instance_prompts:
|
||||||
prompt_embeds, pooled_prompt_embeds = compute_text_embeddings(prompts, text_encoders, tokenizers)
|
if not args.train_text_encoder:
|
||||||
|
prompt_embeds, pooled_prompt_embeds = compute_text_embeddings(
|
||||||
|
prompts, text_encoders, tokenizers
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
tokens_one = tokenize_prompt(tokenizer_one, prompts)
|
||||||
|
tokens_two = tokenize_prompt(tokenizer_two, prompts)
|
||||||
|
tokens_three = tokenize_prompt(tokenizer_three, prompts)
|
||||||
|
prompt_embeds, pooled_prompt_embeds = encode_prompt(
|
||||||
|
text_encoders=[text_encoder_one, text_encoder_two, text_encoder_three],
|
||||||
|
tokenizers=[None, None, None],
|
||||||
|
prompt=prompts,
|
||||||
|
max_sequence_length=args.max_sequence_length,
|
||||||
|
text_input_ids_list=[tokens_one, tokens_two, tokens_three],
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if args.train_text_encoder:
|
||||||
|
prompt_embeds, pooled_prompt_embeds = encode_prompt(
|
||||||
|
text_encoders=[text_encoder_one, text_encoder_two, text_encoder_three],
|
||||||
|
tokenizers=[None, None, tokenizer_three],
|
||||||
|
prompt=args.instance_prompt,
|
||||||
|
max_sequence_length=args.max_sequence_length,
|
||||||
|
text_input_ids_list=[tokens_one, tokens_two, tokens_three],
|
||||||
|
)
|
||||||
|
|
||||||
# Convert images to latent space
|
# Convert images to latent space
|
||||||
model_input = vae.encode(pixel_values).latent_dist.sample()
|
model_input = vae.encode(pixel_values).latent_dist.sample()
|
||||||
@@ -1551,7 +1688,13 @@ def main(args):
|
|||||||
|
|
||||||
accelerator.backward(loss)
|
accelerator.backward(loss)
|
||||||
if accelerator.sync_gradients:
|
if accelerator.sync_gradients:
|
||||||
params_to_clip = transformer_lora_parameters
|
params_to_clip = (
|
||||||
|
itertools.chain(
|
||||||
|
transformer_lora_parameters, text_lora_parameters_one, text_lora_parameters_two
|
||||||
|
)
|
||||||
|
if args.train_text_encoder
|
||||||
|
else transformer_lora_parameters
|
||||||
|
)
|
||||||
accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm)
|
accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm)
|
||||||
|
|
||||||
optimizer.step()
|
optimizer.step()
|
||||||
@@ -1598,10 +1741,11 @@ def main(args):
|
|||||||
|
|
||||||
if accelerator.is_main_process:
|
if accelerator.is_main_process:
|
||||||
if args.validation_prompt is not None and epoch % args.validation_epochs == 0:
|
if args.validation_prompt is not None and epoch % args.validation_epochs == 0:
|
||||||
# create pipeline
|
if not args.train_text_encoder:
|
||||||
text_encoder_one, text_encoder_two, text_encoder_three = load_text_encoders(
|
# create pipeline
|
||||||
text_encoder_cls_one, text_encoder_cls_two, text_encoder_cls_three
|
text_encoder_one, text_encoder_two, text_encoder_three = load_text_encoders(
|
||||||
)
|
text_encoder_cls_one, text_encoder_cls_two, text_encoder_cls_three
|
||||||
|
)
|
||||||
pipeline = StableDiffusion3Pipeline.from_pretrained(
|
pipeline = StableDiffusion3Pipeline.from_pretrained(
|
||||||
args.pretrained_model_name_or_path,
|
args.pretrained_model_name_or_path,
|
||||||
vae=vae,
|
vae=vae,
|
||||||
@@ -1621,7 +1765,9 @@ def main(args):
|
|||||||
pipeline_args=pipeline_args,
|
pipeline_args=pipeline_args,
|
||||||
epoch=epoch,
|
epoch=epoch,
|
||||||
)
|
)
|
||||||
del text_encoder_one, text_encoder_two, text_encoder_three
|
if not args.train_text_encoder:
|
||||||
|
del text_encoder_one, text_encoder_two, text_encoder_three
|
||||||
|
|
||||||
torch.cuda.empty_cache()
|
torch.cuda.empty_cache()
|
||||||
gc.collect()
|
gc.collect()
|
||||||
|
|
||||||
@@ -1632,15 +1778,20 @@ def main(args):
|
|||||||
transformer = transformer.to(torch.float32)
|
transformer = transformer.to(torch.float32)
|
||||||
transformer_lora_layers = get_peft_model_state_dict(transformer)
|
transformer_lora_layers = get_peft_model_state_dict(transformer)
|
||||||
|
|
||||||
StableDiffusion3Pipeline.save_lora_weights(
|
if args.train_text_encoder:
|
||||||
save_directory=args.output_dir, transformer_lora_layers=transformer_lora_layers
|
text_encoder_one = unwrap_model(text_encoder_one)
|
||||||
)
|
text_encoder_lora_layers = get_peft_model_state_dict(text_encoder_one.to(torch.float32))
|
||||||
|
text_encoder_two = unwrap_model(text_encoder_two)
|
||||||
|
text_encoder_2_lora_layers = get_peft_model_state_dict(text_encoder_two.to(torch.float32))
|
||||||
|
else:
|
||||||
|
text_encoder_lora_layers = None
|
||||||
|
text_encoder_2_lora_layers = None
|
||||||
|
|
||||||
pipeline = StableDiffusion3Pipeline.from_pretrained(
|
StableDiffusion3Pipeline.save_lora_weights(
|
||||||
args.pretrained_model_name_or_path,
|
save_directory=args.output_dir,
|
||||||
revision=args.revision,
|
transformer_lora_layers=transformer_lora_layers,
|
||||||
variant=args.variant,
|
text_encoder_lora_layers=text_encoder_lora_layers,
|
||||||
torch_dtype=weight_dtype,
|
text_encoder_2_lora_layers=text_encoder_2_lora_layers,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Final inference
|
# Final inference
|
||||||
@@ -1674,6 +1825,7 @@ def main(args):
|
|||||||
base_model=args.pretrained_model_name_or_path,
|
base_model=args.pretrained_model_name_or_path,
|
||||||
instance_prompt=args.instance_prompt,
|
instance_prompt=args.instance_prompt,
|
||||||
validation_prompt=args.validation_prompt,
|
validation_prompt=args.validation_prompt,
|
||||||
|
train_text_encoder=args.train_text_encoder,
|
||||||
repo_folder=args.output_dir,
|
repo_folder=args.output_dir,
|
||||||
)
|
)
|
||||||
upload_folder(
|
upload_folder(
|
||||||
|
|||||||
@@ -562,7 +562,7 @@ def parse_args(input_args=None):
|
|||||||
"--prodigy_beta3",
|
"--prodigy_beta3",
|
||||||
type=float,
|
type=float,
|
||||||
default=None,
|
default=None,
|
||||||
help="coefficients for computing the Prodidy stepsize using running averages. If set to None, "
|
help="coefficients for computing the Prodigy stepsize using running averages. If set to None, "
|
||||||
"uses the value of square root of beta2. Ignored if optimizer is adamW",
|
"uses the value of square root of beta2. Ignored if optimizer is adamW",
|
||||||
)
|
)
|
||||||
parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay")
|
parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay")
|
||||||
@@ -861,7 +861,7 @@ class DreamBoothDataset(Dataset):
|
|||||||
else:
|
else:
|
||||||
example["instance_prompt"] = self.instance_prompt
|
example["instance_prompt"] = self.instance_prompt
|
||||||
|
|
||||||
else: # costum prompts were provided, but length does not match size of image dataset
|
else: # custom prompts were provided, but length does not match size of image dataset
|
||||||
example["instance_prompt"] = self.instance_prompt
|
example["instance_prompt"] = self.instance_prompt
|
||||||
|
|
||||||
if self.class_data_root:
|
if self.class_data_root:
|
||||||
@@ -1289,8 +1289,8 @@ def main(args):
|
|||||||
models = [unet_]
|
models = [unet_]
|
||||||
if args.train_text_encoder:
|
if args.train_text_encoder:
|
||||||
models.extend([text_encoder_one_, text_encoder_two_])
|
models.extend([text_encoder_one_, text_encoder_two_])
|
||||||
# only upcast trainable parameters (LoRA) into fp32
|
# only upcast trainable parameters (LoRA) into fp32
|
||||||
cast_training_params(models)
|
cast_training_params(models)
|
||||||
|
|
||||||
accelerator.register_save_state_pre_hook(save_model_hook)
|
accelerator.register_save_state_pre_hook(save_model_hook)
|
||||||
accelerator.register_load_state_pre_hook(load_model_hook)
|
accelerator.register_load_state_pre_hook(load_model_hook)
|
||||||
@@ -1488,7 +1488,7 @@ def main(args):
|
|||||||
if args.with_prior_preservation:
|
if args.with_prior_preservation:
|
||||||
prompt_embeds = torch.cat([prompt_embeds, class_prompt_hidden_states], dim=0)
|
prompt_embeds = torch.cat([prompt_embeds, class_prompt_hidden_states], dim=0)
|
||||||
unet_add_text_embeds = torch.cat([unet_add_text_embeds, class_pooled_prompt_embeds], dim=0)
|
unet_add_text_embeds = torch.cat([unet_add_text_embeds, class_pooled_prompt_embeds], dim=0)
|
||||||
# if we're optmizing the text encoder (both if instance prompt is used for all images or custom prompts) we need to tokenize and encode the
|
# if we're optimizing the text encoder (both if instance prompt is used for all images or custom prompts) we need to tokenize and encode the
|
||||||
# batch prompts on all training steps
|
# batch prompts on all training steps
|
||||||
else:
|
else:
|
||||||
tokens_one = tokenize_prompt(tokenizer_one, args.instance_prompt)
|
tokens_one = tokenize_prompt(tokenizer_one, args.instance_prompt)
|
||||||
|
|||||||
@@ -472,7 +472,10 @@ def parse_args(input_args=None):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--weighting_scheme", type=str, default="sigma_sqrt", choices=["sigma_sqrt", "logit_normal", "mode", "cosmap"]
|
"--weighting_scheme",
|
||||||
|
type=str,
|
||||||
|
default="logit_normal",
|
||||||
|
choices=["sigma_sqrt", "logit_normal", "mode", "cosmap"],
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--logit_mean", type=float, default=0.0, help="mean to use when using the `'logit_normal'` weighting scheme."
|
"--logit_mean", type=float, default=0.0, help="mean to use when using the `'logit_normal'` weighting scheme."
|
||||||
@@ -509,7 +512,7 @@ def parse_args(input_args=None):
|
|||||||
"--prodigy_beta3",
|
"--prodigy_beta3",
|
||||||
type=float,
|
type=float,
|
||||||
default=None,
|
default=None,
|
||||||
help="coefficients for computing the Prodidy stepsize using running averages. If set to None, "
|
help="coefficients for computing the Prodigy stepsize using running averages. If set to None, "
|
||||||
"uses the value of square root of beta2. Ignored if optimizer is adamW",
|
"uses the value of square root of beta2. Ignored if optimizer is adamW",
|
||||||
)
|
)
|
||||||
parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay")
|
parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay")
|
||||||
@@ -780,7 +783,7 @@ class DreamBoothDataset(Dataset):
|
|||||||
else:
|
else:
|
||||||
example["instance_prompt"] = self.instance_prompt
|
example["instance_prompt"] = self.instance_prompt
|
||||||
|
|
||||||
else: # costum prompts were provided, but length does not match size of image dataset
|
else: # custom prompts were provided, but length does not match size of image dataset
|
||||||
example["instance_prompt"] = self.instance_prompt
|
example["instance_prompt"] = self.instance_prompt
|
||||||
|
|
||||||
if self.class_data_root:
|
if self.class_data_root:
|
||||||
@@ -1369,7 +1372,6 @@ def main(args):
|
|||||||
if not args.train_text_encoder and not train_dataset.custom_instance_prompts:
|
if not args.train_text_encoder and not train_dataset.custom_instance_prompts:
|
||||||
del tokenizers, text_encoders
|
del tokenizers, text_encoders
|
||||||
# Explicitly delete the objects as well, otherwise only the lists are deleted and the original references remain, preventing garbage collection
|
# Explicitly delete the objects as well, otherwise only the lists are deleted and the original references remain, preventing garbage collection
|
||||||
del tokenizer_one, tokenizer_two, tokenizer_three
|
|
||||||
del text_encoder_one, text_encoder_two, text_encoder_three
|
del text_encoder_one, text_encoder_two, text_encoder_three
|
||||||
gc.collect()
|
gc.collect()
|
||||||
if torch.cuda.is_available():
|
if torch.cuda.is_available():
|
||||||
@@ -1386,7 +1388,7 @@ def main(args):
|
|||||||
if args.with_prior_preservation:
|
if args.with_prior_preservation:
|
||||||
prompt_embeds = torch.cat([prompt_embeds, class_prompt_hidden_states], dim=0)
|
prompt_embeds = torch.cat([prompt_embeds, class_prompt_hidden_states], dim=0)
|
||||||
pooled_prompt_embeds = torch.cat([pooled_prompt_embeds, class_pooled_prompt_embeds], dim=0)
|
pooled_prompt_embeds = torch.cat([pooled_prompt_embeds, class_pooled_prompt_embeds], dim=0)
|
||||||
# if we're optmizing the text encoder (both if instance prompt is used for all images or custom prompts) we need to tokenize and encode the
|
# if we're optimizing the text encoder (both if instance prompt is used for all images or custom prompts) we need to tokenize and encode the
|
||||||
# batch prompts on all training steps
|
# batch prompts on all training steps
|
||||||
else:
|
else:
|
||||||
tokens_one = tokenize_prompt(tokenizer_one, args.instance_prompt)
|
tokens_one = tokenize_prompt(tokenizer_one, args.instance_prompt)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ accelerate launch finetune_instruct_pix2pix.py \
|
|||||||
## Inference
|
## Inference
|
||||||
After training the model and the lora weight of the model is stored in the ```$OUTPUT_DIR```.
|
After training the model and the lora weight of the model is stored in the ```$OUTPUT_DIR```.
|
||||||
|
|
||||||
```bash
|
```py
|
||||||
# load the base model pipeline
|
# load the base model pipeline
|
||||||
pipe_lora = StableDiffusionInstructPix2PixPipeline.from_pretrained("timbrooks/instruct-pix2pix")
|
pipe_lora = StableDiffusionInstructPix2PixPipeline.from_pretrained("timbrooks/instruct-pix2pix")
|
||||||
|
|
||||||
@@ -42,7 +42,6 @@ input_image_path = "/path/to/input_image"
|
|||||||
input_image = Image.open(input_image_path)
|
input_image = Image.open(input_image_path)
|
||||||
edited_images = pipe_lora(num_images_per_prompt=1, prompt=args.edit_prompt, image=input_image, num_inference_steps=1000).images
|
edited_images = pipe_lora(num_images_per_prompt=1, prompt=args.edit_prompt, image=input_image, num_inference_steps=1000).images
|
||||||
edited_images[0].show()
|
edited_images[0].show()
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Results
|
## Results
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ This aims to provide diffusers examples with Intel optimizations such as Bfloat1
|
|||||||
|
|
||||||
## Accelerating the fine-tuning for textual inversion
|
## Accelerating the fine-tuning for textual inversion
|
||||||
|
|
||||||
We accelereate the fine-tuning for textual inversion with Intel Extension for PyTorch. The [examples](textual_inversion) enable both single node and multi-node distributed training with Bfloat16 support on Intel Xeon Scalable Processor.
|
We accelerate the fine-tuning for textual inversion with Intel Extension for PyTorch. The [examples](textual_inversion) enable both single node and multi-node distributed training with Bfloat16 support on Intel Xeon Scalable Processor.
|
||||||
|
|
||||||
## Accelerating the inference for Stable Diffusion using Bfloat16
|
## Accelerating the inference for Stable Diffusion using Bfloat16
|
||||||
|
|
||||||
@@ -29,7 +29,6 @@ export MALLOC_CONF="oversize_threshold:1,background_thread:true,metadata_thp:aut
|
|||||||
numactl --membind <node N> -C <cpu list> python python inference_bf16.py
|
numactl --membind <node N> -C <cpu list> python python inference_bf16.py
|
||||||
# Launch with DPMSolverMultistepScheduler
|
# Launch with DPMSolverMultistepScheduler
|
||||||
numactl --membind <node N> -C <cpu list> python python inference_bf16.py --dpm
|
numactl --membind <node N> -C <cpu list> python python inference_bf16.py --dpm
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Accelerating the inference for Stable Diffusion using INT8
|
## Accelerating the inference for Stable Diffusion using INT8
|
||||||
|
|||||||
@@ -323,7 +323,7 @@ accelerate launch train_dreambooth.py \
|
|||||||
|
|
||||||
### Using DreamBooth for other pipelines than Stable Diffusion
|
### Using DreamBooth for other pipelines than Stable Diffusion
|
||||||
|
|
||||||
Altdiffusion also support dreambooth now, the runing comman is basically the same as above, all you need to do is replace the `MODEL_NAME` like this:
|
Altdiffusion also supports dreambooth now, the running command is basically the same as above, all you need to do is replace the `MODEL_NAME` like this:
|
||||||
One can now simply change the `pretrained_model_name_or_path` to another architecture such as [`AltDiffusion`](https://huggingface.co/docs/diffusers/api/pipelines/alt_diffusion).
|
One can now simply change the `pretrained_model_name_or_path` to another architecture such as [`AltDiffusion`](https://huggingface.co/docs/diffusers/api/pipelines/alt_diffusion).
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
+5
-5
@@ -561,7 +561,7 @@ def parse_args(input_args=None):
|
|||||||
"--prodigy_beta3",
|
"--prodigy_beta3",
|
||||||
type=float,
|
type=float,
|
||||||
default=None,
|
default=None,
|
||||||
help="coefficients for computing the Prodidy stepsize using running averages. If set to None, "
|
help="coefficients for computing the Prodigy stepsize using running averages. If set to None, "
|
||||||
"uses the value of square root of beta2. Ignored if optimizer is adamW",
|
"uses the value of square root of beta2. Ignored if optimizer is adamW",
|
||||||
)
|
)
|
||||||
parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay")
|
parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay")
|
||||||
@@ -880,7 +880,7 @@ class DreamBoothDataset(Dataset):
|
|||||||
else:
|
else:
|
||||||
example["instance_prompt"] = self.instance_prompt
|
example["instance_prompt"] = self.instance_prompt
|
||||||
|
|
||||||
else: # costum prompts were provided, but length does not match size of image dataset
|
else: # custom prompts were provided, but length does not match size of image dataset
|
||||||
example["instance_prompt"] = self.instance_prompt
|
example["instance_prompt"] = self.instance_prompt
|
||||||
|
|
||||||
if self.class_data_root:
|
if self.class_data_root:
|
||||||
@@ -1363,8 +1363,8 @@ def main(args):
|
|||||||
models = [unet_]
|
models = [unet_]
|
||||||
if args.train_text_encoder:
|
if args.train_text_encoder:
|
||||||
models.extend([text_encoder_one_, text_encoder_two_])
|
models.extend([text_encoder_one_, text_encoder_two_])
|
||||||
# only upcast trainable parameters (LoRA) into fp32
|
# only upcast trainable parameters (LoRA) into fp32
|
||||||
cast_training_params(models)
|
cast_training_params(models)
|
||||||
|
|
||||||
accelerator.register_save_state_pre_hook(save_model_hook)
|
accelerator.register_save_state_pre_hook(save_model_hook)
|
||||||
accelerator.register_load_state_pre_hook(load_model_hook)
|
accelerator.register_load_state_pre_hook(load_model_hook)
|
||||||
@@ -1561,7 +1561,7 @@ def main(args):
|
|||||||
if args.with_prior_preservation:
|
if args.with_prior_preservation:
|
||||||
prompt_embeds = torch.cat([prompt_embeds, class_prompt_hidden_states], dim=0)
|
prompt_embeds = torch.cat([prompt_embeds, class_prompt_hidden_states], dim=0)
|
||||||
unet_add_text_embeds = torch.cat([unet_add_text_embeds, class_pooled_prompt_embeds], dim=0)
|
unet_add_text_embeds = torch.cat([unet_add_text_embeds, class_pooled_prompt_embeds], dim=0)
|
||||||
# if we're optmizing the text encoder (both if instance prompt is used for all images or custom prompts) we need to tokenize and encode the
|
# if we're optimizing the text encoder (both if instance prompt is used for all images or custom prompts) we need to tokenize and encode the
|
||||||
# batch prompts on all training steps
|
# batch prompts on all training steps
|
||||||
else:
|
else:
|
||||||
tokens_one = tokenize_prompt(tokenizer_one, args.instance_prompt)
|
tokens_one = tokenize_prompt(tokenizer_one, args.instance_prompt)
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Running Stable Diffusion 3 DreamBooth LoRA training under 16GB
|
||||||
|
|
||||||
|
This is an **EDUCATIONAL** project that provides utilities for DreamBooth LoRA training for [Stable Diffusion 3 (SD3)](ttps://huggingface.co/papers/2403.03206) under 16GB GPU VRAM. This means you can successfully try out this project using a [free-tier Colab Notebook](https://colab.research.google.com/github/huggingface/diffusers/blob/main/examples/research_projects/sd3_lora_colab/sd3_dreambooth_lora_16gb.ipynb) instance. 🤗
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> SD3 is gated, so you need to make sure you agree to [share your contact info](https://huggingface.co/stabilityai/stable-diffusion-3-medium-diffusers) to access the model before using it with Diffusers. Once you have access, you need to log in so your system knows you’re authorized. Use the command below to log in:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
huggingface-cli login
|
||||||
|
```
|
||||||
|
|
||||||
|
This will also allow us to push the trained model parameters to the Hugging Face Hub platform.
|
||||||
|
|
||||||
|
For setup, inference code, and details on how to run the code, please follow the Colab Notebook provided above.
|
||||||
|
|
||||||
|
## How
|
||||||
|
|
||||||
|
We make use of several techniques to make this possible:
|
||||||
|
|
||||||
|
* Compute the embeddings from the instance prompt and serialize them for later reuse. This is implemented in the [`compute_embeddings.py`](./compute_embeddings.py) script. We use an 8bit (as introduced in [`LLM.int8()`](https://arxiv.org/abs/2208.07339)) T5 to reduce memory requirements to ~10.5GB.
|
||||||
|
* In the `train_dreambooth_sd3_lora_miniature.py` script, we make use of:
|
||||||
|
* 8bit Adam for optimization through the `bitsandbytes` library.
|
||||||
|
* Gradient checkpointing and gradient accumulation.
|
||||||
|
* FP16 precision.
|
||||||
|
* Flash attention through `F.scaled_dot_product_attention()`.
|
||||||
|
|
||||||
|
Computing the text embeddings is arguably the most memory-intensive part in the pipeline as SD3 employs three text encoders. If we run them in FP32, it will take about 20GB of VRAM. With FP16, we are down to 12GB.
|
||||||
|
|
||||||
|
|
||||||
|
## Gotchas
|
||||||
|
|
||||||
|
This project is educational. It exists to showcase the possibility of fine-tuning a big diffusion system on consumer GPUs. But additional components might have to be added to obtain state-of-the-art performance. Below are some commonly known gotchas that users should be aware of:
|
||||||
|
|
||||||
|
* Training of text encoders is purposefully disabled.
|
||||||
|
* Techniques such as prior-preservation is unsupported.
|
||||||
|
* Custom instance captions for instance images are unsupported, but this should be relatively easy to integrate.
|
||||||
|
|
||||||
|
Hopefully, this project gives you a template to extend it further to suit your needs.
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# coding=utf-8
|
||||||
|
# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import glob
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
import torch
|
||||||
|
from transformers import T5EncoderModel
|
||||||
|
|
||||||
|
from diffusers import StableDiffusion3Pipeline
|
||||||
|
|
||||||
|
|
||||||
|
PROMPT = "a photo of sks dog"
|
||||||
|
MAX_SEQ_LENGTH = 77
|
||||||
|
LOCAL_DATA_DIR = "dog"
|
||||||
|
OUTPUT_PATH = "sample_embeddings.parquet"
|
||||||
|
|
||||||
|
|
||||||
|
def bytes_to_giga_bytes(bytes):
|
||||||
|
return bytes / 1024 / 1024 / 1024
|
||||||
|
|
||||||
|
|
||||||
|
def generate_image_hash(image_path):
|
||||||
|
with open(image_path, "rb") as f:
|
||||||
|
img_data = f.read()
|
||||||
|
return hashlib.sha256(img_data).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def load_sd3_pipeline():
|
||||||
|
id = "stabilityai/stable-diffusion-3-medium-diffusers"
|
||||||
|
text_encoder = T5EncoderModel.from_pretrained(id, subfolder="text_encoder_3", load_in_8bit=True, device_map="auto")
|
||||||
|
pipeline = StableDiffusion3Pipeline.from_pretrained(
|
||||||
|
id, text_encoder_3=text_encoder, transformer=None, vae=None, device_map="balanced"
|
||||||
|
)
|
||||||
|
return pipeline
|
||||||
|
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
|
def compute_embeddings(pipeline, prompt, max_sequence_length):
|
||||||
|
(
|
||||||
|
prompt_embeds,
|
||||||
|
negative_prompt_embeds,
|
||||||
|
pooled_prompt_embeds,
|
||||||
|
negative_pooled_prompt_embeds,
|
||||||
|
) = pipeline.encode_prompt(prompt=prompt, prompt_2=None, prompt_3=None, max_sequence_length=max_sequence_length)
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"{prompt_embeds.shape=}, {negative_prompt_embeds.shape=}, {pooled_prompt_embeds.shape=}, {negative_pooled_prompt_embeds.shape}"
|
||||||
|
)
|
||||||
|
|
||||||
|
max_memory = bytes_to_giga_bytes(torch.cuda.max_memory_allocated())
|
||||||
|
print(f"Max memory allocated: {max_memory:.3f} GB")
|
||||||
|
return prompt_embeds, negative_prompt_embeds, pooled_prompt_embeds, negative_pooled_prompt_embeds
|
||||||
|
|
||||||
|
|
||||||
|
def run(args):
|
||||||
|
pipeline = load_sd3_pipeline()
|
||||||
|
prompt_embeds, negative_prompt_embeds, pooled_prompt_embeds, negative_pooled_prompt_embeds = compute_embeddings(
|
||||||
|
pipeline, args.prompt, args.max_sequence_length
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assumes that the images within `args.local_image_dir` have a JPEG extension. Change
|
||||||
|
# as needed.
|
||||||
|
image_paths = glob.glob(f"{args.local_data_dir}/*.jpeg")
|
||||||
|
data = []
|
||||||
|
for image_path in image_paths:
|
||||||
|
img_hash = generate_image_hash(image_path)
|
||||||
|
data.append(
|
||||||
|
(img_hash, prompt_embeds, negative_prompt_embeds, pooled_prompt_embeds, negative_pooled_prompt_embeds)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a DataFrame
|
||||||
|
embedding_cols = [
|
||||||
|
"prompt_embeds",
|
||||||
|
"negative_prompt_embeds",
|
||||||
|
"pooled_prompt_embeds",
|
||||||
|
"negative_pooled_prompt_embeds",
|
||||||
|
]
|
||||||
|
df = pd.DataFrame(
|
||||||
|
data,
|
||||||
|
columns=["image_hash"] + embedding_cols,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Convert embedding lists to arrays (for proper storage in parquet)
|
||||||
|
for col in embedding_cols:
|
||||||
|
df[col] = df[col].apply(lambda x: x.cpu().numpy().flatten().tolist())
|
||||||
|
|
||||||
|
# Save the dataframe to a parquet file
|
||||||
|
df.to_parquet(args.output_path)
|
||||||
|
print(f"Data successfully serialized to {args.output_path}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--prompt", type=str, default=PROMPT, help="The instance prompt.")
|
||||||
|
parser.add_argument(
|
||||||
|
"--max_sequence_length",
|
||||||
|
type=int,
|
||||||
|
default=MAX_SEQ_LENGTH,
|
||||||
|
help="Maximum sequence length to use for computing the embeddings. The more the higher computational costs.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--local_data_dir", type=str, default=LOCAL_DATA_DIR, help="Path to the directory containing instance images."
|
||||||
|
)
|
||||||
|
parser.add_argument("--output_path", type=str, default=OUTPUT_PATH, help="Path to serialize the parquet file.")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
run(args)
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
|||||||
|
# VAE
|
||||||
|
|
||||||
|
`vae_roundtrip.py` Demonstrates the use of a VAE by roundtripping an image through the encoder and decoder. Original and reconstructed images are displayed side by side.
|
||||||
|
|
||||||
|
```
|
||||||
|
cd examples/research_projects/vae
|
||||||
|
python vae_roundtrip.py \
|
||||||
|
--pretrained_model_name_or_path="runwayml/stable-diffusion-v1-5" \
|
||||||
|
--subfolder="vae" \
|
||||||
|
--input_image="/path/to/your/input.png"
|
||||||
|
```
|
||||||
@@ -0,0 +1,282 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# coding=utf-8
|
||||||
|
# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import typing
|
||||||
|
from typing import Optional, Union
|
||||||
|
|
||||||
|
import torch
|
||||||
|
from PIL import Image
|
||||||
|
from torchvision import transforms # type: ignore
|
||||||
|
|
||||||
|
from diffusers.image_processor import VaeImageProcessor
|
||||||
|
from diffusers.models.autoencoders.autoencoder_kl import (
|
||||||
|
AutoencoderKL,
|
||||||
|
AutoencoderKLOutput,
|
||||||
|
)
|
||||||
|
from diffusers.models.autoencoders.autoencoder_tiny import (
|
||||||
|
AutoencoderTiny,
|
||||||
|
AutoencoderTinyOutput,
|
||||||
|
)
|
||||||
|
from diffusers.models.autoencoders.vae import DecoderOutput
|
||||||
|
|
||||||
|
|
||||||
|
SupportedAutoencoder = Union[AutoencoderKL, AutoencoderTiny]
|
||||||
|
|
||||||
|
|
||||||
|
def load_vae_model(
|
||||||
|
*,
|
||||||
|
device: torch.device,
|
||||||
|
model_name_or_path: str,
|
||||||
|
revision: Optional[str],
|
||||||
|
variant: Optional[str],
|
||||||
|
# NOTE: use subfolder="vae" if the pointed model is for stable diffusion as a whole instead of just the VAE
|
||||||
|
subfolder: Optional[str],
|
||||||
|
use_tiny_nn: bool,
|
||||||
|
) -> SupportedAutoencoder:
|
||||||
|
if use_tiny_nn:
|
||||||
|
# NOTE: These scaling factors don't have to be the same as each other.
|
||||||
|
down_scale = 2
|
||||||
|
up_scale = 2
|
||||||
|
vae = AutoencoderTiny.from_pretrained( # type: ignore
|
||||||
|
model_name_or_path,
|
||||||
|
subfolder=subfolder,
|
||||||
|
revision=revision,
|
||||||
|
variant=variant,
|
||||||
|
downscaling_scaling_factor=down_scale,
|
||||||
|
upsampling_scaling_factor=up_scale,
|
||||||
|
)
|
||||||
|
assert isinstance(vae, AutoencoderTiny)
|
||||||
|
else:
|
||||||
|
vae = AutoencoderKL.from_pretrained( # type: ignore
|
||||||
|
model_name_or_path,
|
||||||
|
subfolder=subfolder,
|
||||||
|
revision=revision,
|
||||||
|
variant=variant,
|
||||||
|
)
|
||||||
|
assert isinstance(vae, AutoencoderKL)
|
||||||
|
vae = vae.to(device)
|
||||||
|
vae.eval() # Set the model to inference mode
|
||||||
|
return vae
|
||||||
|
|
||||||
|
|
||||||
|
def pil_to_nhwc(
|
||||||
|
*,
|
||||||
|
device: torch.device,
|
||||||
|
image: Image.Image,
|
||||||
|
) -> torch.Tensor:
|
||||||
|
assert image.mode == "RGB"
|
||||||
|
transform = transforms.ToTensor()
|
||||||
|
nhwc = transform(image).unsqueeze(0).to(device) # type: ignore
|
||||||
|
assert isinstance(nhwc, torch.Tensor)
|
||||||
|
return nhwc
|
||||||
|
|
||||||
|
|
||||||
|
def nhwc_to_pil(
|
||||||
|
*,
|
||||||
|
nhwc: torch.Tensor,
|
||||||
|
) -> Image.Image:
|
||||||
|
assert nhwc.shape[0] == 1
|
||||||
|
hwc = nhwc.squeeze(0).cpu()
|
||||||
|
return transforms.ToPILImage()(hwc) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def concatenate_images(
|
||||||
|
*,
|
||||||
|
left: Image.Image,
|
||||||
|
right: Image.Image,
|
||||||
|
vertical: bool = False,
|
||||||
|
) -> Image.Image:
|
||||||
|
width1, height1 = left.size
|
||||||
|
width2, height2 = right.size
|
||||||
|
if vertical:
|
||||||
|
total_height = height1 + height2
|
||||||
|
max_width = max(width1, width2)
|
||||||
|
new_image = Image.new("RGB", (max_width, total_height))
|
||||||
|
new_image.paste(left, (0, 0))
|
||||||
|
new_image.paste(right, (0, height1))
|
||||||
|
else:
|
||||||
|
total_width = width1 + width2
|
||||||
|
max_height = max(height1, height2)
|
||||||
|
new_image = Image.new("RGB", (total_width, max_height))
|
||||||
|
new_image.paste(left, (0, 0))
|
||||||
|
new_image.paste(right, (width1, 0))
|
||||||
|
return new_image
|
||||||
|
|
||||||
|
|
||||||
|
def to_latent(
|
||||||
|
*,
|
||||||
|
rgb_nchw: torch.Tensor,
|
||||||
|
vae: SupportedAutoencoder,
|
||||||
|
) -> torch.Tensor:
|
||||||
|
rgb_nchw = VaeImageProcessor.normalize(rgb_nchw) # type: ignore
|
||||||
|
encoding_nchw = vae.encode(typing.cast(torch.FloatTensor, rgb_nchw))
|
||||||
|
if isinstance(encoding_nchw, AutoencoderKLOutput):
|
||||||
|
latent = encoding_nchw.latent_dist.sample() # type: ignore
|
||||||
|
assert isinstance(latent, torch.Tensor)
|
||||||
|
elif isinstance(encoding_nchw, AutoencoderTinyOutput):
|
||||||
|
latent = encoding_nchw.latents
|
||||||
|
do_internal_vae_scaling = False # Is this needed?
|
||||||
|
if do_internal_vae_scaling:
|
||||||
|
latent = vae.scale_latents(latent).mul(255).round().byte() # type: ignore
|
||||||
|
latent = vae.unscale_latents(latent / 255.0) # type: ignore
|
||||||
|
assert isinstance(latent, torch.Tensor)
|
||||||
|
else:
|
||||||
|
assert False, f"Unknown encoding type: {type(encoding_nchw)}"
|
||||||
|
return latent
|
||||||
|
|
||||||
|
|
||||||
|
def from_latent(
|
||||||
|
*,
|
||||||
|
latent_nchw: torch.Tensor,
|
||||||
|
vae: SupportedAutoencoder,
|
||||||
|
) -> torch.Tensor:
|
||||||
|
decoding_nchw = vae.decode(latent_nchw) # type: ignore
|
||||||
|
assert isinstance(decoding_nchw, DecoderOutput)
|
||||||
|
rgb_nchw = VaeImageProcessor.denormalize(decoding_nchw.sample) # type: ignore
|
||||||
|
assert isinstance(rgb_nchw, torch.Tensor)
|
||||||
|
return rgb_nchw
|
||||||
|
|
||||||
|
|
||||||
|
def main_kwargs(
|
||||||
|
*,
|
||||||
|
device: torch.device,
|
||||||
|
input_image_path: str,
|
||||||
|
pretrained_model_name_or_path: str,
|
||||||
|
revision: Optional[str],
|
||||||
|
variant: Optional[str],
|
||||||
|
subfolder: Optional[str],
|
||||||
|
use_tiny_nn: bool,
|
||||||
|
) -> None:
|
||||||
|
vae = load_vae_model(
|
||||||
|
device=device,
|
||||||
|
model_name_or_path=pretrained_model_name_or_path,
|
||||||
|
revision=revision,
|
||||||
|
variant=variant,
|
||||||
|
subfolder=subfolder,
|
||||||
|
use_tiny_nn=use_tiny_nn,
|
||||||
|
)
|
||||||
|
original_pil = Image.open(input_image_path).convert("RGB")
|
||||||
|
original_image = pil_to_nhwc(
|
||||||
|
device=device,
|
||||||
|
image=original_pil,
|
||||||
|
)
|
||||||
|
print(f"Original image shape: {original_image.shape}")
|
||||||
|
reconstructed_image: Optional[torch.Tensor] = None
|
||||||
|
|
||||||
|
with torch.no_grad():
|
||||||
|
latent_image = to_latent(rgb_nchw=original_image, vae=vae)
|
||||||
|
print(f"Latent shape: {latent_image.shape}")
|
||||||
|
reconstructed_image = from_latent(latent_nchw=latent_image, vae=vae)
|
||||||
|
reconstructed_pil = nhwc_to_pil(nhwc=reconstructed_image)
|
||||||
|
combined_image = concatenate_images(
|
||||||
|
left=original_pil,
|
||||||
|
right=reconstructed_pil,
|
||||||
|
vertical=False,
|
||||||
|
)
|
||||||
|
combined_image.show("Original | Reconstruction")
|
||||||
|
print(f"Reconstructed image shape: {reconstructed_image.shape}")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser(description="Inference with VAE")
|
||||||
|
parser.add_argument(
|
||||||
|
"--input_image",
|
||||||
|
type=str,
|
||||||
|
required=True,
|
||||||
|
help="Path to the input image for inference.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--pretrained_model_name_or_path",
|
||||||
|
type=str,
|
||||||
|
required=True,
|
||||||
|
help="Path to pretrained VAE model.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--revision",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help="Model version.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--variant",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help="Model file variant, e.g., 'fp16'.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--subfolder",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help="Subfolder in the model file.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--use_cuda",
|
||||||
|
action="store_true",
|
||||||
|
help="Use CUDA if available.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--use_tiny_nn",
|
||||||
|
action="store_true",
|
||||||
|
help="Use tiny neural network.",
|
||||||
|
)
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
# EXAMPLE USAGE:
|
||||||
|
#
|
||||||
|
# python vae_roundtrip.py --use_cuda --pretrained_model_name_or_path "runwayml/stable-diffusion-v1-5" --subfolder "vae" --input_image "foo.png"
|
||||||
|
#
|
||||||
|
# python vae_roundtrip.py --use_cuda --pretrained_model_name_or_path "madebyollin/taesd" --use_tiny_nn --input_image "foo.png"
|
||||||
|
#
|
||||||
|
def main_cli() -> None:
|
||||||
|
args = parse_args()
|
||||||
|
|
||||||
|
input_image_path = args.input_image
|
||||||
|
assert isinstance(input_image_path, str)
|
||||||
|
|
||||||
|
pretrained_model_name_or_path = args.pretrained_model_name_or_path
|
||||||
|
assert isinstance(pretrained_model_name_or_path, str)
|
||||||
|
|
||||||
|
revision = args.revision
|
||||||
|
assert isinstance(revision, (str, type(None)))
|
||||||
|
|
||||||
|
variant = args.variant
|
||||||
|
assert isinstance(variant, (str, type(None)))
|
||||||
|
|
||||||
|
subfolder = args.subfolder
|
||||||
|
assert isinstance(subfolder, (str, type(None)))
|
||||||
|
|
||||||
|
use_cuda = args.use_cuda
|
||||||
|
assert isinstance(use_cuda, bool)
|
||||||
|
|
||||||
|
use_tiny_nn = args.use_tiny_nn
|
||||||
|
assert isinstance(use_tiny_nn, bool)
|
||||||
|
|
||||||
|
device = torch.device("cuda" if use_cuda else "cpu")
|
||||||
|
|
||||||
|
main_kwargs(
|
||||||
|
device=device,
|
||||||
|
input_image_path=input_image_path,
|
||||||
|
pretrained_model_name_or_path=pretrained_model_name_or_path,
|
||||||
|
revision=revision,
|
||||||
|
variant=variant,
|
||||||
|
subfolder=subfolder,
|
||||||
|
use_tiny_nn=use_tiny_nn,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main_cli()
|
||||||
@@ -170,11 +170,19 @@ For our small Narutos dataset, the effects of Min-SNR weighting strategy might n
|
|||||||
|
|
||||||
Also, note that in this example, we either predict `epsilon` (i.e., the noise) or the `v_prediction`. For both of these cases, the formulation of the Min-SNR weighting strategy that we have used holds.
|
Also, note that in this example, we either predict `epsilon` (i.e., the noise) or the `v_prediction`. For both of these cases, the formulation of the Min-SNR weighting strategy that we have used holds.
|
||||||
|
|
||||||
|
|
||||||
|
#### Training with EMA weights
|
||||||
|
|
||||||
|
Through the `EMAModel` class, we support a convenient method of tracking an exponential moving average of model parameters. This helps to smooth out noise in model parameter updates and generally improves model performance. If enabled with the `--use_ema` argument, the final model checkpoint that is saved at the end of training will use the EMA weights.
|
||||||
|
|
||||||
|
EMA weights require an additional full-precision copy of the model parameters to be stored in memory, but otherwise have very little performance overhead. `--foreach_ema` can be used to further reduce the overhead. If you are short on VRAM and still want to use EMA weights, you can store them in CPU RAM by using the `--offload_ema` argument. This will keep the EMA weights in pinned CPU memory during the training step. Then, once every model parameter update, it will transfer the EMA weights back to the GPU which can then update the parameters on the GPU, before sending them back to the CPU. Both of these transfers are set up as non-blocking, so CUDA devices should be able to overlap this transfer with other computations. With sufficient bandwidth between the host and device and a sufficiently long gap between model parameter updates, storing EMA weights in CPU RAM should have no additional performance overhead, as long as no other calls force synchronization.
|
||||||
|
|
||||||
#### Training with DREAM
|
#### Training with DREAM
|
||||||
|
|
||||||
We support training epsilon (noise) prediction models using the [DREAM (Diffusion Rectification and Estimation-Adaptive Models) strategy](https://arxiv.org/abs/2312.00210). DREAM claims to increase model fidelity for the performance cost of an extra grad-less unet `forward` step in the training loop. You can turn on DREAM training by using the `--dream_training` argument. The `--dream_detail_preservation` argument controls the detail preservation variable p and is the default of 1 from the paper.
|
We support training epsilon (noise) prediction models using the [DREAM (Diffusion Rectification and Estimation-Adaptive Models) strategy](https://arxiv.org/abs/2312.00210). DREAM claims to increase model fidelity for the performance cost of an extra grad-less unet `forward` step in the training loop. You can turn on DREAM training by using the `--dream_training` argument. The `--dream_detail_preservation` argument controls the detail preservation variable p and is the default of 1 from the paper.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Training with LoRA
|
## Training with LoRA
|
||||||
|
|
||||||
Low-Rank Adaption of Large Language Models was first introduced by Microsoft in [LoRA: Low-Rank Adaptation of Large Language Models](https://arxiv.org/abs/2106.09685) by *Edward J. Hu, Yelong Shen, Phillip Wallis, Zeyuan Allen-Zhu, Yuanzhi Li, Shean Wang, Lu Wang, Weizhu Chen*.
|
Low-Rank Adaption of Large Language Models was first introduced by Microsoft in [LoRA: Low-Rank Adaptation of Large Language Models](https://arxiv.org/abs/2106.09685) by *Edward J. Hu, Yelong Shen, Phillip Wallis, Zeyuan Allen-Zhu, Yuanzhi Li, Shean Wang, Lu Wang, Weizhu Chen*.
|
||||||
|
|||||||
@@ -239,7 +239,6 @@ accelerate launch --config_file $ACCELERATE_CONFIG_FILE train_text_to_image_lor
|
|||||||
--seed=1234 \
|
--seed=1234 \
|
||||||
--output_dir="sd-naruto-model-lora-sdxl" \
|
--output_dir="sd-naruto-model-lora-sdxl" \
|
||||||
--validation_prompt="cute dragon creature"
|
--validation_prompt="cute dragon creature"
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -387,6 +387,8 @@ def parse_args():
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.")
|
parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.")
|
||||||
|
parser.add_argument("--offload_ema", action="store_true", help="Offload EMA model to CPU during training step.")
|
||||||
|
parser.add_argument("--foreach_ema", action="store_true", help="Use faster foreach implementation of EMAModel.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--non_ema_revision",
|
"--non_ema_revision",
|
||||||
type=str,
|
type=str,
|
||||||
@@ -624,7 +626,12 @@ def main():
|
|||||||
ema_unet = UNet2DConditionModel.from_pretrained(
|
ema_unet = UNet2DConditionModel.from_pretrained(
|
||||||
args.pretrained_model_name_or_path, subfolder="unet", revision=args.revision, variant=args.variant
|
args.pretrained_model_name_or_path, subfolder="unet", revision=args.revision, variant=args.variant
|
||||||
)
|
)
|
||||||
ema_unet = EMAModel(ema_unet.parameters(), model_cls=UNet2DConditionModel, model_config=ema_unet.config)
|
ema_unet = EMAModel(
|
||||||
|
ema_unet.parameters(),
|
||||||
|
model_cls=UNet2DConditionModel,
|
||||||
|
model_config=ema_unet.config,
|
||||||
|
foreach=args.foreach_ema,
|
||||||
|
)
|
||||||
|
|
||||||
if args.enable_xformers_memory_efficient_attention:
|
if args.enable_xformers_memory_efficient_attention:
|
||||||
if is_xformers_available():
|
if is_xformers_available():
|
||||||
@@ -655,9 +662,14 @@ def main():
|
|||||||
|
|
||||||
def load_model_hook(models, input_dir):
|
def load_model_hook(models, input_dir):
|
||||||
if args.use_ema:
|
if args.use_ema:
|
||||||
load_model = EMAModel.from_pretrained(os.path.join(input_dir, "unet_ema"), UNet2DConditionModel)
|
load_model = EMAModel.from_pretrained(
|
||||||
|
os.path.join(input_dir, "unet_ema"), UNet2DConditionModel, foreach=args.foreach_ema
|
||||||
|
)
|
||||||
ema_unet.load_state_dict(load_model.state_dict())
|
ema_unet.load_state_dict(load_model.state_dict())
|
||||||
ema_unet.to(accelerator.device)
|
if args.offload_ema:
|
||||||
|
ema_unet.pin_memory()
|
||||||
|
else:
|
||||||
|
ema_unet.to(accelerator.device)
|
||||||
del load_model
|
del load_model
|
||||||
|
|
||||||
for _ in range(len(models)):
|
for _ in range(len(models)):
|
||||||
@@ -833,7 +845,10 @@ def main():
|
|||||||
)
|
)
|
||||||
|
|
||||||
if args.use_ema:
|
if args.use_ema:
|
||||||
ema_unet.to(accelerator.device)
|
if args.offload_ema:
|
||||||
|
ema_unet.pin_memory()
|
||||||
|
else:
|
||||||
|
ema_unet.to(accelerator.device)
|
||||||
|
|
||||||
# For mixed precision training we cast all non-trainable weights (vae, non-lora text_encoder and non-lora unet) to half-precision
|
# For mixed precision training we cast all non-trainable weights (vae, non-lora text_encoder and non-lora unet) to half-precision
|
||||||
# as these weights are only used for inference, keeping weights in full precision is not required.
|
# as these weights are only used for inference, keeping weights in full precision is not required.
|
||||||
@@ -1011,7 +1026,11 @@ def main():
|
|||||||
# Checks if the accelerator has performed an optimization step behind the scenes
|
# Checks if the accelerator has performed an optimization step behind the scenes
|
||||||
if accelerator.sync_gradients:
|
if accelerator.sync_gradients:
|
||||||
if args.use_ema:
|
if args.use_ema:
|
||||||
|
if args.offload_ema:
|
||||||
|
ema_unet.to(device="cuda", non_blocking=True)
|
||||||
ema_unet.step(unet.parameters())
|
ema_unet.step(unet.parameters())
|
||||||
|
if args.offload_ema:
|
||||||
|
ema_unet.to(device="cpu", non_blocking=True)
|
||||||
progress_bar.update(1)
|
progress_bar.update(1)
|
||||||
global_step += 1
|
global_step += 1
|
||||||
accelerator.log({"train_loss": train_loss}, step=global_step)
|
accelerator.log({"train_loss": train_loss}, step=global_step)
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ accelerate launch train_vqgan.py \
|
|||||||
```
|
```
|
||||||
|
|
||||||
An example training run is [here](https://wandb.ai/sayakpaul/vqgan-training/runs/0m5kzdfp) by @sayakpaul and a lower scale one [here](https://wandb.ai/dsbuddy27/vqgan-training/runs/eqd6xi4n?nw=nwuserisamu). The validation images can be obtained from [here](https://huggingface.co/datasets/diffusers/docs-images/tree/main/vqgan_validation_images).
|
An example training run is [here](https://wandb.ai/sayakpaul/vqgan-training/runs/0m5kzdfp) by @sayakpaul and a lower scale one [here](https://wandb.ai/dsbuddy27/vqgan-training/runs/eqd6xi4n?nw=nwuserisamu). The validation images can be obtained from [here](https://huggingface.co/datasets/diffusers/docs-images/tree/main/vqgan_validation_images).
|
||||||
The simplest way to improve the quality of a VQGAN model is to maximize the amount of information present in the bottleneck. The easiest way to do this is increasing the image resolution. However, other ways include, but not limited to, lowering compression by downsampling fewer times or increasing the vocaburary size which at most can be around 16384. How to do this is shown below.
|
The simplest way to improve the quality of a VQGAN model is to maximize the amount of information present in the bottleneck. The easiest way to do this is increasing the image resolution. However, other ways include, but not limited to, lowering compression by downsampling fewer times or increasing the vocabulary size which at most can be around 16384. How to do this is shown below.
|
||||||
|
|
||||||
# Modifying the architecture
|
# Modifying the architecture
|
||||||
|
|
||||||
@@ -118,10 +118,10 @@ To lower the amount of layers in a VQGan, you can remove layers by modifying the
|
|||||||
"vq_embed_dim": 4
|
"vq_embed_dim": 4
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
For increasing the size of the vocaburaries you can increase num_vq_embeddings. However, [some research](https://magvit.cs.cmu.edu/v2/) shows that the representation of VQGANs start degrading after 2^14~16384 vq embeddings so it's not recommended to go past that.
|
For increasing the size of the vocabularies you can increase num_vq_embeddings. However, [some research](https://magvit.cs.cmu.edu/v2/) shows that the representation of VQGANs start degrading after 2^14~16384 vq embeddings so it's not recommended to go past that.
|
||||||
|
|
||||||
## Extra training tips/ideas
|
## Extra training tips/ideas
|
||||||
During logging take care to make sure data_time is low. data_time is the amount spent loading the data and where the GPU is not active. So essentially, it's the time wasted. The easiest way to lower data time is to increase the --dataloader_num_workers to a higher number like 4. Due to a bug in Pytorch, this only works on linux based systems. For more details check [here](https://github.com/huggingface/diffusers/issues/7646)
|
During logging take care to make sure data_time is low. data_time is the amount spent loading the data and where the GPU is not active. So essentially, it's the time wasted. The easiest way to lower data time is to increase the --dataloader_num_workers to a higher number like 4. Due to a bug in Pytorch, this only works on linux based systems. For more details check [here](https://github.com/huggingface/diffusers/issues/7646)
|
||||||
Secondly, training should seem to be done when both the discriminator and the generator loss converges.
|
Secondly, training should seem to be done when both the discriminator and the generator loss converges.
|
||||||
Thirdly, another low hanging fruit is just using ema using the --use_ema parameter. This tends to make the output images smoother. This has a con where you have to lower your batch size by 1 but it may be worth it.
|
Thirdly, another low hanging fruit is just using ema using the --use_ema parameter. This tends to make the output images smoother. This has a con where you have to lower your batch size by 1 but it may be worth it.
|
||||||
Another more experimental low hanging fruit is changing from the vgg19 to different models for the lpips loss using the --timm_model_backend. If you do this, I recommend also changing the timm_model_layers parameter to the layer in your model which you think is best for representation. However, becareful with the feature map norms since this can easily overdominate the loss.
|
Another more experimental low hanging fruit is changing from the vgg19 to different models for the lpips loss using the --timm_model_backend. If you do this, I recommend also changing the timm_model_layers parameter to the layer in your model which you think is best for representation. However, be careful with the feature map norms since this can easily overdominate the loss.
|
||||||
@@ -0,0 +1,241 @@
|
|||||||
|
import argparse
|
||||||
|
|
||||||
|
import torch
|
||||||
|
|
||||||
|
from diffusers import HunyuanDiT2DControlNetModel
|
||||||
|
|
||||||
|
|
||||||
|
def main(args):
|
||||||
|
state_dict = torch.load(args.pt_checkpoint_path, map_location="cpu")
|
||||||
|
|
||||||
|
if args.load_key != "none":
|
||||||
|
try:
|
||||||
|
state_dict = state_dict[args.load_key]
|
||||||
|
except KeyError:
|
||||||
|
raise KeyError(
|
||||||
|
f"{args.load_key} not found in the checkpoint."
|
||||||
|
"Please load from the following keys:{state_dict.keys()}"
|
||||||
|
)
|
||||||
|
device = "cuda"
|
||||||
|
|
||||||
|
model_config = HunyuanDiT2DControlNetModel.load_config(
|
||||||
|
"Tencent-Hunyuan/HunyuanDiT-v1.2-Diffusers", subfolder="transformer"
|
||||||
|
)
|
||||||
|
model_config[
|
||||||
|
"use_style_cond_and_image_meta_size"
|
||||||
|
] = args.use_style_cond_and_image_meta_size ### version <= v1.1: True; version >= v1.2: False
|
||||||
|
print(model_config)
|
||||||
|
|
||||||
|
for key in state_dict:
|
||||||
|
print("local:", key)
|
||||||
|
|
||||||
|
model = HunyuanDiT2DControlNetModel.from_config(model_config).to(device)
|
||||||
|
|
||||||
|
for key in model.state_dict():
|
||||||
|
print("diffusers:", key)
|
||||||
|
|
||||||
|
num_layers = 19
|
||||||
|
for i in range(num_layers):
|
||||||
|
# attn1
|
||||||
|
# Wkqv -> to_q, to_k, to_v
|
||||||
|
q, k, v = torch.chunk(state_dict[f"blocks.{i}.attn1.Wqkv.weight"], 3, dim=0)
|
||||||
|
q_bias, k_bias, v_bias = torch.chunk(state_dict[f"blocks.{i}.attn1.Wqkv.bias"], 3, dim=0)
|
||||||
|
state_dict[f"blocks.{i}.attn1.to_q.weight"] = q
|
||||||
|
state_dict[f"blocks.{i}.attn1.to_q.bias"] = q_bias
|
||||||
|
state_dict[f"blocks.{i}.attn1.to_k.weight"] = k
|
||||||
|
state_dict[f"blocks.{i}.attn1.to_k.bias"] = k_bias
|
||||||
|
state_dict[f"blocks.{i}.attn1.to_v.weight"] = v
|
||||||
|
state_dict[f"blocks.{i}.attn1.to_v.bias"] = v_bias
|
||||||
|
state_dict.pop(f"blocks.{i}.attn1.Wqkv.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn1.Wqkv.bias")
|
||||||
|
|
||||||
|
# q_norm, k_norm -> norm_q, norm_k
|
||||||
|
state_dict[f"blocks.{i}.attn1.norm_q.weight"] = state_dict[f"blocks.{i}.attn1.q_norm.weight"]
|
||||||
|
state_dict[f"blocks.{i}.attn1.norm_q.bias"] = state_dict[f"blocks.{i}.attn1.q_norm.bias"]
|
||||||
|
state_dict[f"blocks.{i}.attn1.norm_k.weight"] = state_dict[f"blocks.{i}.attn1.k_norm.weight"]
|
||||||
|
state_dict[f"blocks.{i}.attn1.norm_k.bias"] = state_dict[f"blocks.{i}.attn1.k_norm.bias"]
|
||||||
|
|
||||||
|
state_dict.pop(f"blocks.{i}.attn1.q_norm.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn1.q_norm.bias")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn1.k_norm.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn1.k_norm.bias")
|
||||||
|
|
||||||
|
# out_proj -> to_out
|
||||||
|
state_dict[f"blocks.{i}.attn1.to_out.0.weight"] = state_dict[f"blocks.{i}.attn1.out_proj.weight"]
|
||||||
|
state_dict[f"blocks.{i}.attn1.to_out.0.bias"] = state_dict[f"blocks.{i}.attn1.out_proj.bias"]
|
||||||
|
state_dict.pop(f"blocks.{i}.attn1.out_proj.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn1.out_proj.bias")
|
||||||
|
|
||||||
|
# attn2
|
||||||
|
# kq_proj -> to_k, to_v
|
||||||
|
k, v = torch.chunk(state_dict[f"blocks.{i}.attn2.kv_proj.weight"], 2, dim=0)
|
||||||
|
k_bias, v_bias = torch.chunk(state_dict[f"blocks.{i}.attn2.kv_proj.bias"], 2, dim=0)
|
||||||
|
state_dict[f"blocks.{i}.attn2.to_k.weight"] = k
|
||||||
|
state_dict[f"blocks.{i}.attn2.to_k.bias"] = k_bias
|
||||||
|
state_dict[f"blocks.{i}.attn2.to_v.weight"] = v
|
||||||
|
state_dict[f"blocks.{i}.attn2.to_v.bias"] = v_bias
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.kv_proj.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.kv_proj.bias")
|
||||||
|
|
||||||
|
# q_proj -> to_q
|
||||||
|
state_dict[f"blocks.{i}.attn2.to_q.weight"] = state_dict[f"blocks.{i}.attn2.q_proj.weight"]
|
||||||
|
state_dict[f"blocks.{i}.attn2.to_q.bias"] = state_dict[f"blocks.{i}.attn2.q_proj.bias"]
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.q_proj.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.q_proj.bias")
|
||||||
|
|
||||||
|
# q_norm, k_norm -> norm_q, norm_k
|
||||||
|
state_dict[f"blocks.{i}.attn2.norm_q.weight"] = state_dict[f"blocks.{i}.attn2.q_norm.weight"]
|
||||||
|
state_dict[f"blocks.{i}.attn2.norm_q.bias"] = state_dict[f"blocks.{i}.attn2.q_norm.bias"]
|
||||||
|
state_dict[f"blocks.{i}.attn2.norm_k.weight"] = state_dict[f"blocks.{i}.attn2.k_norm.weight"]
|
||||||
|
state_dict[f"blocks.{i}.attn2.norm_k.bias"] = state_dict[f"blocks.{i}.attn2.k_norm.bias"]
|
||||||
|
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.q_norm.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.q_norm.bias")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.k_norm.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.k_norm.bias")
|
||||||
|
|
||||||
|
# out_proj -> to_out
|
||||||
|
state_dict[f"blocks.{i}.attn2.to_out.0.weight"] = state_dict[f"blocks.{i}.attn2.out_proj.weight"]
|
||||||
|
state_dict[f"blocks.{i}.attn2.to_out.0.bias"] = state_dict[f"blocks.{i}.attn2.out_proj.bias"]
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.out_proj.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.out_proj.bias")
|
||||||
|
|
||||||
|
# switch norm 2 and norm 3
|
||||||
|
norm2_weight = state_dict[f"blocks.{i}.norm2.weight"]
|
||||||
|
norm2_bias = state_dict[f"blocks.{i}.norm2.bias"]
|
||||||
|
state_dict[f"blocks.{i}.norm2.weight"] = state_dict[f"blocks.{i}.norm3.weight"]
|
||||||
|
state_dict[f"blocks.{i}.norm2.bias"] = state_dict[f"blocks.{i}.norm3.bias"]
|
||||||
|
state_dict[f"blocks.{i}.norm3.weight"] = norm2_weight
|
||||||
|
state_dict[f"blocks.{i}.norm3.bias"] = norm2_bias
|
||||||
|
|
||||||
|
# norm1 -> norm1.norm
|
||||||
|
# default_modulation.1 -> norm1.linear
|
||||||
|
state_dict[f"blocks.{i}.norm1.norm.weight"] = state_dict[f"blocks.{i}.norm1.weight"]
|
||||||
|
state_dict[f"blocks.{i}.norm1.norm.bias"] = state_dict[f"blocks.{i}.norm1.bias"]
|
||||||
|
state_dict[f"blocks.{i}.norm1.linear.weight"] = state_dict[f"blocks.{i}.default_modulation.1.weight"]
|
||||||
|
state_dict[f"blocks.{i}.norm1.linear.bias"] = state_dict[f"blocks.{i}.default_modulation.1.bias"]
|
||||||
|
state_dict.pop(f"blocks.{i}.norm1.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.norm1.bias")
|
||||||
|
state_dict.pop(f"blocks.{i}.default_modulation.1.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.default_modulation.1.bias")
|
||||||
|
|
||||||
|
# mlp.fc1 -> ff.net.0, mlp.fc2 -> ff.net.2
|
||||||
|
state_dict[f"blocks.{i}.ff.net.0.proj.weight"] = state_dict[f"blocks.{i}.mlp.fc1.weight"]
|
||||||
|
state_dict[f"blocks.{i}.ff.net.0.proj.bias"] = state_dict[f"blocks.{i}.mlp.fc1.bias"]
|
||||||
|
state_dict[f"blocks.{i}.ff.net.2.weight"] = state_dict[f"blocks.{i}.mlp.fc2.weight"]
|
||||||
|
state_dict[f"blocks.{i}.ff.net.2.bias"] = state_dict[f"blocks.{i}.mlp.fc2.bias"]
|
||||||
|
state_dict.pop(f"blocks.{i}.mlp.fc1.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.mlp.fc1.bias")
|
||||||
|
state_dict.pop(f"blocks.{i}.mlp.fc2.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.mlp.fc2.bias")
|
||||||
|
|
||||||
|
# after_proj_list -> controlnet_blocks
|
||||||
|
state_dict[f"controlnet_blocks.{i}.weight"] = state_dict[f"after_proj_list.{i}.weight"]
|
||||||
|
state_dict[f"controlnet_blocks.{i}.bias"] = state_dict[f"after_proj_list.{i}.bias"]
|
||||||
|
state_dict.pop(f"after_proj_list.{i}.weight")
|
||||||
|
state_dict.pop(f"after_proj_list.{i}.bias")
|
||||||
|
|
||||||
|
# before_proj -> input_block
|
||||||
|
state_dict["input_block.weight"] = state_dict["before_proj.weight"]
|
||||||
|
state_dict["input_block.bias"] = state_dict["before_proj.bias"]
|
||||||
|
state_dict.pop("before_proj.weight")
|
||||||
|
state_dict.pop("before_proj.bias")
|
||||||
|
|
||||||
|
# pooler -> time_extra_emb
|
||||||
|
state_dict["time_extra_emb.pooler.positional_embedding"] = state_dict["pooler.positional_embedding"]
|
||||||
|
state_dict["time_extra_emb.pooler.k_proj.weight"] = state_dict["pooler.k_proj.weight"]
|
||||||
|
state_dict["time_extra_emb.pooler.k_proj.bias"] = state_dict["pooler.k_proj.bias"]
|
||||||
|
state_dict["time_extra_emb.pooler.q_proj.weight"] = state_dict["pooler.q_proj.weight"]
|
||||||
|
state_dict["time_extra_emb.pooler.q_proj.bias"] = state_dict["pooler.q_proj.bias"]
|
||||||
|
state_dict["time_extra_emb.pooler.v_proj.weight"] = state_dict["pooler.v_proj.weight"]
|
||||||
|
state_dict["time_extra_emb.pooler.v_proj.bias"] = state_dict["pooler.v_proj.bias"]
|
||||||
|
state_dict["time_extra_emb.pooler.c_proj.weight"] = state_dict["pooler.c_proj.weight"]
|
||||||
|
state_dict["time_extra_emb.pooler.c_proj.bias"] = state_dict["pooler.c_proj.bias"]
|
||||||
|
state_dict.pop("pooler.k_proj.weight")
|
||||||
|
state_dict.pop("pooler.k_proj.bias")
|
||||||
|
state_dict.pop("pooler.q_proj.weight")
|
||||||
|
state_dict.pop("pooler.q_proj.bias")
|
||||||
|
state_dict.pop("pooler.v_proj.weight")
|
||||||
|
state_dict.pop("pooler.v_proj.bias")
|
||||||
|
state_dict.pop("pooler.c_proj.weight")
|
||||||
|
state_dict.pop("pooler.c_proj.bias")
|
||||||
|
state_dict.pop("pooler.positional_embedding")
|
||||||
|
|
||||||
|
# t_embedder -> time_embedding (`TimestepEmbedding`)
|
||||||
|
state_dict["time_extra_emb.timestep_embedder.linear_1.bias"] = state_dict["t_embedder.mlp.0.bias"]
|
||||||
|
state_dict["time_extra_emb.timestep_embedder.linear_1.weight"] = state_dict["t_embedder.mlp.0.weight"]
|
||||||
|
state_dict["time_extra_emb.timestep_embedder.linear_2.bias"] = state_dict["t_embedder.mlp.2.bias"]
|
||||||
|
state_dict["time_extra_emb.timestep_embedder.linear_2.weight"] = state_dict["t_embedder.mlp.2.weight"]
|
||||||
|
|
||||||
|
state_dict.pop("t_embedder.mlp.0.bias")
|
||||||
|
state_dict.pop("t_embedder.mlp.0.weight")
|
||||||
|
state_dict.pop("t_embedder.mlp.2.bias")
|
||||||
|
state_dict.pop("t_embedder.mlp.2.weight")
|
||||||
|
|
||||||
|
# x_embedder -> pos_embd (`PatchEmbed`)
|
||||||
|
state_dict["pos_embed.proj.weight"] = state_dict["x_embedder.proj.weight"]
|
||||||
|
state_dict["pos_embed.proj.bias"] = state_dict["x_embedder.proj.bias"]
|
||||||
|
state_dict.pop("x_embedder.proj.weight")
|
||||||
|
state_dict.pop("x_embedder.proj.bias")
|
||||||
|
|
||||||
|
# mlp_t5 -> text_embedder
|
||||||
|
state_dict["text_embedder.linear_1.bias"] = state_dict["mlp_t5.0.bias"]
|
||||||
|
state_dict["text_embedder.linear_1.weight"] = state_dict["mlp_t5.0.weight"]
|
||||||
|
state_dict["text_embedder.linear_2.bias"] = state_dict["mlp_t5.2.bias"]
|
||||||
|
state_dict["text_embedder.linear_2.weight"] = state_dict["mlp_t5.2.weight"]
|
||||||
|
state_dict.pop("mlp_t5.0.bias")
|
||||||
|
state_dict.pop("mlp_t5.0.weight")
|
||||||
|
state_dict.pop("mlp_t5.2.bias")
|
||||||
|
state_dict.pop("mlp_t5.2.weight")
|
||||||
|
|
||||||
|
# extra_embedder -> extra_embedder
|
||||||
|
state_dict["time_extra_emb.extra_embedder.linear_1.bias"] = state_dict["extra_embedder.0.bias"]
|
||||||
|
state_dict["time_extra_emb.extra_embedder.linear_1.weight"] = state_dict["extra_embedder.0.weight"]
|
||||||
|
state_dict["time_extra_emb.extra_embedder.linear_2.bias"] = state_dict["extra_embedder.2.bias"]
|
||||||
|
state_dict["time_extra_emb.extra_embedder.linear_2.weight"] = state_dict["extra_embedder.2.weight"]
|
||||||
|
state_dict.pop("extra_embedder.0.bias")
|
||||||
|
state_dict.pop("extra_embedder.0.weight")
|
||||||
|
state_dict.pop("extra_embedder.2.bias")
|
||||||
|
state_dict.pop("extra_embedder.2.weight")
|
||||||
|
|
||||||
|
# style_embedder
|
||||||
|
if model_config["use_style_cond_and_image_meta_size"]:
|
||||||
|
print(state_dict["style_embedder.weight"])
|
||||||
|
print(state_dict["style_embedder.weight"].shape)
|
||||||
|
state_dict["time_extra_emb.style_embedder.weight"] = state_dict["style_embedder.weight"][0:1]
|
||||||
|
state_dict.pop("style_embedder.weight")
|
||||||
|
|
||||||
|
model.load_state_dict(state_dict)
|
||||||
|
|
||||||
|
if args.save:
|
||||||
|
model.save_pretrained(args.output_checkpoint_path)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--save", default=True, type=bool, required=False, help="Whether to save the converted pipeline or not."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--pt_checkpoint_path", default=None, type=str, required=True, help="Path to the .pt pretrained model."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--output_checkpoint_path",
|
||||||
|
default=None,
|
||||||
|
type=str,
|
||||||
|
required=False,
|
||||||
|
help="Path to the output converted diffusers pipeline.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--load_key", default="none", type=str, required=False, help="The key to load from the pretrained .pt file"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--use_style_cond_and_image_meta_size",
|
||||||
|
type=bool,
|
||||||
|
default=False,
|
||||||
|
help="version <= v1.1: True; version >= v1.2: False",
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
main(args)
|
||||||
@@ -0,0 +1,267 @@
|
|||||||
|
import argparse
|
||||||
|
|
||||||
|
import torch
|
||||||
|
|
||||||
|
from diffusers import HunyuanDiT2DModel
|
||||||
|
|
||||||
|
|
||||||
|
def main(args):
|
||||||
|
state_dict = torch.load(args.pt_checkpoint_path, map_location="cpu")
|
||||||
|
|
||||||
|
if args.load_key != "none":
|
||||||
|
try:
|
||||||
|
state_dict = state_dict[args.load_key]
|
||||||
|
except KeyError:
|
||||||
|
raise KeyError(
|
||||||
|
f"{args.load_key} not found in the checkpoint."
|
||||||
|
f"Please load from the following keys:{state_dict.keys()}"
|
||||||
|
)
|
||||||
|
|
||||||
|
device = "cuda"
|
||||||
|
model_config = HunyuanDiT2DModel.load_config("Tencent-Hunyuan/HunyuanDiT-Diffusers", subfolder="transformer")
|
||||||
|
model_config[
|
||||||
|
"use_style_cond_and_image_meta_size"
|
||||||
|
] = args.use_style_cond_and_image_meta_size ### version <= v1.1: True; version >= v1.2: False
|
||||||
|
|
||||||
|
# input_size -> sample_size, text_dim -> cross_attention_dim
|
||||||
|
for key in state_dict:
|
||||||
|
print("local:", key)
|
||||||
|
|
||||||
|
model = HunyuanDiT2DModel.from_config(model_config).to(device)
|
||||||
|
|
||||||
|
for key in model.state_dict():
|
||||||
|
print("diffusers:", key)
|
||||||
|
|
||||||
|
num_layers = 40
|
||||||
|
for i in range(num_layers):
|
||||||
|
# attn1
|
||||||
|
# Wkqv -> to_q, to_k, to_v
|
||||||
|
q, k, v = torch.chunk(state_dict[f"blocks.{i}.attn1.Wqkv.weight"], 3, dim=0)
|
||||||
|
q_bias, k_bias, v_bias = torch.chunk(state_dict[f"blocks.{i}.attn1.Wqkv.bias"], 3, dim=0)
|
||||||
|
state_dict[f"blocks.{i}.attn1.to_q.weight"] = q
|
||||||
|
state_dict[f"blocks.{i}.attn1.to_q.bias"] = q_bias
|
||||||
|
state_dict[f"blocks.{i}.attn1.to_k.weight"] = k
|
||||||
|
state_dict[f"blocks.{i}.attn1.to_k.bias"] = k_bias
|
||||||
|
state_dict[f"blocks.{i}.attn1.to_v.weight"] = v
|
||||||
|
state_dict[f"blocks.{i}.attn1.to_v.bias"] = v_bias
|
||||||
|
state_dict.pop(f"blocks.{i}.attn1.Wqkv.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn1.Wqkv.bias")
|
||||||
|
|
||||||
|
# q_norm, k_norm -> norm_q, norm_k
|
||||||
|
state_dict[f"blocks.{i}.attn1.norm_q.weight"] = state_dict[f"blocks.{i}.attn1.q_norm.weight"]
|
||||||
|
state_dict[f"blocks.{i}.attn1.norm_q.bias"] = state_dict[f"blocks.{i}.attn1.q_norm.bias"]
|
||||||
|
state_dict[f"blocks.{i}.attn1.norm_k.weight"] = state_dict[f"blocks.{i}.attn1.k_norm.weight"]
|
||||||
|
state_dict[f"blocks.{i}.attn1.norm_k.bias"] = state_dict[f"blocks.{i}.attn1.k_norm.bias"]
|
||||||
|
|
||||||
|
state_dict.pop(f"blocks.{i}.attn1.q_norm.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn1.q_norm.bias")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn1.k_norm.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn1.k_norm.bias")
|
||||||
|
|
||||||
|
# out_proj -> to_out
|
||||||
|
state_dict[f"blocks.{i}.attn1.to_out.0.weight"] = state_dict[f"blocks.{i}.attn1.out_proj.weight"]
|
||||||
|
state_dict[f"blocks.{i}.attn1.to_out.0.bias"] = state_dict[f"blocks.{i}.attn1.out_proj.bias"]
|
||||||
|
state_dict.pop(f"blocks.{i}.attn1.out_proj.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn1.out_proj.bias")
|
||||||
|
|
||||||
|
# attn2
|
||||||
|
# kq_proj -> to_k, to_v
|
||||||
|
k, v = torch.chunk(state_dict[f"blocks.{i}.attn2.kv_proj.weight"], 2, dim=0)
|
||||||
|
k_bias, v_bias = torch.chunk(state_dict[f"blocks.{i}.attn2.kv_proj.bias"], 2, dim=0)
|
||||||
|
state_dict[f"blocks.{i}.attn2.to_k.weight"] = k
|
||||||
|
state_dict[f"blocks.{i}.attn2.to_k.bias"] = k_bias
|
||||||
|
state_dict[f"blocks.{i}.attn2.to_v.weight"] = v
|
||||||
|
state_dict[f"blocks.{i}.attn2.to_v.bias"] = v_bias
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.kv_proj.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.kv_proj.bias")
|
||||||
|
|
||||||
|
# q_proj -> to_q
|
||||||
|
state_dict[f"blocks.{i}.attn2.to_q.weight"] = state_dict[f"blocks.{i}.attn2.q_proj.weight"]
|
||||||
|
state_dict[f"blocks.{i}.attn2.to_q.bias"] = state_dict[f"blocks.{i}.attn2.q_proj.bias"]
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.q_proj.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.q_proj.bias")
|
||||||
|
|
||||||
|
# q_norm, k_norm -> norm_q, norm_k
|
||||||
|
state_dict[f"blocks.{i}.attn2.norm_q.weight"] = state_dict[f"blocks.{i}.attn2.q_norm.weight"]
|
||||||
|
state_dict[f"blocks.{i}.attn2.norm_q.bias"] = state_dict[f"blocks.{i}.attn2.q_norm.bias"]
|
||||||
|
state_dict[f"blocks.{i}.attn2.norm_k.weight"] = state_dict[f"blocks.{i}.attn2.k_norm.weight"]
|
||||||
|
state_dict[f"blocks.{i}.attn2.norm_k.bias"] = state_dict[f"blocks.{i}.attn2.k_norm.bias"]
|
||||||
|
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.q_norm.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.q_norm.bias")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.k_norm.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.k_norm.bias")
|
||||||
|
|
||||||
|
# out_proj -> to_out
|
||||||
|
state_dict[f"blocks.{i}.attn2.to_out.0.weight"] = state_dict[f"blocks.{i}.attn2.out_proj.weight"]
|
||||||
|
state_dict[f"blocks.{i}.attn2.to_out.0.bias"] = state_dict[f"blocks.{i}.attn2.out_proj.bias"]
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.out_proj.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.attn2.out_proj.bias")
|
||||||
|
|
||||||
|
# switch norm 2 and norm 3
|
||||||
|
norm2_weight = state_dict[f"blocks.{i}.norm2.weight"]
|
||||||
|
norm2_bias = state_dict[f"blocks.{i}.norm2.bias"]
|
||||||
|
state_dict[f"blocks.{i}.norm2.weight"] = state_dict[f"blocks.{i}.norm3.weight"]
|
||||||
|
state_dict[f"blocks.{i}.norm2.bias"] = state_dict[f"blocks.{i}.norm3.bias"]
|
||||||
|
state_dict[f"blocks.{i}.norm3.weight"] = norm2_weight
|
||||||
|
state_dict[f"blocks.{i}.norm3.bias"] = norm2_bias
|
||||||
|
|
||||||
|
# norm1 -> norm1.norm
|
||||||
|
# default_modulation.1 -> norm1.linear
|
||||||
|
state_dict[f"blocks.{i}.norm1.norm.weight"] = state_dict[f"blocks.{i}.norm1.weight"]
|
||||||
|
state_dict[f"blocks.{i}.norm1.norm.bias"] = state_dict[f"blocks.{i}.norm1.bias"]
|
||||||
|
state_dict[f"blocks.{i}.norm1.linear.weight"] = state_dict[f"blocks.{i}.default_modulation.1.weight"]
|
||||||
|
state_dict[f"blocks.{i}.norm1.linear.bias"] = state_dict[f"blocks.{i}.default_modulation.1.bias"]
|
||||||
|
state_dict.pop(f"blocks.{i}.norm1.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.norm1.bias")
|
||||||
|
state_dict.pop(f"blocks.{i}.default_modulation.1.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.default_modulation.1.bias")
|
||||||
|
|
||||||
|
# mlp.fc1 -> ff.net.0, mlp.fc2 -> ff.net.2
|
||||||
|
state_dict[f"blocks.{i}.ff.net.0.proj.weight"] = state_dict[f"blocks.{i}.mlp.fc1.weight"]
|
||||||
|
state_dict[f"blocks.{i}.ff.net.0.proj.bias"] = state_dict[f"blocks.{i}.mlp.fc1.bias"]
|
||||||
|
state_dict[f"blocks.{i}.ff.net.2.weight"] = state_dict[f"blocks.{i}.mlp.fc2.weight"]
|
||||||
|
state_dict[f"blocks.{i}.ff.net.2.bias"] = state_dict[f"blocks.{i}.mlp.fc2.bias"]
|
||||||
|
state_dict.pop(f"blocks.{i}.mlp.fc1.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.mlp.fc1.bias")
|
||||||
|
state_dict.pop(f"blocks.{i}.mlp.fc2.weight")
|
||||||
|
state_dict.pop(f"blocks.{i}.mlp.fc2.bias")
|
||||||
|
|
||||||
|
# pooler -> time_extra_emb
|
||||||
|
state_dict["time_extra_emb.pooler.positional_embedding"] = state_dict["pooler.positional_embedding"]
|
||||||
|
state_dict["time_extra_emb.pooler.k_proj.weight"] = state_dict["pooler.k_proj.weight"]
|
||||||
|
state_dict["time_extra_emb.pooler.k_proj.bias"] = state_dict["pooler.k_proj.bias"]
|
||||||
|
state_dict["time_extra_emb.pooler.q_proj.weight"] = state_dict["pooler.q_proj.weight"]
|
||||||
|
state_dict["time_extra_emb.pooler.q_proj.bias"] = state_dict["pooler.q_proj.bias"]
|
||||||
|
state_dict["time_extra_emb.pooler.v_proj.weight"] = state_dict["pooler.v_proj.weight"]
|
||||||
|
state_dict["time_extra_emb.pooler.v_proj.bias"] = state_dict["pooler.v_proj.bias"]
|
||||||
|
state_dict["time_extra_emb.pooler.c_proj.weight"] = state_dict["pooler.c_proj.weight"]
|
||||||
|
state_dict["time_extra_emb.pooler.c_proj.bias"] = state_dict["pooler.c_proj.bias"]
|
||||||
|
state_dict.pop("pooler.k_proj.weight")
|
||||||
|
state_dict.pop("pooler.k_proj.bias")
|
||||||
|
state_dict.pop("pooler.q_proj.weight")
|
||||||
|
state_dict.pop("pooler.q_proj.bias")
|
||||||
|
state_dict.pop("pooler.v_proj.weight")
|
||||||
|
state_dict.pop("pooler.v_proj.bias")
|
||||||
|
state_dict.pop("pooler.c_proj.weight")
|
||||||
|
state_dict.pop("pooler.c_proj.bias")
|
||||||
|
state_dict.pop("pooler.positional_embedding")
|
||||||
|
|
||||||
|
# t_embedder -> time_embedding (`TimestepEmbedding`)
|
||||||
|
state_dict["time_extra_emb.timestep_embedder.linear_1.bias"] = state_dict["t_embedder.mlp.0.bias"]
|
||||||
|
state_dict["time_extra_emb.timestep_embedder.linear_1.weight"] = state_dict["t_embedder.mlp.0.weight"]
|
||||||
|
state_dict["time_extra_emb.timestep_embedder.linear_2.bias"] = state_dict["t_embedder.mlp.2.bias"]
|
||||||
|
state_dict["time_extra_emb.timestep_embedder.linear_2.weight"] = state_dict["t_embedder.mlp.2.weight"]
|
||||||
|
|
||||||
|
state_dict.pop("t_embedder.mlp.0.bias")
|
||||||
|
state_dict.pop("t_embedder.mlp.0.weight")
|
||||||
|
state_dict.pop("t_embedder.mlp.2.bias")
|
||||||
|
state_dict.pop("t_embedder.mlp.2.weight")
|
||||||
|
|
||||||
|
# x_embedder -> pos_embd (`PatchEmbed`)
|
||||||
|
state_dict["pos_embed.proj.weight"] = state_dict["x_embedder.proj.weight"]
|
||||||
|
state_dict["pos_embed.proj.bias"] = state_dict["x_embedder.proj.bias"]
|
||||||
|
state_dict.pop("x_embedder.proj.weight")
|
||||||
|
state_dict.pop("x_embedder.proj.bias")
|
||||||
|
|
||||||
|
# mlp_t5 -> text_embedder
|
||||||
|
state_dict["text_embedder.linear_1.bias"] = state_dict["mlp_t5.0.bias"]
|
||||||
|
state_dict["text_embedder.linear_1.weight"] = state_dict["mlp_t5.0.weight"]
|
||||||
|
state_dict["text_embedder.linear_2.bias"] = state_dict["mlp_t5.2.bias"]
|
||||||
|
state_dict["text_embedder.linear_2.weight"] = state_dict["mlp_t5.2.weight"]
|
||||||
|
state_dict.pop("mlp_t5.0.bias")
|
||||||
|
state_dict.pop("mlp_t5.0.weight")
|
||||||
|
state_dict.pop("mlp_t5.2.bias")
|
||||||
|
state_dict.pop("mlp_t5.2.weight")
|
||||||
|
|
||||||
|
# extra_embedder -> extra_embedder
|
||||||
|
state_dict["time_extra_emb.extra_embedder.linear_1.bias"] = state_dict["extra_embedder.0.bias"]
|
||||||
|
state_dict["time_extra_emb.extra_embedder.linear_1.weight"] = state_dict["extra_embedder.0.weight"]
|
||||||
|
state_dict["time_extra_emb.extra_embedder.linear_2.bias"] = state_dict["extra_embedder.2.bias"]
|
||||||
|
state_dict["time_extra_emb.extra_embedder.linear_2.weight"] = state_dict["extra_embedder.2.weight"]
|
||||||
|
state_dict.pop("extra_embedder.0.bias")
|
||||||
|
state_dict.pop("extra_embedder.0.weight")
|
||||||
|
state_dict.pop("extra_embedder.2.bias")
|
||||||
|
state_dict.pop("extra_embedder.2.weight")
|
||||||
|
|
||||||
|
# model.final_adaLN_modulation.1 -> norm_out.linear
|
||||||
|
def swap_scale_shift(weight):
|
||||||
|
shift, scale = weight.chunk(2, dim=0)
|
||||||
|
new_weight = torch.cat([scale, shift], dim=0)
|
||||||
|
return new_weight
|
||||||
|
|
||||||
|
state_dict["norm_out.linear.weight"] = swap_scale_shift(state_dict["final_layer.adaLN_modulation.1.weight"])
|
||||||
|
state_dict["norm_out.linear.bias"] = swap_scale_shift(state_dict["final_layer.adaLN_modulation.1.bias"])
|
||||||
|
state_dict.pop("final_layer.adaLN_modulation.1.weight")
|
||||||
|
state_dict.pop("final_layer.adaLN_modulation.1.bias")
|
||||||
|
|
||||||
|
# final_linear -> proj_out
|
||||||
|
state_dict["proj_out.weight"] = state_dict["final_layer.linear.weight"]
|
||||||
|
state_dict["proj_out.bias"] = state_dict["final_layer.linear.bias"]
|
||||||
|
state_dict.pop("final_layer.linear.weight")
|
||||||
|
state_dict.pop("final_layer.linear.bias")
|
||||||
|
|
||||||
|
# style_embedder
|
||||||
|
if model_config["use_style_cond_and_image_meta_size"]:
|
||||||
|
print(state_dict["style_embedder.weight"])
|
||||||
|
print(state_dict["style_embedder.weight"].shape)
|
||||||
|
state_dict["time_extra_emb.style_embedder.weight"] = state_dict["style_embedder.weight"][0:1]
|
||||||
|
state_dict.pop("style_embedder.weight")
|
||||||
|
|
||||||
|
model.load_state_dict(state_dict)
|
||||||
|
|
||||||
|
from diffusers import HunyuanDiTPipeline
|
||||||
|
|
||||||
|
if args.use_style_cond_and_image_meta_size:
|
||||||
|
pipe = HunyuanDiTPipeline.from_pretrained(
|
||||||
|
"Tencent-Hunyuan/HunyuanDiT-Diffusers", transformer=model, torch_dtype=torch.float32
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
pipe = HunyuanDiTPipeline.from_pretrained(
|
||||||
|
"Tencent-Hunyuan/HunyuanDiT-v1.2-Diffusers", transformer=model, torch_dtype=torch.float32
|
||||||
|
)
|
||||||
|
pipe.to("cuda")
|
||||||
|
pipe.to(dtype=torch.float32)
|
||||||
|
|
||||||
|
if args.save:
|
||||||
|
pipe.save_pretrained(args.output_checkpoint_path)
|
||||||
|
|
||||||
|
# ### NOTE: HunyuanDiT supports both Chinese and English inputs
|
||||||
|
prompt = "一个宇航员在骑马"
|
||||||
|
# prompt = "An astronaut riding a horse"
|
||||||
|
generator = torch.Generator(device="cuda").manual_seed(0)
|
||||||
|
image = pipe(
|
||||||
|
height=1024, width=1024, prompt=prompt, generator=generator, num_inference_steps=25, guidance_scale=5.0
|
||||||
|
).images[0]
|
||||||
|
|
||||||
|
image.save("img.png")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--save", default=True, type=bool, required=False, help="Whether to save the converted pipeline or not."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--pt_checkpoint_path", default=None, type=str, required=True, help="Path to the .pt pretrained model."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--output_checkpoint_path",
|
||||||
|
default=None,
|
||||||
|
type=str,
|
||||||
|
required=False,
|
||||||
|
help="Path to the output converted diffusers pipeline.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--load_key", default="none", type=str, required=False, help="The key to load from the pretrained .pt file"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--use_style_cond_and_image_meta_size",
|
||||||
|
type=bool,
|
||||||
|
default=False,
|
||||||
|
help="version <= v1.1: True; version >= v1.2: False",
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
main(args)
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
import argparse
|
||||||
|
from contextlib import nullcontext
|
||||||
|
|
||||||
|
import safetensors.torch
|
||||||
|
import torch
|
||||||
|
from accelerate import init_empty_weights
|
||||||
|
|
||||||
|
from diffusers import AutoencoderKL, SD3Transformer2DModel
|
||||||
|
from diffusers.loaders.single_file_utils import convert_ldm_vae_checkpoint
|
||||||
|
from diffusers.models.modeling_utils import load_model_dict_into_meta
|
||||||
|
from diffusers.utils.import_utils import is_accelerate_available
|
||||||
|
|
||||||
|
|
||||||
|
CTX = init_empty_weights if is_accelerate_available else nullcontext
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--checkpoint_path", type=str)
|
||||||
|
parser.add_argument("--output_path", type=str)
|
||||||
|
parser.add_argument("--dtype", type=str, default="fp16")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
dtype = torch.float16 if args.dtype == "fp16" else torch.float32
|
||||||
|
|
||||||
|
|
||||||
|
def load_original_checkpoint(ckpt_path):
|
||||||
|
original_state_dict = safetensors.torch.load_file(ckpt_path)
|
||||||
|
keys = list(original_state_dict.keys())
|
||||||
|
for k in keys:
|
||||||
|
if "model.diffusion_model." in k:
|
||||||
|
original_state_dict[k.replace("model.diffusion_model.", "")] = original_state_dict.pop(k)
|
||||||
|
|
||||||
|
return original_state_dict
|
||||||
|
|
||||||
|
|
||||||
|
# in SD3 original implementation of AdaLayerNormContinuous, it split linear projection output into shift, scale;
|
||||||
|
# while in diffusers it split into scale, shift. Here we swap the linear projection weights in order to be able to use diffusers implementation
|
||||||
|
def swap_scale_shift(weight, dim):
|
||||||
|
shift, scale = weight.chunk(2, dim=0)
|
||||||
|
new_weight = torch.cat([scale, shift], dim=0)
|
||||||
|
return new_weight
|
||||||
|
|
||||||
|
|
||||||
|
def convert_sd3_transformer_checkpoint_to_diffusers(original_state_dict, num_layers, caption_projection_dim):
|
||||||
|
converted_state_dict = {}
|
||||||
|
|
||||||
|
# Positional and patch embeddings.
|
||||||
|
converted_state_dict["pos_embed.pos_embed"] = original_state_dict.pop("pos_embed")
|
||||||
|
converted_state_dict["pos_embed.proj.weight"] = original_state_dict.pop("x_embedder.proj.weight")
|
||||||
|
converted_state_dict["pos_embed.proj.bias"] = original_state_dict.pop("x_embedder.proj.bias")
|
||||||
|
|
||||||
|
# Timestep embeddings.
|
||||||
|
converted_state_dict["time_text_embed.timestep_embedder.linear_1.weight"] = original_state_dict.pop(
|
||||||
|
"t_embedder.mlp.0.weight"
|
||||||
|
)
|
||||||
|
converted_state_dict["time_text_embed.timestep_embedder.linear_1.bias"] = original_state_dict.pop(
|
||||||
|
"t_embedder.mlp.0.bias"
|
||||||
|
)
|
||||||
|
converted_state_dict["time_text_embed.timestep_embedder.linear_2.weight"] = original_state_dict.pop(
|
||||||
|
"t_embedder.mlp.2.weight"
|
||||||
|
)
|
||||||
|
converted_state_dict["time_text_embed.timestep_embedder.linear_2.bias"] = original_state_dict.pop(
|
||||||
|
"t_embedder.mlp.2.bias"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Context projections.
|
||||||
|
converted_state_dict["context_embedder.weight"] = original_state_dict.pop("context_embedder.weight")
|
||||||
|
converted_state_dict["context_embedder.bias"] = original_state_dict.pop("context_embedder.bias")
|
||||||
|
|
||||||
|
# Pooled context projection.
|
||||||
|
converted_state_dict["time_text_embed.text_embedder.linear_1.weight"] = original_state_dict.pop(
|
||||||
|
"y_embedder.mlp.0.weight"
|
||||||
|
)
|
||||||
|
converted_state_dict["time_text_embed.text_embedder.linear_1.bias"] = original_state_dict.pop(
|
||||||
|
"y_embedder.mlp.0.bias"
|
||||||
|
)
|
||||||
|
converted_state_dict["time_text_embed.text_embedder.linear_2.weight"] = original_state_dict.pop(
|
||||||
|
"y_embedder.mlp.2.weight"
|
||||||
|
)
|
||||||
|
converted_state_dict["time_text_embed.text_embedder.linear_2.bias"] = original_state_dict.pop(
|
||||||
|
"y_embedder.mlp.2.bias"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Transformer blocks 🎸.
|
||||||
|
for i in range(num_layers):
|
||||||
|
# Q, K, V
|
||||||
|
sample_q, sample_k, sample_v = torch.chunk(
|
||||||
|
original_state_dict.pop(f"joint_blocks.{i}.x_block.attn.qkv.weight"), 3, dim=0
|
||||||
|
)
|
||||||
|
context_q, context_k, context_v = torch.chunk(
|
||||||
|
original_state_dict.pop(f"joint_blocks.{i}.context_block.attn.qkv.weight"), 3, dim=0
|
||||||
|
)
|
||||||
|
sample_q_bias, sample_k_bias, sample_v_bias = torch.chunk(
|
||||||
|
original_state_dict.pop(f"joint_blocks.{i}.x_block.attn.qkv.bias"), 3, dim=0
|
||||||
|
)
|
||||||
|
context_q_bias, context_k_bias, context_v_bias = torch.chunk(
|
||||||
|
original_state_dict.pop(f"joint_blocks.{i}.context_block.attn.qkv.bias"), 3, dim=0
|
||||||
|
)
|
||||||
|
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.attn.to_q.weight"] = torch.cat([sample_q])
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.attn.to_q.bias"] = torch.cat([sample_q_bias])
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.attn.to_k.weight"] = torch.cat([sample_k])
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.attn.to_k.bias"] = torch.cat([sample_k_bias])
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.attn.to_v.weight"] = torch.cat([sample_v])
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.attn.to_v.bias"] = torch.cat([sample_v_bias])
|
||||||
|
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.attn.add_q_proj.weight"] = torch.cat([context_q])
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.attn.add_q_proj.bias"] = torch.cat([context_q_bias])
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.attn.add_k_proj.weight"] = torch.cat([context_k])
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.attn.add_k_proj.bias"] = torch.cat([context_k_bias])
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.attn.add_v_proj.weight"] = torch.cat([context_v])
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.attn.add_v_proj.bias"] = torch.cat([context_v_bias])
|
||||||
|
|
||||||
|
# output projections.
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.attn.to_out.0.weight"] = original_state_dict.pop(
|
||||||
|
f"joint_blocks.{i}.x_block.attn.proj.weight"
|
||||||
|
)
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.attn.to_out.0.bias"] = original_state_dict.pop(
|
||||||
|
f"joint_blocks.{i}.x_block.attn.proj.bias"
|
||||||
|
)
|
||||||
|
if not (i == num_layers - 1):
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.attn.to_add_out.weight"] = original_state_dict.pop(
|
||||||
|
f"joint_blocks.{i}.context_block.attn.proj.weight"
|
||||||
|
)
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.attn.to_add_out.bias"] = original_state_dict.pop(
|
||||||
|
f"joint_blocks.{i}.context_block.attn.proj.bias"
|
||||||
|
)
|
||||||
|
|
||||||
|
# norms.
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.norm1.linear.weight"] = original_state_dict.pop(
|
||||||
|
f"joint_blocks.{i}.x_block.adaLN_modulation.1.weight"
|
||||||
|
)
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.norm1.linear.bias"] = original_state_dict.pop(
|
||||||
|
f"joint_blocks.{i}.x_block.adaLN_modulation.1.bias"
|
||||||
|
)
|
||||||
|
if not (i == num_layers - 1):
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.norm1_context.linear.weight"] = original_state_dict.pop(
|
||||||
|
f"joint_blocks.{i}.context_block.adaLN_modulation.1.weight"
|
||||||
|
)
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.norm1_context.linear.bias"] = original_state_dict.pop(
|
||||||
|
f"joint_blocks.{i}.context_block.adaLN_modulation.1.bias"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.norm1_context.linear.weight"] = swap_scale_shift(
|
||||||
|
original_state_dict.pop(f"joint_blocks.{i}.context_block.adaLN_modulation.1.weight"),
|
||||||
|
dim=caption_projection_dim,
|
||||||
|
)
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.norm1_context.linear.bias"] = swap_scale_shift(
|
||||||
|
original_state_dict.pop(f"joint_blocks.{i}.context_block.adaLN_modulation.1.bias"),
|
||||||
|
dim=caption_projection_dim,
|
||||||
|
)
|
||||||
|
|
||||||
|
# ffs.
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.ff.net.0.proj.weight"] = original_state_dict.pop(
|
||||||
|
f"joint_blocks.{i}.x_block.mlp.fc1.weight"
|
||||||
|
)
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.ff.net.0.proj.bias"] = original_state_dict.pop(
|
||||||
|
f"joint_blocks.{i}.x_block.mlp.fc1.bias"
|
||||||
|
)
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.ff.net.2.weight"] = original_state_dict.pop(
|
||||||
|
f"joint_blocks.{i}.x_block.mlp.fc2.weight"
|
||||||
|
)
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.ff.net.2.bias"] = original_state_dict.pop(
|
||||||
|
f"joint_blocks.{i}.x_block.mlp.fc2.bias"
|
||||||
|
)
|
||||||
|
if not (i == num_layers - 1):
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.ff_context.net.0.proj.weight"] = original_state_dict.pop(
|
||||||
|
f"joint_blocks.{i}.context_block.mlp.fc1.weight"
|
||||||
|
)
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.ff_context.net.0.proj.bias"] = original_state_dict.pop(
|
||||||
|
f"joint_blocks.{i}.context_block.mlp.fc1.bias"
|
||||||
|
)
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.ff_context.net.2.weight"] = original_state_dict.pop(
|
||||||
|
f"joint_blocks.{i}.context_block.mlp.fc2.weight"
|
||||||
|
)
|
||||||
|
converted_state_dict[f"transformer_blocks.{i}.ff_context.net.2.bias"] = original_state_dict.pop(
|
||||||
|
f"joint_blocks.{i}.context_block.mlp.fc2.bias"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Final blocks.
|
||||||
|
converted_state_dict["proj_out.weight"] = original_state_dict.pop("final_layer.linear.weight")
|
||||||
|
converted_state_dict["proj_out.bias"] = original_state_dict.pop("final_layer.linear.bias")
|
||||||
|
converted_state_dict["norm_out.linear.weight"] = swap_scale_shift(
|
||||||
|
original_state_dict.pop("final_layer.adaLN_modulation.1.weight"), dim=caption_projection_dim
|
||||||
|
)
|
||||||
|
converted_state_dict["norm_out.linear.bias"] = swap_scale_shift(
|
||||||
|
original_state_dict.pop("final_layer.adaLN_modulation.1.bias"), dim=caption_projection_dim
|
||||||
|
)
|
||||||
|
|
||||||
|
return converted_state_dict
|
||||||
|
|
||||||
|
|
||||||
|
def is_vae_in_checkpoint(original_state_dict):
|
||||||
|
return ("first_stage_model.decoder.conv_in.weight" in original_state_dict) and (
|
||||||
|
"first_stage_model.encoder.conv_in.weight" in original_state_dict
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main(args):
|
||||||
|
original_ckpt = load_original_checkpoint(args.checkpoint_path)
|
||||||
|
num_layers = list(set(int(k.split(".", 2)[1]) for k in original_ckpt if "joint_blocks" in k))[-1] + 1 # noqa: C401
|
||||||
|
caption_projection_dim = 1536
|
||||||
|
|
||||||
|
converted_transformer_state_dict = convert_sd3_transformer_checkpoint_to_diffusers(
|
||||||
|
original_ckpt, num_layers, caption_projection_dim
|
||||||
|
)
|
||||||
|
|
||||||
|
with CTX():
|
||||||
|
transformer = SD3Transformer2DModel(
|
||||||
|
sample_size=64,
|
||||||
|
patch_size=2,
|
||||||
|
in_channels=16,
|
||||||
|
joint_attention_dim=4096,
|
||||||
|
num_layers=num_layers,
|
||||||
|
caption_projection_dim=caption_projection_dim,
|
||||||
|
num_attention_heads=24,
|
||||||
|
pos_embed_max_size=192,
|
||||||
|
)
|
||||||
|
if is_accelerate_available():
|
||||||
|
load_model_dict_into_meta(transformer, converted_transformer_state_dict)
|
||||||
|
else:
|
||||||
|
transformer.load_state_dict(converted_transformer_state_dict, strict=True)
|
||||||
|
|
||||||
|
print("Saving SD3 Transformer in Diffusers format.")
|
||||||
|
transformer.to(dtype).save_pretrained(f"{args.output_path}/transformer")
|
||||||
|
|
||||||
|
if is_vae_in_checkpoint(original_ckpt):
|
||||||
|
with CTX():
|
||||||
|
vae = AutoencoderKL.from_config(
|
||||||
|
"stabilityai/stable-diffusion-xl-base-1.0",
|
||||||
|
subfolder="vae",
|
||||||
|
latent_channels=16,
|
||||||
|
use_post_quant_conv=False,
|
||||||
|
use_quant_conv=False,
|
||||||
|
scaling_factor=1.5305,
|
||||||
|
shift_factor=0.0609,
|
||||||
|
)
|
||||||
|
converted_vae_state_dict = convert_ldm_vae_checkpoint(original_ckpt, vae.config)
|
||||||
|
if is_accelerate_available():
|
||||||
|
load_model_dict_into_meta(vae, converted_vae_state_dict)
|
||||||
|
else:
|
||||||
|
vae.load_state_dict(converted_vae_state_dict, strict=True)
|
||||||
|
|
||||||
|
print("Saving SD3 Autoencoder in Diffusers format.")
|
||||||
|
vae.to(dtype).save_pretrained(f"{args.output_path}/vae")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(args)
|
||||||
@@ -83,7 +83,9 @@ else:
|
|||||||
"ControlNetModel",
|
"ControlNetModel",
|
||||||
"ControlNetXSAdapter",
|
"ControlNetXSAdapter",
|
||||||
"DiTTransformer2DModel",
|
"DiTTransformer2DModel",
|
||||||
|
"HunyuanDiT2DControlNetModel",
|
||||||
"HunyuanDiT2DModel",
|
"HunyuanDiT2DModel",
|
||||||
|
"HunyuanDiT2DMultiControlNetModel",
|
||||||
"I2VGenXLUNet",
|
"I2VGenXLUNet",
|
||||||
"Kandinsky3UNet",
|
"Kandinsky3UNet",
|
||||||
"ModelMixin",
|
"ModelMixin",
|
||||||
@@ -91,6 +93,8 @@ else:
|
|||||||
"MultiAdapter",
|
"MultiAdapter",
|
||||||
"PixArtTransformer2DModel",
|
"PixArtTransformer2DModel",
|
||||||
"PriorTransformer",
|
"PriorTransformer",
|
||||||
|
"SD3ControlNetModel",
|
||||||
|
"SD3MultiControlNetModel",
|
||||||
"SD3Transformer2DModel",
|
"SD3Transformer2DModel",
|
||||||
"StableCascadeUNet",
|
"StableCascadeUNet",
|
||||||
"T2IAdapter",
|
"T2IAdapter",
|
||||||
@@ -232,6 +236,7 @@ else:
|
|||||||
"BlipDiffusionPipeline",
|
"BlipDiffusionPipeline",
|
||||||
"CLIPImageProjection",
|
"CLIPImageProjection",
|
||||||
"CycleDiffusionPipeline",
|
"CycleDiffusionPipeline",
|
||||||
|
"HunyuanDiTControlNetPipeline",
|
||||||
"HunyuanDiTPipeline",
|
"HunyuanDiTPipeline",
|
||||||
"I2VGenXLPipeline",
|
"I2VGenXLPipeline",
|
||||||
"IFImg2ImgPipeline",
|
"IFImg2ImgPipeline",
|
||||||
@@ -278,6 +283,7 @@ else:
|
|||||||
"StableCascadeCombinedPipeline",
|
"StableCascadeCombinedPipeline",
|
||||||
"StableCascadeDecoderPipeline",
|
"StableCascadeDecoderPipeline",
|
||||||
"StableCascadePriorPipeline",
|
"StableCascadePriorPipeline",
|
||||||
|
"StableDiffusion3ControlNetPipeline",
|
||||||
"StableDiffusion3Img2ImgPipeline",
|
"StableDiffusion3Img2ImgPipeline",
|
||||||
"StableDiffusion3Pipeline",
|
"StableDiffusion3Pipeline",
|
||||||
"StableDiffusionAdapterPipeline",
|
"StableDiffusionAdapterPipeline",
|
||||||
@@ -298,6 +304,7 @@ else:
|
|||||||
"StableDiffusionLatentUpscalePipeline",
|
"StableDiffusionLatentUpscalePipeline",
|
||||||
"StableDiffusionLDM3DPipeline",
|
"StableDiffusionLDM3DPipeline",
|
||||||
"StableDiffusionModelEditingPipeline",
|
"StableDiffusionModelEditingPipeline",
|
||||||
|
"StableDiffusionPAGPipeline",
|
||||||
"StableDiffusionPanoramaPipeline",
|
"StableDiffusionPanoramaPipeline",
|
||||||
"StableDiffusionParadigmsPipeline",
|
"StableDiffusionParadigmsPipeline",
|
||||||
"StableDiffusionPipeline",
|
"StableDiffusionPipeline",
|
||||||
@@ -308,11 +315,15 @@ else:
|
|||||||
"StableDiffusionXLAdapterPipeline",
|
"StableDiffusionXLAdapterPipeline",
|
||||||
"StableDiffusionXLControlNetImg2ImgPipeline",
|
"StableDiffusionXLControlNetImg2ImgPipeline",
|
||||||
"StableDiffusionXLControlNetInpaintPipeline",
|
"StableDiffusionXLControlNetInpaintPipeline",
|
||||||
|
"StableDiffusionXLControlNetPAGPipeline",
|
||||||
"StableDiffusionXLControlNetPipeline",
|
"StableDiffusionXLControlNetPipeline",
|
||||||
"StableDiffusionXLControlNetXSPipeline",
|
"StableDiffusionXLControlNetXSPipeline",
|
||||||
"StableDiffusionXLImg2ImgPipeline",
|
"StableDiffusionXLImg2ImgPipeline",
|
||||||
"StableDiffusionXLInpaintPipeline",
|
"StableDiffusionXLInpaintPipeline",
|
||||||
"StableDiffusionXLInstructPix2PixPipeline",
|
"StableDiffusionXLInstructPix2PixPipeline",
|
||||||
|
"StableDiffusionXLPAGImg2ImgPipeline",
|
||||||
|
"StableDiffusionXLPAGInpaintPipeline",
|
||||||
|
"StableDiffusionXLPAGPipeline",
|
||||||
"StableDiffusionXLPipeline",
|
"StableDiffusionXLPipeline",
|
||||||
"StableUnCLIPImg2ImgPipeline",
|
"StableUnCLIPImg2ImgPipeline",
|
||||||
"StableUnCLIPPipeline",
|
"StableUnCLIPPipeline",
|
||||||
@@ -493,7 +504,9 @@ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
|||||||
ControlNetModel,
|
ControlNetModel,
|
||||||
ControlNetXSAdapter,
|
ControlNetXSAdapter,
|
||||||
DiTTransformer2DModel,
|
DiTTransformer2DModel,
|
||||||
|
HunyuanDiT2DControlNetModel,
|
||||||
HunyuanDiT2DModel,
|
HunyuanDiT2DModel,
|
||||||
|
HunyuanDiT2DMultiControlNetModel,
|
||||||
I2VGenXLUNet,
|
I2VGenXLUNet,
|
||||||
Kandinsky3UNet,
|
Kandinsky3UNet,
|
||||||
ModelMixin,
|
ModelMixin,
|
||||||
@@ -501,6 +514,8 @@ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
|||||||
MultiAdapter,
|
MultiAdapter,
|
||||||
PixArtTransformer2DModel,
|
PixArtTransformer2DModel,
|
||||||
PriorTransformer,
|
PriorTransformer,
|
||||||
|
SD3ControlNetModel,
|
||||||
|
SD3MultiControlNetModel,
|
||||||
SD3Transformer2DModel,
|
SD3Transformer2DModel,
|
||||||
T2IAdapter,
|
T2IAdapter,
|
||||||
T5FilmDecoder,
|
T5FilmDecoder,
|
||||||
@@ -620,6 +635,7 @@ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
|||||||
AudioLDMPipeline,
|
AudioLDMPipeline,
|
||||||
CLIPImageProjection,
|
CLIPImageProjection,
|
||||||
CycleDiffusionPipeline,
|
CycleDiffusionPipeline,
|
||||||
|
HunyuanDiTControlNetPipeline,
|
||||||
HunyuanDiTPipeline,
|
HunyuanDiTPipeline,
|
||||||
I2VGenXLPipeline,
|
I2VGenXLPipeline,
|
||||||
IFImg2ImgPipeline,
|
IFImg2ImgPipeline,
|
||||||
@@ -666,6 +682,7 @@ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
|||||||
StableCascadeCombinedPipeline,
|
StableCascadeCombinedPipeline,
|
||||||
StableCascadeDecoderPipeline,
|
StableCascadeDecoderPipeline,
|
||||||
StableCascadePriorPipeline,
|
StableCascadePriorPipeline,
|
||||||
|
StableDiffusion3ControlNetPipeline,
|
||||||
StableDiffusion3Img2ImgPipeline,
|
StableDiffusion3Img2ImgPipeline,
|
||||||
StableDiffusion3Pipeline,
|
StableDiffusion3Pipeline,
|
||||||
StableDiffusionAdapterPipeline,
|
StableDiffusionAdapterPipeline,
|
||||||
@@ -686,6 +703,7 @@ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
|||||||
StableDiffusionLatentUpscalePipeline,
|
StableDiffusionLatentUpscalePipeline,
|
||||||
StableDiffusionLDM3DPipeline,
|
StableDiffusionLDM3DPipeline,
|
||||||
StableDiffusionModelEditingPipeline,
|
StableDiffusionModelEditingPipeline,
|
||||||
|
StableDiffusionPAGPipeline,
|
||||||
StableDiffusionPanoramaPipeline,
|
StableDiffusionPanoramaPipeline,
|
||||||
StableDiffusionParadigmsPipeline,
|
StableDiffusionParadigmsPipeline,
|
||||||
StableDiffusionPipeline,
|
StableDiffusionPipeline,
|
||||||
@@ -696,11 +714,15 @@ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
|||||||
StableDiffusionXLAdapterPipeline,
|
StableDiffusionXLAdapterPipeline,
|
||||||
StableDiffusionXLControlNetImg2ImgPipeline,
|
StableDiffusionXLControlNetImg2ImgPipeline,
|
||||||
StableDiffusionXLControlNetInpaintPipeline,
|
StableDiffusionXLControlNetInpaintPipeline,
|
||||||
|
StableDiffusionXLControlNetPAGPipeline,
|
||||||
StableDiffusionXLControlNetPipeline,
|
StableDiffusionXLControlNetPipeline,
|
||||||
StableDiffusionXLControlNetXSPipeline,
|
StableDiffusionXLControlNetXSPipeline,
|
||||||
StableDiffusionXLImg2ImgPipeline,
|
StableDiffusionXLImg2ImgPipeline,
|
||||||
StableDiffusionXLInpaintPipeline,
|
StableDiffusionXLInpaintPipeline,
|
||||||
StableDiffusionXLInstructPix2PixPipeline,
|
StableDiffusionXLInstructPix2PixPipeline,
|
||||||
|
StableDiffusionXLPAGImg2ImgPipeline,
|
||||||
|
StableDiffusionXLPAGInpaintPipeline,
|
||||||
|
StableDiffusionXLPAGPipeline,
|
||||||
StableDiffusionXLPipeline,
|
StableDiffusionXLPipeline,
|
||||||
StableUnCLIPImg2ImgPipeline,
|
StableUnCLIPImg2ImgPipeline,
|
||||||
StableUnCLIPPipeline,
|
StableUnCLIPPipeline,
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import json
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from pathlib import PosixPath
|
from pathlib import Path
|
||||||
from typing import Any, Dict, Tuple, Union
|
from typing import Any, Dict, Tuple, Union
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@@ -587,8 +587,8 @@ class ConfigMixin:
|
|||||||
def to_json_saveable(value):
|
def to_json_saveable(value):
|
||||||
if isinstance(value, np.ndarray):
|
if isinstance(value, np.ndarray):
|
||||||
value = value.tolist()
|
value = value.tolist()
|
||||||
elif isinstance(value, PosixPath):
|
elif isinstance(value, Path):
|
||||||
value = str(value)
|
value = value.as_posix()
|
||||||
return value
|
return value
|
||||||
|
|
||||||
config_dict = {k: to_json_saveable(v) for k, v in config_dict.items()}
|
config_dict = {k: to_json_saveable(v) for k, v in config_dict.items()}
|
||||||
@@ -716,7 +716,7 @@ class LegacyConfigMixin(ConfigMixin):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_config(cls, config: Union[FrozenDict, Dict[str, Any]] = None, return_unused_kwargs=False, **kwargs):
|
def from_config(cls, config: Union[FrozenDict, Dict[str, Any]] = None, return_unused_kwargs=False, **kwargs):
|
||||||
# To prevent depedency import problem.
|
# To prevent dependency import problem.
|
||||||
from .models.model_loading_utils import _fetch_remapped_cls_from_config
|
from .models.model_loading_utils import _fetch_remapped_cls_from_config
|
||||||
|
|
||||||
# resolve remapping
|
# resolve remapping
|
||||||
|
|||||||
@@ -1,146 +0,0 @@
|
|||||||
# Copyright 2024 The HuggingFace Team. All rights reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
from huggingface_hub.utils import validate_hf_hub_args
|
|
||||||
|
|
||||||
from .single_file_utils import (
|
|
||||||
create_diffusers_vae_model_from_ldm,
|
|
||||||
fetch_ldm_config_and_checkpoint,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class FromOriginalVAEMixin:
|
|
||||||
"""
|
|
||||||
Load pretrained AutoencoderKL weights saved in the `.ckpt` or `.safetensors` format into a [`AutoencoderKL`].
|
|
||||||
"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
@validate_hf_hub_args
|
|
||||||
def from_single_file(cls, pretrained_model_link_or_path, **kwargs):
|
|
||||||
r"""
|
|
||||||
Instantiate a [`AutoencoderKL`] from pretrained ControlNet weights saved in the original `.ckpt` or
|
|
||||||
`.safetensors` format. The pipeline is set in evaluation mode (`model.eval()`) by default.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
pretrained_model_link_or_path (`str` or `os.PathLike`, *optional*):
|
|
||||||
Can be either:
|
|
||||||
- A link to the `.ckpt` file (for example
|
|
||||||
`"https://huggingface.co/<repo_id>/blob/main/<path_to_file>.ckpt"`) on the Hub.
|
|
||||||
- A path to a *file* containing all pipeline weights.
|
|
||||||
config_file (`str`, *optional*):
|
|
||||||
Filepath to the configuration YAML file associated with the model. If not provided it will default to:
|
|
||||||
https://raw.githubusercontent.com/CompVis/stable-diffusion/main/configs/stable-diffusion/v1-inference.yaml
|
|
||||||
torch_dtype (`str` or `torch.dtype`, *optional*):
|
|
||||||
Override the default `torch.dtype` and load the model with another dtype. If `"auto"` is passed, the
|
|
||||||
dtype is automatically derived from the model's weights.
|
|
||||||
force_download (`bool`, *optional*, defaults to `False`):
|
|
||||||
Whether or not to force the (re-)download of the model weights and configuration files, overriding the
|
|
||||||
cached versions if they exist.
|
|
||||||
cache_dir (`Union[str, os.PathLike]`, *optional*):
|
|
||||||
Path to a directory where a downloaded pretrained model configuration is cached if the standard cache
|
|
||||||
is not used.
|
|
||||||
resume_download:
|
|
||||||
Deprecated and ignored. All downloads are now resumed by default when possible. Will be removed in v1
|
|
||||||
of Diffusers.
|
|
||||||
proxies (`Dict[str, str]`, *optional*):
|
|
||||||
A dictionary of proxy servers to use by protocol or endpoint, for example, `{'http': 'foo.bar:3128',
|
|
||||||
'http://hostname': 'foo.bar:4012'}`. The proxies are used on each request.
|
|
||||||
local_files_only (`bool`, *optional*, defaults to `False`):
|
|
||||||
Whether to only load local model weights and configuration files or not. If set to True, the model
|
|
||||||
won't be downloaded from the Hub.
|
|
||||||
token (`str` or *bool*, *optional*):
|
|
||||||
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
|
||||||
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
|
||||||
revision (`str`, *optional*, defaults to `"main"`):
|
|
||||||
The specific model version to use. It can be a branch name, a tag name, a commit id, or any identifier
|
|
||||||
allowed by Git.
|
|
||||||
image_size (`int`, *optional*, defaults to 512):
|
|
||||||
The image size the model was trained on. Use 512 for all Stable Diffusion v1 models and the Stable
|
|
||||||
Diffusion v2 base model. Use 768 for Stable Diffusion v2.
|
|
||||||
scaling_factor (`float`, *optional*, defaults to 0.18215):
|
|
||||||
The component-wise standard deviation of the trained latent space computed using the first batch of the
|
|
||||||
training set. This is used to scale the latent space to have unit variance when training the diffusion
|
|
||||||
model. The latents are scaled with the formula `z = z * scaling_factor` before being passed to the
|
|
||||||
diffusion model. When decoding, the latents are scaled back to the original scale with the formula: `z
|
|
||||||
= 1 / scaling_factor * z`. For more details, refer to sections 4.3.2 and D.1 of the [High-Resolution
|
|
||||||
Image Synthesis with Latent Diffusion Models](https://arxiv.org/abs/2112.10752) paper.
|
|
||||||
kwargs (remaining dictionary of keyword arguments, *optional*):
|
|
||||||
Can be used to overwrite load and saveable variables (for example the pipeline components of the
|
|
||||||
specific pipeline class). The overwritten components are directly passed to the pipelines `__init__`
|
|
||||||
method. See example below for more information.
|
|
||||||
|
|
||||||
<Tip warning={true}>
|
|
||||||
|
|
||||||
Make sure to pass both `image_size` and `scaling_factor` to `from_single_file()` if you're loading
|
|
||||||
a VAE from SDXL or a Stable Diffusion v2 model or higher.
|
|
||||||
|
|
||||||
</Tip>
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
```py
|
|
||||||
from diffusers import AutoencoderKL
|
|
||||||
|
|
||||||
url = "https://huggingface.co/stabilityai/sd-vae-ft-mse-original/blob/main/vae-ft-mse-840000-ema-pruned.safetensors" # can also be local file
|
|
||||||
model = AutoencoderKL.from_single_file(url)
|
|
||||||
```
|
|
||||||
"""
|
|
||||||
|
|
||||||
original_config_file = kwargs.pop("original_config_file", None)
|
|
||||||
config_file = kwargs.pop("config_file", None)
|
|
||||||
resume_download = kwargs.pop("resume_download", None)
|
|
||||||
force_download = kwargs.pop("force_download", False)
|
|
||||||
proxies = kwargs.pop("proxies", None)
|
|
||||||
token = kwargs.pop("token", None)
|
|
||||||
cache_dir = kwargs.pop("cache_dir", None)
|
|
||||||
local_files_only = kwargs.pop("local_files_only", None)
|
|
||||||
revision = kwargs.pop("revision", None)
|
|
||||||
torch_dtype = kwargs.pop("torch_dtype", None)
|
|
||||||
|
|
||||||
class_name = cls.__name__
|
|
||||||
|
|
||||||
if (config_file is not None) and (original_config_file is not None):
|
|
||||||
raise ValueError(
|
|
||||||
"You cannot pass both `config_file` and `original_config_file` to `from_single_file`. Please use only one of these arguments."
|
|
||||||
)
|
|
||||||
|
|
||||||
original_config_file = original_config_file or config_file
|
|
||||||
original_config, checkpoint = fetch_ldm_config_and_checkpoint(
|
|
||||||
pretrained_model_link_or_path=pretrained_model_link_or_path,
|
|
||||||
class_name=class_name,
|
|
||||||
original_config_file=original_config_file,
|
|
||||||
resume_download=resume_download,
|
|
||||||
force_download=force_download,
|
|
||||||
proxies=proxies,
|
|
||||||
token=token,
|
|
||||||
revision=revision,
|
|
||||||
local_files_only=local_files_only,
|
|
||||||
cache_dir=cache_dir,
|
|
||||||
)
|
|
||||||
|
|
||||||
image_size = kwargs.pop("image_size", None)
|
|
||||||
scaling_factor = kwargs.pop("scaling_factor", None)
|
|
||||||
component = create_diffusers_vae_model_from_ldm(
|
|
||||||
class_name,
|
|
||||||
original_config,
|
|
||||||
checkpoint,
|
|
||||||
image_size=image_size,
|
|
||||||
scaling_factor=scaling_factor,
|
|
||||||
torch_dtype=torch_dtype,
|
|
||||||
)
|
|
||||||
vae = component["vae"]
|
|
||||||
if torch_dtype is not None:
|
|
||||||
vae = vae.to(torch_dtype)
|
|
||||||
|
|
||||||
return vae
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
# Copyright 2024 The HuggingFace Team. All rights reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
from huggingface_hub.utils import validate_hf_hub_args
|
|
||||||
|
|
||||||
from .single_file_utils import (
|
|
||||||
create_diffusers_controlnet_model_from_ldm,
|
|
||||||
fetch_ldm_config_and_checkpoint,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class FromOriginalControlNetMixin:
|
|
||||||
"""
|
|
||||||
Load pretrained ControlNet weights saved in the `.ckpt` or `.safetensors` format into a [`ControlNetModel`].
|
|
||||||
"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
@validate_hf_hub_args
|
|
||||||
def from_single_file(cls, pretrained_model_link_or_path, **kwargs):
|
|
||||||
r"""
|
|
||||||
Instantiate a [`ControlNetModel`] from pretrained ControlNet weights saved in the original `.ckpt` or
|
|
||||||
`.safetensors` format. The pipeline is set in evaluation mode (`model.eval()`) by default.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
pretrained_model_link_or_path (`str` or `os.PathLike`, *optional*):
|
|
||||||
Can be either:
|
|
||||||
- A link to the `.ckpt` file (for example
|
|
||||||
`"https://huggingface.co/<repo_id>/blob/main/<path_to_file>.ckpt"`) on the Hub.
|
|
||||||
- A path to a *file* containing all pipeline weights.
|
|
||||||
config_file (`str`, *optional*):
|
|
||||||
Filepath to the configuration YAML file associated with the model. If not provided it will default to:
|
|
||||||
https://raw.githubusercontent.com/lllyasviel/ControlNet/main/models/cldm_v15.yaml
|
|
||||||
torch_dtype (`str` or `torch.dtype`, *optional*):
|
|
||||||
Override the default `torch.dtype` and load the model with another dtype. If `"auto"` is passed, the
|
|
||||||
dtype is automatically derived from the model's weights.
|
|
||||||
force_download (`bool`, *optional*, defaults to `False`):
|
|
||||||
Whether or not to force the (re-)download of the model weights and configuration files, overriding the
|
|
||||||
cached versions if they exist.
|
|
||||||
cache_dir (`Union[str, os.PathLike]`, *optional*):
|
|
||||||
Path to a directory where a downloaded pretrained model configuration is cached if the standard cache
|
|
||||||
is not used.
|
|
||||||
resume_download:
|
|
||||||
Deprecated and ignored. All downloads are now resumed by default when possible. Will be removed in v1
|
|
||||||
of Diffusers.
|
|
||||||
proxies (`Dict[str, str]`, *optional*):
|
|
||||||
A dictionary of proxy servers to use by protocol or endpoint, for example, `{'http': 'foo.bar:3128',
|
|
||||||
'http://hostname': 'foo.bar:4012'}`. The proxies are used on each request.
|
|
||||||
local_files_only (`bool`, *optional*, defaults to `False`):
|
|
||||||
Whether to only load local model weights and configuration files or not. If set to True, the model
|
|
||||||
won't be downloaded from the Hub.
|
|
||||||
token (`str` or *bool*, *optional*):
|
|
||||||
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
|
||||||
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
|
||||||
revision (`str`, *optional*, defaults to `"main"`):
|
|
||||||
The specific model version to use. It can be a branch name, a tag name, a commit id, or any identifier
|
|
||||||
allowed by Git.
|
|
||||||
image_size (`int`, *optional*, defaults to 512):
|
|
||||||
The image size the model was trained on. Use 512 for all Stable Diffusion v1 models and the Stable
|
|
||||||
Diffusion v2 base model. Use 768 for Stable Diffusion v2.
|
|
||||||
upcast_attention (`bool`, *optional*, defaults to `None`):
|
|
||||||
Whether the attention computation should always be upcasted.
|
|
||||||
kwargs (remaining dictionary of keyword arguments, *optional*):
|
|
||||||
Can be used to overwrite load and saveable variables (for example the pipeline components of the
|
|
||||||
specific pipeline class). The overwritten components are directly passed to the pipelines `__init__`
|
|
||||||
method. See example below for more information.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
```py
|
|
||||||
from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
|
|
||||||
|
|
||||||
url = "https://huggingface.co/lllyasviel/ControlNet-v1-1/blob/main/control_v11p_sd15_canny.pth" # can also be a local path
|
|
||||||
model = ControlNetModel.from_single_file(url)
|
|
||||||
|
|
||||||
url = "https://huggingface.co/runwayml/stable-diffusion-v1-5/blob/main/v1-5-pruned.safetensors" # can also be a local path
|
|
||||||
pipe = StableDiffusionControlNetPipeline.from_single_file(url, controlnet=controlnet)
|
|
||||||
```
|
|
||||||
"""
|
|
||||||
original_config_file = kwargs.pop("original_config_file", None)
|
|
||||||
config_file = kwargs.pop("config_file", None)
|
|
||||||
resume_download = kwargs.pop("resume_download", None)
|
|
||||||
force_download = kwargs.pop("force_download", False)
|
|
||||||
proxies = kwargs.pop("proxies", None)
|
|
||||||
token = kwargs.pop("token", None)
|
|
||||||
cache_dir = kwargs.pop("cache_dir", None)
|
|
||||||
local_files_only = kwargs.pop("local_files_only", None)
|
|
||||||
revision = kwargs.pop("revision", None)
|
|
||||||
torch_dtype = kwargs.pop("torch_dtype", None)
|
|
||||||
|
|
||||||
class_name = cls.__name__
|
|
||||||
if (config_file is not None) and (original_config_file is not None):
|
|
||||||
raise ValueError(
|
|
||||||
"You cannot pass both `config_file` and `original_config_file` to `from_single_file`. Please use only one of these arguments."
|
|
||||||
)
|
|
||||||
|
|
||||||
original_config_file = config_file or original_config_file
|
|
||||||
original_config, checkpoint = fetch_ldm_config_and_checkpoint(
|
|
||||||
pretrained_model_link_or_path=pretrained_model_link_or_path,
|
|
||||||
class_name=class_name,
|
|
||||||
original_config_file=original_config_file,
|
|
||||||
resume_download=resume_download,
|
|
||||||
force_download=force_download,
|
|
||||||
proxies=proxies,
|
|
||||||
token=token,
|
|
||||||
revision=revision,
|
|
||||||
local_files_only=local_files_only,
|
|
||||||
cache_dir=cache_dir,
|
|
||||||
)
|
|
||||||
|
|
||||||
upcast_attention = kwargs.pop("upcast_attention", False)
|
|
||||||
image_size = kwargs.pop("image_size", None)
|
|
||||||
|
|
||||||
component = create_diffusers_controlnet_model_from_ldm(
|
|
||||||
class_name,
|
|
||||||
original_config,
|
|
||||||
checkpoint,
|
|
||||||
upcast_attention=upcast_attention,
|
|
||||||
image_size=image_size,
|
|
||||||
torch_dtype=torch_dtype,
|
|
||||||
)
|
|
||||||
controlnet = component["controlnet"]
|
|
||||||
if torch_dtype is not None:
|
|
||||||
controlnet = controlnet.to(torch_dtype)
|
|
||||||
|
|
||||||
return controlnet
|
|
||||||
+107
-16
@@ -30,6 +30,7 @@ from ..utils import (
|
|||||||
_get_model_file,
|
_get_model_file,
|
||||||
convert_state_dict_to_diffusers,
|
convert_state_dict_to_diffusers,
|
||||||
convert_state_dict_to_peft,
|
convert_state_dict_to_peft,
|
||||||
|
convert_unet_state_dict_to_peft,
|
||||||
delete_adapter_layers,
|
delete_adapter_layers,
|
||||||
get_adapter_name,
|
get_adapter_name,
|
||||||
get_peft_kwargs,
|
get_peft_kwargs,
|
||||||
@@ -42,7 +43,7 @@ from ..utils import (
|
|||||||
set_adapter_layers,
|
set_adapter_layers,
|
||||||
set_weights_and_activate_adapters,
|
set_weights_and_activate_adapters,
|
||||||
)
|
)
|
||||||
from .lora_conversion_utils import _convert_kohya_lora_to_diffusers, _maybe_map_sgm_blocks_to_diffusers
|
from .lora_conversion_utils import _convert_non_diffusers_lora_to_diffusers, _maybe_map_sgm_blocks_to_diffusers
|
||||||
|
|
||||||
|
|
||||||
if is_transformers_available():
|
if is_transformers_available():
|
||||||
@@ -287,7 +288,7 @@ class LoraLoaderMixin:
|
|||||||
if unet_config is not None:
|
if unet_config is not None:
|
||||||
# use unet config to remap block numbers
|
# use unet config to remap block numbers
|
||||||
state_dict = _maybe_map_sgm_blocks_to_diffusers(state_dict, unet_config)
|
state_dict = _maybe_map_sgm_blocks_to_diffusers(state_dict, unet_config)
|
||||||
state_dict, network_alphas = _convert_kohya_lora_to_diffusers(state_dict)
|
state_dict, network_alphas = _convert_non_diffusers_lora_to_diffusers(state_dict)
|
||||||
|
|
||||||
return state_dict, network_alphas
|
return state_dict, network_alphas
|
||||||
|
|
||||||
@@ -395,8 +396,7 @@ class LoraLoaderMixin:
|
|||||||
# their prefixes.
|
# their prefixes.
|
||||||
keys = list(state_dict.keys())
|
keys = list(state_dict.keys())
|
||||||
only_text_encoder = all(key.startswith(cls.text_encoder_name) for key in keys)
|
only_text_encoder = all(key.startswith(cls.text_encoder_name) for key in keys)
|
||||||
|
if not only_text_encoder:
|
||||||
if any(key.startswith(cls.unet_name) for key in keys) and not only_text_encoder:
|
|
||||||
# Load the layers corresponding to UNet.
|
# Load the layers corresponding to UNet.
|
||||||
logger.info(f"Loading {cls.unet_name}.")
|
logger.info(f"Loading {cls.unet_name}.")
|
||||||
unet.load_attn_procs(
|
unet.load_attn_procs(
|
||||||
@@ -462,17 +462,18 @@ class LoraLoaderMixin:
|
|||||||
text_encoder_lora_state_dict = convert_state_dict_to_peft(text_encoder_lora_state_dict)
|
text_encoder_lora_state_dict = convert_state_dict_to_peft(text_encoder_lora_state_dict)
|
||||||
|
|
||||||
for name, _ in text_encoder_attn_modules(text_encoder):
|
for name, _ in text_encoder_attn_modules(text_encoder):
|
||||||
rank_key = f"{name}.out_proj.lora_B.weight"
|
for module in ("out_proj", "q_proj", "k_proj", "v_proj"):
|
||||||
rank[rank_key] = text_encoder_lora_state_dict[rank_key].shape[1]
|
rank_key = f"{name}.{module}.lora_B.weight"
|
||||||
|
if rank_key not in text_encoder_lora_state_dict:
|
||||||
|
continue
|
||||||
|
rank[rank_key] = text_encoder_lora_state_dict[rank_key].shape[1]
|
||||||
|
|
||||||
patch_mlp = any(".mlp." in key for key in text_encoder_lora_state_dict.keys())
|
for name, _ in text_encoder_mlp_modules(text_encoder):
|
||||||
if patch_mlp:
|
for module in ("fc1", "fc2"):
|
||||||
for name, _ in text_encoder_mlp_modules(text_encoder):
|
rank_key = f"{name}.{module}.lora_B.weight"
|
||||||
rank_key_fc1 = f"{name}.fc1.lora_B.weight"
|
if rank_key not in text_encoder_lora_state_dict:
|
||||||
rank_key_fc2 = f"{name}.fc2.lora_B.weight"
|
continue
|
||||||
|
rank[rank_key] = text_encoder_lora_state_dict[rank_key].shape[1]
|
||||||
rank[rank_key_fc1] = text_encoder_lora_state_dict[rank_key_fc1].shape[1]
|
|
||||||
rank[rank_key_fc2] = text_encoder_lora_state_dict[rank_key_fc2].shape[1]
|
|
||||||
|
|
||||||
if network_alphas is not None:
|
if network_alphas is not None:
|
||||||
alpha_keys = [
|
alpha_keys = [
|
||||||
@@ -1542,6 +1543,11 @@ class SD3LoraLoaderMixin:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(state_dict.keys()) > 0:
|
if len(state_dict.keys()) > 0:
|
||||||
|
# check with first key if is not in peft format
|
||||||
|
first_key = next(iter(state_dict.keys()))
|
||||||
|
if "lora_A" not in first_key:
|
||||||
|
state_dict = convert_unet_state_dict_to_peft(state_dict)
|
||||||
|
|
||||||
if adapter_name in getattr(transformer, "peft_config", {}):
|
if adapter_name in getattr(transformer, "peft_config", {}):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Adapter name {adapter_name} already in use in the transformer - please select a new adapter name."
|
f"Adapter name {adapter_name} already in use in the transformer - please select a new adapter name."
|
||||||
@@ -1594,6 +1600,8 @@ class SD3LoraLoaderMixin:
|
|||||||
cls,
|
cls,
|
||||||
save_directory: Union[str, os.PathLike],
|
save_directory: Union[str, os.PathLike],
|
||||||
transformer_lora_layers: Dict[str, torch.nn.Module] = None,
|
transformer_lora_layers: Dict[str, torch.nn.Module] = None,
|
||||||
|
text_encoder_lora_layers: Dict[str, Union[torch.nn.Module, torch.Tensor]] = None,
|
||||||
|
text_encoder_2_lora_layers: Dict[str, Union[torch.nn.Module, torch.Tensor]] = None,
|
||||||
is_main_process: bool = True,
|
is_main_process: bool = True,
|
||||||
weight_name: str = None,
|
weight_name: str = None,
|
||||||
save_function: Callable = None,
|
save_function: Callable = None,
|
||||||
@@ -1625,12 +1633,20 @@ class SD3LoraLoaderMixin:
|
|||||||
layers_state_dict = {f"{prefix}.{module_name}": param for module_name, param in layers_weights.items()}
|
layers_state_dict = {f"{prefix}.{module_name}": param for module_name, param in layers_weights.items()}
|
||||||
return layers_state_dict
|
return layers_state_dict
|
||||||
|
|
||||||
if not transformer_lora_layers:
|
if not (transformer_lora_layers or text_encoder_lora_layers or text_encoder_2_lora_layers):
|
||||||
raise ValueError("You must pass `transformer_lora_layers`.")
|
raise ValueError(
|
||||||
|
"You must pass at least one of `transformer_lora_layers`, `text_encoder_lora_layers`, `text_encoder_2_lora_layers`."
|
||||||
|
)
|
||||||
|
|
||||||
if transformer_lora_layers:
|
if transformer_lora_layers:
|
||||||
state_dict.update(pack_weights(transformer_lora_layers, cls.transformer_name))
|
state_dict.update(pack_weights(transformer_lora_layers, cls.transformer_name))
|
||||||
|
|
||||||
|
if text_encoder_lora_layers:
|
||||||
|
state_dict.update(pack_weights(text_encoder_lora_layers, "text_encoder"))
|
||||||
|
|
||||||
|
if text_encoder_2_lora_layers:
|
||||||
|
state_dict.update(pack_weights(text_encoder_2_lora_layers, "text_encoder_2"))
|
||||||
|
|
||||||
# Save the model
|
# Save the model
|
||||||
cls.write_lora_layers(
|
cls.write_lora_layers(
|
||||||
state_dict=state_dict,
|
state_dict=state_dict,
|
||||||
@@ -1727,3 +1743,78 @@ class SD3LoraLoaderMixin:
|
|||||||
remove_hook_from_module(component, recurse=is_sequential_cpu_offload)
|
remove_hook_from_module(component, recurse=is_sequential_cpu_offload)
|
||||||
|
|
||||||
return (is_model_cpu_offload, is_sequential_cpu_offload)
|
return (is_model_cpu_offload, is_sequential_cpu_offload)
|
||||||
|
|
||||||
|
def fuse_lora(
|
||||||
|
self,
|
||||||
|
fuse_transformer: bool = True,
|
||||||
|
lora_scale: float = 1.0,
|
||||||
|
safe_fusing: bool = False,
|
||||||
|
adapter_names: Optional[List[str]] = None,
|
||||||
|
):
|
||||||
|
r"""
|
||||||
|
Fuses the LoRA parameters into the original parameters of the corresponding blocks.
|
||||||
|
|
||||||
|
<Tip warning={true}>
|
||||||
|
|
||||||
|
This is an experimental API.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
fuse_transformer (`bool`, defaults to `True`): Whether to fuse the transformer LoRA parameters.
|
||||||
|
lora_scale (`float`, defaults to 1.0):
|
||||||
|
Controls how much to influence the outputs with the LoRA parameters.
|
||||||
|
safe_fusing (`bool`, defaults to `False`):
|
||||||
|
Whether to check fused weights for NaN values before fusing and if values are NaN not fusing them.
|
||||||
|
adapter_names (`List[str]`, *optional*):
|
||||||
|
Adapter names to be used for fusing. If nothing is passed, all active adapters will be fused.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from diffusers import DiffusionPipeline
|
||||||
|
import torch
|
||||||
|
|
||||||
|
pipeline = DiffusionPipeline.from_pretrained(
|
||||||
|
"stabilityai/stable-diffusion-3-medium-diffusers", torch_dtype=torch.float16
|
||||||
|
).to("cuda")
|
||||||
|
pipeline.load_lora_weights(
|
||||||
|
"nerijs/pixel-art-medium-128-v0.1",
|
||||||
|
weight_name="pixel-art-medium-128-v0.1.safetensors",
|
||||||
|
adapter_name="pixel",
|
||||||
|
)
|
||||||
|
pipeline.fuse_lora(lora_scale=0.7)
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
if fuse_transformer:
|
||||||
|
self.num_fused_loras += 1
|
||||||
|
|
||||||
|
if fuse_transformer:
|
||||||
|
transformer = (
|
||||||
|
getattr(self, self.transformer_name) if not hasattr(self, "transformer") else self.transformer
|
||||||
|
)
|
||||||
|
transformer.fuse_lora(lora_scale, safe_fusing=safe_fusing, adapter_names=adapter_names)
|
||||||
|
|
||||||
|
def unfuse_lora(self, unfuse_transformer: bool = True):
|
||||||
|
r"""
|
||||||
|
Reverses the effect of
|
||||||
|
[`pipe.fuse_lora()`](https://huggingface.co/docs/diffusers/main/en/api/loaders#diffusers.loaders.LoraLoaderMixin.fuse_lora).
|
||||||
|
|
||||||
|
<Tip warning={true}>
|
||||||
|
|
||||||
|
This is an experimental API.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
unfuse_transformer (`bool`, defaults to `True`): Whether to unfuse the transformer LoRA parameters.
|
||||||
|
"""
|
||||||
|
from peft.tuners.tuners_utils import BaseTunerLayer
|
||||||
|
|
||||||
|
transformer = getattr(self, self.transformer_name) if not hasattr(self, "transformer") else self.transformer
|
||||||
|
if unfuse_transformer:
|
||||||
|
for module in transformer.modules():
|
||||||
|
if isinstance(module, BaseTunerLayer):
|
||||||
|
module.unmerge()
|
||||||
|
|
||||||
|
self.num_fused_loras -= 1
|
||||||
|
|||||||
@@ -123,134 +123,76 @@ def _maybe_map_sgm_blocks_to_diffusers(state_dict, unet_config, delimiter="_", b
|
|||||||
return new_state_dict
|
return new_state_dict
|
||||||
|
|
||||||
|
|
||||||
def _convert_kohya_lora_to_diffusers(state_dict, unet_name="unet", text_encoder_name="text_encoder"):
|
def _convert_non_diffusers_lora_to_diffusers(state_dict, unet_name="unet", text_encoder_name="text_encoder"):
|
||||||
|
"""
|
||||||
|
Converts a non-Diffusers LoRA state dict to a Diffusers compatible state dict.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
state_dict (`dict`): The state dict to convert.
|
||||||
|
unet_name (`str`, optional): The name of the U-Net module in the Diffusers model. Defaults to "unet".
|
||||||
|
text_encoder_name (`str`, optional): The name of the text encoder module in the Diffusers model. Defaults to
|
||||||
|
"text_encoder".
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
`tuple`: A tuple containing the converted state dict and a dictionary of alphas.
|
||||||
|
"""
|
||||||
unet_state_dict = {}
|
unet_state_dict = {}
|
||||||
te_state_dict = {}
|
te_state_dict = {}
|
||||||
te2_state_dict = {}
|
te2_state_dict = {}
|
||||||
network_alphas = {}
|
network_alphas = {}
|
||||||
is_unet_dora_lora = any("dora_scale" in k and "lora_unet_" in k for k in state_dict)
|
|
||||||
is_te_dora_lora = any("dora_scale" in k and ("lora_te_" in k or "lora_te1_" in k) for k in state_dict)
|
|
||||||
is_te2_dora_lora = any("dora_scale" in k and "lora_te2_" in k for k in state_dict)
|
|
||||||
|
|
||||||
if is_unet_dora_lora or is_te_dora_lora or is_te2_dora_lora:
|
# Check for DoRA-enabled LoRAs.
|
||||||
|
dora_present_in_unet = any("dora_scale" in k and "lora_unet_" in k for k in state_dict)
|
||||||
|
dora_present_in_te = any("dora_scale" in k and ("lora_te_" in k or "lora_te1_" in k) for k in state_dict)
|
||||||
|
dora_present_in_te2 = any("dora_scale" in k and "lora_te2_" in k for k in state_dict)
|
||||||
|
if dora_present_in_unet or dora_present_in_te or dora_present_in_te2:
|
||||||
if is_peft_version("<", "0.9.0"):
|
if is_peft_version("<", "0.9.0"):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"You need `peft` 0.9.0 at least to use DoRA-enabled LoRAs. Please upgrade your installation of `peft`."
|
"You need `peft` 0.9.0 at least to use DoRA-enabled LoRAs. Please upgrade your installation of `peft`."
|
||||||
)
|
)
|
||||||
|
|
||||||
# every down weight has a corresponding up weight and potentially an alpha weight
|
# Iterate over all LoRA weights.
|
||||||
lora_keys = [k for k in state_dict.keys() if k.endswith("lora_down.weight")]
|
all_lora_keys = list(state_dict.keys())
|
||||||
for key in lora_keys:
|
for key in all_lora_keys:
|
||||||
|
if not key.endswith("lora_down.weight"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Extract LoRA name.
|
||||||
lora_name = key.split(".")[0]
|
lora_name = key.split(".")[0]
|
||||||
|
|
||||||
|
# Find corresponding up weight and alpha.
|
||||||
lora_name_up = lora_name + ".lora_up.weight"
|
lora_name_up = lora_name + ".lora_up.weight"
|
||||||
lora_name_alpha = lora_name + ".alpha"
|
lora_name_alpha = lora_name + ".alpha"
|
||||||
|
|
||||||
|
# Handle U-Net LoRAs.
|
||||||
if lora_name.startswith("lora_unet_"):
|
if lora_name.startswith("lora_unet_"):
|
||||||
diffusers_name = key.replace("lora_unet_", "").replace("_", ".")
|
diffusers_name = _convert_unet_lora_key(key)
|
||||||
|
|
||||||
if "input.blocks" in diffusers_name:
|
# Store down and up weights.
|
||||||
diffusers_name = diffusers_name.replace("input.blocks", "down_blocks")
|
unet_state_dict[diffusers_name] = state_dict.pop(key)
|
||||||
else:
|
unet_state_dict[diffusers_name.replace(".down.", ".up.")] = state_dict.pop(lora_name_up)
|
||||||
diffusers_name = diffusers_name.replace("down.blocks", "down_blocks")
|
|
||||||
|
|
||||||
if "middle.block" in diffusers_name:
|
# Store DoRA scale if present.
|
||||||
diffusers_name = diffusers_name.replace("middle.block", "mid_block")
|
if dora_present_in_unet:
|
||||||
else:
|
|
||||||
diffusers_name = diffusers_name.replace("mid.block", "mid_block")
|
|
||||||
if "output.blocks" in diffusers_name:
|
|
||||||
diffusers_name = diffusers_name.replace("output.blocks", "up_blocks")
|
|
||||||
else:
|
|
||||||
diffusers_name = diffusers_name.replace("up.blocks", "up_blocks")
|
|
||||||
|
|
||||||
diffusers_name = diffusers_name.replace("transformer.blocks", "transformer_blocks")
|
|
||||||
diffusers_name = diffusers_name.replace("to.q.lora", "to_q_lora")
|
|
||||||
diffusers_name = diffusers_name.replace("to.k.lora", "to_k_lora")
|
|
||||||
diffusers_name = diffusers_name.replace("to.v.lora", "to_v_lora")
|
|
||||||
diffusers_name = diffusers_name.replace("to.out.0.lora", "to_out_lora")
|
|
||||||
diffusers_name = diffusers_name.replace("proj.in", "proj_in")
|
|
||||||
diffusers_name = diffusers_name.replace("proj.out", "proj_out")
|
|
||||||
diffusers_name = diffusers_name.replace("emb.layers", "time_emb_proj")
|
|
||||||
|
|
||||||
# SDXL specificity.
|
|
||||||
if "emb" in diffusers_name and "time.emb.proj" not in diffusers_name:
|
|
||||||
pattern = r"\.\d+(?=\D*$)"
|
|
||||||
diffusers_name = re.sub(pattern, "", diffusers_name, count=1)
|
|
||||||
if ".in." in diffusers_name:
|
|
||||||
diffusers_name = diffusers_name.replace("in.layers.2", "conv1")
|
|
||||||
if ".out." in diffusers_name:
|
|
||||||
diffusers_name = diffusers_name.replace("out.layers.3", "conv2")
|
|
||||||
if "downsamplers" in diffusers_name or "upsamplers" in diffusers_name:
|
|
||||||
diffusers_name = diffusers_name.replace("op", "conv")
|
|
||||||
if "skip" in diffusers_name:
|
|
||||||
diffusers_name = diffusers_name.replace("skip.connection", "conv_shortcut")
|
|
||||||
|
|
||||||
# LyCORIS specificity.
|
|
||||||
if "time.emb.proj" in diffusers_name:
|
|
||||||
diffusers_name = diffusers_name.replace("time.emb.proj", "time_emb_proj")
|
|
||||||
if "conv.shortcut" in diffusers_name:
|
|
||||||
diffusers_name = diffusers_name.replace("conv.shortcut", "conv_shortcut")
|
|
||||||
|
|
||||||
# General coverage.
|
|
||||||
if "transformer_blocks" in diffusers_name:
|
|
||||||
if "attn1" in diffusers_name or "attn2" in diffusers_name:
|
|
||||||
diffusers_name = diffusers_name.replace("attn1", "attn1.processor")
|
|
||||||
diffusers_name = diffusers_name.replace("attn2", "attn2.processor")
|
|
||||||
unet_state_dict[diffusers_name] = state_dict.pop(key)
|
|
||||||
unet_state_dict[diffusers_name.replace(".down.", ".up.")] = state_dict.pop(lora_name_up)
|
|
||||||
elif "ff" in diffusers_name:
|
|
||||||
unet_state_dict[diffusers_name] = state_dict.pop(key)
|
|
||||||
unet_state_dict[diffusers_name.replace(".down.", ".up.")] = state_dict.pop(lora_name_up)
|
|
||||||
elif any(key in diffusers_name for key in ("proj_in", "proj_out")):
|
|
||||||
unet_state_dict[diffusers_name] = state_dict.pop(key)
|
|
||||||
unet_state_dict[diffusers_name.replace(".down.", ".up.")] = state_dict.pop(lora_name_up)
|
|
||||||
else:
|
|
||||||
unet_state_dict[diffusers_name] = state_dict.pop(key)
|
|
||||||
unet_state_dict[diffusers_name.replace(".down.", ".up.")] = state_dict.pop(lora_name_up)
|
|
||||||
|
|
||||||
if is_unet_dora_lora:
|
|
||||||
dora_scale_key_to_replace = "_lora.down." if "_lora.down." in diffusers_name else ".lora.down."
|
dora_scale_key_to_replace = "_lora.down." if "_lora.down." in diffusers_name else ".lora.down."
|
||||||
unet_state_dict[
|
unet_state_dict[
|
||||||
diffusers_name.replace(dora_scale_key_to_replace, ".lora_magnitude_vector.")
|
diffusers_name.replace(dora_scale_key_to_replace, ".lora_magnitude_vector.")
|
||||||
] = state_dict.pop(key.replace("lora_down.weight", "dora_scale"))
|
] = state_dict.pop(key.replace("lora_down.weight", "dora_scale"))
|
||||||
|
|
||||||
|
# Handle text encoder LoRAs.
|
||||||
elif lora_name.startswith(("lora_te_", "lora_te1_", "lora_te2_")):
|
elif lora_name.startswith(("lora_te_", "lora_te1_", "lora_te2_")):
|
||||||
|
diffusers_name = _convert_text_encoder_lora_key(key, lora_name)
|
||||||
|
|
||||||
|
# Store down and up weights for te or te2.
|
||||||
if lora_name.startswith(("lora_te_", "lora_te1_")):
|
if lora_name.startswith(("lora_te_", "lora_te1_")):
|
||||||
key_to_replace = "lora_te_" if lora_name.startswith("lora_te_") else "lora_te1_"
|
te_state_dict[diffusers_name] = state_dict.pop(key)
|
||||||
|
te_state_dict[diffusers_name.replace(".down.", ".up.")] = state_dict.pop(lora_name_up)
|
||||||
else:
|
else:
|
||||||
key_to_replace = "lora_te2_"
|
|
||||||
|
|
||||||
diffusers_name = key.replace(key_to_replace, "").replace("_", ".")
|
|
||||||
diffusers_name = diffusers_name.replace("text.model", "text_model")
|
|
||||||
diffusers_name = diffusers_name.replace("self.attn", "self_attn")
|
|
||||||
diffusers_name = diffusers_name.replace("q.proj.lora", "to_q_lora")
|
|
||||||
diffusers_name = diffusers_name.replace("k.proj.lora", "to_k_lora")
|
|
||||||
diffusers_name = diffusers_name.replace("v.proj.lora", "to_v_lora")
|
|
||||||
diffusers_name = diffusers_name.replace("out.proj.lora", "to_out_lora")
|
|
||||||
diffusers_name = diffusers_name.replace("text.projection", "text_projection")
|
|
||||||
|
|
||||||
if "self_attn" in diffusers_name:
|
|
||||||
if lora_name.startswith(("lora_te_", "lora_te1_")):
|
|
||||||
te_state_dict[diffusers_name] = state_dict.pop(key)
|
|
||||||
te_state_dict[diffusers_name.replace(".down.", ".up.")] = state_dict.pop(lora_name_up)
|
|
||||||
else:
|
|
||||||
te2_state_dict[diffusers_name] = state_dict.pop(key)
|
|
||||||
te2_state_dict[diffusers_name.replace(".down.", ".up.")] = state_dict.pop(lora_name_up)
|
|
||||||
elif "mlp" in diffusers_name:
|
|
||||||
# Be aware that this is the new diffusers convention and the rest of the code might
|
|
||||||
# not utilize it yet.
|
|
||||||
diffusers_name = diffusers_name.replace(".lora.", ".lora_linear_layer.")
|
|
||||||
if lora_name.startswith(("lora_te_", "lora_te1_")):
|
|
||||||
te_state_dict[diffusers_name] = state_dict.pop(key)
|
|
||||||
te_state_dict[diffusers_name.replace(".down.", ".up.")] = state_dict.pop(lora_name_up)
|
|
||||||
else:
|
|
||||||
te2_state_dict[diffusers_name] = state_dict.pop(key)
|
|
||||||
te2_state_dict[diffusers_name.replace(".down.", ".up.")] = state_dict.pop(lora_name_up)
|
|
||||||
# OneTrainer specificity
|
|
||||||
elif "text_projection" in diffusers_name and lora_name.startswith("lora_te2_"):
|
|
||||||
te2_state_dict[diffusers_name] = state_dict.pop(key)
|
te2_state_dict[diffusers_name] = state_dict.pop(key)
|
||||||
te2_state_dict[diffusers_name.replace(".down.", ".up.")] = state_dict.pop(lora_name_up)
|
te2_state_dict[diffusers_name.replace(".down.", ".up.")] = state_dict.pop(lora_name_up)
|
||||||
|
|
||||||
if (is_te_dora_lora or is_te2_dora_lora) and lora_name.startswith(("lora_te_", "lora_te1_", "lora_te2_")):
|
# Store DoRA scale if present.
|
||||||
|
if dora_present_in_te or dora_present_in_te2:
|
||||||
dora_scale_key_to_replace_te = (
|
dora_scale_key_to_replace_te = (
|
||||||
"_lora.down." if "_lora.down." in diffusers_name else ".lora_linear_layer."
|
"_lora.down." if "_lora.down." in diffusers_name else ".lora_linear_layer."
|
||||||
)
|
)
|
||||||
@@ -263,22 +205,18 @@ def _convert_kohya_lora_to_diffusers(state_dict, unet_name="unet", text_encoder_
|
|||||||
diffusers_name.replace(dora_scale_key_to_replace_te, ".lora_magnitude_vector.")
|
diffusers_name.replace(dora_scale_key_to_replace_te, ".lora_magnitude_vector.")
|
||||||
] = state_dict.pop(key.replace("lora_down.weight", "dora_scale"))
|
] = state_dict.pop(key.replace("lora_down.weight", "dora_scale"))
|
||||||
|
|
||||||
# Rename the alphas so that they can be mapped appropriately.
|
# Store alpha if present.
|
||||||
if lora_name_alpha in state_dict:
|
if lora_name_alpha in state_dict:
|
||||||
alpha = state_dict.pop(lora_name_alpha).item()
|
alpha = state_dict.pop(lora_name_alpha).item()
|
||||||
if lora_name_alpha.startswith("lora_unet_"):
|
network_alphas.update(_get_alpha_name(lora_name_alpha, diffusers_name, alpha))
|
||||||
prefix = "unet."
|
|
||||||
elif lora_name_alpha.startswith(("lora_te_", "lora_te1_")):
|
|
||||||
prefix = "text_encoder."
|
|
||||||
else:
|
|
||||||
prefix = "text_encoder_2."
|
|
||||||
new_name = prefix + diffusers_name.split(".lora.")[0] + ".alpha"
|
|
||||||
network_alphas.update({new_name: alpha})
|
|
||||||
|
|
||||||
|
# Check if any keys remain.
|
||||||
if len(state_dict) > 0:
|
if len(state_dict) > 0:
|
||||||
raise ValueError(f"The following keys have not been correctly renamed: \n\n {', '.join(state_dict.keys())}")
|
raise ValueError(f"The following keys have not been correctly renamed: \n\n {', '.join(state_dict.keys())}")
|
||||||
|
|
||||||
logger.info("Kohya-style checkpoint detected.")
|
logger.info("Non-diffusers checkpoint detected.")
|
||||||
|
|
||||||
|
# Construct final state dict.
|
||||||
unet_state_dict = {f"{unet_name}.{module_name}": params for module_name, params in unet_state_dict.items()}
|
unet_state_dict = {f"{unet_name}.{module_name}": params for module_name, params in unet_state_dict.items()}
|
||||||
te_state_dict = {f"{text_encoder_name}.{module_name}": params for module_name, params in te_state_dict.items()}
|
te_state_dict = {f"{text_encoder_name}.{module_name}": params for module_name, params in te_state_dict.items()}
|
||||||
te2_state_dict = (
|
te2_state_dict = (
|
||||||
@@ -291,3 +229,100 @@ def _convert_kohya_lora_to_diffusers(state_dict, unet_name="unet", text_encoder_
|
|||||||
|
|
||||||
new_state_dict = {**unet_state_dict, **te_state_dict}
|
new_state_dict = {**unet_state_dict, **te_state_dict}
|
||||||
return new_state_dict, network_alphas
|
return new_state_dict, network_alphas
|
||||||
|
|
||||||
|
|
||||||
|
def _convert_unet_lora_key(key):
|
||||||
|
"""
|
||||||
|
Converts a U-Net LoRA key to a Diffusers compatible key.
|
||||||
|
"""
|
||||||
|
diffusers_name = key.replace("lora_unet_", "").replace("_", ".")
|
||||||
|
|
||||||
|
# Replace common U-Net naming patterns.
|
||||||
|
diffusers_name = diffusers_name.replace("input.blocks", "down_blocks")
|
||||||
|
diffusers_name = diffusers_name.replace("down.blocks", "down_blocks")
|
||||||
|
diffusers_name = diffusers_name.replace("middle.block", "mid_block")
|
||||||
|
diffusers_name = diffusers_name.replace("mid.block", "mid_block")
|
||||||
|
diffusers_name = diffusers_name.replace("output.blocks", "up_blocks")
|
||||||
|
diffusers_name = diffusers_name.replace("up.blocks", "up_blocks")
|
||||||
|
diffusers_name = diffusers_name.replace("transformer.blocks", "transformer_blocks")
|
||||||
|
diffusers_name = diffusers_name.replace("to.q.lora", "to_q_lora")
|
||||||
|
diffusers_name = diffusers_name.replace("to.k.lora", "to_k_lora")
|
||||||
|
diffusers_name = diffusers_name.replace("to.v.lora", "to_v_lora")
|
||||||
|
diffusers_name = diffusers_name.replace("to.out.0.lora", "to_out_lora")
|
||||||
|
diffusers_name = diffusers_name.replace("proj.in", "proj_in")
|
||||||
|
diffusers_name = diffusers_name.replace("proj.out", "proj_out")
|
||||||
|
diffusers_name = diffusers_name.replace("emb.layers", "time_emb_proj")
|
||||||
|
|
||||||
|
# SDXL specific conversions.
|
||||||
|
if "emb" in diffusers_name and "time.emb.proj" not in diffusers_name:
|
||||||
|
pattern = r"\.\d+(?=\D*$)"
|
||||||
|
diffusers_name = re.sub(pattern, "", diffusers_name, count=1)
|
||||||
|
if ".in." in diffusers_name:
|
||||||
|
diffusers_name = diffusers_name.replace("in.layers.2", "conv1")
|
||||||
|
if ".out." in diffusers_name:
|
||||||
|
diffusers_name = diffusers_name.replace("out.layers.3", "conv2")
|
||||||
|
if "downsamplers" in diffusers_name or "upsamplers" in diffusers_name:
|
||||||
|
diffusers_name = diffusers_name.replace("op", "conv")
|
||||||
|
if "skip" in diffusers_name:
|
||||||
|
diffusers_name = diffusers_name.replace("skip.connection", "conv_shortcut")
|
||||||
|
|
||||||
|
# LyCORIS specific conversions.
|
||||||
|
if "time.emb.proj" in diffusers_name:
|
||||||
|
diffusers_name = diffusers_name.replace("time.emb.proj", "time_emb_proj")
|
||||||
|
if "conv.shortcut" in diffusers_name:
|
||||||
|
diffusers_name = diffusers_name.replace("conv.shortcut", "conv_shortcut")
|
||||||
|
|
||||||
|
# General conversions.
|
||||||
|
if "transformer_blocks" in diffusers_name:
|
||||||
|
if "attn1" in diffusers_name or "attn2" in diffusers_name:
|
||||||
|
diffusers_name = diffusers_name.replace("attn1", "attn1.processor")
|
||||||
|
diffusers_name = diffusers_name.replace("attn2", "attn2.processor")
|
||||||
|
elif "ff" in diffusers_name:
|
||||||
|
pass
|
||||||
|
elif any(key in diffusers_name for key in ("proj_in", "proj_out")):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return diffusers_name
|
||||||
|
|
||||||
|
|
||||||
|
def _convert_text_encoder_lora_key(key, lora_name):
|
||||||
|
"""
|
||||||
|
Converts a text encoder LoRA key to a Diffusers compatible key.
|
||||||
|
"""
|
||||||
|
if lora_name.startswith(("lora_te_", "lora_te1_")):
|
||||||
|
key_to_replace = "lora_te_" if lora_name.startswith("lora_te_") else "lora_te1_"
|
||||||
|
else:
|
||||||
|
key_to_replace = "lora_te2_"
|
||||||
|
|
||||||
|
diffusers_name = key.replace(key_to_replace, "").replace("_", ".")
|
||||||
|
diffusers_name = diffusers_name.replace("text.model", "text_model")
|
||||||
|
diffusers_name = diffusers_name.replace("self.attn", "self_attn")
|
||||||
|
diffusers_name = diffusers_name.replace("q.proj.lora", "to_q_lora")
|
||||||
|
diffusers_name = diffusers_name.replace("k.proj.lora", "to_k_lora")
|
||||||
|
diffusers_name = diffusers_name.replace("v.proj.lora", "to_v_lora")
|
||||||
|
diffusers_name = diffusers_name.replace("out.proj.lora", "to_out_lora")
|
||||||
|
diffusers_name = diffusers_name.replace("text.projection", "text_projection")
|
||||||
|
|
||||||
|
if "self_attn" in diffusers_name or "text_projection" in diffusers_name:
|
||||||
|
pass
|
||||||
|
elif "mlp" in diffusers_name:
|
||||||
|
# Be aware that this is the new diffusers convention and the rest of the code might
|
||||||
|
# not utilize it yet.
|
||||||
|
diffusers_name = diffusers_name.replace(".lora.", ".lora_linear_layer.")
|
||||||
|
return diffusers_name
|
||||||
|
|
||||||
|
|
||||||
|
def _get_alpha_name(lora_name_alpha, diffusers_name, alpha):
|
||||||
|
"""
|
||||||
|
Gets the correct alpha name for the Diffusers model.
|
||||||
|
"""
|
||||||
|
if lora_name_alpha.startswith("lora_unet_"):
|
||||||
|
prefix = "unet."
|
||||||
|
elif lora_name_alpha.startswith(("lora_te_", "lora_te1_")):
|
||||||
|
prefix = "text_encoder."
|
||||||
|
else:
|
||||||
|
prefix = "text_encoder_2."
|
||||||
|
new_name = prefix + diffusers_name.split(".lora.")[0] + ".alpha"
|
||||||
|
return {new_name: alpha}
|
||||||
|
|||||||
@@ -555,7 +555,4 @@ class FromSingleFileMixin:
|
|||||||
|
|
||||||
pipe = pipeline_class(**init_kwargs)
|
pipe = pipeline_class(**init_kwargs)
|
||||||
|
|
||||||
if torch_dtype is not None:
|
|
||||||
pipe.to(dtype=torch_dtype)
|
|
||||||
|
|
||||||
return pipe
|
return pipe
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
import importlib
|
||||||
import inspect
|
import inspect
|
||||||
import re
|
import re
|
||||||
from contextlib import nullcontext
|
from contextlib import nullcontext
|
||||||
@@ -72,6 +73,17 @@ SINGLE_FILE_LOADABLE_CLASSES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _get_single_file_loadable_mapping_class(cls):
|
||||||
|
diffusers_module = importlib.import_module(__name__.split(".")[0])
|
||||||
|
for loadable_class_str in SINGLE_FILE_LOADABLE_CLASSES:
|
||||||
|
loadable_class = getattr(diffusers_module, loadable_class_str)
|
||||||
|
|
||||||
|
if issubclass(cls, loadable_class):
|
||||||
|
return loadable_class_str
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _get_mapping_function_kwargs(mapping_fn, **kwargs):
|
def _get_mapping_function_kwargs(mapping_fn, **kwargs):
|
||||||
parameters = inspect.signature(mapping_fn).parameters
|
parameters = inspect.signature(mapping_fn).parameters
|
||||||
|
|
||||||
@@ -149,8 +161,9 @@ class FromOriginalModelMixin:
|
|||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class_name = cls.__name__
|
mapping_class_name = _get_single_file_loadable_mapping_class(cls)
|
||||||
if class_name not in SINGLE_FILE_LOADABLE_CLASSES:
|
# if class_name not in SINGLE_FILE_LOADABLE_CLASSES:
|
||||||
|
if mapping_class_name is None:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"FromOriginalModelMixin is currently only compatible with {', '.join(SINGLE_FILE_LOADABLE_CLASSES.keys())}"
|
f"FromOriginalModelMixin is currently only compatible with {', '.join(SINGLE_FILE_LOADABLE_CLASSES.keys())}"
|
||||||
)
|
)
|
||||||
@@ -195,7 +208,7 @@ class FromOriginalModelMixin:
|
|||||||
revision=revision,
|
revision=revision,
|
||||||
)
|
)
|
||||||
|
|
||||||
mapping_functions = SINGLE_FILE_LOADABLE_CLASSES[class_name]
|
mapping_functions = SINGLE_FILE_LOADABLE_CLASSES[mapping_class_name]
|
||||||
|
|
||||||
checkpoint_mapping_fn = mapping_functions["checkpoint_mapping_fn"]
|
checkpoint_mapping_fn = mapping_functions["checkpoint_mapping_fn"]
|
||||||
if original_config:
|
if original_config:
|
||||||
@@ -207,7 +220,7 @@ class FromOriginalModelMixin:
|
|||||||
if config_mapping_fn is None:
|
if config_mapping_fn is None:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
(
|
(
|
||||||
f"`original_config` has been provided for {class_name} but no mapping function"
|
f"`original_config` has been provided for {mapping_class_name} but no mapping function"
|
||||||
"was found to convert the original config to a Diffusers config in"
|
"was found to convert the original config to a Diffusers config in"
|
||||||
"`diffusers.loaders.single_file_utils`"
|
"`diffusers.loaders.single_file_utils`"
|
||||||
)
|
)
|
||||||
@@ -267,7 +280,7 @@ class FromOriginalModelMixin:
|
|||||||
)
|
)
|
||||||
if not diffusers_format_checkpoint:
|
if not diffusers_format_checkpoint:
|
||||||
raise SingleFileComponentError(
|
raise SingleFileComponentError(
|
||||||
f"Failed to load {class_name}. Weights for this component appear to be missing in the checkpoint."
|
f"Failed to load {mapping_class_name}. Weights for this component appear to be missing in the checkpoint."
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx = init_empty_weights if is_accelerate_available() else nullcontext
|
ctx = init_empty_weights if is_accelerate_available() else nullcontext
|
||||||
@@ -276,16 +289,18 @@ class FromOriginalModelMixin:
|
|||||||
|
|
||||||
if is_accelerate_available():
|
if is_accelerate_available():
|
||||||
unexpected_keys = load_model_dict_into_meta(model, diffusers_format_checkpoint, dtype=torch_dtype)
|
unexpected_keys = load_model_dict_into_meta(model, diffusers_format_checkpoint, dtype=torch_dtype)
|
||||||
if model._keys_to_ignore_on_load_unexpected is not None:
|
|
||||||
for pat in model._keys_to_ignore_on_load_unexpected:
|
|
||||||
unexpected_keys = [k for k in unexpected_keys if re.search(pat, k) is None]
|
|
||||||
|
|
||||||
if len(unexpected_keys) > 0:
|
|
||||||
logger.warning(
|
|
||||||
f"Some weights of the model checkpoint were not used when initializing {cls.__name__}: \n {[', '.join(unexpected_keys)]}"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
model.load_state_dict(diffusers_format_checkpoint)
|
_, unexpected_keys = model.load_state_dict(diffusers_format_checkpoint, strict=False)
|
||||||
|
|
||||||
|
if model._keys_to_ignore_on_load_unexpected is not None:
|
||||||
|
for pat in model._keys_to_ignore_on_load_unexpected:
|
||||||
|
unexpected_keys = [k for k in unexpected_keys if re.search(pat, k) is None]
|
||||||
|
|
||||||
|
if len(unexpected_keys) > 0:
|
||||||
|
logger.warning(
|
||||||
|
f"Some weights of the model checkpoint were not used when initializing {cls.__name__}: \n {[', '.join(unexpected_keys)]}"
|
||||||
|
)
|
||||||
|
|
||||||
if torch_dtype is not None:
|
if torch_dtype is not None:
|
||||||
model.to(torch_dtype)
|
model.to(torch_dtype)
|
||||||
|
|||||||
@@ -1268,8 +1268,6 @@ def convert_open_clip_checkpoint(
|
|||||||
else:
|
else:
|
||||||
text_proj_dim = LDM_OPEN_CLIP_TEXT_PROJECTION_DIM
|
text_proj_dim = LDM_OPEN_CLIP_TEXT_PROJECTION_DIM
|
||||||
|
|
||||||
text_model_dict["text_model.embeddings.position_ids"] = text_model.text_model.embeddings.get_buffer("position_ids")
|
|
||||||
|
|
||||||
keys = list(checkpoint.keys())
|
keys = list(checkpoint.keys())
|
||||||
keys_to_ignore = SD_2_TEXT_ENCODER_KEYS_TO_IGNORE
|
keys_to_ignore = SD_2_TEXT_ENCODER_KEYS_TO_IGNORE
|
||||||
|
|
||||||
@@ -1318,9 +1316,6 @@ def convert_open_clip_checkpoint(
|
|||||||
else:
|
else:
|
||||||
text_model_dict[diffusers_key] = checkpoint.get(key)
|
text_model_dict[diffusers_key] = checkpoint.get(key)
|
||||||
|
|
||||||
if not (hasattr(text_model, "embeddings") and hasattr(text_model.embeddings.position_ids)):
|
|
||||||
text_model_dict.pop("text_model.embeddings.position_ids", None)
|
|
||||||
|
|
||||||
return text_model_dict
|
return text_model_dict
|
||||||
|
|
||||||
|
|
||||||
@@ -1414,17 +1409,17 @@ def create_diffusers_clip_model_from_ldm(
|
|||||||
|
|
||||||
if is_accelerate_available():
|
if is_accelerate_available():
|
||||||
unexpected_keys = load_model_dict_into_meta(model, diffusers_format_checkpoint, dtype=torch_dtype)
|
unexpected_keys = load_model_dict_into_meta(model, diffusers_format_checkpoint, dtype=torch_dtype)
|
||||||
if model._keys_to_ignore_on_load_unexpected is not None:
|
|
||||||
for pat in model._keys_to_ignore_on_load_unexpected:
|
|
||||||
unexpected_keys = [k for k in unexpected_keys if re.search(pat, k) is None]
|
|
||||||
|
|
||||||
if len(unexpected_keys) > 0:
|
|
||||||
logger.warning(
|
|
||||||
f"Some weights of the model checkpoint were not used when initializing {cls.__name__}: \n {[', '.join(unexpected_keys)]}"
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
model.load_state_dict(diffusers_format_checkpoint)
|
_, unexpected_keys = model.load_state_dict(diffusers_format_checkpoint, strict=False)
|
||||||
|
|
||||||
|
if model._keys_to_ignore_on_load_unexpected is not None:
|
||||||
|
for pat in model._keys_to_ignore_on_load_unexpected:
|
||||||
|
unexpected_keys = [k for k in unexpected_keys if re.search(pat, k) is None]
|
||||||
|
|
||||||
|
if len(unexpected_keys) > 0:
|
||||||
|
logger.warning(
|
||||||
|
f"Some weights of the model checkpoint were not used when initializing {cls.__name__}: \n {[', '.join(unexpected_keys)]}"
|
||||||
|
)
|
||||||
|
|
||||||
if torch_dtype is not None:
|
if torch_dtype is not None:
|
||||||
model.to(torch_dtype)
|
model.to(torch_dtype)
|
||||||
@@ -1813,3 +1808,17 @@ def create_diffusers_t5_model_from_checkpoint(
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
model.load_state_dict(diffusers_format_checkpoint)
|
model.load_state_dict(diffusers_format_checkpoint)
|
||||||
|
|
||||||
|
use_keep_in_fp32_modules = (cls._keep_in_fp32_modules is not None) and (torch_dtype == torch.float16)
|
||||||
|
if use_keep_in_fp32_modules:
|
||||||
|
keep_in_fp32_modules = model._keep_in_fp32_modules
|
||||||
|
else:
|
||||||
|
keep_in_fp32_modules = []
|
||||||
|
|
||||||
|
if keep_in_fp32_modules is not None:
|
||||||
|
for name, param in model.named_parameters():
|
||||||
|
if any(module_to_keep_in_fp32 in name.split(".") for module_to_keep_in_fp32 in keep_in_fp32_modules):
|
||||||
|
# param = param.to(torch.float32) does not work here as only in the local scope.
|
||||||
|
param.data = param.data.to(torch.float32)
|
||||||
|
|
||||||
|
return model
|
||||||
|
|||||||
@@ -457,6 +457,15 @@ class UNet2DConditionLoadersMixin:
|
|||||||
)
|
)
|
||||||
if is_custom_diffusion:
|
if is_custom_diffusion:
|
||||||
state_dict = self._get_custom_diffusion_state_dict()
|
state_dict = self._get_custom_diffusion_state_dict()
|
||||||
|
if save_function is None and safe_serialization:
|
||||||
|
# safetensors does not support saving dicts with non-tensor values
|
||||||
|
empty_state_dict = {k: v for k, v in state_dict.items() if not isinstance(v, torch.Tensor)}
|
||||||
|
if len(empty_state_dict) > 0:
|
||||||
|
logger.warning(
|
||||||
|
f"Safetensors does not support saving dicts with non-tensor values. "
|
||||||
|
f"The following keys will be ignored: {empty_state_dict.keys()}"
|
||||||
|
)
|
||||||
|
state_dict = {k: v for k, v in state_dict.items() if isinstance(v, torch.Tensor)}
|
||||||
else:
|
else:
|
||||||
if not USE_PEFT_BACKEND:
|
if not USE_PEFT_BACKEND:
|
||||||
raise ValueError("PEFT backend is required for saving LoRAs using the `save_attn_procs()` method.")
|
raise ValueError("PEFT backend is required for saving LoRAs using the `save_attn_procs()` method.")
|
||||||
@@ -922,8 +931,6 @@ class UNet2DConditionLoadersMixin:
|
|||||||
|
|
||||||
def _convert_ip_adapter_attn_to_diffusers(self, state_dicts, low_cpu_mem_usage=False):
|
def _convert_ip_adapter_attn_to_diffusers(self, state_dicts, low_cpu_mem_usage=False):
|
||||||
from ..models.attention_processor import (
|
from ..models.attention_processor import (
|
||||||
AttnProcessor,
|
|
||||||
AttnProcessor2_0,
|
|
||||||
IPAdapterAttnProcessor,
|
IPAdapterAttnProcessor,
|
||||||
IPAdapterAttnProcessor2_0,
|
IPAdapterAttnProcessor2_0,
|
||||||
)
|
)
|
||||||
@@ -963,9 +970,7 @@ class UNet2DConditionLoadersMixin:
|
|||||||
hidden_size = self.config.block_out_channels[block_id]
|
hidden_size = self.config.block_out_channels[block_id]
|
||||||
|
|
||||||
if cross_attention_dim is None or "motion_modules" in name:
|
if cross_attention_dim is None or "motion_modules" in name:
|
||||||
attn_processor_class = (
|
attn_processor_class = self.attn_processors[name].__class__
|
||||||
AttnProcessor2_0 if hasattr(F, "scaled_dot_product_attention") else AttnProcessor
|
|
||||||
)
|
|
||||||
attn_procs[name] = attn_processor_class()
|
attn_procs[name] = attn_processor_class()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ if is_torch_available():
|
|||||||
_import_structure["autoencoders.consistency_decoder_vae"] = ["ConsistencyDecoderVAE"]
|
_import_structure["autoencoders.consistency_decoder_vae"] = ["ConsistencyDecoderVAE"]
|
||||||
_import_structure["autoencoders.vq_model"] = ["VQModel"]
|
_import_structure["autoencoders.vq_model"] = ["VQModel"]
|
||||||
_import_structure["controlnet"] = ["ControlNetModel"]
|
_import_structure["controlnet"] = ["ControlNetModel"]
|
||||||
|
_import_structure["controlnet_hunyuan"] = ["HunyuanDiT2DControlNetModel", "HunyuanDiT2DMultiControlNetModel"]
|
||||||
|
_import_structure["controlnet_sd3"] = ["SD3ControlNetModel", "SD3MultiControlNetModel"]
|
||||||
_import_structure["controlnet_xs"] = ["ControlNetXSAdapter", "UNetControlNetXSModel"]
|
_import_structure["controlnet_xs"] = ["ControlNetXSAdapter", "UNetControlNetXSModel"]
|
||||||
_import_structure["embeddings"] = ["ImageProjection"]
|
_import_structure["embeddings"] = ["ImageProjection"]
|
||||||
_import_structure["modeling_utils"] = ["ModelMixin"]
|
_import_structure["modeling_utils"] = ["ModelMixin"]
|
||||||
@@ -74,6 +76,8 @@ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
|||||||
VQModel,
|
VQModel,
|
||||||
)
|
)
|
||||||
from .controlnet import ControlNetModel
|
from .controlnet import ControlNetModel
|
||||||
|
from .controlnet_hunyuan import HunyuanDiT2DControlNetModel, HunyuanDiT2DMultiControlNetModel
|
||||||
|
from .controlnet_sd3 import SD3ControlNetModel, SD3MultiControlNetModel
|
||||||
from .controlnet_xs import ControlNetXSAdapter, UNetControlNetXSModel
|
from .controlnet_xs import ControlNetXSAdapter, UNetControlNetXSModel
|
||||||
from .embeddings import ImageProjection
|
from .embeddings import ImageProjection
|
||||||
from .modeling_utils import ModelMixin
|
from .modeling_utils import ModelMixin
|
||||||
|
|||||||
@@ -128,9 +128,9 @@ class JointTransformerBlock(nn.Module):
|
|||||||
query_dim=dim,
|
query_dim=dim,
|
||||||
cross_attention_dim=None,
|
cross_attention_dim=None,
|
||||||
added_kv_proj_dim=dim,
|
added_kv_proj_dim=dim,
|
||||||
dim_head=attention_head_dim // num_attention_heads,
|
dim_head=attention_head_dim,
|
||||||
heads=num_attention_heads,
|
heads=num_attention_heads,
|
||||||
out_dim=attention_head_dim,
|
out_dim=dim,
|
||||||
context_pre_only=context_pre_only,
|
context_pre_only=context_pre_only,
|
||||||
bias=True,
|
bias=True,
|
||||||
processor=processor,
|
processor=processor,
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
import inspect
|
import inspect
|
||||||
import math
|
import math
|
||||||
from importlib import import_module
|
|
||||||
from typing import Callable, List, Optional, Union
|
from typing import Callable, List, Optional, Union
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
@@ -24,7 +23,6 @@ from ..image_processor import IPAdapterMaskProcessor
|
|||||||
from ..utils import deprecate, logging
|
from ..utils import deprecate, logging
|
||||||
from ..utils.import_utils import is_torch_npu_available, is_xformers_available
|
from ..utils.import_utils import is_torch_npu_available, is_xformers_available
|
||||||
from ..utils.torch_utils import maybe_allow_in_graph
|
from ..utils.torch_utils import maybe_allow_in_graph
|
||||||
from .lora import LoRALinearLayer
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
||||||
@@ -259,10 +257,6 @@ class Attention(nn.Module):
|
|||||||
The attention operation to use. Defaults to `None` which uses the default attention operation from
|
The attention operation to use. Defaults to `None` which uses the default attention operation from
|
||||||
`xformers`.
|
`xformers`.
|
||||||
"""
|
"""
|
||||||
is_lora = hasattr(self, "processor") and isinstance(
|
|
||||||
self.processor,
|
|
||||||
LORA_ATTENTION_PROCESSORS,
|
|
||||||
)
|
|
||||||
is_custom_diffusion = hasattr(self, "processor") and isinstance(
|
is_custom_diffusion = hasattr(self, "processor") and isinstance(
|
||||||
self.processor,
|
self.processor,
|
||||||
(CustomDiffusionAttnProcessor, CustomDiffusionXFormersAttnProcessor, CustomDiffusionAttnProcessor2_0),
|
(CustomDiffusionAttnProcessor, CustomDiffusionXFormersAttnProcessor, CustomDiffusionAttnProcessor2_0),
|
||||||
@@ -274,14 +268,13 @@ class Attention(nn.Module):
|
|||||||
AttnAddedKVProcessor2_0,
|
AttnAddedKVProcessor2_0,
|
||||||
SlicedAttnAddedKVProcessor,
|
SlicedAttnAddedKVProcessor,
|
||||||
XFormersAttnAddedKVProcessor,
|
XFormersAttnAddedKVProcessor,
|
||||||
LoRAAttnAddedKVProcessor,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
if use_memory_efficient_attention_xformers:
|
if use_memory_efficient_attention_xformers:
|
||||||
if is_added_kv_processor and (is_lora or is_custom_diffusion):
|
if is_added_kv_processor and is_custom_diffusion:
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
f"Memory efficient attention is currently not supported for LoRA or custom diffusion for attention processor type {self.processor}"
|
f"Memory efficient attention is currently not supported for custom diffusion for attention processor type {self.processor}"
|
||||||
)
|
)
|
||||||
if not is_xformers_available():
|
if not is_xformers_available():
|
||||||
raise ModuleNotFoundError(
|
raise ModuleNotFoundError(
|
||||||
@@ -307,18 +300,7 @@ class Attention(nn.Module):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
if is_lora:
|
if is_custom_diffusion:
|
||||||
# TODO (sayakpaul): should we throw a warning if someone wants to use the xformers
|
|
||||||
# variant when using PT 2.0 now that we have LoRAAttnProcessor2_0?
|
|
||||||
processor = LoRAXFormersAttnProcessor(
|
|
||||||
hidden_size=self.processor.hidden_size,
|
|
||||||
cross_attention_dim=self.processor.cross_attention_dim,
|
|
||||||
rank=self.processor.rank,
|
|
||||||
attention_op=attention_op,
|
|
||||||
)
|
|
||||||
processor.load_state_dict(self.processor.state_dict())
|
|
||||||
processor.to(self.processor.to_q_lora.up.weight.device)
|
|
||||||
elif is_custom_diffusion:
|
|
||||||
processor = CustomDiffusionXFormersAttnProcessor(
|
processor = CustomDiffusionXFormersAttnProcessor(
|
||||||
train_kv=self.processor.train_kv,
|
train_kv=self.processor.train_kv,
|
||||||
train_q_out=self.processor.train_q_out,
|
train_q_out=self.processor.train_q_out,
|
||||||
@@ -341,18 +323,7 @@ class Attention(nn.Module):
|
|||||||
else:
|
else:
|
||||||
processor = XFormersAttnProcessor(attention_op=attention_op)
|
processor = XFormersAttnProcessor(attention_op=attention_op)
|
||||||
else:
|
else:
|
||||||
if is_lora:
|
if is_custom_diffusion:
|
||||||
attn_processor_class = (
|
|
||||||
LoRAAttnProcessor2_0 if hasattr(F, "scaled_dot_product_attention") else LoRAAttnProcessor
|
|
||||||
)
|
|
||||||
processor = attn_processor_class(
|
|
||||||
hidden_size=self.processor.hidden_size,
|
|
||||||
cross_attention_dim=self.processor.cross_attention_dim,
|
|
||||||
rank=self.processor.rank,
|
|
||||||
)
|
|
||||||
processor.load_state_dict(self.processor.state_dict())
|
|
||||||
processor.to(self.processor.to_q_lora.up.weight.device)
|
|
||||||
elif is_custom_diffusion:
|
|
||||||
attn_processor_class = (
|
attn_processor_class = (
|
||||||
CustomDiffusionAttnProcessor2_0
|
CustomDiffusionAttnProcessor2_0
|
||||||
if hasattr(F, "scaled_dot_product_attention")
|
if hasattr(F, "scaled_dot_product_attention")
|
||||||
@@ -442,82 +413,6 @@ class Attention(nn.Module):
|
|||||||
if not return_deprecated_lora:
|
if not return_deprecated_lora:
|
||||||
return self.processor
|
return self.processor
|
||||||
|
|
||||||
# TODO(Sayak, Patrick). The rest of the function is needed to ensure backwards compatible
|
|
||||||
# serialization format for LoRA Attention Processors. It should be deleted once the integration
|
|
||||||
# with PEFT is completed.
|
|
||||||
is_lora_activated = {
|
|
||||||
name: module.lora_layer is not None
|
|
||||||
for name, module in self.named_modules()
|
|
||||||
if hasattr(module, "lora_layer")
|
|
||||||
}
|
|
||||||
|
|
||||||
# 1. if no layer has a LoRA activated we can return the processor as usual
|
|
||||||
if not any(is_lora_activated.values()):
|
|
||||||
return self.processor
|
|
||||||
|
|
||||||
# If doesn't apply LoRA do `add_k_proj` or `add_v_proj`
|
|
||||||
is_lora_activated.pop("add_k_proj", None)
|
|
||||||
is_lora_activated.pop("add_v_proj", None)
|
|
||||||
# 2. else it is not possible that only some layers have LoRA activated
|
|
||||||
if not all(is_lora_activated.values()):
|
|
||||||
raise ValueError(
|
|
||||||
f"Make sure that either all layers or no layers have LoRA activated, but have {is_lora_activated}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 3. And we need to merge the current LoRA layers into the corresponding LoRA attention processor
|
|
||||||
non_lora_processor_cls_name = self.processor.__class__.__name__
|
|
||||||
lora_processor_cls = getattr(import_module(__name__), "LoRA" + non_lora_processor_cls_name)
|
|
||||||
|
|
||||||
hidden_size = self.inner_dim
|
|
||||||
|
|
||||||
# now create a LoRA attention processor from the LoRA layers
|
|
||||||
if lora_processor_cls in [LoRAAttnProcessor, LoRAAttnProcessor2_0, LoRAXFormersAttnProcessor]:
|
|
||||||
kwargs = {
|
|
||||||
"cross_attention_dim": self.cross_attention_dim,
|
|
||||||
"rank": self.to_q.lora_layer.rank,
|
|
||||||
"network_alpha": self.to_q.lora_layer.network_alpha,
|
|
||||||
"q_rank": self.to_q.lora_layer.rank,
|
|
||||||
"q_hidden_size": self.to_q.lora_layer.out_features,
|
|
||||||
"k_rank": self.to_k.lora_layer.rank,
|
|
||||||
"k_hidden_size": self.to_k.lora_layer.out_features,
|
|
||||||
"v_rank": self.to_v.lora_layer.rank,
|
|
||||||
"v_hidden_size": self.to_v.lora_layer.out_features,
|
|
||||||
"out_rank": self.to_out[0].lora_layer.rank,
|
|
||||||
"out_hidden_size": self.to_out[0].lora_layer.out_features,
|
|
||||||
}
|
|
||||||
|
|
||||||
if hasattr(self.processor, "attention_op"):
|
|
||||||
kwargs["attention_op"] = self.processor.attention_op
|
|
||||||
|
|
||||||
lora_processor = lora_processor_cls(hidden_size, **kwargs)
|
|
||||||
lora_processor.to_q_lora.load_state_dict(self.to_q.lora_layer.state_dict())
|
|
||||||
lora_processor.to_k_lora.load_state_dict(self.to_k.lora_layer.state_dict())
|
|
||||||
lora_processor.to_v_lora.load_state_dict(self.to_v.lora_layer.state_dict())
|
|
||||||
lora_processor.to_out_lora.load_state_dict(self.to_out[0].lora_layer.state_dict())
|
|
||||||
elif lora_processor_cls == LoRAAttnAddedKVProcessor:
|
|
||||||
lora_processor = lora_processor_cls(
|
|
||||||
hidden_size,
|
|
||||||
cross_attention_dim=self.add_k_proj.weight.shape[0],
|
|
||||||
rank=self.to_q.lora_layer.rank,
|
|
||||||
network_alpha=self.to_q.lora_layer.network_alpha,
|
|
||||||
)
|
|
||||||
lora_processor.to_q_lora.load_state_dict(self.to_q.lora_layer.state_dict())
|
|
||||||
lora_processor.to_k_lora.load_state_dict(self.to_k.lora_layer.state_dict())
|
|
||||||
lora_processor.to_v_lora.load_state_dict(self.to_v.lora_layer.state_dict())
|
|
||||||
lora_processor.to_out_lora.load_state_dict(self.to_out[0].lora_layer.state_dict())
|
|
||||||
|
|
||||||
# only save if used
|
|
||||||
if self.add_k_proj.lora_layer is not None:
|
|
||||||
lora_processor.add_k_proj_lora.load_state_dict(self.add_k_proj.lora_layer.state_dict())
|
|
||||||
lora_processor.add_v_proj_lora.load_state_dict(self.add_v_proj.lora_layer.state_dict())
|
|
||||||
else:
|
|
||||||
lora_processor.add_k_proj_lora = None
|
|
||||||
lora_processor.add_v_proj_lora = None
|
|
||||||
else:
|
|
||||||
raise ValueError(f"{lora_processor_cls} does not exist.")
|
|
||||||
|
|
||||||
return lora_processor
|
|
||||||
|
|
||||||
def forward(
|
def forward(
|
||||||
self,
|
self,
|
||||||
hidden_states: torch.Tensor,
|
hidden_states: torch.Tensor,
|
||||||
@@ -2238,264 +2133,6 @@ class SpatialNorm(nn.Module):
|
|||||||
return new_f
|
return new_f
|
||||||
|
|
||||||
|
|
||||||
class LoRAAttnProcessor(nn.Module):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
hidden_size: int,
|
|
||||||
cross_attention_dim: Optional[int] = None,
|
|
||||||
rank: int = 4,
|
|
||||||
network_alpha: Optional[int] = None,
|
|
||||||
**kwargs,
|
|
||||||
):
|
|
||||||
deprecation_message = "Using LoRAAttnProcessor is deprecated. Please use the PEFT backend for all things LoRA. You can install PEFT by running `pip install peft`."
|
|
||||||
deprecate("LoRAAttnProcessor", "0.30.0", deprecation_message, standard_warn=False)
|
|
||||||
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self.hidden_size = hidden_size
|
|
||||||
self.cross_attention_dim = cross_attention_dim
|
|
||||||
self.rank = rank
|
|
||||||
|
|
||||||
q_rank = kwargs.pop("q_rank", None)
|
|
||||||
q_hidden_size = kwargs.pop("q_hidden_size", None)
|
|
||||||
q_rank = q_rank if q_rank is not None else rank
|
|
||||||
q_hidden_size = q_hidden_size if q_hidden_size is not None else hidden_size
|
|
||||||
|
|
||||||
v_rank = kwargs.pop("v_rank", None)
|
|
||||||
v_hidden_size = kwargs.pop("v_hidden_size", None)
|
|
||||||
v_rank = v_rank if v_rank is not None else rank
|
|
||||||
v_hidden_size = v_hidden_size if v_hidden_size is not None else hidden_size
|
|
||||||
|
|
||||||
out_rank = kwargs.pop("out_rank", None)
|
|
||||||
out_hidden_size = kwargs.pop("out_hidden_size", None)
|
|
||||||
out_rank = out_rank if out_rank is not None else rank
|
|
||||||
out_hidden_size = out_hidden_size if out_hidden_size is not None else hidden_size
|
|
||||||
|
|
||||||
self.to_q_lora = LoRALinearLayer(q_hidden_size, q_hidden_size, q_rank, network_alpha)
|
|
||||||
self.to_k_lora = LoRALinearLayer(cross_attention_dim or hidden_size, hidden_size, rank, network_alpha)
|
|
||||||
self.to_v_lora = LoRALinearLayer(cross_attention_dim or v_hidden_size, v_hidden_size, v_rank, network_alpha)
|
|
||||||
self.to_out_lora = LoRALinearLayer(out_hidden_size, out_hidden_size, out_rank, network_alpha)
|
|
||||||
|
|
||||||
def __call__(self, attn: Attention, hidden_states: torch.Tensor, **kwargs) -> torch.Tensor:
|
|
||||||
self_cls_name = self.__class__.__name__
|
|
||||||
deprecate(
|
|
||||||
self_cls_name,
|
|
||||||
"0.26.0",
|
|
||||||
(
|
|
||||||
f"Make sure use {self_cls_name[4:]} instead by setting"
|
|
||||||
"LoRA layers to `self.{to_q,to_k,to_v,to_out[0]}.lora_layer` respectively. This will be done automatically when using"
|
|
||||||
" `LoraLoaderMixin.load_lora_weights`"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
attn.to_q.lora_layer = self.to_q_lora.to(hidden_states.device)
|
|
||||||
attn.to_k.lora_layer = self.to_k_lora.to(hidden_states.device)
|
|
||||||
attn.to_v.lora_layer = self.to_v_lora.to(hidden_states.device)
|
|
||||||
attn.to_out[0].lora_layer = self.to_out_lora.to(hidden_states.device)
|
|
||||||
|
|
||||||
attn._modules.pop("processor")
|
|
||||||
attn.processor = AttnProcessor()
|
|
||||||
return attn.processor(attn, hidden_states, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class LoRAAttnProcessor2_0(nn.Module):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
hidden_size: int,
|
|
||||||
cross_attention_dim: Optional[int] = None,
|
|
||||||
rank: int = 4,
|
|
||||||
network_alpha: Optional[int] = None,
|
|
||||||
**kwargs,
|
|
||||||
):
|
|
||||||
deprecation_message = "Using LoRAAttnProcessor is deprecated. Please use the PEFT backend for all things LoRA. You can install PEFT by running `pip install peft`."
|
|
||||||
deprecate("LoRAAttnProcessor2_0", "0.30.0", deprecation_message, standard_warn=False)
|
|
||||||
|
|
||||||
super().__init__()
|
|
||||||
if not hasattr(F, "scaled_dot_product_attention"):
|
|
||||||
raise ImportError("AttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.")
|
|
||||||
|
|
||||||
self.hidden_size = hidden_size
|
|
||||||
self.cross_attention_dim = cross_attention_dim
|
|
||||||
self.rank = rank
|
|
||||||
|
|
||||||
q_rank = kwargs.pop("q_rank", None)
|
|
||||||
q_hidden_size = kwargs.pop("q_hidden_size", None)
|
|
||||||
q_rank = q_rank if q_rank is not None else rank
|
|
||||||
q_hidden_size = q_hidden_size if q_hidden_size is not None else hidden_size
|
|
||||||
|
|
||||||
v_rank = kwargs.pop("v_rank", None)
|
|
||||||
v_hidden_size = kwargs.pop("v_hidden_size", None)
|
|
||||||
v_rank = v_rank if v_rank is not None else rank
|
|
||||||
v_hidden_size = v_hidden_size if v_hidden_size is not None else hidden_size
|
|
||||||
|
|
||||||
out_rank = kwargs.pop("out_rank", None)
|
|
||||||
out_hidden_size = kwargs.pop("out_hidden_size", None)
|
|
||||||
out_rank = out_rank if out_rank is not None else rank
|
|
||||||
out_hidden_size = out_hidden_size if out_hidden_size is not None else hidden_size
|
|
||||||
|
|
||||||
self.to_q_lora = LoRALinearLayer(q_hidden_size, q_hidden_size, q_rank, network_alpha)
|
|
||||||
self.to_k_lora = LoRALinearLayer(cross_attention_dim or hidden_size, hidden_size, rank, network_alpha)
|
|
||||||
self.to_v_lora = LoRALinearLayer(cross_attention_dim or v_hidden_size, v_hidden_size, v_rank, network_alpha)
|
|
||||||
self.to_out_lora = LoRALinearLayer(out_hidden_size, out_hidden_size, out_rank, network_alpha)
|
|
||||||
|
|
||||||
def __call__(self, attn: Attention, hidden_states: torch.Tensor, **kwargs) -> torch.Tensor:
|
|
||||||
self_cls_name = self.__class__.__name__
|
|
||||||
deprecate(
|
|
||||||
self_cls_name,
|
|
||||||
"0.26.0",
|
|
||||||
(
|
|
||||||
f"Make sure use {self_cls_name[4:]} instead by setting"
|
|
||||||
"LoRA layers to `self.{to_q,to_k,to_v,to_out[0]}.lora_layer` respectively. This will be done automatically when using"
|
|
||||||
" `LoraLoaderMixin.load_lora_weights`"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
attn.to_q.lora_layer = self.to_q_lora.to(hidden_states.device)
|
|
||||||
attn.to_k.lora_layer = self.to_k_lora.to(hidden_states.device)
|
|
||||||
attn.to_v.lora_layer = self.to_v_lora.to(hidden_states.device)
|
|
||||||
attn.to_out[0].lora_layer = self.to_out_lora.to(hidden_states.device)
|
|
||||||
|
|
||||||
attn._modules.pop("processor")
|
|
||||||
attn.processor = AttnProcessor2_0()
|
|
||||||
return attn.processor(attn, hidden_states, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class LoRAXFormersAttnProcessor(nn.Module):
|
|
||||||
r"""
|
|
||||||
Processor for implementing the LoRA attention mechanism with memory efficient attention using xFormers.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
hidden_size (`int`, *optional*):
|
|
||||||
The hidden size of the attention layer.
|
|
||||||
cross_attention_dim (`int`, *optional*):
|
|
||||||
The number of channels in the `encoder_hidden_states`.
|
|
||||||
rank (`int`, defaults to 4):
|
|
||||||
The dimension of the LoRA update matrices.
|
|
||||||
attention_op (`Callable`, *optional*, defaults to `None`):
|
|
||||||
The base
|
|
||||||
[operator](https://facebookresearch.github.io/xformers/components/ops.html#xformers.ops.AttentionOpBase) to
|
|
||||||
use as the attention operator. It is recommended to set to `None`, and allow xFormers to choose the best
|
|
||||||
operator.
|
|
||||||
network_alpha (`int`, *optional*):
|
|
||||||
Equivalent to `alpha` but it's usage is specific to Kohya (A1111) style LoRAs.
|
|
||||||
kwargs (`dict`):
|
|
||||||
Additional keyword arguments to pass to the `LoRALinearLayer` layers.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
hidden_size: int,
|
|
||||||
cross_attention_dim: int,
|
|
||||||
rank: int = 4,
|
|
||||||
attention_op: Optional[Callable] = None,
|
|
||||||
network_alpha: Optional[int] = None,
|
|
||||||
**kwargs,
|
|
||||||
):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self.hidden_size = hidden_size
|
|
||||||
self.cross_attention_dim = cross_attention_dim
|
|
||||||
self.rank = rank
|
|
||||||
self.attention_op = attention_op
|
|
||||||
|
|
||||||
q_rank = kwargs.pop("q_rank", None)
|
|
||||||
q_hidden_size = kwargs.pop("q_hidden_size", None)
|
|
||||||
q_rank = q_rank if q_rank is not None else rank
|
|
||||||
q_hidden_size = q_hidden_size if q_hidden_size is not None else hidden_size
|
|
||||||
|
|
||||||
v_rank = kwargs.pop("v_rank", None)
|
|
||||||
v_hidden_size = kwargs.pop("v_hidden_size", None)
|
|
||||||
v_rank = v_rank if v_rank is not None else rank
|
|
||||||
v_hidden_size = v_hidden_size if v_hidden_size is not None else hidden_size
|
|
||||||
|
|
||||||
out_rank = kwargs.pop("out_rank", None)
|
|
||||||
out_hidden_size = kwargs.pop("out_hidden_size", None)
|
|
||||||
out_rank = out_rank if out_rank is not None else rank
|
|
||||||
out_hidden_size = out_hidden_size if out_hidden_size is not None else hidden_size
|
|
||||||
|
|
||||||
self.to_q_lora = LoRALinearLayer(q_hidden_size, q_hidden_size, q_rank, network_alpha)
|
|
||||||
self.to_k_lora = LoRALinearLayer(cross_attention_dim or hidden_size, hidden_size, rank, network_alpha)
|
|
||||||
self.to_v_lora = LoRALinearLayer(cross_attention_dim or v_hidden_size, v_hidden_size, v_rank, network_alpha)
|
|
||||||
self.to_out_lora = LoRALinearLayer(out_hidden_size, out_hidden_size, out_rank, network_alpha)
|
|
||||||
|
|
||||||
def __call__(self, attn: Attention, hidden_states: torch.Tensor, **kwargs) -> torch.Tensor:
|
|
||||||
self_cls_name = self.__class__.__name__
|
|
||||||
deprecate(
|
|
||||||
self_cls_name,
|
|
||||||
"0.26.0",
|
|
||||||
(
|
|
||||||
f"Make sure use {self_cls_name[4:]} instead by setting"
|
|
||||||
"LoRA layers to `self.{to_q,to_k,to_v,add_k_proj,add_v_proj,to_out[0]}.lora_layer` respectively. This will be done automatically when using"
|
|
||||||
" `LoraLoaderMixin.load_lora_weights`"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
attn.to_q.lora_layer = self.to_q_lora.to(hidden_states.device)
|
|
||||||
attn.to_k.lora_layer = self.to_k_lora.to(hidden_states.device)
|
|
||||||
attn.to_v.lora_layer = self.to_v_lora.to(hidden_states.device)
|
|
||||||
attn.to_out[0].lora_layer = self.to_out_lora.to(hidden_states.device)
|
|
||||||
|
|
||||||
attn._modules.pop("processor")
|
|
||||||
attn.processor = XFormersAttnProcessor()
|
|
||||||
return attn.processor(attn, hidden_states, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class LoRAAttnAddedKVProcessor(nn.Module):
|
|
||||||
r"""
|
|
||||||
Processor for implementing the LoRA attention mechanism with extra learnable key and value matrices for the text
|
|
||||||
encoder.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
hidden_size (`int`, *optional*):
|
|
||||||
The hidden size of the attention layer.
|
|
||||||
cross_attention_dim (`int`, *optional*, defaults to `None`):
|
|
||||||
The number of channels in the `encoder_hidden_states`.
|
|
||||||
rank (`int`, defaults to 4):
|
|
||||||
The dimension of the LoRA update matrices.
|
|
||||||
network_alpha (`int`, *optional*):
|
|
||||||
Equivalent to `alpha` but it's usage is specific to Kohya (A1111) style LoRAs.
|
|
||||||
kwargs (`dict`):
|
|
||||||
Additional keyword arguments to pass to the `LoRALinearLayer` layers.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
hidden_size: int,
|
|
||||||
cross_attention_dim: Optional[int] = None,
|
|
||||||
rank: int = 4,
|
|
||||||
network_alpha: Optional[int] = None,
|
|
||||||
):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self.hidden_size = hidden_size
|
|
||||||
self.cross_attention_dim = cross_attention_dim
|
|
||||||
self.rank = rank
|
|
||||||
|
|
||||||
self.to_q_lora = LoRALinearLayer(hidden_size, hidden_size, rank, network_alpha)
|
|
||||||
self.add_k_proj_lora = LoRALinearLayer(cross_attention_dim or hidden_size, hidden_size, rank, network_alpha)
|
|
||||||
self.add_v_proj_lora = LoRALinearLayer(cross_attention_dim or hidden_size, hidden_size, rank, network_alpha)
|
|
||||||
self.to_k_lora = LoRALinearLayer(hidden_size, hidden_size, rank, network_alpha)
|
|
||||||
self.to_v_lora = LoRALinearLayer(hidden_size, hidden_size, rank, network_alpha)
|
|
||||||
self.to_out_lora = LoRALinearLayer(hidden_size, hidden_size, rank, network_alpha)
|
|
||||||
|
|
||||||
def __call__(self, attn: Attention, hidden_states: torch.Tensor, **kwargs) -> torch.Tensor:
|
|
||||||
self_cls_name = self.__class__.__name__
|
|
||||||
deprecate(
|
|
||||||
self_cls_name,
|
|
||||||
"0.26.0",
|
|
||||||
(
|
|
||||||
f"Make sure use {self_cls_name[4:]} instead by setting"
|
|
||||||
"LoRA layers to `self.{to_q,to_k,to_v,add_k_proj,add_v_proj,to_out[0]}.lora_layer` respectively. This will be done automatically when using"
|
|
||||||
" `LoraLoaderMixin.load_lora_weights`"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
attn.to_q.lora_layer = self.to_q_lora.to(hidden_states.device)
|
|
||||||
attn.to_k.lora_layer = self.to_k_lora.to(hidden_states.device)
|
|
||||||
attn.to_v.lora_layer = self.to_v_lora.to(hidden_states.device)
|
|
||||||
attn.to_out[0].lora_layer = self.to_out_lora.to(hidden_states.device)
|
|
||||||
|
|
||||||
attn._modules.pop("processor")
|
|
||||||
attn.processor = AttnAddedKVProcessor()
|
|
||||||
return attn.processor(attn, hidden_states, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class IPAdapterAttnProcessor(nn.Module):
|
class IPAdapterAttnProcessor(nn.Module):
|
||||||
r"""
|
r"""
|
||||||
Attention processor for Multiple IP-Adapters.
|
Attention processor for Multiple IP-Adapters.
|
||||||
@@ -2924,19 +2561,245 @@ class IPAdapterAttnProcessor2_0(torch.nn.Module):
|
|||||||
return hidden_states
|
return hidden_states
|
||||||
|
|
||||||
|
|
||||||
LORA_ATTENTION_PROCESSORS = (
|
class PAGIdentitySelfAttnProcessor2_0:
|
||||||
LoRAAttnProcessor,
|
r"""
|
||||||
LoRAAttnProcessor2_0,
|
Processor for implementing PAG using scaled dot-product attention (enabled by default if you're using PyTorch 2.0).
|
||||||
LoRAXFormersAttnProcessor,
|
PAG reference: https://arxiv.org/abs/2403.17377
|
||||||
LoRAAttnAddedKVProcessor,
|
"""
|
||||||
)
|
|
||||||
|
def __init__(self):
|
||||||
|
if not hasattr(F, "scaled_dot_product_attention"):
|
||||||
|
raise ImportError(
|
||||||
|
"PAGIdentitySelfAttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0."
|
||||||
|
)
|
||||||
|
|
||||||
|
def __call__(
|
||||||
|
self,
|
||||||
|
attn: Attention,
|
||||||
|
hidden_states: torch.FloatTensor,
|
||||||
|
encoder_hidden_states: Optional[torch.FloatTensor] = None,
|
||||||
|
attention_mask: Optional[torch.FloatTensor] = None,
|
||||||
|
temb: Optional[torch.FloatTensor] = None,
|
||||||
|
) -> torch.Tensor:
|
||||||
|
residual = hidden_states
|
||||||
|
if attn.spatial_norm is not None:
|
||||||
|
hidden_states = attn.spatial_norm(hidden_states, temb)
|
||||||
|
|
||||||
|
input_ndim = hidden_states.ndim
|
||||||
|
if input_ndim == 4:
|
||||||
|
batch_size, channel, height, width = hidden_states.shape
|
||||||
|
hidden_states = hidden_states.view(batch_size, channel, height * width).transpose(1, 2)
|
||||||
|
|
||||||
|
# chunk
|
||||||
|
hidden_states_org, hidden_states_ptb = hidden_states.chunk(2)
|
||||||
|
|
||||||
|
# original path
|
||||||
|
batch_size, sequence_length, _ = hidden_states_org.shape
|
||||||
|
|
||||||
|
if attention_mask is not None:
|
||||||
|
attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size)
|
||||||
|
# scaled_dot_product_attention expects attention_mask shape to be
|
||||||
|
# (batch, heads, source_length, target_length)
|
||||||
|
attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1])
|
||||||
|
|
||||||
|
if attn.group_norm is not None:
|
||||||
|
hidden_states_org = attn.group_norm(hidden_states_org.transpose(1, 2)).transpose(1, 2)
|
||||||
|
|
||||||
|
query = attn.to_q(hidden_states_org)
|
||||||
|
key = attn.to_k(hidden_states_org)
|
||||||
|
value = attn.to_v(hidden_states_org)
|
||||||
|
|
||||||
|
inner_dim = key.shape[-1]
|
||||||
|
head_dim = inner_dim // attn.heads
|
||||||
|
|
||||||
|
query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
|
||||||
|
key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
|
||||||
|
value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
|
||||||
|
|
||||||
|
# the output of sdp = (batch, num_heads, seq_len, head_dim)
|
||||||
|
# TODO: add support for attn.scale when we move to Torch 2.1
|
||||||
|
hidden_states_org = F.scaled_dot_product_attention(
|
||||||
|
query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False
|
||||||
|
)
|
||||||
|
hidden_states_org = hidden_states_org.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim)
|
||||||
|
hidden_states_org = hidden_states_org.to(query.dtype)
|
||||||
|
|
||||||
|
# linear proj
|
||||||
|
hidden_states_org = attn.to_out[0](hidden_states_org)
|
||||||
|
# dropout
|
||||||
|
hidden_states_org = attn.to_out[1](hidden_states_org)
|
||||||
|
|
||||||
|
if input_ndim == 4:
|
||||||
|
hidden_states_org = hidden_states_org.transpose(-1, -2).reshape(batch_size, channel, height, width)
|
||||||
|
|
||||||
|
# perturbed path (identity attention)
|
||||||
|
batch_size, sequence_length, _ = hidden_states_ptb.shape
|
||||||
|
|
||||||
|
if attention_mask is not None:
|
||||||
|
attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size)
|
||||||
|
# scaled_dot_product_attention expects attention_mask shape to be
|
||||||
|
# (batch, heads, source_length, target_length)
|
||||||
|
attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1])
|
||||||
|
|
||||||
|
if attn.group_norm is not None:
|
||||||
|
hidden_states_ptb = attn.group_norm(hidden_states_ptb.transpose(1, 2)).transpose(1, 2)
|
||||||
|
|
||||||
|
hidden_states_ptb = attn.to_v(hidden_states_ptb)
|
||||||
|
hidden_states_ptb = hidden_states_ptb.to(query.dtype)
|
||||||
|
|
||||||
|
# linear proj
|
||||||
|
hidden_states_ptb = attn.to_out[0](hidden_states_ptb)
|
||||||
|
# dropout
|
||||||
|
hidden_states_ptb = attn.to_out[1](hidden_states_ptb)
|
||||||
|
|
||||||
|
if input_ndim == 4:
|
||||||
|
hidden_states_ptb = hidden_states_ptb.transpose(-1, -2).reshape(batch_size, channel, height, width)
|
||||||
|
|
||||||
|
# cat
|
||||||
|
hidden_states = torch.cat([hidden_states_org, hidden_states_ptb])
|
||||||
|
|
||||||
|
if attn.residual_connection:
|
||||||
|
hidden_states = hidden_states + residual
|
||||||
|
|
||||||
|
hidden_states = hidden_states / attn.rescale_output_factor
|
||||||
|
|
||||||
|
return hidden_states
|
||||||
|
|
||||||
|
|
||||||
|
class PAGCFGIdentitySelfAttnProcessor2_0:
|
||||||
|
r"""
|
||||||
|
Processor for implementing PAG using scaled dot-product attention (enabled by default if you're using PyTorch 2.0).
|
||||||
|
PAG reference: https://arxiv.org/abs/2403.17377
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
if not hasattr(F, "scaled_dot_product_attention"):
|
||||||
|
raise ImportError(
|
||||||
|
"PAGCFGIdentitySelfAttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0."
|
||||||
|
)
|
||||||
|
|
||||||
|
def __call__(
|
||||||
|
self,
|
||||||
|
attn: Attention,
|
||||||
|
hidden_states: torch.FloatTensor,
|
||||||
|
encoder_hidden_states: Optional[torch.FloatTensor] = None,
|
||||||
|
attention_mask: Optional[torch.FloatTensor] = None,
|
||||||
|
temb: Optional[torch.FloatTensor] = None,
|
||||||
|
) -> torch.Tensor:
|
||||||
|
residual = hidden_states
|
||||||
|
if attn.spatial_norm is not None:
|
||||||
|
hidden_states = attn.spatial_norm(hidden_states, temb)
|
||||||
|
|
||||||
|
input_ndim = hidden_states.ndim
|
||||||
|
if input_ndim == 4:
|
||||||
|
batch_size, channel, height, width = hidden_states.shape
|
||||||
|
hidden_states = hidden_states.view(batch_size, channel, height * width).transpose(1, 2)
|
||||||
|
|
||||||
|
# chunk
|
||||||
|
hidden_states_uncond, hidden_states_org, hidden_states_ptb = hidden_states.chunk(3)
|
||||||
|
hidden_states_org = torch.cat([hidden_states_uncond, hidden_states_org])
|
||||||
|
|
||||||
|
# original path
|
||||||
|
batch_size, sequence_length, _ = hidden_states_org.shape
|
||||||
|
|
||||||
|
if attention_mask is not None:
|
||||||
|
attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size)
|
||||||
|
# scaled_dot_product_attention expects attention_mask shape to be
|
||||||
|
# (batch, heads, source_length, target_length)
|
||||||
|
attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1])
|
||||||
|
|
||||||
|
if attn.group_norm is not None:
|
||||||
|
hidden_states_org = attn.group_norm(hidden_states_org.transpose(1, 2)).transpose(1, 2)
|
||||||
|
|
||||||
|
query = attn.to_q(hidden_states_org)
|
||||||
|
key = attn.to_k(hidden_states_org)
|
||||||
|
value = attn.to_v(hidden_states_org)
|
||||||
|
|
||||||
|
inner_dim = key.shape[-1]
|
||||||
|
head_dim = inner_dim // attn.heads
|
||||||
|
|
||||||
|
query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
|
||||||
|
|
||||||
|
key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
|
||||||
|
value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
|
||||||
|
|
||||||
|
# the output of sdp = (batch, num_heads, seq_len, head_dim)
|
||||||
|
# TODO: add support for attn.scale when we move to Torch 2.1
|
||||||
|
hidden_states_org = F.scaled_dot_product_attention(
|
||||||
|
query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False
|
||||||
|
)
|
||||||
|
|
||||||
|
hidden_states_org = hidden_states_org.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim)
|
||||||
|
hidden_states_org = hidden_states_org.to(query.dtype)
|
||||||
|
|
||||||
|
# linear proj
|
||||||
|
hidden_states_org = attn.to_out[0](hidden_states_org)
|
||||||
|
# dropout
|
||||||
|
hidden_states_org = attn.to_out[1](hidden_states_org)
|
||||||
|
|
||||||
|
if input_ndim == 4:
|
||||||
|
hidden_states_org = hidden_states_org.transpose(-1, -2).reshape(batch_size, channel, height, width)
|
||||||
|
|
||||||
|
# perturbed path (identity attention)
|
||||||
|
batch_size, sequence_length, _ = hidden_states_ptb.shape
|
||||||
|
|
||||||
|
if attention_mask is not None:
|
||||||
|
attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size)
|
||||||
|
# scaled_dot_product_attention expects attention_mask shape to be
|
||||||
|
# (batch, heads, source_length, target_length)
|
||||||
|
attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1])
|
||||||
|
|
||||||
|
if attn.group_norm is not None:
|
||||||
|
hidden_states_ptb = attn.group_norm(hidden_states_ptb.transpose(1, 2)).transpose(1, 2)
|
||||||
|
|
||||||
|
value = attn.to_v(hidden_states_ptb)
|
||||||
|
hidden_states_ptb = value
|
||||||
|
hidden_states_ptb = hidden_states_ptb.to(query.dtype)
|
||||||
|
|
||||||
|
# linear proj
|
||||||
|
hidden_states_ptb = attn.to_out[0](hidden_states_ptb)
|
||||||
|
# dropout
|
||||||
|
hidden_states_ptb = attn.to_out[1](hidden_states_ptb)
|
||||||
|
|
||||||
|
if input_ndim == 4:
|
||||||
|
hidden_states_ptb = hidden_states_ptb.transpose(-1, -2).reshape(batch_size, channel, height, width)
|
||||||
|
|
||||||
|
# cat
|
||||||
|
hidden_states = torch.cat([hidden_states_org, hidden_states_ptb])
|
||||||
|
|
||||||
|
if attn.residual_connection:
|
||||||
|
hidden_states = hidden_states + residual
|
||||||
|
|
||||||
|
hidden_states = hidden_states / attn.rescale_output_factor
|
||||||
|
|
||||||
|
return hidden_states
|
||||||
|
|
||||||
|
|
||||||
|
class LoRAAttnProcessor:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class LoRAAttnProcessor2_0:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class LoRAXFormersAttnProcessor:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class LoRAAttnAddedKVProcessor:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
ADDED_KV_ATTENTION_PROCESSORS = (
|
ADDED_KV_ATTENTION_PROCESSORS = (
|
||||||
AttnAddedKVProcessor,
|
AttnAddedKVProcessor,
|
||||||
SlicedAttnAddedKVProcessor,
|
SlicedAttnAddedKVProcessor,
|
||||||
AttnAddedKVProcessor2_0,
|
AttnAddedKVProcessor2_0,
|
||||||
XFormersAttnAddedKVProcessor,
|
XFormersAttnAddedKVProcessor,
|
||||||
LoRAAttnAddedKVProcessor,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
CROSS_ATTENTION_PROCESSORS = (
|
CROSS_ATTENTION_PROCESSORS = (
|
||||||
@@ -2944,9 +2807,6 @@ CROSS_ATTENTION_PROCESSORS = (
|
|||||||
AttnProcessor2_0,
|
AttnProcessor2_0,
|
||||||
XFormersAttnProcessor,
|
XFormersAttnProcessor,
|
||||||
SlicedAttnProcessor,
|
SlicedAttnProcessor,
|
||||||
LoRAAttnProcessor,
|
|
||||||
LoRAAttnProcessor2_0,
|
|
||||||
LoRAXFormersAttnProcessor,
|
|
||||||
IPAdapterAttnProcessor,
|
IPAdapterAttnProcessor,
|
||||||
IPAdapterAttnProcessor2_0,
|
IPAdapterAttnProcessor2_0,
|
||||||
)
|
)
|
||||||
@@ -2964,9 +2824,6 @@ AttentionProcessor = Union[
|
|||||||
CustomDiffusionAttnProcessor,
|
CustomDiffusionAttnProcessor,
|
||||||
CustomDiffusionXFormersAttnProcessor,
|
CustomDiffusionXFormersAttnProcessor,
|
||||||
CustomDiffusionAttnProcessor2_0,
|
CustomDiffusionAttnProcessor2_0,
|
||||||
# deprecated
|
PAGCFGIdentitySelfAttnProcessor2_0,
|
||||||
LoRAAttnProcessor,
|
PAGIdentitySelfAttnProcessor2_0,
|
||||||
LoRAAttnProcessor2_0,
|
|
||||||
LoRAXFormersAttnProcessor,
|
|
||||||
LoRAAttnAddedKVProcessor,
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ class AutoencoderKL(ModelMixin, ConfigMixin, FromOriginalModelMixin):
|
|||||||
|
|
||||||
def fn_recursive_add_processors(name: str, module: torch.nn.Module, processors: Dict[str, AttentionProcessor]):
|
def fn_recursive_add_processors(name: str, module: torch.nn.Module, processors: Dict[str, AttentionProcessor]):
|
||||||
if hasattr(module, "get_processor"):
|
if hasattr(module, "get_processor"):
|
||||||
processors[f"{name}.processor"] = module.get_processor(return_deprecated_lora=True)
|
processors[f"{name}.processor"] = module.get_processor()
|
||||||
|
|
||||||
for sub_name, child in module.named_children():
|
for sub_name, child in module.named_children():
|
||||||
fn_recursive_add_processors(f"{name}.{sub_name}", child, processors)
|
fn_recursive_add_processors(f"{name}.{sub_name}", child, processors)
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ class AutoencoderKLTemporalDecoder(ModelMixin, ConfigMixin):
|
|||||||
|
|
||||||
def fn_recursive_add_processors(name: str, module: torch.nn.Module, processors: Dict[str, AttentionProcessor]):
|
def fn_recursive_add_processors(name: str, module: torch.nn.Module, processors: Dict[str, AttentionProcessor]):
|
||||||
if hasattr(module, "get_processor"):
|
if hasattr(module, "get_processor"):
|
||||||
processors[f"{name}.processor"] = module.get_processor(return_deprecated_lora=True)
|
processors[f"{name}.processor"] = module.get_processor()
|
||||||
|
|
||||||
for sub_name, child in module.named_children():
|
for sub_name, child in module.named_children():
|
||||||
fn_recursive_add_processors(f"{name}.{sub_name}", child, processors)
|
fn_recursive_add_processors(f"{name}.{sub_name}", child, processors)
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ class AutoencoderTiny(ModelMixin, ConfigMixin):
|
|||||||
latent_shift: float = 0.5,
|
latent_shift: float = 0.5,
|
||||||
force_upcast: bool = False,
|
force_upcast: bool = False,
|
||||||
scaling_factor: float = 1.0,
|
scaling_factor: float = 1.0,
|
||||||
|
shift_factor: float = 0.0,
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ class ConsistencyDecoderVAE(ModelMixin, ConfigMixin):
|
|||||||
|
|
||||||
def fn_recursive_add_processors(name: str, module: torch.nn.Module, processors: Dict[str, AttentionProcessor]):
|
def fn_recursive_add_processors(name: str, module: torch.nn.Module, processors: Dict[str, AttentionProcessor]):
|
||||||
if hasattr(module, "get_processor"):
|
if hasattr(module, "get_processor"):
|
||||||
processors[f"{name}.processor"] = module.get_processor(return_deprecated_lora=True)
|
processors[f"{name}.processor"] = module.get_processor()
|
||||||
|
|
||||||
for sub_name, child in module.named_children():
|
for sub_name, child in module.named_children():
|
||||||
fn_recursive_add_processors(f"{name}.{sub_name}", child, processors)
|
fn_recursive_add_processors(f"{name}.{sub_name}", child, processors)
|
||||||
|
|||||||
@@ -166,12 +166,12 @@ class VQModel(ModelMixin, ConfigMixin):
|
|||||||
Args:
|
Args:
|
||||||
sample (`torch.Tensor`): Input sample.
|
sample (`torch.Tensor`): Input sample.
|
||||||
return_dict (`bool`, *optional*, defaults to `True`):
|
return_dict (`bool`, *optional*, defaults to `True`):
|
||||||
Whether or not to return a [`models.vq_model.VQEncoderOutput`] instead of a plain tuple.
|
Whether or not to return a [`models.autoencoders.vq_model.VQEncoderOutput`] instead of a plain tuple.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
[`~models.vq_model.VQEncoderOutput`] or `tuple`:
|
[`~models.autoencoders.vq_model.VQEncoderOutput`] or `tuple`:
|
||||||
If return_dict is True, a [`~models.vq_model.VQEncoderOutput`] is returned, otherwise a plain `tuple`
|
If return_dict is True, a [`~models.autoencoders.vq_model.VQEncoderOutput`] is returned, otherwise a
|
||||||
is returned.
|
plain `tuple` is returned.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
h = self.encode(sample).latents
|
h = self.encode(sample).latents
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class ControlNetOutput(BaseOutput):
|
|||||||
be of shape `(batch_size, channel * resolution, height //resolution, width // resolution)`. Output can be
|
be of shape `(batch_size, channel * resolution, height //resolution, width // resolution)`. Output can be
|
||||||
used to condition the original UNet's downsampling activations.
|
used to condition the original UNet's downsampling activations.
|
||||||
mid_down_block_re_sample (`torch.Tensor`):
|
mid_down_block_re_sample (`torch.Tensor`):
|
||||||
The activation of the midde block (the lowest sample resolution). Each tensor should be of shape
|
The activation of the middle block (the lowest sample resolution). Each tensor should be of shape
|
||||||
`(batch_size, channel * lowest_resolution, height // lowest_resolution, width // lowest_resolution)`.
|
`(batch_size, channel * lowest_resolution, height // lowest_resolution, width // lowest_resolution)`.
|
||||||
Output can be used to condition the original UNet's middle block activation.
|
Output can be used to condition the original UNet's middle block activation.
|
||||||
"""
|
"""
|
||||||
@@ -530,7 +530,7 @@ class ControlNetModel(ModelMixin, ConfigMixin, FromOriginalModelMixin):
|
|||||||
|
|
||||||
def fn_recursive_add_processors(name: str, module: torch.nn.Module, processors: Dict[str, AttentionProcessor]):
|
def fn_recursive_add_processors(name: str, module: torch.nn.Module, processors: Dict[str, AttentionProcessor]):
|
||||||
if hasattr(module, "get_processor"):
|
if hasattr(module, "get_processor"):
|
||||||
processors[f"{name}.processor"] = module.get_processor(return_deprecated_lora=True)
|
processors[f"{name}.processor"] = module.get_processor()
|
||||||
|
|
||||||
for sub_name, child in module.named_children():
|
for sub_name, child in module.named_children():
|
||||||
fn_recursive_add_processors(f"{name}.{sub_name}", child, processors)
|
fn_recursive_add_processors(f"{name}.{sub_name}", child, processors)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user