Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f9c7b327cb | |||
| a81334e3f0 | |||
| 255adf0466 | |||
| d704a730cd | |||
| 5d04eebd1f | |||
| f145d48ed7 | |||
| 49db233b35 | |||
| 765fef7134 | |||
| 8c98a187c7 | |||
| ece6d89cf2 | |||
| 16ac1b2f4f | |||
| ec9df6fc48 | |||
| 09618d09a6 | |||
| d24e7d3ea9 | |||
| 57a16f35ee | |||
| f4adaae5cb | |||
| 49a0f3ab02 | |||
| 93ea26f272 | |||
| f5dfe2a8b0 | |||
| 4836cfad98 | |||
| 1ccbfbb663 | |||
| 29dfe22a8e | |||
| 56806cdbfd | |||
| 8ccc76ab37 | |||
| c46711e895 | |||
| 1d686bac81 | |||
| 0a401b95b7 | |||
| 24cb282d36 | |||
| bcf0f4a789 | |||
| fdb114618d | |||
| ed333f06ae | |||
| 32212b6df6 | |||
| 9ecb271ac8 | |||
| a2792cd942 | |||
| c341111d69 | |||
| b868e8a2fc | |||
| 41b9cd8787 | |||
| 0d08249c9f | |||
| 3b27b23082 | |||
| e4c00bc5c2 | |||
| cf132fb6b0 | |||
| 981ea82591 | |||
| 20fac7bc9d | |||
| 79b16373b7 | |||
| ff3d380824 | |||
| 664e931bcb | |||
| 88bdd97ccd | |||
| 08b453e382 | |||
| 2a111bc9fe | |||
| 16e6997f0d | |||
| 3b9b98656e | |||
| b65928b556 | |||
| 6bf1ca2c79 | |||
| 978dec9014 | |||
| 79a7ab92d1 | |||
| c2717317f0 | |||
| bf7f9b49a2 | |||
| e192ae08d3 | |||
| 87a09d66f3 | |||
| 75ada25048 | |||
| 2243a59483 | |||
| 466d32c442 | |||
| 20ba1fdbbd |
@@ -0,0 +1,52 @@
|
|||||||
|
name: Benchmarking tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "30 1 1,15 * *" # every 2 weeks on the 1st and the 15th of every month at 1:30 AM
|
||||||
|
|
||||||
|
env:
|
||||||
|
DIFFUSERS_IS_CI: yes
|
||||||
|
HF_HOME: /mnt/cache
|
||||||
|
OMP_NUM_THREADS: 8
|
||||||
|
MKL_NUM_THREADS: 8
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
torch_pipelines_cuda_benchmark_tests:
|
||||||
|
name: Torch Core Pipelines CUDA Benchmarking Tests
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
max-parallel: 1
|
||||||
|
runs-on: [single-gpu, nvidia-gpu, a10, ci]
|
||||||
|
container:
|
||||||
|
image: diffusers/diffusers-pytorch-cuda
|
||||||
|
options: --shm-size "16gb" --ipc host -v /mnt/hf_cache:/mnt/cache/ --gpus 0
|
||||||
|
steps:
|
||||||
|
- name: Checkout diffusers
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
- name: NVIDIA-SMI
|
||||||
|
run: |
|
||||||
|
nvidia-smi
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
apt-get update && apt-get install libsndfile1-dev libgl1 -y
|
||||||
|
python -m pip install -e .[quality,test]
|
||||||
|
python -m pip install pandas
|
||||||
|
- name: Environment
|
||||||
|
run: |
|
||||||
|
python utils/print_env.py
|
||||||
|
- name: Diffusers Benchmarking
|
||||||
|
env:
|
||||||
|
HUGGING_FACE_HUB_TOKEN: ${{ secrets.DIFFUSERS_BOT_TOKEN }}
|
||||||
|
BASE_PATH: benchmark_outputs
|
||||||
|
run: |
|
||||||
|
export TOTAL_GPU_MEMORY=$(python -c "import torch; print(torch.cuda.get_device_properties(0).total_memory / (1024**3))")
|
||||||
|
cd benchmarks && mkdir ${BASE_PATH} && python run_all.py && python push_results.py
|
||||||
|
|
||||||
|
- name: Test suite reports artifacts
|
||||||
|
if: ${{ always() }}
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: benchmark_test_reports
|
||||||
|
path: benchmarks/benchmark_outputs
|
||||||
@@ -1,12 +1,6 @@
|
|||||||
name: Fast tests for PRs - Test Fetcher
|
name: Fast tests for PRs - Test Fetcher
|
||||||
|
|
||||||
on:
|
on: workflow_dispatch
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- ci-*
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DIFFUSERS_IS_CI: yes
|
DIFFUSERS_IS_CI: yes
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ jobs:
|
|||||||
- name: Run example PyTorch CPU tests
|
- name: Run example PyTorch CPU tests
|
||||||
if: ${{ matrix.config.framework == 'pytorch_examples' }}
|
if: ${{ matrix.config.framework == 'pytorch_examples' }}
|
||||||
run: |
|
run: |
|
||||||
|
python -m pip install peft
|
||||||
python -m pytest -n 2 --max-worker-restart=0 --dist=loadfile \
|
python -m pytest -n 2 --max-worker-restart=0 --dist=loadfile \
|
||||||
--make-reports=tests_${{ matrix.config.report }} \
|
--make-reports=tests_${{ matrix.config.report }} \
|
||||||
examples
|
examples
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# make sure to test the local checkout in scripts and not the pre-installed one (don't use quotes!)
|
# make sure to test the local checkout in scripts and not the pre-installed one (don't use quotes!)
|
||||||
export PYTHONPATH = src
|
export PYTHONPATH = src
|
||||||
|
|
||||||
check_dirs := examples scripts src tests utils
|
check_dirs := examples scripts src tests utils benchmarks
|
||||||
|
|
||||||
modified_only_fixup:
|
modified_only_fixup:
|
||||||
$(eval modified_py_files := $(shell python utils/get_modified_files.py $(check_dirs)))
|
$(eval modified_py_files := $(shell python utils/get_modified_files.py $(check_dirs)))
|
||||||
|
|||||||
@@ -77,7 +77,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 15000+ 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 16000+ checkpoints):
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from diffusers import DiffusionPipeline
|
from diffusers import DiffusionPipeline
|
||||||
@@ -219,7 +219,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
|
||||||
- +6000 other amazing GitHub repositories 💪
|
- +7000 other amazing GitHub repositories 💪
|
||||||
|
|
||||||
Thank you for using us ❤️.
|
Thank you for using us ❤️.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,297 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import torch
|
||||||
|
|
||||||
|
from diffusers import (
|
||||||
|
AutoPipelineForImage2Image,
|
||||||
|
AutoPipelineForInpainting,
|
||||||
|
AutoPipelineForText2Image,
|
||||||
|
ControlNetModel,
|
||||||
|
LCMScheduler,
|
||||||
|
StableDiffusionAdapterPipeline,
|
||||||
|
StableDiffusionControlNetPipeline,
|
||||||
|
StableDiffusionXLAdapterPipeline,
|
||||||
|
StableDiffusionXLControlNetPipeline,
|
||||||
|
T2IAdapter,
|
||||||
|
WuerstchenCombinedPipeline,
|
||||||
|
)
|
||||||
|
from diffusers.utils import load_image
|
||||||
|
|
||||||
|
|
||||||
|
sys.path.append(".")
|
||||||
|
|
||||||
|
from utils import ( # noqa: E402
|
||||||
|
BASE_PATH,
|
||||||
|
PROMPT,
|
||||||
|
BenchmarkInfo,
|
||||||
|
benchmark_fn,
|
||||||
|
bytes_to_giga_bytes,
|
||||||
|
flush,
|
||||||
|
generate_csv_dict,
|
||||||
|
write_to_csv,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
RESOLUTION_MAPPING = {
|
||||||
|
"runwayml/stable-diffusion-v1-5": (512, 512),
|
||||||
|
"lllyasviel/sd-controlnet-canny": (512, 512),
|
||||||
|
"diffusers/controlnet-canny-sdxl-1.0": (1024, 1024),
|
||||||
|
"TencentARC/t2iadapter_canny_sd14v1": (512, 512),
|
||||||
|
"TencentARC/t2i-adapter-canny-sdxl-1.0": (1024, 1024),
|
||||||
|
"stabilityai/stable-diffusion-2-1": (768, 768),
|
||||||
|
"stabilityai/stable-diffusion-xl-base-1.0": (1024, 1024),
|
||||||
|
"stabilityai/stable-diffusion-xl-refiner-1.0": (1024, 1024),
|
||||||
|
"stabilityai/sdxl-turbo": (512, 512),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BaseBenchmak:
|
||||||
|
pipeline_class = None
|
||||||
|
|
||||||
|
def __init__(self, args):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def run_inference(self, args):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def benchmark(self, args):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_result_filepath(self, args):
|
||||||
|
pipeline_class_name = str(self.pipe.__class__.__name__)
|
||||||
|
name = (
|
||||||
|
args.ckpt.replace("/", "_")
|
||||||
|
+ "_"
|
||||||
|
+ pipeline_class_name
|
||||||
|
+ f"-bs@{args.batch_size}-steps@{args.num_inference_steps}-mco@{args.model_cpu_offload}-compile@{args.run_compile}.csv"
|
||||||
|
)
|
||||||
|
filepath = os.path.join(BASE_PATH, name)
|
||||||
|
return filepath
|
||||||
|
|
||||||
|
|
||||||
|
class TextToImageBenchmark(BaseBenchmak):
|
||||||
|
pipeline_class = AutoPipelineForText2Image
|
||||||
|
|
||||||
|
def __init__(self, args):
|
||||||
|
pipe = self.pipeline_class.from_pretrained(args.ckpt, torch_dtype=torch.float16)
|
||||||
|
pipe = pipe.to("cuda")
|
||||||
|
|
||||||
|
if args.run_compile:
|
||||||
|
if not isinstance(pipe, WuerstchenCombinedPipeline):
|
||||||
|
pipe.unet.to(memory_format=torch.channels_last)
|
||||||
|
print("Run torch compile")
|
||||||
|
pipe.unet = torch.compile(pipe.unet, mode="reduce-overhead", fullgraph=True)
|
||||||
|
|
||||||
|
if hasattr(pipe, "movq") and getattr(pipe, "movq", None) is not None:
|
||||||
|
pipe.movq.to(memory_format=torch.channels_last)
|
||||||
|
pipe.movq = torch.compile(pipe.movq, mode="reduce-overhead", fullgraph=True)
|
||||||
|
else:
|
||||||
|
print("Run torch compile")
|
||||||
|
pipe.decoder = torch.compile(pipe.decoder, mode="reduce-overhead", fullgraph=True)
|
||||||
|
pipe.vqgan = torch.compile(pipe.vqgan, mode="reduce-overhead", fullgraph=True)
|
||||||
|
|
||||||
|
pipe.set_progress_bar_config(disable=True)
|
||||||
|
self.pipe = pipe
|
||||||
|
|
||||||
|
def run_inference(self, pipe, args):
|
||||||
|
_ = pipe(
|
||||||
|
prompt=PROMPT,
|
||||||
|
num_inference_steps=args.num_inference_steps,
|
||||||
|
num_images_per_prompt=args.batch_size,
|
||||||
|
)
|
||||||
|
|
||||||
|
def benchmark(self, args):
|
||||||
|
flush()
|
||||||
|
|
||||||
|
print(f"[INFO] {self.pipe.__class__.__name__}: Running benchmark with: {vars(args)}\n")
|
||||||
|
|
||||||
|
time = benchmark_fn(self.run_inference, self.pipe, args) # in seconds.
|
||||||
|
memory = bytes_to_giga_bytes(torch.cuda.max_memory_allocated()) # in GBs.
|
||||||
|
benchmark_info = BenchmarkInfo(time=time, memory=memory)
|
||||||
|
|
||||||
|
pipeline_class_name = str(self.pipe.__class__.__name__)
|
||||||
|
flush()
|
||||||
|
csv_dict = generate_csv_dict(
|
||||||
|
pipeline_cls=pipeline_class_name, ckpt=args.ckpt, args=args, benchmark_info=benchmark_info
|
||||||
|
)
|
||||||
|
filepath = self.get_result_filepath(args)
|
||||||
|
write_to_csv(filepath, csv_dict)
|
||||||
|
print(f"Logs written to: {filepath}")
|
||||||
|
flush()
|
||||||
|
|
||||||
|
|
||||||
|
class TurboTextToImageBenchmark(TextToImageBenchmark):
|
||||||
|
def __init__(self, args):
|
||||||
|
super().__init__(args)
|
||||||
|
|
||||||
|
def run_inference(self, pipe, args):
|
||||||
|
_ = pipe(
|
||||||
|
prompt=PROMPT,
|
||||||
|
num_inference_steps=args.num_inference_steps,
|
||||||
|
num_images_per_prompt=args.batch_size,
|
||||||
|
guidance_scale=0.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class LCMLoRATextToImageBenchmark(TextToImageBenchmark):
|
||||||
|
lora_id = "latent-consistency/lcm-lora-sdxl"
|
||||||
|
|
||||||
|
def __init__(self, args):
|
||||||
|
super().__init__(args)
|
||||||
|
self.pipe.load_lora_weights(self.lora_id)
|
||||||
|
self.pipe.fuse_lora()
|
||||||
|
self.pipe.scheduler = LCMScheduler.from_config(self.pipe.scheduler.config)
|
||||||
|
|
||||||
|
def get_result_filepath(self, args):
|
||||||
|
pipeline_class_name = str(self.pipe.__class__.__name__)
|
||||||
|
name = (
|
||||||
|
self.lora_id.replace("/", "_")
|
||||||
|
+ "_"
|
||||||
|
+ pipeline_class_name
|
||||||
|
+ f"-bs@{args.batch_size}-steps@{args.num_inference_steps}-mco@{args.model_cpu_offload}-compile@{args.run_compile}.csv"
|
||||||
|
)
|
||||||
|
filepath = os.path.join(BASE_PATH, name)
|
||||||
|
return filepath
|
||||||
|
|
||||||
|
def run_inference(self, pipe, args):
|
||||||
|
_ = pipe(
|
||||||
|
prompt=PROMPT,
|
||||||
|
num_inference_steps=args.num_inference_steps,
|
||||||
|
num_images_per_prompt=args.batch_size,
|
||||||
|
guidance_scale=1.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ImageToImageBenchmark(TextToImageBenchmark):
|
||||||
|
pipeline_class = AutoPipelineForImage2Image
|
||||||
|
url = "https://huggingface.co/datasets/diffusers/docs-images/resolve/main/benchmarking/1665_Girl_with_a_Pearl_Earring.jpg"
|
||||||
|
image = load_image(url).convert("RGB")
|
||||||
|
|
||||||
|
def __init__(self, args):
|
||||||
|
super().__init__(args)
|
||||||
|
self.image = self.image.resize(RESOLUTION_MAPPING[args.ckpt])
|
||||||
|
|
||||||
|
def run_inference(self, pipe, args):
|
||||||
|
_ = pipe(
|
||||||
|
prompt=PROMPT,
|
||||||
|
image=self.image,
|
||||||
|
num_inference_steps=args.num_inference_steps,
|
||||||
|
num_images_per_prompt=args.batch_size,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TurboImageToImageBenchmark(ImageToImageBenchmark):
|
||||||
|
def __init__(self, args):
|
||||||
|
super().__init__(args)
|
||||||
|
|
||||||
|
def run_inference(self, pipe, args):
|
||||||
|
_ = pipe(
|
||||||
|
prompt=PROMPT,
|
||||||
|
image=self.image,
|
||||||
|
num_inference_steps=args.num_inference_steps,
|
||||||
|
num_images_per_prompt=args.batch_size,
|
||||||
|
guidance_scale=0.0,
|
||||||
|
strength=0.5,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class InpaintingBenchmark(ImageToImageBenchmark):
|
||||||
|
pipeline_class = AutoPipelineForInpainting
|
||||||
|
mask_url = "https://huggingface.co/datasets/diffusers/docs-images/resolve/main/benchmarking/overture-creations-5sI6fQgYIuo_mask.png"
|
||||||
|
mask = load_image(mask_url).convert("RGB")
|
||||||
|
|
||||||
|
def __init__(self, args):
|
||||||
|
super().__init__(args)
|
||||||
|
self.image = self.image.resize(RESOLUTION_MAPPING[args.ckpt])
|
||||||
|
self.mask = self.mask.resize(RESOLUTION_MAPPING[args.ckpt])
|
||||||
|
|
||||||
|
def run_inference(self, pipe, args):
|
||||||
|
_ = pipe(
|
||||||
|
prompt=PROMPT,
|
||||||
|
image=self.image,
|
||||||
|
mask_image=self.mask,
|
||||||
|
num_inference_steps=args.num_inference_steps,
|
||||||
|
num_images_per_prompt=args.batch_size,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ControlNetBenchmark(TextToImageBenchmark):
|
||||||
|
pipeline_class = StableDiffusionControlNetPipeline
|
||||||
|
aux_network_class = ControlNetModel
|
||||||
|
root_ckpt = "runwayml/stable-diffusion-v1-5"
|
||||||
|
|
||||||
|
url = "https://huggingface.co/datasets/diffusers/docs-images/resolve/main/benchmarking/canny_image_condition.png"
|
||||||
|
image = load_image(url).convert("RGB")
|
||||||
|
|
||||||
|
def __init__(self, args):
|
||||||
|
aux_network = self.aux_network_class.from_pretrained(args.ckpt, torch_dtype=torch.float16)
|
||||||
|
pipe = self.pipeline_class.from_pretrained(self.root_ckpt, controlnet=aux_network, torch_dtype=torch.float16)
|
||||||
|
pipe = pipe.to("cuda")
|
||||||
|
|
||||||
|
pipe.set_progress_bar_config(disable=True)
|
||||||
|
self.pipe = pipe
|
||||||
|
|
||||||
|
if args.run_compile:
|
||||||
|
pipe.unet.to(memory_format=torch.channels_last)
|
||||||
|
pipe.controlnet.to(memory_format=torch.channels_last)
|
||||||
|
|
||||||
|
print("Run torch compile")
|
||||||
|
pipe.unet = torch.compile(pipe.unet, mode="reduce-overhead", fullgraph=True)
|
||||||
|
pipe.controlnet = torch.compile(pipe.controlnet, mode="reduce-overhead", fullgraph=True)
|
||||||
|
|
||||||
|
self.image = self.image.resize(RESOLUTION_MAPPING[args.ckpt])
|
||||||
|
|
||||||
|
def run_inference(self, pipe, args):
|
||||||
|
_ = pipe(
|
||||||
|
prompt=PROMPT,
|
||||||
|
image=self.image,
|
||||||
|
num_inference_steps=args.num_inference_steps,
|
||||||
|
num_images_per_prompt=args.batch_size,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ControlNetSDXLBenchmark(ControlNetBenchmark):
|
||||||
|
pipeline_class = StableDiffusionXLControlNetPipeline
|
||||||
|
root_ckpt = "stabilityai/stable-diffusion-xl-base-1.0"
|
||||||
|
|
||||||
|
def __init__(self, args):
|
||||||
|
super().__init__(args)
|
||||||
|
|
||||||
|
|
||||||
|
class T2IAdapterBenchmark(ControlNetBenchmark):
|
||||||
|
pipeline_class = StableDiffusionAdapterPipeline
|
||||||
|
aux_network_class = T2IAdapter
|
||||||
|
root_ckpt = "CompVis/stable-diffusion-v1-4"
|
||||||
|
|
||||||
|
url = "https://huggingface.co/datasets/diffusers/docs-images/resolve/main/benchmarking/canny_for_adapter.png"
|
||||||
|
image = load_image(url).convert("L")
|
||||||
|
|
||||||
|
def __init__(self, args):
|
||||||
|
aux_network = self.aux_network_class.from_pretrained(args.ckpt, torch_dtype=torch.float16)
|
||||||
|
pipe = self.pipeline_class.from_pretrained(self.root_ckpt, adapter=aux_network, torch_dtype=torch.float16)
|
||||||
|
pipe = pipe.to("cuda")
|
||||||
|
|
||||||
|
pipe.set_progress_bar_config(disable=True)
|
||||||
|
self.pipe = pipe
|
||||||
|
|
||||||
|
if args.run_compile:
|
||||||
|
pipe.unet.to(memory_format=torch.channels_last)
|
||||||
|
pipe.adapter.to(memory_format=torch.channels_last)
|
||||||
|
|
||||||
|
print("Run torch compile")
|
||||||
|
pipe.unet = torch.compile(pipe.unet, mode="reduce-overhead", fullgraph=True)
|
||||||
|
pipe.adapter = torch.compile(pipe.adapter, mode="reduce-overhead", fullgraph=True)
|
||||||
|
|
||||||
|
self.image = self.image.resize(RESOLUTION_MAPPING[args.ckpt])
|
||||||
|
|
||||||
|
|
||||||
|
class T2IAdapterSDXLBenchmark(T2IAdapterBenchmark):
|
||||||
|
pipeline_class = StableDiffusionXLAdapterPipeline
|
||||||
|
root_ckpt = "stabilityai/stable-diffusion-xl-base-1.0"
|
||||||
|
|
||||||
|
url = "https://huggingface.co/datasets/diffusers/docs-images/resolve/main/benchmarking/canny_for_adapter_sdxl.png"
|
||||||
|
image = load_image(url)
|
||||||
|
|
||||||
|
def __init__(self, args):
|
||||||
|
super().__init__(args)
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
sys.path.append(".")
|
||||||
|
from base_classes import ControlNetBenchmark, ControlNetSDXLBenchmark # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
"--ckpt",
|
||||||
|
type=str,
|
||||||
|
default="lllyasviel/sd-controlnet-canny",
|
||||||
|
choices=["lllyasviel/sd-controlnet-canny", "diffusers/controlnet-canny-sdxl-1.0"],
|
||||||
|
)
|
||||||
|
parser.add_argument("--batch_size", type=int, default=1)
|
||||||
|
parser.add_argument("--num_inference_steps", type=int, default=50)
|
||||||
|
parser.add_argument("--model_cpu_offload", action="store_true")
|
||||||
|
parser.add_argument("--run_compile", action="store_true")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
benchmark_pipe = (
|
||||||
|
ControlNetBenchmark(args) if args.ckpt == "lllyasviel/sd-controlnet-canny" else ControlNetSDXLBenchmark(args)
|
||||||
|
)
|
||||||
|
benchmark_pipe.benchmark(args)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
sys.path.append(".")
|
||||||
|
from base_classes import ImageToImageBenchmark, TurboImageToImageBenchmark # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
"--ckpt",
|
||||||
|
type=str,
|
||||||
|
default="runwayml/stable-diffusion-v1-5",
|
||||||
|
choices=[
|
||||||
|
"runwayml/stable-diffusion-v1-5",
|
||||||
|
"stabilityai/stable-diffusion-2-1",
|
||||||
|
"stabilityai/stable-diffusion-xl-refiner-1.0",
|
||||||
|
"stabilityai/sdxl-turbo",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
parser.add_argument("--batch_size", type=int, default=1)
|
||||||
|
parser.add_argument("--num_inference_steps", type=int, default=50)
|
||||||
|
parser.add_argument("--model_cpu_offload", action="store_true")
|
||||||
|
parser.add_argument("--run_compile", action="store_true")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
benchmark_pipe = ImageToImageBenchmark(args) if "turbo" not in args.ckpt else TurboImageToImageBenchmark(args)
|
||||||
|
benchmark_pipe.benchmark(args)
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
sys.path.append(".")
|
||||||
|
from base_classes import InpaintingBenchmark # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
"--ckpt",
|
||||||
|
type=str,
|
||||||
|
default="runwayml/stable-diffusion-v1-5",
|
||||||
|
choices=[
|
||||||
|
"runwayml/stable-diffusion-v1-5",
|
||||||
|
"stabilityai/stable-diffusion-2-1",
|
||||||
|
"stabilityai/stable-diffusion-xl-base-1.0",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
parser.add_argument("--batch_size", type=int, default=1)
|
||||||
|
parser.add_argument("--num_inference_steps", type=int, default=50)
|
||||||
|
parser.add_argument("--model_cpu_offload", action="store_true")
|
||||||
|
parser.add_argument("--run_compile", action="store_true")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
benchmark_pipe = InpaintingBenchmark(args)
|
||||||
|
benchmark_pipe.benchmark(args)
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
sys.path.append(".")
|
||||||
|
from base_classes import T2IAdapterBenchmark, T2IAdapterSDXLBenchmark # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
"--ckpt",
|
||||||
|
type=str,
|
||||||
|
default="TencentARC/t2iadapter_canny_sd14v1",
|
||||||
|
choices=["TencentARC/t2iadapter_canny_sd14v1", "TencentARC/t2i-adapter-canny-sdxl-1.0"],
|
||||||
|
)
|
||||||
|
parser.add_argument("--batch_size", type=int, default=1)
|
||||||
|
parser.add_argument("--num_inference_steps", type=int, default=50)
|
||||||
|
parser.add_argument("--model_cpu_offload", action="store_true")
|
||||||
|
parser.add_argument("--run_compile", action="store_true")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
benchmark_pipe = (
|
||||||
|
T2IAdapterBenchmark(args)
|
||||||
|
if args.ckpt == "TencentARC/t2iadapter_canny_sd14v1"
|
||||||
|
else T2IAdapterSDXLBenchmark(args)
|
||||||
|
)
|
||||||
|
benchmark_pipe.benchmark(args)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
sys.path.append(".")
|
||||||
|
from base_classes import LCMLoRATextToImageBenchmark # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
"--ckpt",
|
||||||
|
type=str,
|
||||||
|
default="stabilityai/stable-diffusion-xl-base-1.0",
|
||||||
|
)
|
||||||
|
parser.add_argument("--batch_size", type=int, default=1)
|
||||||
|
parser.add_argument("--num_inference_steps", type=int, default=4)
|
||||||
|
parser.add_argument("--model_cpu_offload", action="store_true")
|
||||||
|
parser.add_argument("--run_compile", action="store_true")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
benchmark_pipe = LCMLoRATextToImageBenchmark(args)
|
||||||
|
benchmark_pipe.benchmark(args)
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
sys.path.append(".")
|
||||||
|
from base_classes import TextToImageBenchmark, TurboTextToImageBenchmark # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
ALL_T2I_CKPTS = [
|
||||||
|
"runwayml/stable-diffusion-v1-5",
|
||||||
|
"segmind/SSD-1B",
|
||||||
|
"stabilityai/stable-diffusion-xl-base-1.0",
|
||||||
|
"kandinsky-community/kandinsky-2-2-decoder",
|
||||||
|
"warp-ai/wuerstchen",
|
||||||
|
"stabilityai/sdxl-turbo",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
"--ckpt",
|
||||||
|
type=str,
|
||||||
|
default="runwayml/stable-diffusion-v1-5",
|
||||||
|
choices=ALL_T2I_CKPTS,
|
||||||
|
)
|
||||||
|
parser.add_argument("--batch_size", type=int, default=1)
|
||||||
|
parser.add_argument("--num_inference_steps", type=int, default=50)
|
||||||
|
parser.add_argument("--model_cpu_offload", action="store_true")
|
||||||
|
parser.add_argument("--run_compile", action="store_true")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
benchmark_cls = None
|
||||||
|
if "turbo" in args.ckpt:
|
||||||
|
benchmark_cls = TurboTextToImageBenchmark
|
||||||
|
else:
|
||||||
|
benchmark_cls = TextToImageBenchmark
|
||||||
|
|
||||||
|
benchmark_pipe = benchmark_cls(args)
|
||||||
|
benchmark_pipe.benchmark(args)
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import glob
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
from huggingface_hub import hf_hub_download, upload_file
|
||||||
|
from huggingface_hub.utils._errors import EntryNotFoundError
|
||||||
|
|
||||||
|
|
||||||
|
sys.path.append(".")
|
||||||
|
from utils import BASE_PATH, FINAL_CSV_FILE, GITHUB_SHA, REPO_ID, collate_csv # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
def has_previous_benchmark() -> str:
|
||||||
|
csv_path = None
|
||||||
|
try:
|
||||||
|
csv_path = hf_hub_download(repo_id=REPO_ID, repo_type="dataset", filename=FINAL_CSV_FILE)
|
||||||
|
except EntryNotFoundError:
|
||||||
|
csv_path = None
|
||||||
|
return csv_path
|
||||||
|
|
||||||
|
|
||||||
|
def filter_float(value):
|
||||||
|
if isinstance(value, str):
|
||||||
|
return float(value.split()[0])
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def push_to_hf_dataset():
|
||||||
|
all_csvs = sorted(glob.glob(f"{BASE_PATH}/*.csv"))
|
||||||
|
collate_csv(all_csvs, FINAL_CSV_FILE)
|
||||||
|
|
||||||
|
# If there's an existing benchmark file, we should report the changes.
|
||||||
|
csv_path = has_previous_benchmark()
|
||||||
|
if csv_path is not None:
|
||||||
|
current_results = pd.read_csv(FINAL_CSV_FILE)
|
||||||
|
previous_results = pd.read_csv(csv_path)
|
||||||
|
|
||||||
|
numeric_columns = current_results.select_dtypes(include=["float64", "int64"]).columns
|
||||||
|
numeric_columns = [
|
||||||
|
c for c in numeric_columns if c not in ["batch_size", "num_inference_steps", "actual_gpu_memory (gbs)"]
|
||||||
|
]
|
||||||
|
|
||||||
|
for column in numeric_columns:
|
||||||
|
previous_results[column] = previous_results[column].map(lambda x: filter_float(x))
|
||||||
|
|
||||||
|
# Calculate the percentage change
|
||||||
|
current_results[column] = current_results[column].astype(float)
|
||||||
|
previous_results[column] = previous_results[column].astype(float)
|
||||||
|
percent_change = ((current_results[column] - previous_results[column]) / previous_results[column]) * 100
|
||||||
|
|
||||||
|
# Format the values with '+' or '-' sign and append to original values
|
||||||
|
current_results[column] = current_results[column].map(str) + percent_change.map(
|
||||||
|
lambda x: f" ({'+' if x > 0 else ''}{x:.2f}%)"
|
||||||
|
)
|
||||||
|
# There might be newly added rows. So, filter out the NaNs.
|
||||||
|
current_results[column] = current_results[column].map(lambda x: x.replace(" (nan%)", ""))
|
||||||
|
|
||||||
|
# Overwrite the current result file.
|
||||||
|
current_results.to_csv(FINAL_CSV_FILE, index=False)
|
||||||
|
|
||||||
|
commit_message = f"upload from sha: {GITHUB_SHA}" if GITHUB_SHA is not None else "upload benchmark results"
|
||||||
|
upload_file(
|
||||||
|
repo_id=REPO_ID,
|
||||||
|
path_in_repo=FINAL_CSV_FILE,
|
||||||
|
path_or_fileobj=FINAL_CSV_FILE,
|
||||||
|
repo_type="dataset",
|
||||||
|
commit_message=commit_message,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
push_to_hf_dataset()
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import glob
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
sys.path.append(".")
|
||||||
|
from benchmark_text_to_image import ALL_T2I_CKPTS # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
PATTERN = "benchmark_*.py"
|
||||||
|
|
||||||
|
|
||||||
|
class SubprocessCallException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Taken from `test_examples_utils.py`
|
||||||
|
def run_command(command: List[str], return_stdout=False):
|
||||||
|
"""
|
||||||
|
Runs `command` with `subprocess.check_output` and will potentially return the `stdout`. Will also properly capture
|
||||||
|
if an error occurred while running `command`
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||||
|
if return_stdout:
|
||||||
|
if hasattr(output, "decode"):
|
||||||
|
output = output.decode("utf-8")
|
||||||
|
return output
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
raise SubprocessCallException(
|
||||||
|
f"Command `{' '.join(command)}` failed with the following error:\n\n{e.output.decode()}"
|
||||||
|
) from e
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
python_files = glob.glob(PATTERN)
|
||||||
|
|
||||||
|
for file in python_files:
|
||||||
|
print(f"****** Running file: {file} ******")
|
||||||
|
|
||||||
|
# Run with canonical settings.
|
||||||
|
if file != "benchmark_text_to_image.py":
|
||||||
|
command = f"python {file}"
|
||||||
|
run_command(command.split())
|
||||||
|
|
||||||
|
command += " --run_compile"
|
||||||
|
run_command(command.split())
|
||||||
|
|
||||||
|
# Run variants.
|
||||||
|
for file in python_files:
|
||||||
|
if file == "benchmark_text_to_image.py":
|
||||||
|
for ckpt in ALL_T2I_CKPTS:
|
||||||
|
command = f"python {file} --ckpt {ckpt}"
|
||||||
|
|
||||||
|
if "turbo" in ckpt:
|
||||||
|
command += " --num_inference_steps 1"
|
||||||
|
|
||||||
|
run_command(command.split())
|
||||||
|
|
||||||
|
command += " --run_compile"
|
||||||
|
run_command(command.split())
|
||||||
|
|
||||||
|
elif file == "benchmark_sd_img.py":
|
||||||
|
for ckpt in ["stabilityai/stable-diffusion-xl-refiner-1.0", "stabilityai/sdxl-turbo"]:
|
||||||
|
command = f"python {file} --ckpt {ckpt}"
|
||||||
|
|
||||||
|
if ckpt == "stabilityai/sdxl-turbo":
|
||||||
|
command += " --num_inference_steps 2"
|
||||||
|
|
||||||
|
run_command(command.split())
|
||||||
|
command += " --run_compile"
|
||||||
|
run_command(command.split())
|
||||||
|
|
||||||
|
elif file == "benchmark_sd_inpainting.py":
|
||||||
|
sdxl_ckpt = "stabilityai/stable-diffusion-xl-base-1.0"
|
||||||
|
command = f"python {file} --ckpt {sdxl_ckpt}"
|
||||||
|
run_command(command.split())
|
||||||
|
|
||||||
|
command += " --run_compile"
|
||||||
|
run_command(command.split())
|
||||||
|
|
||||||
|
elif file in ["benchmark_controlnet.py", "benchmark_t2i_adapter.py"]:
|
||||||
|
sdxl_ckpt = (
|
||||||
|
"diffusers/controlnet-canny-sdxl-1.0"
|
||||||
|
if "controlnet" in file
|
||||||
|
else "TencentARC/t2i-adapter-canny-sdxl-1.0"
|
||||||
|
)
|
||||||
|
command = f"python {file} --ckpt {sdxl_ckpt}"
|
||||||
|
run_command(command.split())
|
||||||
|
|
||||||
|
command += " --run_compile"
|
||||||
|
run_command(command.split())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import argparse
|
||||||
|
import csv
|
||||||
|
import gc
|
||||||
|
import os
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Dict, List, Union
|
||||||
|
|
||||||
|
import torch
|
||||||
|
import torch.utils.benchmark as benchmark
|
||||||
|
|
||||||
|
|
||||||
|
GITHUB_SHA = os.getenv("GITHUB_SHA", None)
|
||||||
|
BENCHMARK_FIELDS = [
|
||||||
|
"pipeline_cls",
|
||||||
|
"ckpt_id",
|
||||||
|
"batch_size",
|
||||||
|
"num_inference_steps",
|
||||||
|
"model_cpu_offload",
|
||||||
|
"run_compile",
|
||||||
|
"time (secs)",
|
||||||
|
"memory (gbs)",
|
||||||
|
"actual_gpu_memory (gbs)",
|
||||||
|
"github_sha",
|
||||||
|
]
|
||||||
|
|
||||||
|
PROMPT = "ghibli style, a fantasy landscape with castles"
|
||||||
|
BASE_PATH = os.getenv("BASE_PATH", ".")
|
||||||
|
TOTAL_GPU_MEMORY = float(os.getenv("TOTAL_GPU_MEMORY", torch.cuda.get_device_properties(0).total_memory / (1024**3)))
|
||||||
|
|
||||||
|
REPO_ID = "diffusers/benchmarks"
|
||||||
|
FINAL_CSV_FILE = "collated_results.csv"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BenchmarkInfo:
|
||||||
|
time: float
|
||||||
|
memory: float
|
||||||
|
|
||||||
|
|
||||||
|
def flush():
|
||||||
|
"""Wipes off memory."""
|
||||||
|
gc.collect()
|
||||||
|
torch.cuda.empty_cache()
|
||||||
|
torch.cuda.reset_max_memory_allocated()
|
||||||
|
torch.cuda.reset_peak_memory_stats()
|
||||||
|
|
||||||
|
|
||||||
|
def bytes_to_giga_bytes(bytes):
|
||||||
|
return f"{(bytes / 1024 / 1024 / 1024):.3f}"
|
||||||
|
|
||||||
|
|
||||||
|
def benchmark_fn(f, *args, **kwargs):
|
||||||
|
t0 = benchmark.Timer(
|
||||||
|
stmt="f(*args, **kwargs)",
|
||||||
|
globals={"args": args, "kwargs": kwargs, "f": f},
|
||||||
|
num_threads=torch.get_num_threads(),
|
||||||
|
)
|
||||||
|
return f"{(t0.blocked_autorange().mean):.3f}"
|
||||||
|
|
||||||
|
|
||||||
|
def generate_csv_dict(
|
||||||
|
pipeline_cls: str, ckpt: str, args: argparse.Namespace, benchmark_info: BenchmarkInfo
|
||||||
|
) -> Dict[str, Union[str, bool, float]]:
|
||||||
|
"""Packs benchmarking data into a dictionary for latter serialization."""
|
||||||
|
data_dict = {
|
||||||
|
"pipeline_cls": pipeline_cls,
|
||||||
|
"ckpt_id": ckpt,
|
||||||
|
"batch_size": args.batch_size,
|
||||||
|
"num_inference_steps": args.num_inference_steps,
|
||||||
|
"model_cpu_offload": args.model_cpu_offload,
|
||||||
|
"run_compile": args.run_compile,
|
||||||
|
"time (secs)": benchmark_info.time,
|
||||||
|
"memory (gbs)": benchmark_info.memory,
|
||||||
|
"actual_gpu_memory (gbs)": f"{(TOTAL_GPU_MEMORY):.3f}",
|
||||||
|
"github_sha": GITHUB_SHA,
|
||||||
|
}
|
||||||
|
return data_dict
|
||||||
|
|
||||||
|
|
||||||
|
def write_to_csv(file_name: str, data_dict: Dict[str, Union[str, bool, float]]):
|
||||||
|
"""Serializes a dictionary into a CSV file."""
|
||||||
|
with open(file_name, mode="w", newline="") as csvfile:
|
||||||
|
writer = csv.DictWriter(csvfile, fieldnames=BENCHMARK_FIELDS)
|
||||||
|
writer.writeheader()
|
||||||
|
writer.writerow(data_dict)
|
||||||
|
|
||||||
|
|
||||||
|
def collate_csv(input_files: List[str], output_file: str):
|
||||||
|
"""Collates multiple identically structured CSVs into a single CSV file."""
|
||||||
|
with open(output_file, mode="w", newline="") as outfile:
|
||||||
|
writer = csv.DictWriter(outfile, fieldnames=BENCHMARK_FIELDS)
|
||||||
|
writer.writeheader()
|
||||||
|
|
||||||
|
for file in input_files:
|
||||||
|
with open(file, mode="r") as infile:
|
||||||
|
reader = csv.DictReader(infile)
|
||||||
|
for row in reader:
|
||||||
|
writer.writerow(row)
|
||||||
@@ -198,6 +198,8 @@
|
|||||||
title: Outputs
|
title: Outputs
|
||||||
title: Main Classes
|
title: Main Classes
|
||||||
- sections:
|
- sections:
|
||||||
|
- local: api/loaders/ip_adapter
|
||||||
|
title: IP-Adapter
|
||||||
- local: api/loaders/lora
|
- local: api/loaders/lora
|
||||||
title: LoRA
|
title: LoRA
|
||||||
- local: api/loaders/single_file
|
- local: api/loaders/single_file
|
||||||
@@ -264,6 +266,10 @@
|
|||||||
title: ControlNet
|
title: ControlNet
|
||||||
- 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
|
||||||
|
title: ControlNet-XS
|
||||||
|
- local: api/pipelines/controlnetxs_sdxl
|
||||||
|
title: ControlNet-XS with Stable Diffusion XL
|
||||||
- local: api/pipelines/cycle_diffusion
|
- local: api/pipelines/cycle_diffusion
|
||||||
title: Cycle Diffusion
|
title: Cycle Diffusion
|
||||||
- local: api/pipelines/dance_diffusion
|
- local: api/pipelines/dance_diffusion
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<!--Copyright 2023 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
# IP-Adapter
|
||||||
|
|
||||||
|
[IP-Adapter](https://hf.co/papers/2308.06721) is a lightweight adapter that enables prompting a diffusion model with an image. This method decouples the cross-attention layers of the image and text features. The image features are generated from an image encoder. Files generated from IP-Adapter are only ~100MBs.
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
|
||||||
|
Learn how to load an IP-Adapter checkpoint and image in the [IP-Adapter](../../using-diffusers/loading_adapters#ip-adapter) loading guide.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
## IPAdapterMixin
|
||||||
|
|
||||||
|
[[autodoc]] loaders.ip_adapter.IPAdapterMixin
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<!--Copyright 2023 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
# ControlNet-XS
|
||||||
|
|
||||||
|
ControlNet-XS was introduced in [ControlNet-XS](https://vislearn.github.io/ControlNet-XS/) by Denis Zavadski and Carsten Rother. It is based on the observation that the control model in the [original ControlNet](https://huggingface.co/papers/2302.05543) can be made much smaller and still produce good results.
|
||||||
|
|
||||||
|
Like the original 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.
|
||||||
|
|
||||||
|
ControlNet-XS generates images with comparable quality to a regular ControlNet, but it is 20-25% faster ([see benchmark](https://github.com/UmerHA/controlnet-xs-benchmark/blob/main/Speed%20Benchmark.ipynb) with StableDiffusion-XL) and uses ~45% less memory.
|
||||||
|
|
||||||
|
Here's the overview from the [project page](https://vislearn.github.io/ControlNet-XS/):
|
||||||
|
|
||||||
|
*With increasing computing capabilities, current model architectures appear to follow the trend of simply upscaling all components without validating the necessity for doing so. In this project we investigate the size and architectural design of ControlNet [Zhang et al., 2023] for controlling the image generation process with stable diffusion-based models. We show that a new architecture with as little as 1% of the parameters of the base model achieves state-of-the art results, considerably better than ControlNet in terms of FID score. Hence we call it ControlNet-XS. We provide the code for controlling StableDiffusion-XL [Podell et al., 2023] (Model B, 48M Parameters) and StableDiffusion 2.1 [Rombach et al. 2022] (Model B, 14M Parameters), all under openrail license.*
|
||||||
|
|
||||||
|
This model was contributed by [UmerHA](https://twitter.com/UmerHAdil). ❤️
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
## StableDiffusionControlNetXSPipeline
|
||||||
|
[[autodoc]] StableDiffusionControlNetXSPipeline
|
||||||
|
- all
|
||||||
|
- __call__
|
||||||
|
|
||||||
|
## StableDiffusionPipelineOutput
|
||||||
|
[[autodoc]] pipelines.stable_diffusion.StableDiffusionPipelineOutput
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<!--Copyright 2023 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
# ControlNet-XS with Stable Diffusion XL
|
||||||
|
|
||||||
|
ControlNet-XS was introduced in [ControlNet-XS](https://vislearn.github.io/ControlNet-XS/) by Denis Zavadski and Carsten Rother. It is based on the observation that the control model in the [original ControlNet](https://huggingface.co/papers/2302.05543) can be made much smaller and still produce good results.
|
||||||
|
|
||||||
|
Like the original 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.
|
||||||
|
|
||||||
|
ControlNet-XS generates images with comparable quality to a regular ControlNet, but it is 20-25% faster ([see benchmark](https://github.com/UmerHA/controlnet-xs-benchmark/blob/main/Speed%20Benchmark.ipynb)) and uses ~45% less memory.
|
||||||
|
|
||||||
|
Here's the overview from the [project page](https://vislearn.github.io/ControlNet-XS/):
|
||||||
|
|
||||||
|
*With increasing computing capabilities, current model architectures appear to follow the trend of simply upscaling all components without validating the necessity for doing so. In this project we investigate the size and architectural design of ControlNet [Zhang et al., 2023] for controlling the image generation process with stable diffusion-based models. We show that a new architecture with as little as 1% of the parameters of the base model achieves state-of-the art results, considerably better than ControlNet in terms of FID score. Hence we call it ControlNet-XS. We provide the code for controlling StableDiffusion-XL [Podell et al., 2023] (Model B, 48M Parameters) and StableDiffusion 2.1 [Rombach et al. 2022] (Model B, 14M Parameters), all under openrail license.*
|
||||||
|
|
||||||
|
This model was contributed by [UmerHA](https://twitter.com/UmerHAdil). ❤️
|
||||||
|
|
||||||
|
<Tip warning={true}>
|
||||||
|
|
||||||
|
🧪 Many of the SDXL ControlNet checkpoints are experimental, and there is a lot of room for improvement. Feel free to open an [Issue](https://github.com/huggingface/diffusers/issues/new/choose) and leave us feedback on how we can improve!
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
## StableDiffusionXLControlNetXSPipeline
|
||||||
|
[[autodoc]] StableDiffusionXLControlNetXSPipeline
|
||||||
|
- all
|
||||||
|
- __call__
|
||||||
|
|
||||||
|
## StableDiffusionPipelineOutput
|
||||||
|
[[autodoc]] pipelines.stable_diffusion.StableDiffusionPipelineOutput
|
||||||
@@ -40,6 +40,8 @@ The table below lists all the pipelines currently available in 🤗 Diffusers an
|
|||||||
| [Consistency Models](consistency_models) | unconditional image generation |
|
| [Consistency Models](consistency_models) | unconditional image generation |
|
||||||
| [ControlNet](controlnet) | text2image, image2image, inpainting |
|
| [ControlNet](controlnet) | text2image, image2image, inpainting |
|
||||||
| [ControlNet with Stable Diffusion XL](controlnet_sdxl) | text2image |
|
| [ControlNet with Stable Diffusion XL](controlnet_sdxl) | text2image |
|
||||||
|
| [ControlNet-XS](controlnetxs) | text2image |
|
||||||
|
| [ControlNet-XS with Stable Diffusion XL](controlnetxs_sdxl) | text2image |
|
||||||
| [Cycle Diffusion](cycle_diffusion) | image2image |
|
| [Cycle Diffusion](cycle_diffusion) | image2image |
|
||||||
| [Dance Diffusion](dance_diffusion) | unconditional audio generation |
|
| [Dance Diffusion](dance_diffusion) | unconditional audio generation |
|
||||||
| [DDIM](ddim) | unconditional image generation |
|
| [DDIM](ddim) | unconditional image generation |
|
||||||
@@ -71,6 +73,7 @@ The table below lists all the pipelines currently available in 🤗 Diffusers an
|
|||||||
| [Stable Diffusion](stable_diffusion/overview) | text2image, image2image, depth2image, inpainting, image variation, latent upscaler, super-resolution |
|
| [Stable Diffusion](stable_diffusion/overview) | text2image, image2image, depth2image, inpainting, image variation, latent upscaler, super-resolution |
|
||||||
| [Stable Diffusion Model Editing](model_editing) | model editing |
|
| [Stable Diffusion Model Editing](model_editing) | model editing |
|
||||||
| [Stable Diffusion XL](stable_diffusion/stable_diffusion_xl) | text2image, image2image, inpainting |
|
| [Stable Diffusion XL](stable_diffusion/stable_diffusion_xl) | text2image, image2image, inpainting |
|
||||||
|
| [Stable Diffusion XL Turbo](stable_diffusion/sdxl_turbo) | text2image, image2image, inpainting |
|
||||||
| [Stable unCLIP](stable_unclip) | text2image, image variation |
|
| [Stable unCLIP](stable_unclip) | text2image, image variation |
|
||||||
| [Stochastic Karras VE](stochastic_karras_ve) | unconditional image generation |
|
| [Stochastic Karras VE](stochastic_karras_ve) | unconditional image generation |
|
||||||
| [T2I-Adapter](stable_diffusion/adapter) | text2image |
|
| [T2I-Adapter](stable_diffusion/adapter) | text2image |
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ The abstract from the paper is:
|
|||||||
|
|
||||||
## Tips
|
## Tips
|
||||||
|
|
||||||
- SDXL Turbo uses the exact same architecture as [SDXL](./stable_diffusion_xl).
|
- SDXL Turbo uses the exact same architecture as [SDXL](./stable_diffusion_xl), which means it also has the same API. Please refer to the [SDXL](./stable_diffusion_xl) API reference for more details.
|
||||||
- SDXL Turbo should disable guidance scale by setting `guidance_scale=0.0`
|
- SDXL Turbo should disable guidance scale by setting `guidance_scale=0.0`
|
||||||
- SDXL Turbo should use `timestep_spacing='trailing'` for the scheduler and use between 1 and 4 steps.
|
- SDXL Turbo should use `timestep_spacing='trailing'` for the scheduler and use between 1 and 4 steps.
|
||||||
- SDXL Turbo has been trained to generate images of size 512x512.
|
- SDXL Turbo has been trained to generate images of size 512x512.
|
||||||
@@ -28,26 +28,8 @@ The abstract from the paper is:
|
|||||||
|
|
||||||
<Tip>
|
<Tip>
|
||||||
|
|
||||||
To learn how to use SDXL Turbo for various tasks, how to optimize performance, and other usage examples, take a look at the [Stable Diffusion XL](../../../using-diffusers/sdxl_turbo) guide.
|
To learn how to use SDXL Turbo for various tasks, how to optimize performance, and other usage examples, take a look at the [SDXL Turbo](../../../using-diffusers/sdxl_turbo) guide.
|
||||||
|
|
||||||
Check out the [Stability AI](https://huggingface.co/stabilityai) Hub organization for the official base and refiner model checkpoints!
|
Check out the [Stability AI](https://huggingface.co/stabilityai) Hub organization for the official base and refiner model checkpoints!
|
||||||
|
|
||||||
</Tip>
|
</Tip>
|
||||||
|
|
||||||
## StableDiffusionXLPipeline
|
|
||||||
|
|
||||||
[[autodoc]] StableDiffusionXLPipeline
|
|
||||||
- all
|
|
||||||
- __call__
|
|
||||||
|
|
||||||
## StableDiffusionXLImg2ImgPipeline
|
|
||||||
|
|
||||||
[[autodoc]] StableDiffusionXLImg2ImgPipeline
|
|
||||||
- all
|
|
||||||
- __call__
|
|
||||||
|
|
||||||
## StableDiffusionXLInpaintPipeline
|
|
||||||
|
|
||||||
[[autodoc]] StableDiffusionXLInpaintPipeline
|
|
||||||
- all
|
|
||||||
- __call__
|
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ accelerate launch --mixed_precision="fp16" train_text_to_image_lora.py \
|
|||||||
--pretrained_model_name_or_path=$MODEL_NAME \
|
--pretrained_model_name_or_path=$MODEL_NAME \
|
||||||
--dataset_name=$DATASET_NAME \
|
--dataset_name=$DATASET_NAME \
|
||||||
--dataloader_num_workers=8 \
|
--dataloader_num_workers=8 \
|
||||||
--resolution=512
|
--resolution=512 \
|
||||||
--center_crop \
|
--center_crop \
|
||||||
--random_flip \
|
--random_flip \
|
||||||
--train_batch_size=1 \
|
--train_batch_size=1 \
|
||||||
|
|||||||
@@ -485,6 +485,69 @@ image.save("sdxl_t2i.png")
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
You can use the IP-Adapter face model to apply specific faces to your images. It is an effective way to maintain consistent characters in your image generations.
|
||||||
|
Weights are loaded with the same method used for the other IP-Adapters.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Load ip-adapter-full-face_sd15.bin
|
||||||
|
pipeline.load_ip_adapter("h94/IP-Adapter", subfolder="models", weight_name="ip-adapter-full-face_sd15.bin")
|
||||||
|
```
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
|
||||||
|
It is recommended to use `DDIMScheduler` and `EulerDiscreteScheduler` for face model.
|
||||||
|
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
```python
|
||||||
|
import torch
|
||||||
|
from diffusers import StableDiffusionPipeline, DDIMScheduler
|
||||||
|
from diffusers.utils import load_image
|
||||||
|
|
||||||
|
noise_scheduler = DDIMScheduler(
|
||||||
|
num_train_timesteps=1000,
|
||||||
|
beta_start=0.00085,
|
||||||
|
beta_end=0.012,
|
||||||
|
beta_schedule="scaled_linear",
|
||||||
|
clip_sample=False,
|
||||||
|
set_alpha_to_one=False,
|
||||||
|
steps_offset=1
|
||||||
|
)
|
||||||
|
|
||||||
|
pipeline = StableDiffusionPipeline.from_pretrained(
|
||||||
|
"runwayml/stable-diffusion-v1-5",
|
||||||
|
torch_dtype=torch.float16,
|
||||||
|
scheduler=noise_scheduler,
|
||||||
|
).to("cuda")
|
||||||
|
|
||||||
|
pipeline.load_ip_adapter("h94/IP-Adapter", subfolder="models", weight_name="ip-adapter-full-face_sd15.bin")
|
||||||
|
|
||||||
|
pipeline.set_ip_adapter_scale(0.7)
|
||||||
|
|
||||||
|
image = load_image("https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/ai_face2.png")
|
||||||
|
|
||||||
|
generator = torch.Generator(device="cpu").manual_seed(33)
|
||||||
|
|
||||||
|
image = pipeline(
|
||||||
|
prompt="A photo of a girl wearing a black dress, holding red roses in hand, upper body, behind is the Eiffel Tower",
|
||||||
|
ip_adapter_image=image,
|
||||||
|
negative_prompt="monochrome, lowres, bad anatomy, worst quality, low quality",
|
||||||
|
num_inference_steps=50, num_images_per_prompt=1, width=512, height=704,
|
||||||
|
generator=generator,
|
||||||
|
).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/ai_face2.png"/>
|
||||||
|
<figcaption class="mt-2 text-center text-sm text-gray-500">input image</figcaption>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<img class="rounded-xl" src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/ipadapter_full_face_output.png"/>
|
||||||
|
<figcaption class="mt-2 text-center text-sm text-gray-500">output image</figcaption>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
### LCM-Lora
|
### LCM-Lora
|
||||||
|
|
||||||
|
|||||||
@@ -174,10 +174,4 @@ Set `private=True` in the [`~diffusers.utils.PushToHubMixin.push_to_hub`] functi
|
|||||||
controlnet.push_to_hub("my-controlnet-model-private", private=True)
|
controlnet.push_to_hub("my-controlnet-model-private", private=True)
|
||||||
```
|
```
|
||||||
|
|
||||||
Private repositories are only visible to you, and other users won't be able to clone the repository and your repository won't appear in search results. Even if a user has the URL to your private repository, they'll receive a `404 - Sorry, we can't find the page you are looking for.`
|
Private repositories are only visible to you, and other users won't be able to clone the repository and your repository won't appear in search results. Even if a user has the URL to your private repository, they'll receive a `404 - Sorry, we can't find the page you are looking for`. You must be [logged in](https://huggingface.co/docs/huggingface_hub/quick-start#login) to load a model from a private repository.
|
||||||
|
|
||||||
To load a model, scheduler, or pipeline from private or gated repositories, set `use_auth_token=True`:
|
|
||||||
|
|
||||||
```py
|
|
||||||
model = ControlNetModel.from_pretrained("your-namespace/my-controlnet-model-private", use_auth_token=True)
|
|
||||||
```
|
|
||||||
+2
-4
@@ -18,8 +18,7 @@ limitations under the License.
|
|||||||
Diffusers examples are a collection of scripts to demonstrate how to effectively use the `diffusers` library
|
Diffusers examples are a collection of scripts to demonstrate how to effectively use the `diffusers` library
|
||||||
for a variety of use cases involving training or fine-tuning.
|
for a variety of use cases involving training or fine-tuning.
|
||||||
|
|
||||||
**Note**: If you are looking for **official** examples on how to use `diffusers` for inference,
|
**Note**: If you are looking for **official** examples on how to use `diffusers` for inference, please have a look at [src/diffusers/pipelines](https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines).
|
||||||
please have a look at [src/diffusers/pipelines](https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines).
|
|
||||||
|
|
||||||
Our examples aspire to be **self-contained**, **easy-to-tweak**, **beginner-friendly** and for **one-purpose-only**.
|
Our examples aspire to be **self-contained**, **easy-to-tweak**, **beginner-friendly** and for **one-purpose-only**.
|
||||||
More specifically, this means:
|
More specifically, this means:
|
||||||
@@ -27,8 +26,7 @@ More specifically, this means:
|
|||||||
- **Self-contained**: An example script shall only depend on "pip-install-able" Python packages that can be found in a `requirements.txt` file. Example scripts shall **not** depend on any local files. This means that one can simply download an example script, *e.g.* [train_unconditional.py](https://github.com/huggingface/diffusers/blob/main/examples/unconditional_image_generation/train_unconditional.py), install the required dependencies, *e.g.* [requirements.txt](https://github.com/huggingface/diffusers/blob/main/examples/unconditional_image_generation/requirements.txt) and execute the example script.
|
- **Self-contained**: An example script shall only depend on "pip-install-able" Python packages that can be found in a `requirements.txt` file. Example scripts shall **not** depend on any local files. This means that one can simply download an example script, *e.g.* [train_unconditional.py](https://github.com/huggingface/diffusers/blob/main/examples/unconditional_image_generation/train_unconditional.py), install the required dependencies, *e.g.* [requirements.txt](https://github.com/huggingface/diffusers/blob/main/examples/unconditional_image_generation/requirements.txt) and execute the example script.
|
||||||
- **Easy-to-tweak**: While we strive to present as many use cases as possible, the example scripts are just that - examples. It is expected that they won't work out-of-the box on your specific problem and that you will be required to change a few lines of code to adapt them to your needs. To help you with that, most of the examples fully expose the preprocessing of the data and the training loop to allow you to tweak and edit them as required.
|
- **Easy-to-tweak**: While we strive to present as many use cases as possible, the example scripts are just that - examples. It is expected that they won't work out-of-the box on your specific problem and that you will be required to change a few lines of code to adapt them to your needs. To help you with that, most of the examples fully expose the preprocessing of the data and the training loop to allow you to tweak and edit them as required.
|
||||||
- **Beginner-friendly**: We do not aim for providing state-of-the-art training scripts for the newest models, but rather examples that can be used as a way to better understand diffusion models and how to use them with the `diffusers` library. We often purposefully leave out certain state-of-the-art methods if we consider them too complex for beginners.
|
- **Beginner-friendly**: We do not aim for providing state-of-the-art training scripts for the newest models, but rather examples that can be used as a way to better understand diffusion models and how to use them with the `diffusers` library. We often purposefully leave out certain state-of-the-art methods if we consider them too complex for beginners.
|
||||||
- **One-purpose-only**: Examples should show one task and one task only. Even if a task is from a modeling
|
- **One-purpose-only**: Examples should show one task and one task only. Even if a task is from a modeling point of view very similar, *e.g.* image super-resolution and image modification tend to use the same model and training method, we want examples to showcase only one task to keep them as readable and easy-to-understand as possible.
|
||||||
point of view very similar, *e.g.* image super-resolution and image modification tend to use the same model and training method, we want examples to showcase only one task to keep them as readable and easy-to-understand as possible.
|
|
||||||
|
|
||||||
We provide **official** examples that cover the most popular tasks of diffusion models.
|
We provide **official** examples that cover the most popular tasks of diffusion models.
|
||||||
*Official* examples are **actively** maintained by the `diffusers` maintainers and we try to rigorously follow our example philosophy as defined above.
|
*Official* examples are **actively** maintained by the `diffusers` maintainers and we try to rigorously follow our example philosophy as defined above.
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ def save_model_card(
|
|||||||
repo_folder=None,
|
repo_folder=None,
|
||||||
vae_path=None,
|
vae_path=None,
|
||||||
):
|
):
|
||||||
img_str = "widget:\n" if images else ""
|
img_str = "widget:\n"
|
||||||
for i, image in enumerate(images):
|
for i, image in enumerate(images):
|
||||||
image.save(os.path.join(repo_folder, f"image_{i}.png"))
|
image.save(os.path.join(repo_folder, f"image_{i}.png"))
|
||||||
img_str += f"""
|
img_str += f"""
|
||||||
@@ -121,6 +121,10 @@ def save_model_card(
|
|||||||
url:
|
url:
|
||||||
"image_{i}.png"
|
"image_{i}.png"
|
||||||
"""
|
"""
|
||||||
|
if not images:
|
||||||
|
img_str += f"""
|
||||||
|
- text: '{instance_prompt}'
|
||||||
|
"""
|
||||||
|
|
||||||
trigger_str = f"You should use {instance_prompt} to trigger the image generation."
|
trigger_str = f"You should use {instance_prompt} to trigger the image generation."
|
||||||
diffusers_imports_pivotal = ""
|
diffusers_imports_pivotal = ""
|
||||||
@@ -133,10 +137,10 @@ def save_model_card(
|
|||||||
diffusers_imports_pivotal = """from huggingface_hub import hf_hub_download
|
diffusers_imports_pivotal = """from huggingface_hub import hf_hub_download
|
||||||
from safetensors.torch import load_file
|
from safetensors.torch import load_file
|
||||||
"""
|
"""
|
||||||
diffusers_example_pivotal = f"""embedding_path = hf_hub_download(repo_id="{repo_id}", filename="embeddings.safetensors", repo_type="model")
|
diffusers_example_pivotal = f"""embedding_path = hf_hub_download(repo_id='{repo_id}', filename="embeddings.safetensors", repo_type="model")
|
||||||
state_dict = load_file(embedding_path)
|
state_dict = load_file(embedding_path)
|
||||||
pipeline.load_textual_inversion(state_dict["clip_l"], token=["<s0>", "<s1>"], text_encoder=pipe.text_encoder, tokenizer=pipe.tokenizer)
|
pipeline.load_textual_inversion(state_dict["clip_l"], token=["<s0>", "<s1>"], text_encoder=pipeline.text_encoder, tokenizer=pipeline.tokenizer)
|
||||||
pipeline.load_textual_inversion(state_dict["clip_g"], token=["<s0>", "<s1>"], text_encoder=pipe.text_encoder_2, tokenizer=pipe.tokenizer_2)
|
pipeline.load_textual_inversion(state_dict["clip_g"], token=["<s0>", "<s1>"], text_encoder=pipeline.text_encoder_2, tokenizer=pipeline.tokenizer_2)
|
||||||
"""
|
"""
|
||||||
if token_abstraction_dict:
|
if token_abstraction_dict:
|
||||||
for key, value in token_abstraction_dict.items():
|
for key, value in token_abstraction_dict.items():
|
||||||
@@ -145,8 +149,7 @@ pipeline.load_textual_inversion(state_dict["clip_g"], token=["<s0>", "<s1>"], te
|
|||||||
to trigger concept `{key}` → use `{tokens}` in your prompt \n
|
to trigger concept `{key}` → use `{tokens}` in your prompt \n
|
||||||
"""
|
"""
|
||||||
|
|
||||||
yaml = f"""
|
yaml = f"""---
|
||||||
---
|
|
||||||
tags:
|
tags:
|
||||||
- stable-diffusion-xl
|
- stable-diffusion-xl
|
||||||
- stable-diffusion-xl-diffusers
|
- stable-diffusion-xl-diffusers
|
||||||
@@ -159,7 +162,7 @@ base_model: {base_model}
|
|||||||
instance_prompt: {instance_prompt}
|
instance_prompt: {instance_prompt}
|
||||||
license: openrail++
|
license: openrail++
|
||||||
---
|
---
|
||||||
"""
|
"""
|
||||||
|
|
||||||
model_card = f"""
|
model_card = f"""
|
||||||
# SDXL LoRA DreamBooth - {repo_id}
|
# SDXL LoRA DreamBooth - {repo_id}
|
||||||
@@ -170,14 +173,6 @@ license: openrail++
|
|||||||
|
|
||||||
### These are {repo_id} LoRA adaption weights for {base_model}.
|
### These are {repo_id} LoRA adaption weights for {base_model}.
|
||||||
|
|
||||||
The weights were trained using [DreamBooth](https://dreambooth.github.io/).
|
|
||||||
|
|
||||||
LoRA for the text encoder was enabled: {train_text_encoder}.
|
|
||||||
|
|
||||||
Pivotal tuning was enabled: {train_text_encoder_ti}.
|
|
||||||
|
|
||||||
Special VAE used for training: {vae_path}.
|
|
||||||
|
|
||||||
## Trigger words
|
## Trigger words
|
||||||
|
|
||||||
{trigger_str}
|
{trigger_str}
|
||||||
@@ -196,11 +191,24 @@ image = pipeline('{validation_prompt if validation_prompt else instance_prompt}'
|
|||||||
|
|
||||||
For more details, including weighting, merging and fusing LoRAs, check the [documentation on loading LoRAs in diffusers](https://huggingface.co/docs/diffusers/main/en/using-diffusers/loading_adapters)
|
For more details, including weighting, merging and fusing LoRAs, check the [documentation on loading LoRAs in diffusers](https://huggingface.co/docs/diffusers/main/en/using-diffusers/loading_adapters)
|
||||||
|
|
||||||
## Download model (use it with UIs such as AUTO1111, Comfy, SD.Next, Invoke)
|
## Download model
|
||||||
|
|
||||||
Weights for this model are available in Safetensors format.
|
### Use it with UIs such as AUTOMATIC1111, Comfy UI, SD.Next, Invoke
|
||||||
|
|
||||||
[Download]({repo_id}/tree/main) them in the Files & versions tab.
|
- Download the LoRA *.safetensors [here](/{repo_id}/blob/main/pytorch_lora_weights.safetensors). Rename it and place it on your Lora folder.
|
||||||
|
- Download the text embeddings *.safetensors [here](/{repo_id}/blob/main/embeddings.safetensors). Rename it and place it on it on your embeddings folder.
|
||||||
|
|
||||||
|
All [Files & versions](/{repo_id}/tree/main).
|
||||||
|
|
||||||
|
## Details
|
||||||
|
|
||||||
|
The weights were trained using [🧨 diffusers Advanced Dreambooth Training Script](https://github.com/huggingface/diffusers/blob/main/examples/advanced_diffusion_training/train_dreambooth_lora_sdxl_advanced.py).
|
||||||
|
|
||||||
|
LoRA for the text encoder was enabled. {train_text_encoder}.
|
||||||
|
|
||||||
|
Pivotal tuning was enabled: {train_text_encoder_ti}.
|
||||||
|
|
||||||
|
Special VAE used for training: {vae_path}.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
with open(os.path.join(repo_folder, "README.md"), "w") as f:
|
with open(os.path.join(repo_folder, "README.md"), "w") as f:
|
||||||
@@ -667,6 +675,12 @@ def parse_args(input_args=None):
|
|||||||
default=4,
|
default=4,
|
||||||
help=("The dimension of the LoRA update matrices."),
|
help=("The dimension of the LoRA update matrices."),
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--cache_latents",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Cache the VAE latents",
|
||||||
|
)
|
||||||
|
|
||||||
if input_args is not None:
|
if input_args is not None:
|
||||||
args = parser.parse_args(input_args)
|
args = parser.parse_args(input_args)
|
||||||
@@ -1170,6 +1184,7 @@ def main(args):
|
|||||||
revision=args.revision,
|
revision=args.revision,
|
||||||
variant=args.variant,
|
variant=args.variant,
|
||||||
)
|
)
|
||||||
|
vae_scaling_factor = vae.config.scaling_factor
|
||||||
unet = UNet2DConditionModel.from_pretrained(
|
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
|
||||||
)
|
)
|
||||||
@@ -1600,6 +1615,20 @@ def main(args):
|
|||||||
args.validation_prompt = args.validation_prompt.replace(token_abs, "".join(token_replacement))
|
args.validation_prompt = args.validation_prompt.replace(token_abs, "".join(token_replacement))
|
||||||
print("validation prompt:", args.validation_prompt)
|
print("validation prompt:", args.validation_prompt)
|
||||||
|
|
||||||
|
if args.cache_latents:
|
||||||
|
latents_cache = []
|
||||||
|
for batch in tqdm(train_dataloader, desc="Caching latents"):
|
||||||
|
with torch.no_grad():
|
||||||
|
batch["pixel_values"] = batch["pixel_values"].to(
|
||||||
|
accelerator.device, non_blocking=True, dtype=torch.float32
|
||||||
|
)
|
||||||
|
latents_cache.append(vae.encode(batch["pixel_values"]).latent_dist)
|
||||||
|
|
||||||
|
if args.validation_prompt is None:
|
||||||
|
del vae
|
||||||
|
if torch.cuda.is_available():
|
||||||
|
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
|
overrode_max_train_steps = False
|
||||||
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)
|
||||||
@@ -1715,9 +1744,7 @@ def main(args):
|
|||||||
unet.train()
|
unet.train()
|
||||||
for step, batch in enumerate(train_dataloader):
|
for step, batch in enumerate(train_dataloader):
|
||||||
with accelerator.accumulate(unet):
|
with accelerator.accumulate(unet):
|
||||||
pixel_values = batch["pixel_values"].to(dtype=vae.dtype)
|
|
||||||
prompts = batch["prompts"]
|
prompts = batch["prompts"]
|
||||||
# print(prompts)
|
|
||||||
# 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:
|
||||||
if freeze_text_encoder:
|
if freeze_text_encoder:
|
||||||
@@ -1729,9 +1756,13 @@ def main(args):
|
|||||||
tokens_one = tokenize_prompt(tokenizer_one, prompts, add_special_tokens)
|
tokens_one = tokenize_prompt(tokenizer_one, prompts, add_special_tokens)
|
||||||
tokens_two = tokenize_prompt(tokenizer_two, prompts, add_special_tokens)
|
tokens_two = tokenize_prompt(tokenizer_two, prompts, add_special_tokens)
|
||||||
|
|
||||||
# Convert images to latent space
|
if args.cache_latents:
|
||||||
model_input = vae.encode(pixel_values).latent_dist.sample()
|
model_input = latents_cache[step].sample()
|
||||||
model_input = model_input * vae.config.scaling_factor
|
else:
|
||||||
|
pixel_values = batch["pixel_values"].to(dtype=vae.dtype)
|
||||||
|
model_input = vae.encode(pixel_values).latent_dist.sample()
|
||||||
|
|
||||||
|
model_input = model_input * vae_scaling_factor
|
||||||
if args.pretrained_vae_model_name_or_path is None:
|
if args.pretrained_vae_model_name_or_path is None:
|
||||||
model_input = model_input.to(weight_dtype)
|
model_input = model_input.to(weight_dtype)
|
||||||
|
|
||||||
@@ -1981,43 +2012,42 @@ def main(args):
|
|||||||
text_encoder_lora_layers=text_encoder_lora_layers,
|
text_encoder_lora_layers=text_encoder_lora_layers,
|
||||||
text_encoder_2_lora_layers=text_encoder_2_lora_layers,
|
text_encoder_2_lora_layers=text_encoder_2_lora_layers,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 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,
|
|
||||||
)
|
|
||||||
|
|
||||||
# We train on the simplified learning objective. If we were previously predicting a variance, we need the scheduler to ignore it
|
|
||||||
scheduler_args = {}
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
# 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
|
||||||
|
# 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,
|
||||||
|
)
|
||||||
|
|
||||||
|
# We train on the simplified learning objective. If we were previously predicting a variance, we need the scheduler to ignore it
|
||||||
|
scheduler_args = {}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
# run inference
|
||||||
pipeline = pipeline.to(accelerator.device)
|
pipeline = pipeline.to(accelerator.device)
|
||||||
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
|
||||||
images = [
|
images = [
|
||||||
|
|||||||
@@ -48,8 +48,10 @@ prompt-to-prompt | change parts of a prompt and retain image structure (see [pap
|
|||||||
| Latent Consistency Pipeline | Implementation of [Latent Consistency Models: Synthesizing High-Resolution Images with Few-Step Inference](https://arxiv.org/abs/2310.04378) | [Latent Consistency Pipeline](#latent-consistency-pipeline) | - | [Simian Luo](https://github.com/luosiallen) |
|
| Latent Consistency Pipeline | Implementation of [Latent Consistency Models: Synthesizing High-Resolution Images with Few-Step Inference](https://arxiv.org/abs/2310.04378) | [Latent Consistency Pipeline](#latent-consistency-pipeline) | - | [Simian Luo](https://github.com/luosiallen) |
|
||||||
| Latent Consistency Img2img Pipeline | Img2img pipeline for Latent Consistency Models | [Latent Consistency Img2Img Pipeline](#latent-consistency-img2img-pipeline) | - | [Logan Zoellner](https://github.com/nagolinc) |
|
| Latent Consistency Img2img Pipeline | Img2img pipeline for Latent Consistency Models | [Latent Consistency Img2Img Pipeline](#latent-consistency-img2img-pipeline) | - | [Logan Zoellner](https://github.com/nagolinc) |
|
||||||
| Latent Consistency Interpolation Pipeline | Interpolate the latent space of Latent Consistency Models with multiple prompts | [Latent Consistency Interpolation Pipeline](#latent-consistency-interpolation-pipeline) | [](https://colab.research.google.com/drive/1pK3NrLWJSiJsBynLns1K1-IDTW9zbPvl?usp=sharing) | [Aryan V S](https://github.com/a-r-r-o-w) |
|
| Latent Consistency Interpolation Pipeline | Interpolate the latent space of Latent Consistency Models with multiple prompts | [Latent Consistency Interpolation Pipeline](#latent-consistency-interpolation-pipeline) | [](https://colab.research.google.com/drive/1pK3NrLWJSiJsBynLns1K1-IDTW9zbPvl?usp=sharing) | [Aryan V S](https://github.com/a-r-r-o-w) |
|
||||||
|
| SDE Drag Pipeline | The pipeline supports drag editing of images using stochastic differential equations | [SDE Drag Pipeline](#sde-drag-pipeline) | - | [NieShen](https://github.com/NieShenRuc) [Fengqi Zhu](https://github.com/Monohydroxides) |
|
||||||
| Regional Prompting Pipeline | Assign multiple prompts for different regions | [Regional Prompting Pipeline](#regional-prompting-pipeline) | - | [hako-mikan](https://github.com/hako-mikan) |
|
| Regional Prompting Pipeline | Assign multiple prompts for different regions | [Regional Prompting Pipeline](#regional-prompting-pipeline) | - | [hako-mikan](https://github.com/hako-mikan) |
|
||||||
| LDM3D-sr (LDM3D upscaler) | Upscale low resolution RGB and depth inputs to high resolution | [StableDiffusionUpscaleLDM3D Pipeline](https://github.com/estelleafl/diffusers/tree/ldm3d_upscaler_community/examples/community#stablediffusionupscaleldm3d-pipeline) | - | [Estelle Aflalo](https://github.com/estelleafl) |
|
| LDM3D-sr (LDM3D upscaler) | Upscale low resolution RGB and depth inputs to high resolution | [StableDiffusionUpscaleLDM3D Pipeline](https://github.com/estelleafl/diffusers/tree/ldm3d_upscaler_community/examples/community#stablediffusionupscaleldm3d-pipeline) | - | [Estelle Aflalo](https://github.com/estelleafl) |
|
||||||
|
| AnimateDiff ControlNet Pipeline | Combines AnimateDiff with precise motion control using ControlNets | [AnimateDiff ControlNet Pipeline](#animatediff-controlnet-pipeline) | [](https://colab.research.google.com/drive/1SKboYeGjEQmQPWoFC0aLYpBlYdHXkvAu?usp=sharing) | [Aryan V S](https://github.com/a-r-r-o-w) and [Edoardo Botta](https://github.com/EdoardoBotta) |
|
||||||
| DemoFusion Pipeline | Implementation of [DemoFusion: Democratising High-Resolution Image Generation With No $$$](https://arxiv.org/abs/2311.16973) | [DemoFusion Pipeline](#DemoFusion) | - | [Ruoyi Du](https://github.com/RuoyiDu) |
|
| DemoFusion Pipeline | Implementation of [DemoFusion: Democratising High-Resolution Image Generation With No $$$](https://arxiv.org/abs/2311.16973) | [DemoFusion Pipeline](#DemoFusion) | - | [Ruoyi Du](https://github.com/RuoyiDu) |
|
||||||
|
|
||||||
To load a custom pipeline you just need to pass the `custom_pipeline` argument to `DiffusionPipeline`, as one of the files in `diffusers/examples/community`. Feel free to send a PR with your own pipelines, we will merge them quickly.
|
To load a custom pipeline you just need to pass the `custom_pipeline` argument to `DiffusionPipeline`, as one of the files in `diffusers/examples/community`. Feel free to send a PR with your own pipelines, we will merge them quickly.
|
||||||
@@ -512,7 +514,6 @@ device = torch.device('cpu' if not has_cuda else 'cuda')
|
|||||||
pipe = DiffusionPipeline.from_pretrained(
|
pipe = DiffusionPipeline.from_pretrained(
|
||||||
"CompVis/stable-diffusion-v1-4",
|
"CompVis/stable-diffusion-v1-4",
|
||||||
safety_checker=None,
|
safety_checker=None,
|
||||||
use_auth_token=True,
|
|
||||||
custom_pipeline="imagic_stable_diffusion",
|
custom_pipeline="imagic_stable_diffusion",
|
||||||
scheduler = DDIMScheduler(beta_start=0.00085, beta_end=0.012, beta_schedule="scaled_linear", clip_sample=False, set_alpha_to_one=False)
|
scheduler = DDIMScheduler(beta_start=0.00085, beta_end=0.012, beta_schedule="scaled_linear", clip_sample=False, set_alpha_to_one=False)
|
||||||
).to(device)
|
).to(device)
|
||||||
@@ -552,7 +553,6 @@ device = th.device('cpu' if not has_cuda else 'cuda')
|
|||||||
|
|
||||||
pipe = DiffusionPipeline.from_pretrained(
|
pipe = DiffusionPipeline.from_pretrained(
|
||||||
"CompVis/stable-diffusion-v1-4",
|
"CompVis/stable-diffusion-v1-4",
|
||||||
use_auth_token=True,
|
|
||||||
custom_pipeline="seed_resize_stable_diffusion"
|
custom_pipeline="seed_resize_stable_diffusion"
|
||||||
).to(device)
|
).to(device)
|
||||||
|
|
||||||
@@ -588,7 +588,6 @@ generator = th.Generator("cuda").manual_seed(0)
|
|||||||
|
|
||||||
pipe = DiffusionPipeline.from_pretrained(
|
pipe = DiffusionPipeline.from_pretrained(
|
||||||
"CompVis/stable-diffusion-v1-4",
|
"CompVis/stable-diffusion-v1-4",
|
||||||
use_auth_token=True,
|
|
||||||
custom_pipeline="/home/mark/open_source/diffusers/examples/community/"
|
custom_pipeline="/home/mark/open_source/diffusers/examples/community/"
|
||||||
).to(device)
|
).to(device)
|
||||||
|
|
||||||
@@ -607,7 +606,6 @@ image.save('./seed_resize/seed_resize_{w}_{h}_image.png'.format(w=width, h=heigh
|
|||||||
|
|
||||||
pipe_compare = DiffusionPipeline.from_pretrained(
|
pipe_compare = DiffusionPipeline.from_pretrained(
|
||||||
"CompVis/stable-diffusion-v1-4",
|
"CompVis/stable-diffusion-v1-4",
|
||||||
use_auth_token=True,
|
|
||||||
custom_pipeline="/home/mark/open_source/diffusers/examples/community/"
|
custom_pipeline="/home/mark/open_source/diffusers/examples/community/"
|
||||||
).to(device)
|
).to(device)
|
||||||
|
|
||||||
@@ -2843,6 +2841,70 @@ The Pipeline supports `compel` syntax. Input prompts using the `compel` structur
|
|||||||
* Reconstructed image:
|
* Reconstructed image:
|
||||||
* 
|
* 
|
||||||
|
|
||||||
|
### AnimateDiff ControlNet Pipeline
|
||||||
|
|
||||||
|
This pipeline combines AnimateDiff and ControlNet. Enjoy precise motion control for your videos! Refer to [this](https://github.com/huggingface/diffusers/issues/5866) issue for more details.
|
||||||
|
|
||||||
|
```py
|
||||||
|
import torch
|
||||||
|
from diffusers import AutoencoderKL, ControlNetModel, MotionAdapter
|
||||||
|
from diffusers.pipelines import DiffusionPipeline
|
||||||
|
from diffusers.schedulers import DPMSolverMultistepScheduler
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
motion_id = "guoyww/animatediff-motion-adapter-v1-5-2"
|
||||||
|
adapter = MotionAdapter.from_pretrained(motion_id)
|
||||||
|
controlnet = ControlNetModel.from_pretrained("lllyasviel/control_v11p_sd15_openpose", torch_dtype=torch.float16)
|
||||||
|
vae = AutoencoderKL.from_pretrained("stabilityai/sd-vae-ft-mse", torch_dtype=torch.float16)
|
||||||
|
|
||||||
|
model_id = "SG161222/Realistic_Vision_V5.1_noVAE"
|
||||||
|
pipe = DiffusionPipeline.from_pretrained(
|
||||||
|
model_id,
|
||||||
|
motion_adapter=adapter,
|
||||||
|
controlnet=controlnet,
|
||||||
|
vae=vae,
|
||||||
|
custom_pipeline="pipeline_animatediff_controlnet",
|
||||||
|
).to(device="cuda", dtype=torch.float16)
|
||||||
|
pipe.scheduler = DPMSolverMultistepScheduler.from_pretrained(
|
||||||
|
model_id, subfolder="scheduler", clip_sample=False, timestep_spacing="linspace", steps_offset=1
|
||||||
|
)
|
||||||
|
pipe.enable_vae_slicing()
|
||||||
|
|
||||||
|
conditioning_frames = []
|
||||||
|
for i in range(1, 16 + 1):
|
||||||
|
conditioning_frames.append(Image.open(f"frame_{i}.png"))
|
||||||
|
|
||||||
|
prompt = "astronaut in space, dancing"
|
||||||
|
negative_prompt = "bad quality, worst quality, jpeg artifacts, ugly"
|
||||||
|
result = pipe(
|
||||||
|
prompt=prompt,
|
||||||
|
negative_prompt=negative_prompt,
|
||||||
|
width=512,
|
||||||
|
height=768,
|
||||||
|
conditioning_frames=conditioning_frames,
|
||||||
|
num_inference_steps=12,
|
||||||
|
).frames[0]
|
||||||
|
|
||||||
|
from diffusers.utils import export_to_gif
|
||||||
|
export_to_gif(result.frames[0], "result.gif")
|
||||||
|
```
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr><td colspan="2" align=center><b>Conditioning Frames</b></td></tr>
|
||||||
|
<tr align=center>
|
||||||
|
<td align=center><img src="https://user-images.githubusercontent.com/7365912/265043418-23291941-864d-495a-8ba8-d02e05756396.gif" alt="input-frames"></td>
|
||||||
|
</tr>
|
||||||
|
<tr><td colspan="2" align=center><b>AnimateDiff model: SG161222/Realistic_Vision_V5.1_noVAE</b></td></tr>
|
||||||
|
<tr>
|
||||||
|
<td align=center><img src="https://github.com/huggingface/diffusers/assets/72266394/baf301e2-d03c-4129-bd84-203a1de2b2be" alt="gif-1"></td>
|
||||||
|
<td align=center><img src="https://github.com/huggingface/diffusers/assets/72266394/9f923475-ecaf-452b-92c8-4e42171182d8" alt="gif-2"></td>
|
||||||
|
</tr>
|
||||||
|
<tr><td colspan="2" align=center><b>AnimateDiff model: CardosAnime</b></td></tr>
|
||||||
|
<tr>
|
||||||
|
<td align=center><img src="https://github.com/huggingface/diffusers/assets/72266394/b2c41028-38a0-45d6-86ed-fec7446b87f7" alt="gif-1"></td>
|
||||||
|
<td align=center><img src="https://github.com/huggingface/diffusers/assets/72266394/eb7d2952-72e4-44fa-b664-077c79b4fc70" alt="gif-2"></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
### DemoFusion
|
### DemoFusion
|
||||||
This pipeline is the official implementation of [DemoFusion: Democratising High-Resolution Image Generation With No $$$](https://arxiv.org/abs/2311.16973).
|
This pipeline is the official implementation of [DemoFusion: Democratising High-Resolution Image Generation With No $$$](https://arxiv.org/abs/2311.16973).
|
||||||
The original repo can be found at [repo](https://github.com/PRIS-CV/DemoFusion).
|
The original repo can be found at [repo](https://github.com/PRIS-CV/DemoFusion).
|
||||||
@@ -2869,7 +2931,7 @@ The original repo can be found at [repo](https://github.com/PRIS-CV/DemoFusion).
|
|||||||
|
|
||||||
- `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.
|
||||||
```
|
```py
|
||||||
from diffusers import DiffusionPipeline
|
from diffusers import DiffusionPipeline
|
||||||
|
|
||||||
pipe = DiffusionPipeline.from_pretrained(
|
pipe = DiffusionPipeline.from_pretrained(
|
||||||
@@ -2901,7 +2963,7 @@ images = pipe(
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
You can display and save the generated images as:
|
You can display and save the generated images as:
|
||||||
```
|
```py
|
||||||
def image_grid(imgs, save_path=None):
|
def image_grid(imgs, save_path=None):
|
||||||
|
|
||||||
w = 0
|
w = 0
|
||||||
@@ -2925,3 +2987,42 @@ def image_grid(imgs, save_path=None):
|
|||||||
image_grid(images, save_path="./outputs/")
|
image_grid(images, save_path="./outputs/")
|
||||||
```
|
```
|
||||||

|

|
||||||
|
|
||||||
|
### SDE Drag pipeline
|
||||||
|
|
||||||
|
This pipeline provides drag-and-drop image editing using stochastic differential equations. It enables image editing by inputting prompt, image, mask_image, source_points, and target_points.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
See [paper](https://arxiv.org/abs/2311.01410), [paper page](https://ml-gsai.github.io/SDE-Drag-demo/), [original repo](https://github.com/ML-GSAI/SDE-Drag) for more infomation.
|
||||||
|
|
||||||
|
```py
|
||||||
|
import PIL
|
||||||
|
import torch
|
||||||
|
from diffusers import DDIMScheduler, DiffusionPipeline
|
||||||
|
|
||||||
|
# Load the pipeline
|
||||||
|
model_path = "runwayml/stable-diffusion-v1-5"
|
||||||
|
scheduler = DDIMScheduler.from_pretrained(model_path, subfolder="scheduler")
|
||||||
|
pipe = DiffusionPipeline.from_pretrained(model_path, scheduler=scheduler, custom_pipeline="sde_drag")
|
||||||
|
pipe.to('cuda')
|
||||||
|
|
||||||
|
# To save GPU memory, torch.float16 can be used, but it may compromise image quality.
|
||||||
|
# If not training LoRA, please avoid using torch.float16
|
||||||
|
# pipe.to(torch.float16)
|
||||||
|
|
||||||
|
# Provide prompt, image, mask image, and the starting and target points for drag editing.
|
||||||
|
prompt = "prompt of the image"
|
||||||
|
image = PIL.Image.open('/path/to/image')
|
||||||
|
mask_image = PIL.Image.open('/path/to/mask_image')
|
||||||
|
source_points = [[123, 456]]
|
||||||
|
target_points = [[234, 567]]
|
||||||
|
|
||||||
|
# train_lora is optional, and in most cases, using train_lora can better preserve consistency with the original image.
|
||||||
|
pipe.train_lora(prompt, image)
|
||||||
|
|
||||||
|
output = pipe(prompt, image, mask_image, source_points, target_points)
|
||||||
|
output_image = PIL.Image.fromarray(output)
|
||||||
|
output_image.save("./output.png")
|
||||||
|
|
||||||
|
```
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ from typing import Dict, List, Union
|
|||||||
import safetensors.torch
|
import safetensors.torch
|
||||||
import torch
|
import torch
|
||||||
from huggingface_hub import snapshot_download
|
from huggingface_hub import snapshot_download
|
||||||
|
from huggingface_hub.utils import validate_hf_hub_args
|
||||||
|
|
||||||
from diffusers import DiffusionPipeline, __version__
|
from diffusers import DiffusionPipeline, __version__
|
||||||
from diffusers.schedulers.scheduling_utils import SCHEDULER_CONFIG_NAME
|
from diffusers.schedulers.scheduling_utils import SCHEDULER_CONFIG_NAME
|
||||||
from diffusers.utils import CONFIG_NAME, DIFFUSERS_CACHE, ONNX_WEIGHTS_NAME, WEIGHTS_NAME
|
from diffusers.utils import CONFIG_NAME, ONNX_WEIGHTS_NAME, WEIGHTS_NAME
|
||||||
|
|
||||||
|
|
||||||
class CheckpointMergerPipeline(DiffusionPipeline):
|
class CheckpointMergerPipeline(DiffusionPipeline):
|
||||||
@@ -57,6 +58,7 @@ class CheckpointMergerPipeline(DiffusionPipeline):
|
|||||||
return (temp_dict, meta_keys)
|
return (temp_dict, meta_keys)
|
||||||
|
|
||||||
@torch.no_grad()
|
@torch.no_grad()
|
||||||
|
@validate_hf_hub_args
|
||||||
def merge(self, pretrained_model_name_or_path_list: List[Union[str, os.PathLike]], **kwargs):
|
def merge(self, pretrained_model_name_or_path_list: List[Union[str, os.PathLike]], **kwargs):
|
||||||
"""
|
"""
|
||||||
Returns a new pipeline object of the class 'DiffusionPipeline' with the merged checkpoints(weights) of the models passed
|
Returns a new pipeline object of the class 'DiffusionPipeline' with the merged checkpoints(weights) of the models passed
|
||||||
@@ -69,7 +71,7 @@ class CheckpointMergerPipeline(DiffusionPipeline):
|
|||||||
**kwargs:
|
**kwargs:
|
||||||
Supports all the default DiffusionPipeline.get_config_dict kwargs viz..
|
Supports all the default DiffusionPipeline.get_config_dict kwargs viz..
|
||||||
|
|
||||||
cache_dir, resume_download, force_download, proxies, local_files_only, use_auth_token, revision, torch_dtype, device_map.
|
cache_dir, resume_download, force_download, proxies, local_files_only, token, revision, torch_dtype, device_map.
|
||||||
|
|
||||||
alpha - The interpolation parameter. Ranges from 0 to 1. It affects the ratio in which the checkpoints are merged. A 0.8 alpha
|
alpha - The interpolation parameter. Ranges from 0 to 1. It affects the ratio in which the checkpoints are merged. A 0.8 alpha
|
||||||
would mean that the first model checkpoints would affect the final result far less than an alpha of 0.2
|
would mean that the first model checkpoints would affect the final result far less than an alpha of 0.2
|
||||||
@@ -81,12 +83,12 @@ class CheckpointMergerPipeline(DiffusionPipeline):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
# Default kwargs from DiffusionPipeline
|
# Default kwargs from DiffusionPipeline
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
force_download = kwargs.pop("force_download", False)
|
force_download = kwargs.pop("force_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", False)
|
local_files_only = kwargs.pop("local_files_only", False)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
torch_dtype = kwargs.pop("torch_dtype", None)
|
torch_dtype = kwargs.pop("torch_dtype", None)
|
||||||
device_map = kwargs.pop("device_map", None)
|
device_map = kwargs.pop("device_map", None)
|
||||||
@@ -123,7 +125,7 @@ class CheckpointMergerPipeline(DiffusionPipeline):
|
|||||||
force_download=force_download,
|
force_download=force_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
)
|
)
|
||||||
config_dicts.append(config_dict)
|
config_dicts.append(config_dict)
|
||||||
@@ -159,7 +161,7 @@ class CheckpointMergerPipeline(DiffusionPipeline):
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
allow_patterns=allow_patterns,
|
allow_patterns=allow_patterns,
|
||||||
user_agent=user_agent,
|
user_agent=user_agent,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,594 @@
|
|||||||
|
import math
|
||||||
|
import tempfile
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import PIL.Image
|
||||||
|
import torch
|
||||||
|
from accelerate import Accelerator
|
||||||
|
from torchvision import transforms
|
||||||
|
from tqdm.auto import tqdm
|
||||||
|
from transformers import CLIPTextModel, CLIPTokenizer
|
||||||
|
|
||||||
|
from diffusers import AutoencoderKL, DiffusionPipeline, DPMSolverMultistepScheduler, UNet2DConditionModel
|
||||||
|
from diffusers.loaders import AttnProcsLayers, LoraLoaderMixin
|
||||||
|
from diffusers.models.attention_processor import (
|
||||||
|
AttnAddedKVProcessor,
|
||||||
|
AttnAddedKVProcessor2_0,
|
||||||
|
LoRAAttnAddedKVProcessor,
|
||||||
|
LoRAAttnProcessor,
|
||||||
|
LoRAAttnProcessor2_0,
|
||||||
|
SlicedAttnAddedKVProcessor,
|
||||||
|
)
|
||||||
|
from diffusers.optimization import get_scheduler
|
||||||
|
|
||||||
|
|
||||||
|
class SdeDragPipeline(DiffusionPipeline):
|
||||||
|
r"""
|
||||||
|
Pipeline for image drag-and-drop editing using stochastic differential equations: https://arxiv.org/abs/2311.01410.
|
||||||
|
Please refer to the [official repository](https://github.com/ML-GSAI/SDE-Drag) for more information.
|
||||||
|
|
||||||
|
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods the
|
||||||
|
library implements for all the pipelines (such as downloading or saving, running on a particular device, etc.)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
vae ([`AutoencoderKL`]):
|
||||||
|
Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representations.
|
||||||
|
text_encoder ([`CLIPTextModel`]):
|
||||||
|
Frozen text-encoder. Stable Diffusion uses the text portion of
|
||||||
|
[CLIP](https://huggingface.co/docs/transformers/model_doc/clip#transformers.CLIPTextModel), specifically
|
||||||
|
the [clip-vit-large-patch14](https://huggingface.co/openai/clip-vit-large-patch14) variant.
|
||||||
|
tokenizer (`CLIPTokenizer`):
|
||||||
|
Tokenizer of class
|
||||||
|
[CLIPTokenizer](https://huggingface.co/docs/transformers/v4.21.0/en/model_doc/clip#transformers.CLIPTokenizer).
|
||||||
|
unet ([`UNet2DConditionModel`]): Conditional U-Net architecture to denoise the encoded image latents.
|
||||||
|
scheduler ([`SchedulerMixin`]):
|
||||||
|
A scheduler to be used in combination with `unet` to denoise the encoded image latents. Please use
|
||||||
|
[`DDIMScheduler`].
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
vae: AutoencoderKL,
|
||||||
|
text_encoder: CLIPTextModel,
|
||||||
|
tokenizer: CLIPTokenizer,
|
||||||
|
unet: UNet2DConditionModel,
|
||||||
|
scheduler: DPMSolverMultistepScheduler,
|
||||||
|
):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.register_modules(vae=vae, text_encoder=text_encoder, tokenizer=tokenizer, unet=unet, scheduler=scheduler)
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
|
def __call__(
|
||||||
|
self,
|
||||||
|
prompt: str,
|
||||||
|
image: PIL.Image.Image,
|
||||||
|
mask_image: PIL.Image.Image,
|
||||||
|
source_points: List[List[int]],
|
||||||
|
target_points: List[List[int]],
|
||||||
|
t0: Optional[float] = 0.6,
|
||||||
|
steps: Optional[int] = 200,
|
||||||
|
step_size: Optional[int] = 2,
|
||||||
|
image_scale: Optional[float] = 0.3,
|
||||||
|
adapt_radius: Optional[int] = 5,
|
||||||
|
min_lora_scale: Optional[float] = 0.5,
|
||||||
|
generator: Optional[torch.Generator] = None,
|
||||||
|
):
|
||||||
|
r"""
|
||||||
|
Function invoked when calling the pipeline for image editing.
|
||||||
|
Args:
|
||||||
|
prompt (`str`, *required*):
|
||||||
|
The prompt to guide the image editing.
|
||||||
|
image (`PIL.Image.Image`, *required*):
|
||||||
|
Which will be edited, parts of the image will be masked out with `mask_image` and edited
|
||||||
|
according to `prompt`.
|
||||||
|
mask_image (`PIL.Image.Image`, *required*):
|
||||||
|
To mask `image`. White pixels in the mask will be edited, while black pixels will be preserved.
|
||||||
|
source_points (`List[List[int]]`, *required*):
|
||||||
|
Used to mark the starting positions of drag editing in the image, with each pixel represented as a
|
||||||
|
`List[int]` of length 2.
|
||||||
|
target_points (`List[List[int]]`, *required*):
|
||||||
|
Used to mark the target positions of drag editing in the image, with each pixel represented as a
|
||||||
|
`List[int]` of length 2.
|
||||||
|
t0 (`float`, *optional*, defaults to 0.6):
|
||||||
|
The time parameter. Higher t0 improves the fidelity while lowering the faithfulness of the edited images
|
||||||
|
and vice versa.
|
||||||
|
steps (`int`, *optional*, defaults to 200):
|
||||||
|
The number of sampling iterations.
|
||||||
|
step_size (`int`, *optional*, defaults to 2):
|
||||||
|
The drag diatance of each drag step.
|
||||||
|
image_scale (`float`, *optional*, defaults to 0.3):
|
||||||
|
To avoid duplicating the content, use image_scale to perturbs the source.
|
||||||
|
adapt_radius (`int`, *optional*, defaults to 5):
|
||||||
|
The size of the region for copy and paste operations during each step of the drag process.
|
||||||
|
min_lora_scale (`float`, *optional*, defaults to 0.5):
|
||||||
|
A lora scale that will be applied to all LoRA layers of the text encoder if LoRA layers are loaded.
|
||||||
|
min_lora_scale specifies the minimum LoRA scale during the image drag-editing process.
|
||||||
|
generator ('torch.Generator', *optional*, defaults to None):
|
||||||
|
To make generation deterministic(https://pytorch.org/docs/stable/generated/torch.Generator.html).
|
||||||
|
Examples:
|
||||||
|
```py
|
||||||
|
>>> import PIL
|
||||||
|
>>> import torch
|
||||||
|
>>> from diffusers import DDIMScheduler, DiffusionPipeline
|
||||||
|
|
||||||
|
>>> # Load the pipeline
|
||||||
|
>>> model_path = "runwayml/stable-diffusion-v1-5"
|
||||||
|
>>> scheduler = DDIMScheduler.from_pretrained(model_path, subfolder="scheduler")
|
||||||
|
>>> pipe = DiffusionPipeline.from_pretrained(model_path, scheduler=scheduler, custom_pipeline="sde_drag")
|
||||||
|
>>> pipe.to('cuda')
|
||||||
|
|
||||||
|
>>> # To save GPU memory, torch.float16 can be used, but it may compromise image quality.
|
||||||
|
>>> # If not training LoRA, please avoid using torch.float16
|
||||||
|
>>> # pipe.to(torch.float16)
|
||||||
|
|
||||||
|
>>> # Provide prompt, image, mask image, and the starting and target points for drag editing.
|
||||||
|
>>> prompt = "prompt of the image"
|
||||||
|
>>> image = PIL.Image.open('/path/to/image')
|
||||||
|
>>> mask_image = PIL.Image.open('/path/to/mask_image')
|
||||||
|
>>> source_points = [[123, 456]]
|
||||||
|
>>> target_points = [[234, 567]]
|
||||||
|
|
||||||
|
>>> # train_lora is optional, and in most cases, using train_lora can better preserve consistency with the original image.
|
||||||
|
>>> pipe.train_lora(prompt, image)
|
||||||
|
|
||||||
|
>>> output = pipe(prompt, image, mask_image, source_points, target_points)
|
||||||
|
>>> output_image = PIL.Image.fromarray(output)
|
||||||
|
>>> output_image.save("./output.png")
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.scheduler.set_timesteps(steps)
|
||||||
|
|
||||||
|
noise_scale = (1 - image_scale**2) ** (0.5)
|
||||||
|
|
||||||
|
text_embeddings = self._get_text_embed(prompt)
|
||||||
|
uncond_embeddings = self._get_text_embed([""])
|
||||||
|
text_embeddings = torch.cat([uncond_embeddings, text_embeddings])
|
||||||
|
|
||||||
|
latent = self._get_img_latent(image)
|
||||||
|
|
||||||
|
mask = mask_image.resize((latent.shape[3], latent.shape[2]))
|
||||||
|
mask = torch.tensor(np.array(mask))
|
||||||
|
mask = mask.unsqueeze(0).expand_as(latent).to(self.device)
|
||||||
|
|
||||||
|
source_points = torch.tensor(source_points).div(torch.tensor([8]), rounding_mode="trunc")
|
||||||
|
target_points = torch.tensor(target_points).div(torch.tensor([8]), rounding_mode="trunc")
|
||||||
|
|
||||||
|
distance = target_points - source_points
|
||||||
|
distance_norm_max = torch.norm(distance.float(), dim=1, keepdim=True).max()
|
||||||
|
|
||||||
|
if distance_norm_max <= step_size:
|
||||||
|
drag_num = 1
|
||||||
|
else:
|
||||||
|
drag_num = distance_norm_max.div(torch.tensor([step_size]), rounding_mode="trunc")
|
||||||
|
if (distance_norm_max / drag_num - step_size).abs() > (
|
||||||
|
distance_norm_max / (drag_num + 1) - step_size
|
||||||
|
).abs():
|
||||||
|
drag_num += 1
|
||||||
|
|
||||||
|
latents = []
|
||||||
|
for i in tqdm(range(int(drag_num)), desc="SDE Drag"):
|
||||||
|
source_new = source_points + (i / drag_num * distance).to(torch.int)
|
||||||
|
target_new = source_points + ((i + 1) / drag_num * distance).to(torch.int)
|
||||||
|
|
||||||
|
latent, noises, hook_latents, lora_scales, cfg_scales = self._forward(
|
||||||
|
latent, steps, t0, min_lora_scale, text_embeddings, generator
|
||||||
|
)
|
||||||
|
latent = self._copy_and_paste(
|
||||||
|
latent,
|
||||||
|
source_new,
|
||||||
|
target_new,
|
||||||
|
adapt_radius,
|
||||||
|
latent.shape[2] - 1,
|
||||||
|
latent.shape[3] - 1,
|
||||||
|
image_scale,
|
||||||
|
noise_scale,
|
||||||
|
generator,
|
||||||
|
)
|
||||||
|
latent = self._backward(
|
||||||
|
latent, mask, steps, t0, noises, hook_latents, lora_scales, cfg_scales, text_embeddings, generator
|
||||||
|
)
|
||||||
|
|
||||||
|
latents.append(latent)
|
||||||
|
|
||||||
|
result_image = 1 / 0.18215 * latents[-1]
|
||||||
|
|
||||||
|
with torch.no_grad():
|
||||||
|
result_image = self.vae.decode(result_image).sample
|
||||||
|
|
||||||
|
result_image = (result_image / 2 + 0.5).clamp(0, 1)
|
||||||
|
result_image = result_image.cpu().permute(0, 2, 3, 1).numpy()[0]
|
||||||
|
result_image = (result_image * 255).astype(np.uint8)
|
||||||
|
|
||||||
|
return result_image
|
||||||
|
|
||||||
|
def train_lora(self, prompt, image, lora_step=100, lora_rank=16, generator=None):
|
||||||
|
accelerator = Accelerator(gradient_accumulation_steps=1, mixed_precision="fp16")
|
||||||
|
|
||||||
|
self.vae.requires_grad_(False)
|
||||||
|
self.text_encoder.requires_grad_(False)
|
||||||
|
self.unet.requires_grad_(False)
|
||||||
|
|
||||||
|
unet_lora_attn_procs = {}
|
||||||
|
for name, attn_processor in self.unet.attn_processors.items():
|
||||||
|
cross_attention_dim = None if name.endswith("attn1.processor") else self.unet.config.cross_attention_dim
|
||||||
|
if name.startswith("mid_block"):
|
||||||
|
hidden_size = self.unet.config.block_out_channels[-1]
|
||||||
|
elif name.startswith("up_blocks"):
|
||||||
|
block_id = int(name[len("up_blocks.")])
|
||||||
|
hidden_size = list(reversed(self.unet.config.block_out_channels))[block_id]
|
||||||
|
elif name.startswith("down_blocks"):
|
||||||
|
block_id = int(name[len("down_blocks.")])
|
||||||
|
hidden_size = self.unet.config.block_out_channels[block_id]
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("name must start with up_blocks, mid_blocks, or down_blocks")
|
||||||
|
|
||||||
|
if isinstance(attn_processor, (AttnAddedKVProcessor, SlicedAttnAddedKVProcessor, AttnAddedKVProcessor2_0)):
|
||||||
|
lora_attn_processor_class = LoRAAttnAddedKVProcessor
|
||||||
|
else:
|
||||||
|
lora_attn_processor_class = (
|
||||||
|
LoRAAttnProcessor2_0
|
||||||
|
if hasattr(torch.nn.functional, "scaled_dot_product_attention")
|
||||||
|
else LoRAAttnProcessor
|
||||||
|
)
|
||||||
|
unet_lora_attn_procs[name] = lora_attn_processor_class(
|
||||||
|
hidden_size=hidden_size, cross_attention_dim=cross_attention_dim, rank=lora_rank
|
||||||
|
)
|
||||||
|
|
||||||
|
self.unet.set_attn_processor(unet_lora_attn_procs)
|
||||||
|
unet_lora_layers = AttnProcsLayers(self.unet.attn_processors)
|
||||||
|
params_to_optimize = unet_lora_layers.parameters()
|
||||||
|
|
||||||
|
optimizer = torch.optim.AdamW(
|
||||||
|
params_to_optimize,
|
||||||
|
lr=2e-4,
|
||||||
|
betas=(0.9, 0.999),
|
||||||
|
weight_decay=1e-2,
|
||||||
|
eps=1e-08,
|
||||||
|
)
|
||||||
|
|
||||||
|
lr_scheduler = get_scheduler(
|
||||||
|
"constant",
|
||||||
|
optimizer=optimizer,
|
||||||
|
num_warmup_steps=0,
|
||||||
|
num_training_steps=lora_step,
|
||||||
|
num_cycles=1,
|
||||||
|
power=1.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
unet_lora_layers = accelerator.prepare_model(unet_lora_layers)
|
||||||
|
optimizer = accelerator.prepare_optimizer(optimizer)
|
||||||
|
lr_scheduler = accelerator.prepare_scheduler(lr_scheduler)
|
||||||
|
|
||||||
|
with torch.no_grad():
|
||||||
|
text_inputs = self._tokenize_prompt(prompt, tokenizer_max_length=None)
|
||||||
|
text_embedding = self._encode_prompt(
|
||||||
|
text_inputs.input_ids, text_inputs.attention_mask, text_encoder_use_attention_mask=False
|
||||||
|
)
|
||||||
|
|
||||||
|
image_transforms = transforms.Compose(
|
||||||
|
[
|
||||||
|
transforms.ToTensor(),
|
||||||
|
transforms.Normalize([0.5], [0.5]),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
image = image_transforms(image).to(self.device, dtype=self.vae.dtype)
|
||||||
|
image = image.unsqueeze(dim=0)
|
||||||
|
latents_dist = self.vae.encode(image).latent_dist
|
||||||
|
|
||||||
|
for _ in tqdm(range(lora_step), desc="Train LoRA"):
|
||||||
|
self.unet.train()
|
||||||
|
model_input = latents_dist.sample() * self.vae.config.scaling_factor
|
||||||
|
|
||||||
|
# Sample noise that we'll add to the latents
|
||||||
|
noise = torch.randn(
|
||||||
|
model_input.size(),
|
||||||
|
dtype=model_input.dtype,
|
||||||
|
layout=model_input.layout,
|
||||||
|
device=model_input.device,
|
||||||
|
generator=generator,
|
||||||
|
)
|
||||||
|
bsz, channels, height, width = model_input.shape
|
||||||
|
|
||||||
|
# Sample a random timestep for each image
|
||||||
|
timesteps = torch.randint(
|
||||||
|
0, self.scheduler.config.num_train_timesteps, (bsz,), device=model_input.device, generator=generator
|
||||||
|
)
|
||||||
|
timesteps = timesteps.long()
|
||||||
|
|
||||||
|
# Add noise to the model input according to the noise magnitude at each timestep
|
||||||
|
# (this is the forward diffusion process)
|
||||||
|
noisy_model_input = self.scheduler.add_noise(model_input, noise, timesteps)
|
||||||
|
|
||||||
|
# Predict the noise residual
|
||||||
|
model_pred = self.unet(noisy_model_input, timesteps, text_embedding).sample
|
||||||
|
|
||||||
|
# Get the target for loss depending on the prediction type
|
||||||
|
if self.scheduler.config.prediction_type == "epsilon":
|
||||||
|
target = noise
|
||||||
|
elif self.scheduler.config.prediction_type == "v_prediction":
|
||||||
|
target = self.scheduler.get_velocity(model_input, noise, timesteps)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unknown prediction type {self.scheduler.config.prediction_type}")
|
||||||
|
|
||||||
|
loss = torch.nn.functional.mse_loss(model_pred.float(), target.float(), reduction="mean")
|
||||||
|
accelerator.backward(loss)
|
||||||
|
optimizer.step()
|
||||||
|
lr_scheduler.step()
|
||||||
|
optimizer.zero_grad()
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as save_lora_dir:
|
||||||
|
LoraLoaderMixin.save_lora_weights(
|
||||||
|
save_directory=save_lora_dir,
|
||||||
|
unet_lora_layers=unet_lora_layers,
|
||||||
|
text_encoder_lora_layers=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.unet.load_attn_procs(save_lora_dir)
|
||||||
|
|
||||||
|
def _tokenize_prompt(self, prompt, tokenizer_max_length=None):
|
||||||
|
if tokenizer_max_length is not None:
|
||||||
|
max_length = tokenizer_max_length
|
||||||
|
else:
|
||||||
|
max_length = self.tokenizer.model_max_length
|
||||||
|
|
||||||
|
text_inputs = self.tokenizer(
|
||||||
|
prompt,
|
||||||
|
truncation=True,
|
||||||
|
padding="max_length",
|
||||||
|
max_length=max_length,
|
||||||
|
return_tensors="pt",
|
||||||
|
)
|
||||||
|
|
||||||
|
return text_inputs
|
||||||
|
|
||||||
|
def _encode_prompt(self, input_ids, attention_mask, text_encoder_use_attention_mask=False):
|
||||||
|
text_input_ids = input_ids.to(self.device)
|
||||||
|
|
||||||
|
if text_encoder_use_attention_mask:
|
||||||
|
attention_mask = attention_mask.to(self.device)
|
||||||
|
else:
|
||||||
|
attention_mask = None
|
||||||
|
|
||||||
|
prompt_embeds = self.text_encoder(
|
||||||
|
text_input_ids,
|
||||||
|
attention_mask=attention_mask,
|
||||||
|
)
|
||||||
|
prompt_embeds = prompt_embeds[0]
|
||||||
|
|
||||||
|
return prompt_embeds
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
|
def _get_text_embed(self, prompt):
|
||||||
|
text_input = self.tokenizer(
|
||||||
|
prompt,
|
||||||
|
padding="max_length",
|
||||||
|
max_length=self.tokenizer.model_max_length,
|
||||||
|
truncation=True,
|
||||||
|
return_tensors="pt",
|
||||||
|
)
|
||||||
|
text_embeddings = self.text_encoder(text_input.input_ids.to(self.device))[0]
|
||||||
|
return text_embeddings
|
||||||
|
|
||||||
|
def _copy_and_paste(
|
||||||
|
self, latent, source_new, target_new, adapt_radius, max_height, max_width, image_scale, noise_scale, generator
|
||||||
|
):
|
||||||
|
def adaption_r(source, target, adapt_radius, max_height, max_width):
|
||||||
|
r_x_lower = min(adapt_radius, source[0], target[0])
|
||||||
|
r_x_upper = min(adapt_radius, max_width - source[0], max_width - target[0])
|
||||||
|
r_y_lower = min(adapt_radius, source[1], target[1])
|
||||||
|
r_y_upper = min(adapt_radius, max_height - source[1], max_height - target[1])
|
||||||
|
return r_x_lower, r_x_upper, r_y_lower, r_y_upper
|
||||||
|
|
||||||
|
for source_, target_ in zip(source_new, target_new):
|
||||||
|
r_x_lower, r_x_upper, r_y_lower, r_y_upper = adaption_r(
|
||||||
|
source_, target_, adapt_radius, max_height, max_width
|
||||||
|
)
|
||||||
|
|
||||||
|
source_feature = latent[
|
||||||
|
:, :, source_[1] - r_y_lower : source_[1] + r_y_upper, source_[0] - r_x_lower : source_[0] + r_x_upper
|
||||||
|
].clone()
|
||||||
|
|
||||||
|
latent[
|
||||||
|
:, :, source_[1] - r_y_lower : source_[1] + r_y_upper, source_[0] - r_x_lower : source_[0] + r_x_upper
|
||||||
|
] = image_scale * source_feature + noise_scale * torch.randn(
|
||||||
|
latent.shape[0],
|
||||||
|
4,
|
||||||
|
r_y_lower + r_y_upper,
|
||||||
|
r_x_lower + r_x_upper,
|
||||||
|
device=self.device,
|
||||||
|
generator=generator,
|
||||||
|
)
|
||||||
|
|
||||||
|
latent[
|
||||||
|
:, :, target_[1] - r_y_lower : target_[1] + r_y_upper, target_[0] - r_x_lower : target_[0] + r_x_upper
|
||||||
|
] = source_feature * 1.1
|
||||||
|
return latent
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
|
def _get_img_latent(self, image, height=None, weight=None):
|
||||||
|
data = image.convert("RGB")
|
||||||
|
if height is not None:
|
||||||
|
data = data.resize((weight, height))
|
||||||
|
transform = transforms.ToTensor()
|
||||||
|
data = transform(data).unsqueeze(0)
|
||||||
|
data = (data * 2.0) - 1.0
|
||||||
|
data = data.to(self.device, dtype=self.vae.dtype)
|
||||||
|
latent = self.vae.encode(data).latent_dist.sample()
|
||||||
|
latent = 0.18215 * latent
|
||||||
|
return latent
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
|
def _get_eps(self, latent, timestep, guidance_scale, text_embeddings, lora_scale=None):
|
||||||
|
latent_model_input = torch.cat([latent] * 2) if guidance_scale > 1.0 else latent
|
||||||
|
text_embeddings = text_embeddings if guidance_scale > 1.0 else text_embeddings.chunk(2)[1]
|
||||||
|
|
||||||
|
cross_attention_kwargs = None if lora_scale is None else {"scale": lora_scale}
|
||||||
|
|
||||||
|
with torch.no_grad():
|
||||||
|
noise_pred = self.unet(
|
||||||
|
latent_model_input,
|
||||||
|
timestep,
|
||||||
|
encoder_hidden_states=text_embeddings,
|
||||||
|
cross_attention_kwargs=cross_attention_kwargs,
|
||||||
|
).sample
|
||||||
|
|
||||||
|
if guidance_scale > 1.0:
|
||||||
|
noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
|
||||||
|
elif guidance_scale == 1.0:
|
||||||
|
noise_pred_text = noise_pred
|
||||||
|
noise_pred_uncond = 0.0
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(guidance_scale)
|
||||||
|
noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)
|
||||||
|
|
||||||
|
return noise_pred
|
||||||
|
|
||||||
|
def _forward_sde(
|
||||||
|
self, timestep, sample, guidance_scale, text_embeddings, steps, eta=1.0, lora_scale=None, generator=None
|
||||||
|
):
|
||||||
|
num_train_timesteps = len(self.scheduler)
|
||||||
|
alphas_cumprod = self.scheduler.alphas_cumprod
|
||||||
|
initial_alpha_cumprod = torch.tensor(1.0)
|
||||||
|
|
||||||
|
prev_timestep = timestep + num_train_timesteps // steps
|
||||||
|
|
||||||
|
alpha_prod_t = alphas_cumprod[timestep] if timestep >= 0 else initial_alpha_cumprod
|
||||||
|
alpha_prod_t_prev = alphas_cumprod[prev_timestep]
|
||||||
|
|
||||||
|
beta_prod_t_prev = 1 - alpha_prod_t_prev
|
||||||
|
|
||||||
|
x_prev = (alpha_prod_t_prev / alpha_prod_t) ** (0.5) * sample + (1 - alpha_prod_t_prev / alpha_prod_t) ** (
|
||||||
|
0.5
|
||||||
|
) * torch.randn(
|
||||||
|
sample.size(), dtype=sample.dtype, layout=sample.layout, device=self.device, generator=generator
|
||||||
|
)
|
||||||
|
eps = self._get_eps(x_prev, prev_timestep, guidance_scale, text_embeddings, lora_scale)
|
||||||
|
|
||||||
|
sigma_t_prev = (
|
||||||
|
eta
|
||||||
|
* (1 - alpha_prod_t) ** (0.5)
|
||||||
|
* (1 - alpha_prod_t_prev / (1 - alpha_prod_t_prev) * (1 - alpha_prod_t) / alpha_prod_t) ** (0.5)
|
||||||
|
)
|
||||||
|
|
||||||
|
pred_original_sample = (x_prev - beta_prod_t_prev ** (0.5) * eps) / alpha_prod_t_prev ** (0.5)
|
||||||
|
pred_sample_direction_coeff = (1 - alpha_prod_t - sigma_t_prev**2) ** (0.5)
|
||||||
|
|
||||||
|
noise = (
|
||||||
|
sample - alpha_prod_t ** (0.5) * pred_original_sample - pred_sample_direction_coeff * eps
|
||||||
|
) / sigma_t_prev
|
||||||
|
|
||||||
|
return x_prev, noise
|
||||||
|
|
||||||
|
def _sample(
|
||||||
|
self,
|
||||||
|
timestep,
|
||||||
|
sample,
|
||||||
|
guidance_scale,
|
||||||
|
text_embeddings,
|
||||||
|
steps,
|
||||||
|
sde=False,
|
||||||
|
noise=None,
|
||||||
|
eta=1.0,
|
||||||
|
lora_scale=None,
|
||||||
|
generator=None,
|
||||||
|
):
|
||||||
|
num_train_timesteps = len(self.scheduler)
|
||||||
|
alphas_cumprod = self.scheduler.alphas_cumprod
|
||||||
|
final_alpha_cumprod = torch.tensor(1.0)
|
||||||
|
|
||||||
|
eps = self._get_eps(sample, timestep, guidance_scale, text_embeddings, lora_scale)
|
||||||
|
|
||||||
|
prev_timestep = timestep - num_train_timesteps // steps
|
||||||
|
|
||||||
|
alpha_prod_t = alphas_cumprod[timestep]
|
||||||
|
alpha_prod_t_prev = alphas_cumprod[prev_timestep] if prev_timestep >= 0 else final_alpha_cumprod
|
||||||
|
|
||||||
|
beta_prod_t = 1 - alpha_prod_t
|
||||||
|
|
||||||
|
sigma_t = (
|
||||||
|
eta
|
||||||
|
* ((1 - alpha_prod_t_prev) / (1 - alpha_prod_t)) ** (0.5)
|
||||||
|
* (1 - alpha_prod_t / alpha_prod_t_prev) ** (0.5)
|
||||||
|
if sde
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
|
||||||
|
pred_original_sample = (sample - beta_prod_t ** (0.5) * eps) / alpha_prod_t ** (0.5)
|
||||||
|
pred_sample_direction_coeff = (1 - alpha_prod_t_prev - sigma_t**2) ** (0.5)
|
||||||
|
|
||||||
|
noise = (
|
||||||
|
torch.randn(
|
||||||
|
sample.size(), dtype=sample.dtype, layout=sample.layout, device=self.device, generator=generator
|
||||||
|
)
|
||||||
|
if noise is None
|
||||||
|
else noise
|
||||||
|
)
|
||||||
|
latent = (
|
||||||
|
alpha_prod_t_prev ** (0.5) * pred_original_sample + pred_sample_direction_coeff * eps + sigma_t * noise
|
||||||
|
)
|
||||||
|
|
||||||
|
return latent
|
||||||
|
|
||||||
|
def _forward(self, latent, steps, t0, lora_scale_min, text_embeddings, generator):
|
||||||
|
def scale_schedule(begin, end, n, length, type="linear"):
|
||||||
|
if type == "constant":
|
||||||
|
return end
|
||||||
|
elif type == "linear":
|
||||||
|
return begin + (end - begin) * n / length
|
||||||
|
elif type == "cos":
|
||||||
|
factor = (1 - math.cos(n * math.pi / length)) / 2
|
||||||
|
return (1 - factor) * begin + factor * end
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(type)
|
||||||
|
|
||||||
|
noises = []
|
||||||
|
latents = []
|
||||||
|
lora_scales = []
|
||||||
|
cfg_scales = []
|
||||||
|
latents.append(latent)
|
||||||
|
t0 = int(t0 * steps)
|
||||||
|
t_begin = steps - t0
|
||||||
|
|
||||||
|
length = len(self.scheduler.timesteps[t_begin - 1 : -1]) - 1
|
||||||
|
index = 1
|
||||||
|
for t in self.scheduler.timesteps[t_begin:].flip(dims=[0]):
|
||||||
|
lora_scale = scale_schedule(1, lora_scale_min, index, length, type="cos")
|
||||||
|
cfg_scale = scale_schedule(1, 3.0, index, length, type="linear")
|
||||||
|
latent, noise = self._forward_sde(
|
||||||
|
t, latent, cfg_scale, text_embeddings, steps, lora_scale=lora_scale, generator=generator
|
||||||
|
)
|
||||||
|
|
||||||
|
noises.append(noise)
|
||||||
|
latents.append(latent)
|
||||||
|
lora_scales.append(lora_scale)
|
||||||
|
cfg_scales.append(cfg_scale)
|
||||||
|
index += 1
|
||||||
|
return latent, noises, latents, lora_scales, cfg_scales
|
||||||
|
|
||||||
|
def _backward(
|
||||||
|
self, latent, mask, steps, t0, noises, hook_latents, lora_scales, cfg_scales, text_embeddings, generator
|
||||||
|
):
|
||||||
|
t0 = int(t0 * steps)
|
||||||
|
t_begin = steps - t0
|
||||||
|
|
||||||
|
hook_latent = hook_latents.pop()
|
||||||
|
latent = torch.where(mask > 128, latent, hook_latent)
|
||||||
|
for t in self.scheduler.timesteps[t_begin - 1 : -1]:
|
||||||
|
latent = self._sample(
|
||||||
|
t,
|
||||||
|
latent,
|
||||||
|
cfg_scales.pop(),
|
||||||
|
text_embeddings,
|
||||||
|
steps,
|
||||||
|
sde=True,
|
||||||
|
noise=noises.pop(),
|
||||||
|
lora_scale=lora_scales.pop(),
|
||||||
|
generator=generator,
|
||||||
|
)
|
||||||
|
hook_latent = hook_latents.pop()
|
||||||
|
latent = torch.where(mask > 128, latent, hook_latent)
|
||||||
|
return latent
|
||||||
@@ -28,6 +28,7 @@ import PIL.Image
|
|||||||
import tensorrt as trt
|
import tensorrt as trt
|
||||||
import torch
|
import torch
|
||||||
from huggingface_hub import snapshot_download
|
from huggingface_hub import snapshot_download
|
||||||
|
from huggingface_hub.utils import validate_hf_hub_args
|
||||||
from onnx import shape_inference
|
from onnx import shape_inference
|
||||||
from polygraphy import cuda
|
from polygraphy import cuda
|
||||||
from polygraphy.backend.common import bytes_from_path
|
from polygraphy.backend.common import bytes_from_path
|
||||||
@@ -50,7 +51,7 @@ from diffusers.pipelines.stable_diffusion import (
|
|||||||
StableDiffusionSafetyChecker,
|
StableDiffusionSafetyChecker,
|
||||||
)
|
)
|
||||||
from diffusers.schedulers import DDIMScheduler
|
from diffusers.schedulers import DDIMScheduler
|
||||||
from diffusers.utils import DIFFUSERS_CACHE, logging
|
from diffusers.utils import logging
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -778,12 +779,13 @@ class TensorRTStableDiffusionImg2ImgPipeline(StableDiffusionImg2ImgPipeline):
|
|||||||
self.models["vae_encoder"] = make_VAEEncoder(self.vae, **models_args)
|
self.models["vae_encoder"] = make_VAEEncoder(self.vae, **models_args)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@validate_hf_hub_args
|
||||||
def set_cached_folder(cls, pretrained_model_name_or_path: Optional[Union[str, os.PathLike]], **kwargs):
|
def set_cached_folder(cls, pretrained_model_name_or_path: Optional[Union[str, os.PathLike]], **kwargs):
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", False)
|
local_files_only = kwargs.pop("local_files_only", False)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
|
|
||||||
cls.cached_folder = (
|
cls.cached_folder = (
|
||||||
@@ -795,7 +797,7 @@ class TensorRTStableDiffusionImg2ImgPipeline(StableDiffusionImg2ImgPipeline):
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import PIL.Image
|
|||||||
import tensorrt as trt
|
import tensorrt as trt
|
||||||
import torch
|
import torch
|
||||||
from huggingface_hub import snapshot_download
|
from huggingface_hub import snapshot_download
|
||||||
|
from huggingface_hub.utils import validate_hf_hub_args
|
||||||
from onnx import shape_inference
|
from onnx import shape_inference
|
||||||
from polygraphy import cuda
|
from polygraphy import cuda
|
||||||
from polygraphy.backend.common import bytes_from_path
|
from polygraphy.backend.common import bytes_from_path
|
||||||
@@ -51,7 +52,7 @@ from diffusers.pipelines.stable_diffusion import (
|
|||||||
)
|
)
|
||||||
from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_inpaint import prepare_mask_and_masked_image
|
from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_inpaint import prepare_mask_and_masked_image
|
||||||
from diffusers.schedulers import DDIMScheduler
|
from diffusers.schedulers import DDIMScheduler
|
||||||
from diffusers.utils import DIFFUSERS_CACHE, logging
|
from diffusers.utils import logging
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -779,12 +780,13 @@ class TensorRTStableDiffusionInpaintPipeline(StableDiffusionInpaintPipeline):
|
|||||||
self.models["vae_encoder"] = make_VAEEncoder(self.vae, **models_args)
|
self.models["vae_encoder"] = make_VAEEncoder(self.vae, **models_args)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@validate_hf_hub_args
|
||||||
def set_cached_folder(cls, pretrained_model_name_or_path: Optional[Union[str, os.PathLike]], **kwargs):
|
def set_cached_folder(cls, pretrained_model_name_or_path: Optional[Union[str, os.PathLike]], **kwargs):
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", False)
|
local_files_only = kwargs.pop("local_files_only", False)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
|
|
||||||
cls.cached_folder = (
|
cls.cached_folder = (
|
||||||
@@ -796,7 +798,7 @@ class TensorRTStableDiffusionInpaintPipeline(StableDiffusionInpaintPipeline):
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import onnx_graphsurgeon as gs
|
|||||||
import tensorrt as trt
|
import tensorrt as trt
|
||||||
import torch
|
import torch
|
||||||
from huggingface_hub import snapshot_download
|
from huggingface_hub import snapshot_download
|
||||||
|
from huggingface_hub.utils import validate_hf_hub_args
|
||||||
from onnx import shape_inference
|
from onnx import shape_inference
|
||||||
from polygraphy import cuda
|
from polygraphy import cuda
|
||||||
from polygraphy.backend.common import bytes_from_path
|
from polygraphy.backend.common import bytes_from_path
|
||||||
@@ -49,7 +50,7 @@ from diffusers.pipelines.stable_diffusion import (
|
|||||||
StableDiffusionSafetyChecker,
|
StableDiffusionSafetyChecker,
|
||||||
)
|
)
|
||||||
from diffusers.schedulers import DDIMScheduler
|
from diffusers.schedulers import DDIMScheduler
|
||||||
from diffusers.utils import DIFFUSERS_CACHE, logging
|
from diffusers.utils import logging
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -691,12 +692,13 @@ class TensorRTStableDiffusionPipeline(StableDiffusionPipeline):
|
|||||||
self.models["vae"] = make_VAE(self.vae, **models_args)
|
self.models["vae"] = make_VAE(self.vae, **models_args)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@validate_hf_hub_args
|
||||||
def set_cached_folder(cls, pretrained_model_name_or_path: Optional[Union[str, os.PathLike]], **kwargs):
|
def set_cached_folder(cls, pretrained_model_name_or_path: Optional[Union[str, os.PathLike]], **kwargs):
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", False)
|
local_files_only = kwargs.pop("local_files_only", False)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
|
|
||||||
cls.cached_folder = (
|
cls.cached_folder = (
|
||||||
@@ -708,7 +710,7 @@ class TensorRTStableDiffusionPipeline(StableDiffusionPipeline):
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ class WebdatasetFilter:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Text2ImageDataset:
|
class SDText2ImageDataset:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
train_shards_path_or_url: Union[str, List[str]],
|
train_shards_path_or_url: Union[str, List[str]],
|
||||||
@@ -359,19 +359,43 @@ def scalings_for_boundary_conditions(timestep, sigma_data=0.5, timestep_scaling=
|
|||||||
|
|
||||||
|
|
||||||
# Compare LCMScheduler.step, Step 4
|
# Compare LCMScheduler.step, Step 4
|
||||||
def predicted_origin(model_output, timesteps, sample, prediction_type, alphas, sigmas):
|
def get_predicted_original_sample(model_output, timesteps, sample, prediction_type, alphas, sigmas):
|
||||||
|
alphas = extract_into_tensor(alphas, timesteps, sample.shape)
|
||||||
|
sigmas = extract_into_tensor(sigmas, timesteps, sample.shape)
|
||||||
if prediction_type == "epsilon":
|
if prediction_type == "epsilon":
|
||||||
sigmas = extract_into_tensor(sigmas, timesteps, sample.shape)
|
|
||||||
alphas = extract_into_tensor(alphas, timesteps, sample.shape)
|
|
||||||
pred_x_0 = (sample - sigmas * model_output) / alphas
|
pred_x_0 = (sample - sigmas * model_output) / alphas
|
||||||
|
elif prediction_type == "sample":
|
||||||
|
pred_x_0 = model_output
|
||||||
elif prediction_type == "v_prediction":
|
elif prediction_type == "v_prediction":
|
||||||
pred_x_0 = alphas[timesteps] * sample - sigmas[timesteps] * model_output
|
pred_x_0 = alphas * sample - sigmas * model_output
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Prediction type {prediction_type} currently not supported.")
|
raise ValueError(
|
||||||
|
f"Prediction type {prediction_type} is not supported; currently, `epsilon`, `sample`, and `v_prediction`"
|
||||||
|
f" are supported."
|
||||||
|
)
|
||||||
|
|
||||||
return pred_x_0
|
return pred_x_0
|
||||||
|
|
||||||
|
|
||||||
|
# Based on step 4 in DDIMScheduler.step
|
||||||
|
def get_predicted_noise(model_output, timesteps, sample, prediction_type, alphas, sigmas):
|
||||||
|
alphas = extract_into_tensor(alphas, timesteps, sample.shape)
|
||||||
|
sigmas = extract_into_tensor(sigmas, timesteps, sample.shape)
|
||||||
|
if prediction_type == "epsilon":
|
||||||
|
pred_epsilon = model_output
|
||||||
|
elif prediction_type == "sample":
|
||||||
|
pred_epsilon = (sample - alphas * model_output) / sigmas
|
||||||
|
elif prediction_type == "v_prediction":
|
||||||
|
pred_epsilon = alphas * model_output + sigmas * sample
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Prediction type {prediction_type} is not supported; currently, `epsilon`, `sample`, and `v_prediction`"
|
||||||
|
f" are supported."
|
||||||
|
)
|
||||||
|
|
||||||
|
return pred_epsilon
|
||||||
|
|
||||||
|
|
||||||
def extract_into_tensor(a, t, x_shape):
|
def extract_into_tensor(a, t, x_shape):
|
||||||
b, *_ = t.shape
|
b, *_ = t.shape
|
||||||
out = a.gather(-1, t)
|
out = a.gather(-1, t)
|
||||||
@@ -423,7 +447,7 @@ 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"
|
||||||
):
|
):
|
||||||
text_encoder_config = PretrainedConfig.from_pretrained(
|
text_encoder_config = PretrainedConfig.from_pretrained(
|
||||||
pretrained_model_name_or_path, subfolder=subfolder, revision=revision, use_auth_token=True
|
pretrained_model_name_or_path, subfolder=subfolder, revision=revision
|
||||||
)
|
)
|
||||||
model_class = text_encoder_config.architectures[0]
|
model_class = text_encoder_config.architectures[0]
|
||||||
|
|
||||||
@@ -835,34 +859,35 @@ def main(args):
|
|||||||
args.pretrained_teacher_model, subfolder="scheduler", revision=args.teacher_revision
|
args.pretrained_teacher_model, subfolder="scheduler", revision=args.teacher_revision
|
||||||
)
|
)
|
||||||
|
|
||||||
# The scheduler calculates the alpha and sigma schedule for us
|
# DDPMScheduler calculates the alpha and sigma noise schedules (based on the alpha bars) for us
|
||||||
alpha_schedule = torch.sqrt(noise_scheduler.alphas_cumprod)
|
alpha_schedule = torch.sqrt(noise_scheduler.alphas_cumprod)
|
||||||
sigma_schedule = torch.sqrt(1 - noise_scheduler.alphas_cumprod)
|
sigma_schedule = torch.sqrt(1 - noise_scheduler.alphas_cumprod)
|
||||||
|
# Initialize the DDIM ODE solver for distillation.
|
||||||
solver = DDIMSolver(
|
solver = DDIMSolver(
|
||||||
noise_scheduler.alphas_cumprod.numpy(),
|
noise_scheduler.alphas_cumprod.numpy(),
|
||||||
timesteps=noise_scheduler.config.num_train_timesteps,
|
timesteps=noise_scheduler.config.num_train_timesteps,
|
||||||
ddim_timesteps=args.num_ddim_timesteps,
|
ddim_timesteps=args.num_ddim_timesteps,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2. Load tokenizers from SD-XL checkpoint.
|
# 2. Load tokenizers from SD 1.X/2.X checkpoint.
|
||||||
tokenizer = AutoTokenizer.from_pretrained(
|
tokenizer = AutoTokenizer.from_pretrained(
|
||||||
args.pretrained_teacher_model, subfolder="tokenizer", revision=args.teacher_revision, use_fast=False
|
args.pretrained_teacher_model, subfolder="tokenizer", revision=args.teacher_revision, use_fast=False
|
||||||
)
|
)
|
||||||
|
|
||||||
# 3. Load text encoders from SD-1.5 checkpoint.
|
# 3. Load text encoders from SD 1.X/2.X checkpoint.
|
||||||
# import correct text encoder classes
|
# import correct text encoder classes
|
||||||
text_encoder = CLIPTextModel.from_pretrained(
|
text_encoder = CLIPTextModel.from_pretrained(
|
||||||
args.pretrained_teacher_model, subfolder="text_encoder", revision=args.teacher_revision
|
args.pretrained_teacher_model, subfolder="text_encoder", revision=args.teacher_revision
|
||||||
)
|
)
|
||||||
|
|
||||||
# 4. Load VAE from SD-XL checkpoint (or more stable VAE)
|
# 4. Load VAE from SD 1.X/2.X checkpoint
|
||||||
vae = AutoencoderKL.from_pretrained(
|
vae = AutoencoderKL.from_pretrained(
|
||||||
args.pretrained_teacher_model,
|
args.pretrained_teacher_model,
|
||||||
subfolder="vae",
|
subfolder="vae",
|
||||||
revision=args.teacher_revision,
|
revision=args.teacher_revision,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 5. Load teacher U-Net from SD-XL checkpoint
|
# 5. Load teacher U-Net from SD 1.X/2.X checkpoint
|
||||||
teacher_unet = UNet2DConditionModel.from_pretrained(
|
teacher_unet = UNet2DConditionModel.from_pretrained(
|
||||||
args.pretrained_teacher_model, subfolder="unet", revision=args.teacher_revision
|
args.pretrained_teacher_model, subfolder="unet", revision=args.teacher_revision
|
||||||
)
|
)
|
||||||
@@ -872,7 +897,7 @@ def main(args):
|
|||||||
text_encoder.requires_grad_(False)
|
text_encoder.requires_grad_(False)
|
||||||
teacher_unet.requires_grad_(False)
|
teacher_unet.requires_grad_(False)
|
||||||
|
|
||||||
# 7. Create online (`unet`) student U-Nets.
|
# 7. Create online student U-Net.
|
||||||
unet = UNet2DConditionModel.from_pretrained(
|
unet = UNet2DConditionModel.from_pretrained(
|
||||||
args.pretrained_teacher_model, subfolder="unet", revision=args.teacher_revision
|
args.pretrained_teacher_model, subfolder="unet", revision=args.teacher_revision
|
||||||
)
|
)
|
||||||
@@ -935,6 +960,7 @@ def main(args):
|
|||||||
# Also move the alpha and sigma noise schedules to accelerator.device.
|
# Also move the alpha and sigma noise schedules to accelerator.device.
|
||||||
alpha_schedule = alpha_schedule.to(accelerator.device)
|
alpha_schedule = alpha_schedule.to(accelerator.device)
|
||||||
sigma_schedule = sigma_schedule.to(accelerator.device)
|
sigma_schedule = sigma_schedule.to(accelerator.device)
|
||||||
|
# Move the ODE solver to accelerator.device.
|
||||||
solver = solver.to(accelerator.device)
|
solver = solver.to(accelerator.device)
|
||||||
|
|
||||||
# 10. Handle saving and loading of checkpoints
|
# 10. Handle saving and loading of checkpoints
|
||||||
@@ -1011,13 +1037,14 @@ def main(args):
|
|||||||
eps=args.adam_epsilon,
|
eps=args.adam_epsilon,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 13. Dataset creation and data processing
|
||||||
# Here, we compute not just the text embeddings but also the additional embeddings
|
# Here, we compute not just the text embeddings but also the additional embeddings
|
||||||
# needed for the SD XL UNet to operate.
|
# needed for the SD XL UNet to operate.
|
||||||
def compute_embeddings(prompt_batch, proportion_empty_prompts, text_encoder, tokenizer, is_train=True):
|
def compute_embeddings(prompt_batch, proportion_empty_prompts, text_encoder, tokenizer, is_train=True):
|
||||||
prompt_embeds = encode_prompt(prompt_batch, text_encoder, tokenizer, proportion_empty_prompts, is_train)
|
prompt_embeds = encode_prompt(prompt_batch, text_encoder, tokenizer, proportion_empty_prompts, is_train)
|
||||||
return {"prompt_embeds": prompt_embeds}
|
return {"prompt_embeds": prompt_embeds}
|
||||||
|
|
||||||
dataset = Text2ImageDataset(
|
dataset = SDText2ImageDataset(
|
||||||
train_shards_path_or_url=args.train_shards_path_or_url,
|
train_shards_path_or_url=args.train_shards_path_or_url,
|
||||||
num_train_examples=args.max_train_samples,
|
num_train_examples=args.max_train_samples,
|
||||||
per_gpu_batch_size=args.train_batch_size,
|
per_gpu_batch_size=args.train_batch_size,
|
||||||
@@ -1037,6 +1064,7 @@ def main(args):
|
|||||||
tokenizer=tokenizer,
|
tokenizer=tokenizer,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 14. 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
|
overrode_max_train_steps = False
|
||||||
num_update_steps_per_epoch = math.ceil(train_dataloader.num_batches / args.gradient_accumulation_steps)
|
num_update_steps_per_epoch = math.ceil(train_dataloader.num_batches / args.gradient_accumulation_steps)
|
||||||
@@ -1051,6 +1079,7 @@ def main(args):
|
|||||||
num_training_steps=args.max_train_steps,
|
num_training_steps=args.max_train_steps,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 15. Prepare for training
|
||||||
# Prepare everything with our `accelerator`.
|
# Prepare everything with our `accelerator`.
|
||||||
unet, optimizer, lr_scheduler = accelerator.prepare(unet, optimizer, lr_scheduler)
|
unet, optimizer, lr_scheduler = accelerator.prepare(unet, optimizer, lr_scheduler)
|
||||||
|
|
||||||
@@ -1072,7 +1101,7 @@ def main(args):
|
|||||||
).input_ids.to(accelerator.device)
|
).input_ids.to(accelerator.device)
|
||||||
uncond_prompt_embeds = text_encoder(uncond_input_ids)[0]
|
uncond_prompt_embeds = text_encoder(uncond_input_ids)[0]
|
||||||
|
|
||||||
# Train!
|
# 16. Train!
|
||||||
total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps
|
total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps
|
||||||
|
|
||||||
logger.info("***** Running training *****")
|
logger.info("***** Running training *****")
|
||||||
@@ -1123,6 +1152,7 @@ def main(args):
|
|||||||
for epoch in range(first_epoch, args.num_train_epochs):
|
for epoch in range(first_epoch, args.num_train_epochs):
|
||||||
for step, batch in enumerate(train_dataloader):
|
for step, batch in enumerate(train_dataloader):
|
||||||
with accelerator.accumulate(unet):
|
with accelerator.accumulate(unet):
|
||||||
|
# 1. Load and process the image and text conditioning
|
||||||
image, text = batch
|
image, text = batch
|
||||||
|
|
||||||
image = image.to(accelerator.device, non_blocking=True)
|
image = image.to(accelerator.device, non_blocking=True)
|
||||||
@@ -1140,37 +1170,37 @@ def main(args):
|
|||||||
|
|
||||||
latents = latents * vae.config.scaling_factor
|
latents = latents * vae.config.scaling_factor
|
||||||
latents = latents.to(weight_dtype)
|
latents = latents.to(weight_dtype)
|
||||||
|
|
||||||
# Sample noise that we'll add to the latents
|
|
||||||
noise = torch.randn_like(latents)
|
|
||||||
bsz = latents.shape[0]
|
bsz = latents.shape[0]
|
||||||
|
|
||||||
# Sample a random timestep for each image t_n ~ U[0, N - k - 1] without bias.
|
# 2. Sample a random timestep for each image t_n from the ODE solver timesteps without bias.
|
||||||
|
# For the DDIM solver, the timestep schedule is [T - 1, T - k - 1, T - 2 * k - 1, ...]
|
||||||
topk = noise_scheduler.config.num_train_timesteps // args.num_ddim_timesteps
|
topk = noise_scheduler.config.num_train_timesteps // args.num_ddim_timesteps
|
||||||
index = torch.randint(0, args.num_ddim_timesteps, (bsz,), device=latents.device).long()
|
index = torch.randint(0, args.num_ddim_timesteps, (bsz,), device=latents.device).long()
|
||||||
start_timesteps = solver.ddim_timesteps[index]
|
start_timesteps = solver.ddim_timesteps[index]
|
||||||
timesteps = start_timesteps - topk
|
timesteps = start_timesteps - topk
|
||||||
timesteps = torch.where(timesteps < 0, torch.zeros_like(timesteps), timesteps)
|
timesteps = torch.where(timesteps < 0, torch.zeros_like(timesteps), timesteps)
|
||||||
|
|
||||||
# 20.4.4. Get boundary scalings for start_timesteps and (end) timesteps.
|
# 3. Get boundary scalings for start_timesteps and (end) timesteps.
|
||||||
c_skip_start, c_out_start = scalings_for_boundary_conditions(start_timesteps)
|
c_skip_start, c_out_start = scalings_for_boundary_conditions(start_timesteps)
|
||||||
c_skip_start, c_out_start = [append_dims(x, latents.ndim) for x in [c_skip_start, c_out_start]]
|
c_skip_start, c_out_start = [append_dims(x, latents.ndim) for x in [c_skip_start, c_out_start]]
|
||||||
c_skip, c_out = scalings_for_boundary_conditions(timesteps)
|
c_skip, c_out = scalings_for_boundary_conditions(timesteps)
|
||||||
c_skip, c_out = [append_dims(x, latents.ndim) for x in [c_skip, c_out]]
|
c_skip, c_out = [append_dims(x, latents.ndim) for x in [c_skip, c_out]]
|
||||||
|
|
||||||
# 20.4.5. Add noise to the latents according to the noise magnitude at each timestep
|
# 4. Sample noise from the prior and add it to the latents according to the noise magnitude at each
|
||||||
# (this is the forward diffusion process) [z_{t_{n + k}} in Algorithm 1]
|
# timestep (this is the forward diffusion process) [z_{t_{n + k}} in Algorithm 1]
|
||||||
|
noise = torch.randn_like(latents)
|
||||||
noisy_model_input = noise_scheduler.add_noise(latents, noise, start_timesteps)
|
noisy_model_input = noise_scheduler.add_noise(latents, noise, start_timesteps)
|
||||||
|
|
||||||
# 20.4.6. Sample a random guidance scale w from U[w_min, w_max] and embed it
|
# 5. Sample a random guidance scale w from U[w_min, w_max]
|
||||||
|
# Note that for LCM-LoRA distillation it is not necessary to use a guidance scale embedding
|
||||||
w = (args.w_max - args.w_min) * torch.rand((bsz,)) + args.w_min
|
w = (args.w_max - args.w_min) * torch.rand((bsz,)) + args.w_min
|
||||||
w = w.reshape(bsz, 1, 1, 1)
|
w = w.reshape(bsz, 1, 1, 1)
|
||||||
w = w.to(device=latents.device, dtype=latents.dtype)
|
w = w.to(device=latents.device, dtype=latents.dtype)
|
||||||
|
|
||||||
# 20.4.8. Prepare prompt embeds and unet_added_conditions
|
# 6. Prepare prompt embeds and unet_added_conditions
|
||||||
prompt_embeds = encoded_text.pop("prompt_embeds")
|
prompt_embeds = encoded_text.pop("prompt_embeds")
|
||||||
|
|
||||||
# 20.4.9. Get online LCM prediction on z_{t_{n + k}}, w, c, t_{n + k}
|
# 7. Get online LCM prediction on z_{t_{n + k}} (noisy_model_input), w, c, t_{n + k} (start_timesteps)
|
||||||
noise_pred = unet(
|
noise_pred = unet(
|
||||||
noisy_model_input,
|
noisy_model_input,
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
@@ -1179,7 +1209,7 @@ def main(args):
|
|||||||
added_cond_kwargs=encoded_text,
|
added_cond_kwargs=encoded_text,
|
||||||
).sample
|
).sample
|
||||||
|
|
||||||
pred_x_0 = predicted_origin(
|
pred_x_0 = get_predicted_original_sample(
|
||||||
noise_pred,
|
noise_pred,
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
noisy_model_input,
|
noisy_model_input,
|
||||||
@@ -1190,17 +1220,27 @@ def main(args):
|
|||||||
|
|
||||||
model_pred = c_skip_start * noisy_model_input + c_out_start * pred_x_0
|
model_pred = c_skip_start * noisy_model_input + c_out_start * pred_x_0
|
||||||
|
|
||||||
# 20.4.10. Use the ODE solver to predict the kth step in the augmented PF-ODE trajectory after
|
# 8. Compute the conditional and unconditional teacher model predictions to get CFG estimates of the
|
||||||
# noisy_latents with both the conditioning embedding c and unconditional embedding 0
|
# predicted noise eps_0 and predicted original sample x_0, then run the ODE solver using these
|
||||||
# Get teacher model prediction on noisy_latents and conditional embedding
|
# estimates to predict the data point in the augmented PF-ODE trajectory corresponding to the next ODE
|
||||||
|
# solver timestep.
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
with torch.autocast("cuda"):
|
with torch.autocast("cuda"):
|
||||||
|
# 1. Get teacher model prediction on noisy_model_input z_{t_{n + k}} and conditional embedding c
|
||||||
cond_teacher_output = teacher_unet(
|
cond_teacher_output = teacher_unet(
|
||||||
noisy_model_input.to(weight_dtype),
|
noisy_model_input.to(weight_dtype),
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
encoder_hidden_states=prompt_embeds.to(weight_dtype),
|
encoder_hidden_states=prompt_embeds.to(weight_dtype),
|
||||||
).sample
|
).sample
|
||||||
cond_pred_x0 = predicted_origin(
|
cond_pred_x0 = get_predicted_original_sample(
|
||||||
|
cond_teacher_output,
|
||||||
|
start_timesteps,
|
||||||
|
noisy_model_input,
|
||||||
|
noise_scheduler.config.prediction_type,
|
||||||
|
alpha_schedule,
|
||||||
|
sigma_schedule,
|
||||||
|
)
|
||||||
|
cond_pred_noise = get_predicted_noise(
|
||||||
cond_teacher_output,
|
cond_teacher_output,
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
noisy_model_input,
|
noisy_model_input,
|
||||||
@@ -1209,13 +1249,21 @@ def main(args):
|
|||||||
sigma_schedule,
|
sigma_schedule,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get teacher model prediction on noisy_latents and unconditional embedding
|
# 2. Get teacher model prediction on noisy_model_input z_{t_{n + k}} and unconditional embedding 0
|
||||||
uncond_teacher_output = teacher_unet(
|
uncond_teacher_output = teacher_unet(
|
||||||
noisy_model_input.to(weight_dtype),
|
noisy_model_input.to(weight_dtype),
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
encoder_hidden_states=uncond_prompt_embeds.to(weight_dtype),
|
encoder_hidden_states=uncond_prompt_embeds.to(weight_dtype),
|
||||||
).sample
|
).sample
|
||||||
uncond_pred_x0 = predicted_origin(
|
uncond_pred_x0 = get_predicted_original_sample(
|
||||||
|
uncond_teacher_output,
|
||||||
|
start_timesteps,
|
||||||
|
noisy_model_input,
|
||||||
|
noise_scheduler.config.prediction_type,
|
||||||
|
alpha_schedule,
|
||||||
|
sigma_schedule,
|
||||||
|
)
|
||||||
|
uncond_pred_noise = get_predicted_noise(
|
||||||
uncond_teacher_output,
|
uncond_teacher_output,
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
noisy_model_input,
|
noisy_model_input,
|
||||||
@@ -1224,12 +1272,17 @@ def main(args):
|
|||||||
sigma_schedule,
|
sigma_schedule,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 20.4.11. Perform "CFG" to get x_prev estimate (using the LCM paper's CFG formulation)
|
# 3. Calculate the CFG estimate of x_0 (pred_x0) and eps_0 (pred_noise)
|
||||||
|
# Note that this uses the LCM paper's CFG formulation rather than the Imagen CFG formulation
|
||||||
pred_x0 = cond_pred_x0 + w * (cond_pred_x0 - uncond_pred_x0)
|
pred_x0 = cond_pred_x0 + w * (cond_pred_x0 - uncond_pred_x0)
|
||||||
pred_noise = cond_teacher_output + w * (cond_teacher_output - uncond_teacher_output)
|
pred_noise = cond_pred_noise + w * (cond_pred_noise - uncond_pred_noise)
|
||||||
|
# 4. Run one step of the ODE solver to estimate the next point x_prev on the
|
||||||
|
# augmented PF-ODE trajectory (solving backward in time)
|
||||||
|
# Note that the DDIM step depends on both the predicted x_0 and source noise eps_0.
|
||||||
x_prev = solver.ddim_step(pred_x0, pred_noise, index)
|
x_prev = solver.ddim_step(pred_x0, pred_noise, index)
|
||||||
|
|
||||||
# 20.4.12. Get target LCM prediction on x_prev, w, c, t_n
|
# 9. Get target LCM prediction on x_prev, w, c, t_n (timesteps)
|
||||||
|
# Note that we do not use a separate target network for LCM-LoRA distillation.
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
with torch.autocast("cuda", dtype=weight_dtype):
|
with torch.autocast("cuda", dtype=weight_dtype):
|
||||||
target_noise_pred = unet(
|
target_noise_pred = unet(
|
||||||
@@ -1238,7 +1291,7 @@ def main(args):
|
|||||||
timestep_cond=None,
|
timestep_cond=None,
|
||||||
encoder_hidden_states=prompt_embeds.float(),
|
encoder_hidden_states=prompt_embeds.float(),
|
||||||
).sample
|
).sample
|
||||||
pred_x_0 = predicted_origin(
|
pred_x_0 = get_predicted_original_sample(
|
||||||
target_noise_pred,
|
target_noise_pred,
|
||||||
timesteps,
|
timesteps,
|
||||||
x_prev,
|
x_prev,
|
||||||
@@ -1248,7 +1301,7 @@ def main(args):
|
|||||||
)
|
)
|
||||||
target = c_skip * x_prev + c_out * pred_x_0
|
target = c_skip * x_prev + c_out * pred_x_0
|
||||||
|
|
||||||
# 20.4.13. Calculate loss
|
# 10. Calculate loss
|
||||||
if args.loss_type == "l2":
|
if args.loss_type == "l2":
|
||||||
loss = F.mse_loss(model_pred.float(), target.float(), reduction="mean")
|
loss = F.mse_loss(model_pred.float(), target.float(), reduction="mean")
|
||||||
elif args.loss_type == "huber":
|
elif args.loss_type == "huber":
|
||||||
@@ -1256,7 +1309,7 @@ def main(args):
|
|||||||
torch.sqrt((model_pred.float() - target.float()) ** 2 + args.huber_c**2) - args.huber_c
|
torch.sqrt((model_pred.float() - target.float()) ** 2 + args.huber_c**2) - args.huber_c
|
||||||
)
|
)
|
||||||
|
|
||||||
# 20.4.14. Backpropagate on the online student model (`unet`)
|
# 11. Backpropagate on the online student model (`unet`)
|
||||||
accelerator.backward(loss)
|
accelerator.backward(loss)
|
||||||
if accelerator.sync_gradients:
|
if accelerator.sync_gradients:
|
||||||
accelerator.clip_grad_norm_(unet.parameters(), args.max_grad_norm)
|
accelerator.clip_grad_norm_(unet.parameters(), args.max_grad_norm)
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ class WebdatasetFilter:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Text2ImageDataset:
|
class SDXLText2ImageDataset:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
train_shards_path_or_url: Union[str, List[str]],
|
train_shards_path_or_url: Union[str, List[str]],
|
||||||
@@ -346,19 +346,43 @@ def scalings_for_boundary_conditions(timestep, sigma_data=0.5, timestep_scaling=
|
|||||||
|
|
||||||
|
|
||||||
# Compare LCMScheduler.step, Step 4
|
# Compare LCMScheduler.step, Step 4
|
||||||
def predicted_origin(model_output, timesteps, sample, prediction_type, alphas, sigmas):
|
def get_predicted_original_sample(model_output, timesteps, sample, prediction_type, alphas, sigmas):
|
||||||
|
alphas = extract_into_tensor(alphas, timesteps, sample.shape)
|
||||||
|
sigmas = extract_into_tensor(sigmas, timesteps, sample.shape)
|
||||||
if prediction_type == "epsilon":
|
if prediction_type == "epsilon":
|
||||||
sigmas = extract_into_tensor(sigmas, timesteps, sample.shape)
|
|
||||||
alphas = extract_into_tensor(alphas, timesteps, sample.shape)
|
|
||||||
pred_x_0 = (sample - sigmas * model_output) / alphas
|
pred_x_0 = (sample - sigmas * model_output) / alphas
|
||||||
|
elif prediction_type == "sample":
|
||||||
|
pred_x_0 = model_output
|
||||||
elif prediction_type == "v_prediction":
|
elif prediction_type == "v_prediction":
|
||||||
pred_x_0 = alphas[timesteps] * sample - sigmas[timesteps] * model_output
|
pred_x_0 = alphas * sample - sigmas * model_output
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Prediction type {prediction_type} currently not supported.")
|
raise ValueError(
|
||||||
|
f"Prediction type {prediction_type} is not supported; currently, `epsilon`, `sample`, and `v_prediction`"
|
||||||
|
f" are supported."
|
||||||
|
)
|
||||||
|
|
||||||
return pred_x_0
|
return pred_x_0
|
||||||
|
|
||||||
|
|
||||||
|
# Based on step 4 in DDIMScheduler.step
|
||||||
|
def get_predicted_noise(model_output, timesteps, sample, prediction_type, alphas, sigmas):
|
||||||
|
alphas = extract_into_tensor(alphas, timesteps, sample.shape)
|
||||||
|
sigmas = extract_into_tensor(sigmas, timesteps, sample.shape)
|
||||||
|
if prediction_type == "epsilon":
|
||||||
|
pred_epsilon = model_output
|
||||||
|
elif prediction_type == "sample":
|
||||||
|
pred_epsilon = (sample - alphas * model_output) / sigmas
|
||||||
|
elif prediction_type == "v_prediction":
|
||||||
|
pred_epsilon = alphas * model_output + sigmas * sample
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Prediction type {prediction_type} is not supported; currently, `epsilon`, `sample`, and `v_prediction`"
|
||||||
|
f" are supported."
|
||||||
|
)
|
||||||
|
|
||||||
|
return pred_epsilon
|
||||||
|
|
||||||
|
|
||||||
def extract_into_tensor(a, t, x_shape):
|
def extract_into_tensor(a, t, x_shape):
|
||||||
b, *_ = t.shape
|
b, *_ = t.shape
|
||||||
out = a.gather(-1, t)
|
out = a.gather(-1, t)
|
||||||
@@ -397,7 +421,7 @@ 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"
|
||||||
):
|
):
|
||||||
text_encoder_config = PretrainedConfig.from_pretrained(
|
text_encoder_config = PretrainedConfig.from_pretrained(
|
||||||
pretrained_model_name_or_path, subfolder=subfolder, revision=revision, use_auth_token=True
|
pretrained_model_name_or_path, subfolder=subfolder, revision=revision
|
||||||
)
|
)
|
||||||
model_class = text_encoder_config.architectures[0]
|
model_class = text_encoder_config.architectures[0]
|
||||||
|
|
||||||
@@ -830,9 +854,10 @@ def main(args):
|
|||||||
args.pretrained_teacher_model, subfolder="scheduler", revision=args.teacher_revision
|
args.pretrained_teacher_model, subfolder="scheduler", revision=args.teacher_revision
|
||||||
)
|
)
|
||||||
|
|
||||||
# The scheduler calculates the alpha and sigma schedule for us
|
# DDPMScheduler calculates the alpha and sigma noise schedules (based on the alpha bars) for us
|
||||||
alpha_schedule = torch.sqrt(noise_scheduler.alphas_cumprod)
|
alpha_schedule = torch.sqrt(noise_scheduler.alphas_cumprod)
|
||||||
sigma_schedule = torch.sqrt(1 - noise_scheduler.alphas_cumprod)
|
sigma_schedule = torch.sqrt(1 - noise_scheduler.alphas_cumprod)
|
||||||
|
# Initialize the DDIM ODE solver for distillation.
|
||||||
solver = DDIMSolver(
|
solver = DDIMSolver(
|
||||||
noise_scheduler.alphas_cumprod.numpy(),
|
noise_scheduler.alphas_cumprod.numpy(),
|
||||||
timesteps=noise_scheduler.config.num_train_timesteps,
|
timesteps=noise_scheduler.config.num_train_timesteps,
|
||||||
@@ -886,7 +911,7 @@ def main(args):
|
|||||||
text_encoder_two.requires_grad_(False)
|
text_encoder_two.requires_grad_(False)
|
||||||
teacher_unet.requires_grad_(False)
|
teacher_unet.requires_grad_(False)
|
||||||
|
|
||||||
# 7. Create online (`unet`) student U-Nets.
|
# 7. Create online student U-Net.
|
||||||
unet = UNet2DConditionModel.from_pretrained(
|
unet = UNet2DConditionModel.from_pretrained(
|
||||||
args.pretrained_teacher_model, subfolder="unet", revision=args.teacher_revision
|
args.pretrained_teacher_model, subfolder="unet", revision=args.teacher_revision
|
||||||
)
|
)
|
||||||
@@ -950,6 +975,7 @@ def main(args):
|
|||||||
# Also move the alpha and sigma noise schedules to accelerator.device.
|
# Also move the alpha and sigma noise schedules to accelerator.device.
|
||||||
alpha_schedule = alpha_schedule.to(accelerator.device)
|
alpha_schedule = alpha_schedule.to(accelerator.device)
|
||||||
sigma_schedule = sigma_schedule.to(accelerator.device)
|
sigma_schedule = sigma_schedule.to(accelerator.device)
|
||||||
|
# Move the ODE solver to accelerator.device.
|
||||||
solver = solver.to(accelerator.device)
|
solver = solver.to(accelerator.device)
|
||||||
|
|
||||||
# 10. Handle saving and loading of checkpoints
|
# 10. Handle saving and loading of checkpoints
|
||||||
@@ -1057,7 +1083,7 @@ def main(args):
|
|||||||
|
|
||||||
return {"prompt_embeds": prompt_embeds, **unet_added_cond_kwargs}
|
return {"prompt_embeds": prompt_embeds, **unet_added_cond_kwargs}
|
||||||
|
|
||||||
dataset = Text2ImageDataset(
|
dataset = SDXLText2ImageDataset(
|
||||||
train_shards_path_or_url=args.train_shards_path_or_url,
|
train_shards_path_or_url=args.train_shards_path_or_url,
|
||||||
num_train_examples=args.max_train_samples,
|
num_train_examples=args.max_train_samples,
|
||||||
per_gpu_batch_size=args.train_batch_size,
|
per_gpu_batch_size=args.train_batch_size,
|
||||||
@@ -1175,6 +1201,7 @@ def main(args):
|
|||||||
for epoch in range(first_epoch, args.num_train_epochs):
|
for epoch in range(first_epoch, args.num_train_epochs):
|
||||||
for step, batch in enumerate(train_dataloader):
|
for step, batch in enumerate(train_dataloader):
|
||||||
with accelerator.accumulate(unet):
|
with accelerator.accumulate(unet):
|
||||||
|
# 1. Load and process the image, text, and micro-conditioning (original image size, crop coordinates)
|
||||||
image, text, orig_size, crop_coords = batch
|
image, text, orig_size, crop_coords = batch
|
||||||
|
|
||||||
image = image.to(accelerator.device, non_blocking=True)
|
image = image.to(accelerator.device, non_blocking=True)
|
||||||
@@ -1196,37 +1223,37 @@ def main(args):
|
|||||||
latents = latents * vae.config.scaling_factor
|
latents = latents * vae.config.scaling_factor
|
||||||
if args.pretrained_vae_model_name_or_path is None:
|
if args.pretrained_vae_model_name_or_path is None:
|
||||||
latents = latents.to(weight_dtype)
|
latents = latents.to(weight_dtype)
|
||||||
|
|
||||||
# Sample noise that we'll add to the latents
|
|
||||||
noise = torch.randn_like(latents)
|
|
||||||
bsz = latents.shape[0]
|
bsz = latents.shape[0]
|
||||||
|
|
||||||
# Sample a random timestep for each image t_n ~ U[0, N - k - 1] without bias.
|
# 2. Sample a random timestep for each image t_n from the ODE solver timesteps without bias.
|
||||||
|
# For the DDIM solver, the timestep schedule is [T - 1, T - k - 1, T - 2 * k - 1, ...]
|
||||||
topk = noise_scheduler.config.num_train_timesteps // args.num_ddim_timesteps
|
topk = noise_scheduler.config.num_train_timesteps // args.num_ddim_timesteps
|
||||||
index = torch.randint(0, args.num_ddim_timesteps, (bsz,), device=latents.device).long()
|
index = torch.randint(0, args.num_ddim_timesteps, (bsz,), device=latents.device).long()
|
||||||
start_timesteps = solver.ddim_timesteps[index]
|
start_timesteps = solver.ddim_timesteps[index]
|
||||||
timesteps = start_timesteps - topk
|
timesteps = start_timesteps - topk
|
||||||
timesteps = torch.where(timesteps < 0, torch.zeros_like(timesteps), timesteps)
|
timesteps = torch.where(timesteps < 0, torch.zeros_like(timesteps), timesteps)
|
||||||
|
|
||||||
# 20.4.4. Get boundary scalings for start_timesteps and (end) timesteps.
|
# 3. Get boundary scalings for start_timesteps and (end) timesteps.
|
||||||
c_skip_start, c_out_start = scalings_for_boundary_conditions(start_timesteps)
|
c_skip_start, c_out_start = scalings_for_boundary_conditions(start_timesteps)
|
||||||
c_skip_start, c_out_start = [append_dims(x, latents.ndim) for x in [c_skip_start, c_out_start]]
|
c_skip_start, c_out_start = [append_dims(x, latents.ndim) for x in [c_skip_start, c_out_start]]
|
||||||
c_skip, c_out = scalings_for_boundary_conditions(timesteps)
|
c_skip, c_out = scalings_for_boundary_conditions(timesteps)
|
||||||
c_skip, c_out = [append_dims(x, latents.ndim) for x in [c_skip, c_out]]
|
c_skip, c_out = [append_dims(x, latents.ndim) for x in [c_skip, c_out]]
|
||||||
|
|
||||||
# 20.4.5. Add noise to the latents according to the noise magnitude at each timestep
|
# 4. Sample noise from the prior and add it to the latents according to the noise magnitude at each
|
||||||
# (this is the forward diffusion process) [z_{t_{n + k}} in Algorithm 1]
|
# timestep (this is the forward diffusion process) [z_{t_{n + k}} in Algorithm 1]
|
||||||
|
noise = torch.randn_like(latents)
|
||||||
noisy_model_input = noise_scheduler.add_noise(latents, noise, start_timesteps)
|
noisy_model_input = noise_scheduler.add_noise(latents, noise, start_timesteps)
|
||||||
|
|
||||||
# 20.4.6. Sample a random guidance scale w from U[w_min, w_max] and embed it
|
# 5. Sample a random guidance scale w from U[w_min, w_max]
|
||||||
|
# Note that for LCM-LoRA distillation it is not necessary to use a guidance scale embedding
|
||||||
w = (args.w_max - args.w_min) * torch.rand((bsz,)) + args.w_min
|
w = (args.w_max - args.w_min) * torch.rand((bsz,)) + args.w_min
|
||||||
w = w.reshape(bsz, 1, 1, 1)
|
w = w.reshape(bsz, 1, 1, 1)
|
||||||
w = w.to(device=latents.device, dtype=latents.dtype)
|
w = w.to(device=latents.device, dtype=latents.dtype)
|
||||||
|
|
||||||
# 20.4.8. Prepare prompt embeds and unet_added_conditions
|
# 6. Prepare prompt embeds and unet_added_conditions
|
||||||
prompt_embeds = encoded_text.pop("prompt_embeds")
|
prompt_embeds = encoded_text.pop("prompt_embeds")
|
||||||
|
|
||||||
# 20.4.9. Get online LCM prediction on z_{t_{n + k}}, w, c, t_{n + k}
|
# 7. Get online LCM prediction on z_{t_{n + k}} (noisy_model_input), w, c, t_{n + k} (start_timesteps)
|
||||||
noise_pred = unet(
|
noise_pred = unet(
|
||||||
noisy_model_input,
|
noisy_model_input,
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
@@ -1235,7 +1262,7 @@ def main(args):
|
|||||||
added_cond_kwargs=encoded_text,
|
added_cond_kwargs=encoded_text,
|
||||||
).sample
|
).sample
|
||||||
|
|
||||||
pred_x_0 = predicted_origin(
|
pred_x_0 = get_predicted_original_sample(
|
||||||
noise_pred,
|
noise_pred,
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
noisy_model_input,
|
noisy_model_input,
|
||||||
@@ -1246,18 +1273,28 @@ def main(args):
|
|||||||
|
|
||||||
model_pred = c_skip_start * noisy_model_input + c_out_start * pred_x_0
|
model_pred = c_skip_start * noisy_model_input + c_out_start * pred_x_0
|
||||||
|
|
||||||
# 20.4.10. Use the ODE solver to predict the kth step in the augmented PF-ODE trajectory after
|
# 8. Compute the conditional and unconditional teacher model predictions to get CFG estimates of the
|
||||||
# noisy_latents with both the conditioning embedding c and unconditional embedding 0
|
# predicted noise eps_0 and predicted original sample x_0, then run the ODE solver using these
|
||||||
# Get teacher model prediction on noisy_latents and conditional embedding
|
# estimates to predict the data point in the augmented PF-ODE trajectory corresponding to the next ODE
|
||||||
|
# solver timestep.
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
with torch.autocast("cuda"):
|
with torch.autocast("cuda"):
|
||||||
|
# 1. Get teacher model prediction on noisy_model_input z_{t_{n + k}} and conditional embedding c
|
||||||
cond_teacher_output = teacher_unet(
|
cond_teacher_output = teacher_unet(
|
||||||
noisy_model_input.to(weight_dtype),
|
noisy_model_input.to(weight_dtype),
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
encoder_hidden_states=prompt_embeds.to(weight_dtype),
|
encoder_hidden_states=prompt_embeds.to(weight_dtype),
|
||||||
added_cond_kwargs={k: v.to(weight_dtype) for k, v in encoded_text.items()},
|
added_cond_kwargs={k: v.to(weight_dtype) for k, v in encoded_text.items()},
|
||||||
).sample
|
).sample
|
||||||
cond_pred_x0 = predicted_origin(
|
cond_pred_x0 = get_predicted_original_sample(
|
||||||
|
cond_teacher_output,
|
||||||
|
start_timesteps,
|
||||||
|
noisy_model_input,
|
||||||
|
noise_scheduler.config.prediction_type,
|
||||||
|
alpha_schedule,
|
||||||
|
sigma_schedule,
|
||||||
|
)
|
||||||
|
cond_pred_noise = get_predicted_noise(
|
||||||
cond_teacher_output,
|
cond_teacher_output,
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
noisy_model_input,
|
noisy_model_input,
|
||||||
@@ -1266,7 +1303,7 @@ def main(args):
|
|||||||
sigma_schedule,
|
sigma_schedule,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get teacher model prediction on noisy_latents and unconditional embedding
|
# 2. Get teacher model prediction on noisy_model_input z_{t_{n + k}} and unconditional embedding 0
|
||||||
uncond_added_conditions = copy.deepcopy(encoded_text)
|
uncond_added_conditions = copy.deepcopy(encoded_text)
|
||||||
uncond_added_conditions["text_embeds"] = uncond_pooled_prompt_embeds
|
uncond_added_conditions["text_embeds"] = uncond_pooled_prompt_embeds
|
||||||
uncond_teacher_output = teacher_unet(
|
uncond_teacher_output = teacher_unet(
|
||||||
@@ -1275,7 +1312,15 @@ def main(args):
|
|||||||
encoder_hidden_states=uncond_prompt_embeds.to(weight_dtype),
|
encoder_hidden_states=uncond_prompt_embeds.to(weight_dtype),
|
||||||
added_cond_kwargs={k: v.to(weight_dtype) for k, v in uncond_added_conditions.items()},
|
added_cond_kwargs={k: v.to(weight_dtype) for k, v in uncond_added_conditions.items()},
|
||||||
).sample
|
).sample
|
||||||
uncond_pred_x0 = predicted_origin(
|
uncond_pred_x0 = get_predicted_original_sample(
|
||||||
|
uncond_teacher_output,
|
||||||
|
start_timesteps,
|
||||||
|
noisy_model_input,
|
||||||
|
noise_scheduler.config.prediction_type,
|
||||||
|
alpha_schedule,
|
||||||
|
sigma_schedule,
|
||||||
|
)
|
||||||
|
uncond_pred_noise = get_predicted_noise(
|
||||||
uncond_teacher_output,
|
uncond_teacher_output,
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
noisy_model_input,
|
noisy_model_input,
|
||||||
@@ -1284,12 +1329,17 @@ def main(args):
|
|||||||
sigma_schedule,
|
sigma_schedule,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 20.4.11. Perform "CFG" to get x_prev estimate (using the LCM paper's CFG formulation)
|
# 3. Calculate the CFG estimate of x_0 (pred_x0) and eps_0 (pred_noise)
|
||||||
|
# Note that this uses the LCM paper's CFG formulation rather than the Imagen CFG formulation
|
||||||
pred_x0 = cond_pred_x0 + w * (cond_pred_x0 - uncond_pred_x0)
|
pred_x0 = cond_pred_x0 + w * (cond_pred_x0 - uncond_pred_x0)
|
||||||
pred_noise = cond_teacher_output + w * (cond_teacher_output - uncond_teacher_output)
|
pred_noise = cond_pred_noise + w * (cond_pred_noise - uncond_pred_noise)
|
||||||
|
# 4. Run one step of the ODE solver to estimate the next point x_prev on the
|
||||||
|
# augmented PF-ODE trajectory (solving backward in time)
|
||||||
|
# Note that the DDIM step depends on both the predicted x_0 and source noise eps_0.
|
||||||
x_prev = solver.ddim_step(pred_x0, pred_noise, index)
|
x_prev = solver.ddim_step(pred_x0, pred_noise, index)
|
||||||
|
|
||||||
# 20.4.12. Get target LCM prediction on x_prev, w, c, t_n
|
# 9. Get target LCM prediction on x_prev, w, c, t_n (timesteps)
|
||||||
|
# Note that we do not use a separate target network for LCM-LoRA distillation.
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
with torch.autocast("cuda", enabled=True, dtype=weight_dtype):
|
with torch.autocast("cuda", enabled=True, dtype=weight_dtype):
|
||||||
target_noise_pred = unet(
|
target_noise_pred = unet(
|
||||||
@@ -1299,7 +1349,7 @@ def main(args):
|
|||||||
encoder_hidden_states=prompt_embeds.float(),
|
encoder_hidden_states=prompt_embeds.float(),
|
||||||
added_cond_kwargs=encoded_text,
|
added_cond_kwargs=encoded_text,
|
||||||
).sample
|
).sample
|
||||||
pred_x_0 = predicted_origin(
|
pred_x_0 = get_predicted_original_sample(
|
||||||
target_noise_pred,
|
target_noise_pred,
|
||||||
timesteps,
|
timesteps,
|
||||||
x_prev,
|
x_prev,
|
||||||
@@ -1309,7 +1359,7 @@ def main(args):
|
|||||||
)
|
)
|
||||||
target = c_skip * x_prev + c_out * pred_x_0
|
target = c_skip * x_prev + c_out * pred_x_0
|
||||||
|
|
||||||
# 20.4.13. Calculate loss
|
# 10. Calculate loss
|
||||||
if args.loss_type == "l2":
|
if args.loss_type == "l2":
|
||||||
loss = F.mse_loss(model_pred.float(), target.float(), reduction="mean")
|
loss = F.mse_loss(model_pred.float(), target.float(), reduction="mean")
|
||||||
elif args.loss_type == "huber":
|
elif args.loss_type == "huber":
|
||||||
@@ -1317,7 +1367,7 @@ def main(args):
|
|||||||
torch.sqrt((model_pred.float() - target.float()) ** 2 + args.huber_c**2) - args.huber_c
|
torch.sqrt((model_pred.float() - target.float()) ** 2 + args.huber_c**2) - args.huber_c
|
||||||
)
|
)
|
||||||
|
|
||||||
# 20.4.14. Backpropagate on the online student model (`unet`)
|
# 11. Backpropagate on the online student model (`unet`)
|
||||||
accelerator.backward(loss)
|
accelerator.backward(loss)
|
||||||
if accelerator.sync_gradients:
|
if accelerator.sync_gradients:
|
||||||
accelerator.clip_grad_norm_(unet.parameters(), args.max_grad_norm)
|
accelerator.clip_grad_norm_(unet.parameters(), args.max_grad_norm)
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ class WebdatasetFilter:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Text2ImageDataset:
|
class SDText2ImageDataset:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
train_shards_path_or_url: Union[str, List[str]],
|
train_shards_path_or_url: Union[str, List[str]],
|
||||||
@@ -336,19 +336,43 @@ def scalings_for_boundary_conditions(timestep, sigma_data=0.5, timestep_scaling=
|
|||||||
|
|
||||||
|
|
||||||
# Compare LCMScheduler.step, Step 4
|
# Compare LCMScheduler.step, Step 4
|
||||||
def predicted_origin(model_output, timesteps, sample, prediction_type, alphas, sigmas):
|
def get_predicted_original_sample(model_output, timesteps, sample, prediction_type, alphas, sigmas):
|
||||||
|
alphas = extract_into_tensor(alphas, timesteps, sample.shape)
|
||||||
|
sigmas = extract_into_tensor(sigmas, timesteps, sample.shape)
|
||||||
if prediction_type == "epsilon":
|
if prediction_type == "epsilon":
|
||||||
sigmas = extract_into_tensor(sigmas, timesteps, sample.shape)
|
|
||||||
alphas = extract_into_tensor(alphas, timesteps, sample.shape)
|
|
||||||
pred_x_0 = (sample - sigmas * model_output) / alphas
|
pred_x_0 = (sample - sigmas * model_output) / alphas
|
||||||
|
elif prediction_type == "sample":
|
||||||
|
pred_x_0 = model_output
|
||||||
elif prediction_type == "v_prediction":
|
elif prediction_type == "v_prediction":
|
||||||
pred_x_0 = alphas[timesteps] * sample - sigmas[timesteps] * model_output
|
pred_x_0 = alphas * sample - sigmas * model_output
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Prediction type {prediction_type} currently not supported.")
|
raise ValueError(
|
||||||
|
f"Prediction type {prediction_type} is not supported; currently, `epsilon`, `sample`, and `v_prediction`"
|
||||||
|
f" are supported."
|
||||||
|
)
|
||||||
|
|
||||||
return pred_x_0
|
return pred_x_0
|
||||||
|
|
||||||
|
|
||||||
|
# Based on step 4 in DDIMScheduler.step
|
||||||
|
def get_predicted_noise(model_output, timesteps, sample, prediction_type, alphas, sigmas):
|
||||||
|
alphas = extract_into_tensor(alphas, timesteps, sample.shape)
|
||||||
|
sigmas = extract_into_tensor(sigmas, timesteps, sample.shape)
|
||||||
|
if prediction_type == "epsilon":
|
||||||
|
pred_epsilon = model_output
|
||||||
|
elif prediction_type == "sample":
|
||||||
|
pred_epsilon = (sample - alphas * model_output) / sigmas
|
||||||
|
elif prediction_type == "v_prediction":
|
||||||
|
pred_epsilon = alphas * model_output + sigmas * sample
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Prediction type {prediction_type} is not supported; currently, `epsilon`, `sample`, and `v_prediction`"
|
||||||
|
f" are supported."
|
||||||
|
)
|
||||||
|
|
||||||
|
return pred_epsilon
|
||||||
|
|
||||||
|
|
||||||
def extract_into_tensor(a, t, x_shape):
|
def extract_into_tensor(a, t, x_shape):
|
||||||
b, *_ = t.shape
|
b, *_ = t.shape
|
||||||
out = a.gather(-1, t)
|
out = a.gather(-1, t)
|
||||||
@@ -400,7 +424,7 @@ 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"
|
||||||
):
|
):
|
||||||
text_encoder_config = PretrainedConfig.from_pretrained(
|
text_encoder_config = PretrainedConfig.from_pretrained(
|
||||||
pretrained_model_name_or_path, subfolder=subfolder, revision=revision, use_auth_token=True
|
pretrained_model_name_or_path, subfolder=subfolder, revision=revision
|
||||||
)
|
)
|
||||||
model_class = text_encoder_config.architectures[0]
|
model_class = text_encoder_config.architectures[0]
|
||||||
|
|
||||||
@@ -823,34 +847,35 @@ def main(args):
|
|||||||
args.pretrained_teacher_model, subfolder="scheduler", revision=args.teacher_revision
|
args.pretrained_teacher_model, subfolder="scheduler", revision=args.teacher_revision
|
||||||
)
|
)
|
||||||
|
|
||||||
# The scheduler calculates the alpha and sigma schedule for us
|
# DDPMScheduler calculates the alpha and sigma noise schedules (based on the alpha bars) for us
|
||||||
alpha_schedule = torch.sqrt(noise_scheduler.alphas_cumprod)
|
alpha_schedule = torch.sqrt(noise_scheduler.alphas_cumprod)
|
||||||
sigma_schedule = torch.sqrt(1 - noise_scheduler.alphas_cumprod)
|
sigma_schedule = torch.sqrt(1 - noise_scheduler.alphas_cumprod)
|
||||||
|
# Initialize the DDIM ODE solver for distillation.
|
||||||
solver = DDIMSolver(
|
solver = DDIMSolver(
|
||||||
noise_scheduler.alphas_cumprod.numpy(),
|
noise_scheduler.alphas_cumprod.numpy(),
|
||||||
timesteps=noise_scheduler.config.num_train_timesteps,
|
timesteps=noise_scheduler.config.num_train_timesteps,
|
||||||
ddim_timesteps=args.num_ddim_timesteps,
|
ddim_timesteps=args.num_ddim_timesteps,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2. Load tokenizers from SD-XL checkpoint.
|
# 2. Load tokenizers from SD 1.X/2.X checkpoint.
|
||||||
tokenizer = AutoTokenizer.from_pretrained(
|
tokenizer = AutoTokenizer.from_pretrained(
|
||||||
args.pretrained_teacher_model, subfolder="tokenizer", revision=args.teacher_revision, use_fast=False
|
args.pretrained_teacher_model, subfolder="tokenizer", revision=args.teacher_revision, use_fast=False
|
||||||
)
|
)
|
||||||
|
|
||||||
# 3. Load text encoders from SD-1.5 checkpoint.
|
# 3. Load text encoders from SD 1.X/2.X checkpoint.
|
||||||
# import correct text encoder classes
|
# import correct text encoder classes
|
||||||
text_encoder = CLIPTextModel.from_pretrained(
|
text_encoder = CLIPTextModel.from_pretrained(
|
||||||
args.pretrained_teacher_model, subfolder="text_encoder", revision=args.teacher_revision
|
args.pretrained_teacher_model, subfolder="text_encoder", revision=args.teacher_revision
|
||||||
)
|
)
|
||||||
|
|
||||||
# 4. Load VAE from SD-XL checkpoint (or more stable VAE)
|
# 4. Load VAE from SD 1.X/2.X checkpoint
|
||||||
vae = AutoencoderKL.from_pretrained(
|
vae = AutoencoderKL.from_pretrained(
|
||||||
args.pretrained_teacher_model,
|
args.pretrained_teacher_model,
|
||||||
subfolder="vae",
|
subfolder="vae",
|
||||||
revision=args.teacher_revision,
|
revision=args.teacher_revision,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 5. Load teacher U-Net from SD-XL checkpoint
|
# 5. Load teacher U-Net from SD 1.X/2.X checkpoint
|
||||||
teacher_unet = UNet2DConditionModel.from_pretrained(
|
teacher_unet = UNet2DConditionModel.from_pretrained(
|
||||||
args.pretrained_teacher_model, subfolder="unet", revision=args.teacher_revision
|
args.pretrained_teacher_model, subfolder="unet", revision=args.teacher_revision
|
||||||
)
|
)
|
||||||
@@ -860,7 +885,7 @@ def main(args):
|
|||||||
text_encoder.requires_grad_(False)
|
text_encoder.requires_grad_(False)
|
||||||
teacher_unet.requires_grad_(False)
|
teacher_unet.requires_grad_(False)
|
||||||
|
|
||||||
# 8. Create online (`unet`) student U-Nets. This will be updated by the optimizer (e.g. via backpropagation.)
|
# 7. Create online student U-Net. This will be updated by the optimizer (e.g. via backpropagation.)
|
||||||
# Add `time_cond_proj_dim` to the student U-Net if `teacher_unet.config.time_cond_proj_dim` is None
|
# Add `time_cond_proj_dim` to the student U-Net if `teacher_unet.config.time_cond_proj_dim` is None
|
||||||
if teacher_unet.config.time_cond_proj_dim is None:
|
if teacher_unet.config.time_cond_proj_dim is None:
|
||||||
teacher_unet.config["time_cond_proj_dim"] = args.unet_time_cond_proj_dim
|
teacher_unet.config["time_cond_proj_dim"] = args.unet_time_cond_proj_dim
|
||||||
@@ -869,8 +894,8 @@ def main(args):
|
|||||||
unet.load_state_dict(teacher_unet.state_dict(), strict=False)
|
unet.load_state_dict(teacher_unet.state_dict(), strict=False)
|
||||||
unet.train()
|
unet.train()
|
||||||
|
|
||||||
# 9. Create target (`ema_unet`) student U-Net parameters. This will be updated via EMA updates (polyak averaging).
|
# 8. Create target student U-Net. This will be updated via EMA updates (polyak averaging).
|
||||||
# Initialize from unet
|
# Initialize from (online) unet
|
||||||
target_unet = UNet2DConditionModel(**teacher_unet.config)
|
target_unet = UNet2DConditionModel(**teacher_unet.config)
|
||||||
target_unet.load_state_dict(unet.state_dict())
|
target_unet.load_state_dict(unet.state_dict())
|
||||||
target_unet.train()
|
target_unet.train()
|
||||||
@@ -887,7 +912,7 @@ def main(args):
|
|||||||
f"Controlnet loaded as datatype {accelerator.unwrap_model(unet).dtype}. {low_precision_error_string}"
|
f"Controlnet loaded as datatype {accelerator.unwrap_model(unet).dtype}. {low_precision_error_string}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 10. Handle mixed precision and device placement
|
# 9. Handle mixed precision and device placement
|
||||||
# For mixed precision training we cast all non-trainable weigths to half-precision
|
# For mixed precision training we cast all non-trainable weigths 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.
|
||||||
weight_dtype = torch.float32
|
weight_dtype = torch.float32
|
||||||
@@ -914,7 +939,7 @@ def main(args):
|
|||||||
sigma_schedule = sigma_schedule.to(accelerator.device)
|
sigma_schedule = sigma_schedule.to(accelerator.device)
|
||||||
solver = solver.to(accelerator.device)
|
solver = solver.to(accelerator.device)
|
||||||
|
|
||||||
# 11. Handle saving and loading of checkpoints
|
# 10. Handle saving and loading of checkpoints
|
||||||
# `accelerate` 0.16.0 will have better support for customized saving
|
# `accelerate` 0.16.0 will have better support for customized saving
|
||||||
if version.parse(accelerate.__version__) >= version.parse("0.16.0"):
|
if version.parse(accelerate.__version__) >= version.parse("0.16.0"):
|
||||||
# create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format
|
# create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format
|
||||||
@@ -948,7 +973,7 @@ def main(args):
|
|||||||
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)
|
||||||
|
|
||||||
# 12. Enable optimizations
|
# 11. Enable optimizations
|
||||||
if args.enable_xformers_memory_efficient_attention:
|
if args.enable_xformers_memory_efficient_attention:
|
||||||
if is_xformers_available():
|
if is_xformers_available():
|
||||||
import xformers
|
import xformers
|
||||||
@@ -994,13 +1019,14 @@ def main(args):
|
|||||||
eps=args.adam_epsilon,
|
eps=args.adam_epsilon,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 13. Dataset creation and data processing
|
||||||
# Here, we compute not just the text embeddings but also the additional embeddings
|
# Here, we compute not just the text embeddings but also the additional embeddings
|
||||||
# needed for the SD XL UNet to operate.
|
# needed for the SD XL UNet to operate.
|
||||||
def compute_embeddings(prompt_batch, proportion_empty_prompts, text_encoder, tokenizer, is_train=True):
|
def compute_embeddings(prompt_batch, proportion_empty_prompts, text_encoder, tokenizer, is_train=True):
|
||||||
prompt_embeds = encode_prompt(prompt_batch, text_encoder, tokenizer, proportion_empty_prompts, is_train)
|
prompt_embeds = encode_prompt(prompt_batch, text_encoder, tokenizer, proportion_empty_prompts, is_train)
|
||||||
return {"prompt_embeds": prompt_embeds}
|
return {"prompt_embeds": prompt_embeds}
|
||||||
|
|
||||||
dataset = Text2ImageDataset(
|
dataset = SDText2ImageDataset(
|
||||||
train_shards_path_or_url=args.train_shards_path_or_url,
|
train_shards_path_or_url=args.train_shards_path_or_url,
|
||||||
num_train_examples=args.max_train_samples,
|
num_train_examples=args.max_train_samples,
|
||||||
per_gpu_batch_size=args.train_batch_size,
|
per_gpu_batch_size=args.train_batch_size,
|
||||||
@@ -1020,6 +1046,7 @@ def main(args):
|
|||||||
tokenizer=tokenizer,
|
tokenizer=tokenizer,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 14. 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
|
overrode_max_train_steps = False
|
||||||
num_update_steps_per_epoch = math.ceil(train_dataloader.num_batches / args.gradient_accumulation_steps)
|
num_update_steps_per_epoch = math.ceil(train_dataloader.num_batches / args.gradient_accumulation_steps)
|
||||||
@@ -1034,6 +1061,7 @@ def main(args):
|
|||||||
num_training_steps=args.max_train_steps,
|
num_training_steps=args.max_train_steps,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 15. Prepare for training
|
||||||
# Prepare everything with our `accelerator`.
|
# Prepare everything with our `accelerator`.
|
||||||
unet, optimizer, lr_scheduler = accelerator.prepare(unet, optimizer, lr_scheduler)
|
unet, optimizer, lr_scheduler = accelerator.prepare(unet, optimizer, lr_scheduler)
|
||||||
|
|
||||||
@@ -1055,7 +1083,7 @@ def main(args):
|
|||||||
).input_ids.to(accelerator.device)
|
).input_ids.to(accelerator.device)
|
||||||
uncond_prompt_embeds = text_encoder(uncond_input_ids)[0]
|
uncond_prompt_embeds = text_encoder(uncond_input_ids)[0]
|
||||||
|
|
||||||
# Train!
|
# 16. Train!
|
||||||
total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps
|
total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps
|
||||||
|
|
||||||
logger.info("***** Running training *****")
|
logger.info("***** Running training *****")
|
||||||
@@ -1106,6 +1134,7 @@ def main(args):
|
|||||||
for epoch in range(first_epoch, args.num_train_epochs):
|
for epoch in range(first_epoch, args.num_train_epochs):
|
||||||
for step, batch in enumerate(train_dataloader):
|
for step, batch in enumerate(train_dataloader):
|
||||||
with accelerator.accumulate(unet):
|
with accelerator.accumulate(unet):
|
||||||
|
# 1. Load and process the image and text conditioning
|
||||||
image, text = batch
|
image, text = batch
|
||||||
|
|
||||||
image = image.to(accelerator.device, non_blocking=True)
|
image = image.to(accelerator.device, non_blocking=True)
|
||||||
@@ -1123,29 +1152,28 @@ def main(args):
|
|||||||
|
|
||||||
latents = latents * vae.config.scaling_factor
|
latents = latents * vae.config.scaling_factor
|
||||||
latents = latents.to(weight_dtype)
|
latents = latents.to(weight_dtype)
|
||||||
|
|
||||||
# Sample noise that we'll add to the latents
|
|
||||||
noise = torch.randn_like(latents)
|
|
||||||
bsz = latents.shape[0]
|
bsz = latents.shape[0]
|
||||||
|
|
||||||
# Sample a random timestep for each image t_n ~ U[0, N - k - 1] without bias.
|
# 2. Sample a random timestep for each image t_n from the ODE solver timesteps without bias.
|
||||||
|
# For the DDIM solver, the timestep schedule is [T - 1, T - k - 1, T - 2 * k - 1, ...]
|
||||||
topk = noise_scheduler.config.num_train_timesteps // args.num_ddim_timesteps
|
topk = noise_scheduler.config.num_train_timesteps // args.num_ddim_timesteps
|
||||||
index = torch.randint(0, args.num_ddim_timesteps, (bsz,), device=latents.device).long()
|
index = torch.randint(0, args.num_ddim_timesteps, (bsz,), device=latents.device).long()
|
||||||
start_timesteps = solver.ddim_timesteps[index]
|
start_timesteps = solver.ddim_timesteps[index]
|
||||||
timesteps = start_timesteps - topk
|
timesteps = start_timesteps - topk
|
||||||
timesteps = torch.where(timesteps < 0, torch.zeros_like(timesteps), timesteps)
|
timesteps = torch.where(timesteps < 0, torch.zeros_like(timesteps), timesteps)
|
||||||
|
|
||||||
# 20.4.4. Get boundary scalings for start_timesteps and (end) timesteps.
|
# 3. Get boundary scalings for start_timesteps and (end) timesteps.
|
||||||
c_skip_start, c_out_start = scalings_for_boundary_conditions(start_timesteps)
|
c_skip_start, c_out_start = scalings_for_boundary_conditions(start_timesteps)
|
||||||
c_skip_start, c_out_start = [append_dims(x, latents.ndim) for x in [c_skip_start, c_out_start]]
|
c_skip_start, c_out_start = [append_dims(x, latents.ndim) for x in [c_skip_start, c_out_start]]
|
||||||
c_skip, c_out = scalings_for_boundary_conditions(timesteps)
|
c_skip, c_out = scalings_for_boundary_conditions(timesteps)
|
||||||
c_skip, c_out = [append_dims(x, latents.ndim) for x in [c_skip, c_out]]
|
c_skip, c_out = [append_dims(x, latents.ndim) for x in [c_skip, c_out]]
|
||||||
|
|
||||||
# 20.4.5. Add noise to the latents according to the noise magnitude at each timestep
|
# 4. Sample noise from the prior and add it to the latents according to the noise magnitude at each
|
||||||
# (this is the forward diffusion process) [z_{t_{n + k}} in Algorithm 1]
|
# timestep (this is the forward diffusion process) [z_{t_{n + k}} in Algorithm 1]
|
||||||
|
noise = torch.randn_like(latents)
|
||||||
noisy_model_input = noise_scheduler.add_noise(latents, noise, start_timesteps)
|
noisy_model_input = noise_scheduler.add_noise(latents, noise, start_timesteps)
|
||||||
|
|
||||||
# 20.4.6. Sample a random guidance scale w from U[w_min, w_max] and embed it
|
# 5. Sample a random guidance scale w from U[w_min, w_max] and embed it
|
||||||
w = (args.w_max - args.w_min) * torch.rand((bsz,)) + args.w_min
|
w = (args.w_max - args.w_min) * torch.rand((bsz,)) + args.w_min
|
||||||
w_embedding = guidance_scale_embedding(w, embedding_dim=unet.config.time_cond_proj_dim)
|
w_embedding = guidance_scale_embedding(w, embedding_dim=unet.config.time_cond_proj_dim)
|
||||||
w = w.reshape(bsz, 1, 1, 1)
|
w = w.reshape(bsz, 1, 1, 1)
|
||||||
@@ -1153,10 +1181,10 @@ def main(args):
|
|||||||
w = w.to(device=latents.device, dtype=latents.dtype)
|
w = w.to(device=latents.device, dtype=latents.dtype)
|
||||||
w_embedding = w_embedding.to(device=latents.device, dtype=latents.dtype)
|
w_embedding = w_embedding.to(device=latents.device, dtype=latents.dtype)
|
||||||
|
|
||||||
# 20.4.8. Prepare prompt embeds and unet_added_conditions
|
# 6. Prepare prompt embeds and unet_added_conditions
|
||||||
prompt_embeds = encoded_text.pop("prompt_embeds")
|
prompt_embeds = encoded_text.pop("prompt_embeds")
|
||||||
|
|
||||||
# 20.4.9. Get online LCM prediction on z_{t_{n + k}}, w, c, t_{n + k}
|
# 7. Get online LCM prediction on z_{t_{n + k}} (noisy_model_input), w, c, t_{n + k} (start_timesteps)
|
||||||
noise_pred = unet(
|
noise_pred = unet(
|
||||||
noisy_model_input,
|
noisy_model_input,
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
@@ -1165,7 +1193,7 @@ def main(args):
|
|||||||
added_cond_kwargs=encoded_text,
|
added_cond_kwargs=encoded_text,
|
||||||
).sample
|
).sample
|
||||||
|
|
||||||
pred_x_0 = predicted_origin(
|
pred_x_0 = get_predicted_original_sample(
|
||||||
noise_pred,
|
noise_pred,
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
noisy_model_input,
|
noisy_model_input,
|
||||||
@@ -1176,17 +1204,27 @@ def main(args):
|
|||||||
|
|
||||||
model_pred = c_skip_start * noisy_model_input + c_out_start * pred_x_0
|
model_pred = c_skip_start * noisy_model_input + c_out_start * pred_x_0
|
||||||
|
|
||||||
# 20.4.10. Use the ODE solver to predict the kth step in the augmented PF-ODE trajectory after
|
# 8. Compute the conditional and unconditional teacher model predictions to get CFG estimates of the
|
||||||
# noisy_latents with both the conditioning embedding c and unconditional embedding 0
|
# predicted noise eps_0 and predicted original sample x_0, then run the ODE solver using these
|
||||||
# Get teacher model prediction on noisy_latents and conditional embedding
|
# estimates to predict the data point in the augmented PF-ODE trajectory corresponding to the next ODE
|
||||||
|
# solver timestep.
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
with torch.autocast("cuda"):
|
with torch.autocast("cuda"):
|
||||||
|
# 1. Get teacher model prediction on noisy_model_input z_{t_{n + k}} and conditional embedding c
|
||||||
cond_teacher_output = teacher_unet(
|
cond_teacher_output = teacher_unet(
|
||||||
noisy_model_input.to(weight_dtype),
|
noisy_model_input.to(weight_dtype),
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
encoder_hidden_states=prompt_embeds.to(weight_dtype),
|
encoder_hidden_states=prompt_embeds.to(weight_dtype),
|
||||||
).sample
|
).sample
|
||||||
cond_pred_x0 = predicted_origin(
|
cond_pred_x0 = get_predicted_original_sample(
|
||||||
|
cond_teacher_output,
|
||||||
|
start_timesteps,
|
||||||
|
noisy_model_input,
|
||||||
|
noise_scheduler.config.prediction_type,
|
||||||
|
alpha_schedule,
|
||||||
|
sigma_schedule,
|
||||||
|
)
|
||||||
|
cond_pred_noise = get_predicted_noise(
|
||||||
cond_teacher_output,
|
cond_teacher_output,
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
noisy_model_input,
|
noisy_model_input,
|
||||||
@@ -1195,13 +1233,21 @@ def main(args):
|
|||||||
sigma_schedule,
|
sigma_schedule,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get teacher model prediction on noisy_latents and unconditional embedding
|
# 2. Get teacher model prediction on noisy_model_input z_{t_{n + k}} and unconditional embedding 0
|
||||||
uncond_teacher_output = teacher_unet(
|
uncond_teacher_output = teacher_unet(
|
||||||
noisy_model_input.to(weight_dtype),
|
noisy_model_input.to(weight_dtype),
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
encoder_hidden_states=uncond_prompt_embeds.to(weight_dtype),
|
encoder_hidden_states=uncond_prompt_embeds.to(weight_dtype),
|
||||||
).sample
|
).sample
|
||||||
uncond_pred_x0 = predicted_origin(
|
uncond_pred_x0 = get_predicted_original_sample(
|
||||||
|
uncond_teacher_output,
|
||||||
|
start_timesteps,
|
||||||
|
noisy_model_input,
|
||||||
|
noise_scheduler.config.prediction_type,
|
||||||
|
alpha_schedule,
|
||||||
|
sigma_schedule,
|
||||||
|
)
|
||||||
|
uncond_pred_noise = get_predicted_noise(
|
||||||
uncond_teacher_output,
|
uncond_teacher_output,
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
noisy_model_input,
|
noisy_model_input,
|
||||||
@@ -1210,12 +1256,16 @@ def main(args):
|
|||||||
sigma_schedule,
|
sigma_schedule,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 20.4.11. Perform "CFG" to get x_prev estimate (using the LCM paper's CFG formulation)
|
# 3. Calculate the CFG estimate of x_0 (pred_x0) and eps_0 (pred_noise)
|
||||||
|
# Note that this uses the LCM paper's CFG formulation rather than the Imagen CFG formulation
|
||||||
pred_x0 = cond_pred_x0 + w * (cond_pred_x0 - uncond_pred_x0)
|
pred_x0 = cond_pred_x0 + w * (cond_pred_x0 - uncond_pred_x0)
|
||||||
pred_noise = cond_teacher_output + w * (cond_teacher_output - uncond_teacher_output)
|
pred_noise = cond_pred_noise + w * (cond_pred_noise - uncond_pred_noise)
|
||||||
|
# 4. Run one step of the ODE solver to estimate the next point x_prev on the
|
||||||
|
# augmented PF-ODE trajectory (solving backward in time)
|
||||||
|
# Note that the DDIM step depends on both the predicted x_0 and source noise eps_0.
|
||||||
x_prev = solver.ddim_step(pred_x0, pred_noise, index)
|
x_prev = solver.ddim_step(pred_x0, pred_noise, index)
|
||||||
|
|
||||||
# 20.4.12. Get target LCM prediction on x_prev, w, c, t_n
|
# 9. Get target LCM prediction on x_prev, w, c, t_n (timesteps)
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
with torch.autocast("cuda", dtype=weight_dtype):
|
with torch.autocast("cuda", dtype=weight_dtype):
|
||||||
target_noise_pred = target_unet(
|
target_noise_pred = target_unet(
|
||||||
@@ -1224,7 +1274,7 @@ def main(args):
|
|||||||
timestep_cond=w_embedding,
|
timestep_cond=w_embedding,
|
||||||
encoder_hidden_states=prompt_embeds.float(),
|
encoder_hidden_states=prompt_embeds.float(),
|
||||||
).sample
|
).sample
|
||||||
pred_x_0 = predicted_origin(
|
pred_x_0 = get_predicted_original_sample(
|
||||||
target_noise_pred,
|
target_noise_pred,
|
||||||
timesteps,
|
timesteps,
|
||||||
x_prev,
|
x_prev,
|
||||||
@@ -1234,7 +1284,7 @@ def main(args):
|
|||||||
)
|
)
|
||||||
target = c_skip * x_prev + c_out * pred_x_0
|
target = c_skip * x_prev + c_out * pred_x_0
|
||||||
|
|
||||||
# 20.4.13. Calculate loss
|
# 10. Calculate loss
|
||||||
if args.loss_type == "l2":
|
if args.loss_type == "l2":
|
||||||
loss = F.mse_loss(model_pred.float(), target.float(), reduction="mean")
|
loss = F.mse_loss(model_pred.float(), target.float(), reduction="mean")
|
||||||
elif args.loss_type == "huber":
|
elif args.loss_type == "huber":
|
||||||
@@ -1242,7 +1292,7 @@ def main(args):
|
|||||||
torch.sqrt((model_pred.float() - target.float()) ** 2 + args.huber_c**2) - args.huber_c
|
torch.sqrt((model_pred.float() - target.float()) ** 2 + args.huber_c**2) - args.huber_c
|
||||||
)
|
)
|
||||||
|
|
||||||
# 20.4.14. Backpropagate on the online student model (`unet`)
|
# 11. Backpropagate on the online student model (`unet`)
|
||||||
accelerator.backward(loss)
|
accelerator.backward(loss)
|
||||||
if accelerator.sync_gradients:
|
if accelerator.sync_gradients:
|
||||||
accelerator.clip_grad_norm_(unet.parameters(), args.max_grad_norm)
|
accelerator.clip_grad_norm_(unet.parameters(), args.max_grad_norm)
|
||||||
@@ -1252,7 +1302,7 @@ def main(args):
|
|||||||
|
|
||||||
# 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:
|
||||||
# 20.4.15. Make EMA update to target student model parameters
|
# 12. Make EMA update to target student model parameters (`target_unet`)
|
||||||
update_ema(target_unet.parameters(), unet.parameters(), args.ema_decay)
|
update_ema(target_unet.parameters(), unet.parameters(), args.ema_decay)
|
||||||
progress_bar.update(1)
|
progress_bar.update(1)
|
||||||
global_step += 1
|
global_step += 1
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ class WebdatasetFilter:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Text2ImageDataset:
|
class SDXLText2ImageDataset:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
train_shards_path_or_url: Union[str, List[str]],
|
train_shards_path_or_url: Union[str, List[str]],
|
||||||
@@ -324,19 +324,43 @@ def scalings_for_boundary_conditions(timestep, sigma_data=0.5, timestep_scaling=
|
|||||||
|
|
||||||
|
|
||||||
# Compare LCMScheduler.step, Step 4
|
# Compare LCMScheduler.step, Step 4
|
||||||
def predicted_origin(model_output, timesteps, sample, prediction_type, alphas, sigmas):
|
def get_predicted_original_sample(model_output, timesteps, sample, prediction_type, alphas, sigmas):
|
||||||
|
alphas = extract_into_tensor(alphas, timesteps, sample.shape)
|
||||||
|
sigmas = extract_into_tensor(sigmas, timesteps, sample.shape)
|
||||||
if prediction_type == "epsilon":
|
if prediction_type == "epsilon":
|
||||||
sigmas = extract_into_tensor(sigmas, timesteps, sample.shape)
|
|
||||||
alphas = extract_into_tensor(alphas, timesteps, sample.shape)
|
|
||||||
pred_x_0 = (sample - sigmas * model_output) / alphas
|
pred_x_0 = (sample - sigmas * model_output) / alphas
|
||||||
|
elif prediction_type == "sample":
|
||||||
|
pred_x_0 = model_output
|
||||||
elif prediction_type == "v_prediction":
|
elif prediction_type == "v_prediction":
|
||||||
pred_x_0 = alphas[timesteps] * sample - sigmas[timesteps] * model_output
|
pred_x_0 = alphas * sample - sigmas * model_output
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Prediction type {prediction_type} currently not supported.")
|
raise ValueError(
|
||||||
|
f"Prediction type {prediction_type} is not supported; currently, `epsilon`, `sample`, and `v_prediction`"
|
||||||
|
f" are supported."
|
||||||
|
)
|
||||||
|
|
||||||
return pred_x_0
|
return pred_x_0
|
||||||
|
|
||||||
|
|
||||||
|
# Based on step 4 in DDIMScheduler.step
|
||||||
|
def get_predicted_noise(model_output, timesteps, sample, prediction_type, alphas, sigmas):
|
||||||
|
alphas = extract_into_tensor(alphas, timesteps, sample.shape)
|
||||||
|
sigmas = extract_into_tensor(sigmas, timesteps, sample.shape)
|
||||||
|
if prediction_type == "epsilon":
|
||||||
|
pred_epsilon = model_output
|
||||||
|
elif prediction_type == "sample":
|
||||||
|
pred_epsilon = (sample - alphas * model_output) / sigmas
|
||||||
|
elif prediction_type == "v_prediction":
|
||||||
|
pred_epsilon = alphas * model_output + sigmas * sample
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Prediction type {prediction_type} is not supported; currently, `epsilon`, `sample`, and `v_prediction`"
|
||||||
|
f" are supported."
|
||||||
|
)
|
||||||
|
|
||||||
|
return pred_epsilon
|
||||||
|
|
||||||
|
|
||||||
def extract_into_tensor(a, t, x_shape):
|
def extract_into_tensor(a, t, x_shape):
|
||||||
b, *_ = t.shape
|
b, *_ = t.shape
|
||||||
out = a.gather(-1, t)
|
out = a.gather(-1, t)
|
||||||
@@ -419,7 +443,7 @@ 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"
|
||||||
):
|
):
|
||||||
text_encoder_config = PretrainedConfig.from_pretrained(
|
text_encoder_config = PretrainedConfig.from_pretrained(
|
||||||
pretrained_model_name_or_path, subfolder=subfolder, revision=revision, use_auth_token=True
|
pretrained_model_name_or_path, subfolder=subfolder, revision=revision
|
||||||
)
|
)
|
||||||
model_class = text_encoder_config.architectures[0]
|
model_class = text_encoder_config.architectures[0]
|
||||||
|
|
||||||
@@ -863,9 +887,10 @@ def main(args):
|
|||||||
args.pretrained_teacher_model, subfolder="scheduler", revision=args.teacher_revision
|
args.pretrained_teacher_model, subfolder="scheduler", revision=args.teacher_revision
|
||||||
)
|
)
|
||||||
|
|
||||||
# The scheduler calculates the alpha and sigma schedule for us
|
# DDPMScheduler calculates the alpha and sigma noise schedules (based on the alpha bars) for us
|
||||||
alpha_schedule = torch.sqrt(noise_scheduler.alphas_cumprod)
|
alpha_schedule = torch.sqrt(noise_scheduler.alphas_cumprod)
|
||||||
sigma_schedule = torch.sqrt(1 - noise_scheduler.alphas_cumprod)
|
sigma_schedule = torch.sqrt(1 - noise_scheduler.alphas_cumprod)
|
||||||
|
# Initialize the DDIM ODE solver for distillation.
|
||||||
solver = DDIMSolver(
|
solver = DDIMSolver(
|
||||||
noise_scheduler.alphas_cumprod.numpy(),
|
noise_scheduler.alphas_cumprod.numpy(),
|
||||||
timesteps=noise_scheduler.config.num_train_timesteps,
|
timesteps=noise_scheduler.config.num_train_timesteps,
|
||||||
@@ -919,7 +944,7 @@ def main(args):
|
|||||||
text_encoder_two.requires_grad_(False)
|
text_encoder_two.requires_grad_(False)
|
||||||
teacher_unet.requires_grad_(False)
|
teacher_unet.requires_grad_(False)
|
||||||
|
|
||||||
# 8. Create online (`unet`) student U-Nets. This will be updated by the optimizer (e.g. via backpropagation.)
|
# 7. Create online student U-Net. This will be updated by the optimizer (e.g. via backpropagation.)
|
||||||
# Add `time_cond_proj_dim` to the student U-Net if `teacher_unet.config.time_cond_proj_dim` is None
|
# Add `time_cond_proj_dim` to the student U-Net if `teacher_unet.config.time_cond_proj_dim` is None
|
||||||
if teacher_unet.config.time_cond_proj_dim is None:
|
if teacher_unet.config.time_cond_proj_dim is None:
|
||||||
teacher_unet.config["time_cond_proj_dim"] = args.unet_time_cond_proj_dim
|
teacher_unet.config["time_cond_proj_dim"] = args.unet_time_cond_proj_dim
|
||||||
@@ -928,8 +953,8 @@ def main(args):
|
|||||||
unet.load_state_dict(teacher_unet.state_dict(), strict=False)
|
unet.load_state_dict(teacher_unet.state_dict(), strict=False)
|
||||||
unet.train()
|
unet.train()
|
||||||
|
|
||||||
# 9. Create target (`ema_unet`) student U-Net parameters. This will be updated via EMA updates (polyak averaging).
|
# 8. Create target student U-Net. This will be updated via EMA updates (polyak averaging).
|
||||||
# Initialize from unet
|
# Initialize from (online) unet
|
||||||
target_unet = UNet2DConditionModel(**teacher_unet.config)
|
target_unet = UNet2DConditionModel(**teacher_unet.config)
|
||||||
target_unet.load_state_dict(unet.state_dict())
|
target_unet.load_state_dict(unet.state_dict())
|
||||||
target_unet.train()
|
target_unet.train()
|
||||||
@@ -971,6 +996,7 @@ def main(args):
|
|||||||
# Also move the alpha and sigma noise schedules to accelerator.device.
|
# Also move the alpha and sigma noise schedules to accelerator.device.
|
||||||
alpha_schedule = alpha_schedule.to(accelerator.device)
|
alpha_schedule = alpha_schedule.to(accelerator.device)
|
||||||
sigma_schedule = sigma_schedule.to(accelerator.device)
|
sigma_schedule = sigma_schedule.to(accelerator.device)
|
||||||
|
# Move the ODE solver to accelerator.device.
|
||||||
solver = solver.to(accelerator.device)
|
solver = solver.to(accelerator.device)
|
||||||
|
|
||||||
# 10. Handle saving and loading of checkpoints
|
# 10. Handle saving and loading of checkpoints
|
||||||
@@ -1084,7 +1110,7 @@ def main(args):
|
|||||||
|
|
||||||
return {"prompt_embeds": prompt_embeds, **unet_added_cond_kwargs}
|
return {"prompt_embeds": prompt_embeds, **unet_added_cond_kwargs}
|
||||||
|
|
||||||
dataset = Text2ImageDataset(
|
dataset = SDXLText2ImageDataset(
|
||||||
train_shards_path_or_url=args.train_shards_path_or_url,
|
train_shards_path_or_url=args.train_shards_path_or_url,
|
||||||
num_train_examples=args.max_train_samples,
|
num_train_examples=args.max_train_samples,
|
||||||
per_gpu_batch_size=args.train_batch_size,
|
per_gpu_batch_size=args.train_batch_size,
|
||||||
@@ -1202,6 +1228,7 @@ def main(args):
|
|||||||
for epoch in range(first_epoch, args.num_train_epochs):
|
for epoch in range(first_epoch, args.num_train_epochs):
|
||||||
for step, batch in enumerate(train_dataloader):
|
for step, batch in enumerate(train_dataloader):
|
||||||
with accelerator.accumulate(unet):
|
with accelerator.accumulate(unet):
|
||||||
|
# 1. Load and process the image, text, and micro-conditioning (original image size, crop coordinates)
|
||||||
image, text, orig_size, crop_coords = batch
|
image, text, orig_size, crop_coords = batch
|
||||||
|
|
||||||
image = image.to(accelerator.device, non_blocking=True)
|
image = image.to(accelerator.device, non_blocking=True)
|
||||||
@@ -1223,38 +1250,39 @@ def main(args):
|
|||||||
latents = latents * vae.config.scaling_factor
|
latents = latents * vae.config.scaling_factor
|
||||||
if args.pretrained_vae_model_name_or_path is None:
|
if args.pretrained_vae_model_name_or_path is None:
|
||||||
latents = latents.to(weight_dtype)
|
latents = latents.to(weight_dtype)
|
||||||
|
|
||||||
# Sample noise that we'll add to the latents
|
|
||||||
noise = torch.randn_like(latents)
|
|
||||||
bsz = latents.shape[0]
|
bsz = latents.shape[0]
|
||||||
|
|
||||||
# Sample a random timestep for each image t_n ~ U[0, N - k - 1] without bias.
|
# 2. Sample a random timestep for each image t_n from the ODE solver timesteps without bias.
|
||||||
|
# For the DDIM solver, the timestep schedule is [T - 1, T - k - 1, T - 2 * k - 1, ...]
|
||||||
topk = noise_scheduler.config.num_train_timesteps // args.num_ddim_timesteps
|
topk = noise_scheduler.config.num_train_timesteps // args.num_ddim_timesteps
|
||||||
index = torch.randint(0, args.num_ddim_timesteps, (bsz,), device=latents.device).long()
|
index = torch.randint(0, args.num_ddim_timesteps, (bsz,), device=latents.device).long()
|
||||||
start_timesteps = solver.ddim_timesteps[index]
|
start_timesteps = solver.ddim_timesteps[index]
|
||||||
timesteps = start_timesteps - topk
|
timesteps = start_timesteps - topk
|
||||||
timesteps = torch.where(timesteps < 0, torch.zeros_like(timesteps), timesteps)
|
timesteps = torch.where(timesteps < 0, torch.zeros_like(timesteps), timesteps)
|
||||||
|
|
||||||
# 20.4.4. Get boundary scalings for start_timesteps and (end) timesteps.
|
# 3. Get boundary scalings for start_timesteps and (end) timesteps.
|
||||||
c_skip_start, c_out_start = scalings_for_boundary_conditions(start_timesteps)
|
c_skip_start, c_out_start = scalings_for_boundary_conditions(start_timesteps)
|
||||||
c_skip_start, c_out_start = [append_dims(x, latents.ndim) for x in [c_skip_start, c_out_start]]
|
c_skip_start, c_out_start = [append_dims(x, latents.ndim) for x in [c_skip_start, c_out_start]]
|
||||||
c_skip, c_out = scalings_for_boundary_conditions(timesteps)
|
c_skip, c_out = scalings_for_boundary_conditions(timesteps)
|
||||||
c_skip, c_out = [append_dims(x, latents.ndim) for x in [c_skip, c_out]]
|
c_skip, c_out = [append_dims(x, latents.ndim) for x in [c_skip, c_out]]
|
||||||
|
|
||||||
# 20.4.5. Add noise to the latents according to the noise magnitude at each timestep
|
# 4. Sample noise from the prior and add it to the latents according to the noise magnitude at each
|
||||||
# (this is the forward diffusion process) [z_{t_{n + k}} in Algorithm 1]
|
# timestep (this is the forward diffusion process) [z_{t_{n + k}} in Algorithm 1]
|
||||||
|
noise = torch.randn_like(latents)
|
||||||
noisy_model_input = noise_scheduler.add_noise(latents, noise, start_timesteps)
|
noisy_model_input = noise_scheduler.add_noise(latents, noise, start_timesteps)
|
||||||
|
|
||||||
# 20.4.6. Sample a random guidance scale w from U[w_min, w_max] and embed it
|
# 5. Sample a random guidance scale w from U[w_min, w_max] and embed it
|
||||||
w = (args.w_max - args.w_min) * torch.rand((bsz,)) + args.w_min
|
w = (args.w_max - args.w_min) * torch.rand((bsz,)) + args.w_min
|
||||||
w_embedding = guidance_scale_embedding(w, embedding_dim=unet.config.time_cond_proj_dim)
|
w_embedding = guidance_scale_embedding(w, embedding_dim=unet.config.time_cond_proj_dim)
|
||||||
w = w.reshape(bsz, 1, 1, 1)
|
w = w.reshape(bsz, 1, 1, 1)
|
||||||
|
# Move to U-Net device and dtype
|
||||||
w = w.to(device=latents.device, dtype=latents.dtype)
|
w = w.to(device=latents.device, dtype=latents.dtype)
|
||||||
|
w_embedding = w_embedding.to(device=latents.device, dtype=latents.dtype)
|
||||||
|
|
||||||
# 20.4.8. Prepare prompt embeds and unet_added_conditions
|
# 6. Prepare prompt embeds and unet_added_conditions
|
||||||
prompt_embeds = encoded_text.pop("prompt_embeds")
|
prompt_embeds = encoded_text.pop("prompt_embeds")
|
||||||
|
|
||||||
# 20.4.9. Get online LCM prediction on z_{t_{n + k}}, w, c, t_{n + k}
|
# 7. Get online LCM prediction on z_{t_{n + k}} (noisy_model_input), w, c, t_{n + k} (start_timesteps)
|
||||||
noise_pred = unet(
|
noise_pred = unet(
|
||||||
noisy_model_input,
|
noisy_model_input,
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
@@ -1263,7 +1291,7 @@ def main(args):
|
|||||||
added_cond_kwargs=encoded_text,
|
added_cond_kwargs=encoded_text,
|
||||||
).sample
|
).sample
|
||||||
|
|
||||||
pred_x_0 = predicted_origin(
|
pred_x_0 = get_predicted_original_sample(
|
||||||
noise_pred,
|
noise_pred,
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
noisy_model_input,
|
noisy_model_input,
|
||||||
@@ -1274,18 +1302,28 @@ def main(args):
|
|||||||
|
|
||||||
model_pred = c_skip_start * noisy_model_input + c_out_start * pred_x_0
|
model_pred = c_skip_start * noisy_model_input + c_out_start * pred_x_0
|
||||||
|
|
||||||
# 20.4.10. Use the ODE solver to predict the kth step in the augmented PF-ODE trajectory after
|
# 8. Compute the conditional and unconditional teacher model predictions to get CFG estimates of the
|
||||||
# noisy_latents with both the conditioning embedding c and unconditional embedding 0
|
# predicted noise eps_0 and predicted original sample x_0, then run the ODE solver using these
|
||||||
# Get teacher model prediction on noisy_latents and conditional embedding
|
# estimates to predict the data point in the augmented PF-ODE trajectory corresponding to the next ODE
|
||||||
|
# solver timestep.
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
with torch.autocast("cuda"):
|
with torch.autocast("cuda"):
|
||||||
|
# 1. Get teacher model prediction on noisy_model_input z_{t_{n + k}} and conditional embedding c
|
||||||
cond_teacher_output = teacher_unet(
|
cond_teacher_output = teacher_unet(
|
||||||
noisy_model_input.to(weight_dtype),
|
noisy_model_input.to(weight_dtype),
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
encoder_hidden_states=prompt_embeds.to(weight_dtype),
|
encoder_hidden_states=prompt_embeds.to(weight_dtype),
|
||||||
added_cond_kwargs={k: v.to(weight_dtype) for k, v in encoded_text.items()},
|
added_cond_kwargs={k: v.to(weight_dtype) for k, v in encoded_text.items()},
|
||||||
).sample
|
).sample
|
||||||
cond_pred_x0 = predicted_origin(
|
cond_pred_x0 = get_predicted_original_sample(
|
||||||
|
cond_teacher_output,
|
||||||
|
start_timesteps,
|
||||||
|
noisy_model_input,
|
||||||
|
noise_scheduler.config.prediction_type,
|
||||||
|
alpha_schedule,
|
||||||
|
sigma_schedule,
|
||||||
|
)
|
||||||
|
cond_pred_noise = get_predicted_noise(
|
||||||
cond_teacher_output,
|
cond_teacher_output,
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
noisy_model_input,
|
noisy_model_input,
|
||||||
@@ -1294,7 +1332,7 @@ def main(args):
|
|||||||
sigma_schedule,
|
sigma_schedule,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get teacher model prediction on noisy_latents and unconditional embedding
|
# 2. Get teacher model prediction on noisy_model_input z_{t_{n + k}} and unconditional embedding 0
|
||||||
uncond_added_conditions = copy.deepcopy(encoded_text)
|
uncond_added_conditions = copy.deepcopy(encoded_text)
|
||||||
uncond_added_conditions["text_embeds"] = uncond_pooled_prompt_embeds
|
uncond_added_conditions["text_embeds"] = uncond_pooled_prompt_embeds
|
||||||
uncond_teacher_output = teacher_unet(
|
uncond_teacher_output = teacher_unet(
|
||||||
@@ -1303,7 +1341,15 @@ def main(args):
|
|||||||
encoder_hidden_states=uncond_prompt_embeds.to(weight_dtype),
|
encoder_hidden_states=uncond_prompt_embeds.to(weight_dtype),
|
||||||
added_cond_kwargs={k: v.to(weight_dtype) for k, v in uncond_added_conditions.items()},
|
added_cond_kwargs={k: v.to(weight_dtype) for k, v in uncond_added_conditions.items()},
|
||||||
).sample
|
).sample
|
||||||
uncond_pred_x0 = predicted_origin(
|
uncond_pred_x0 = get_predicted_original_sample(
|
||||||
|
uncond_teacher_output,
|
||||||
|
start_timesteps,
|
||||||
|
noisy_model_input,
|
||||||
|
noise_scheduler.config.prediction_type,
|
||||||
|
alpha_schedule,
|
||||||
|
sigma_schedule,
|
||||||
|
)
|
||||||
|
uncond_pred_noise = get_predicted_noise(
|
||||||
uncond_teacher_output,
|
uncond_teacher_output,
|
||||||
start_timesteps,
|
start_timesteps,
|
||||||
noisy_model_input,
|
noisy_model_input,
|
||||||
@@ -1312,12 +1358,16 @@ def main(args):
|
|||||||
sigma_schedule,
|
sigma_schedule,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 20.4.11. Perform "CFG" to get x_prev estimate (using the LCM paper's CFG formulation)
|
# 3. Calculate the CFG estimate of x_0 (pred_x0) and eps_0 (pred_noise)
|
||||||
|
# Note that this uses the LCM paper's CFG formulation rather than the Imagen CFG formulation
|
||||||
pred_x0 = cond_pred_x0 + w * (cond_pred_x0 - uncond_pred_x0)
|
pred_x0 = cond_pred_x0 + w * (cond_pred_x0 - uncond_pred_x0)
|
||||||
pred_noise = cond_teacher_output + w * (cond_teacher_output - uncond_teacher_output)
|
pred_noise = cond_pred_noise + w * (cond_pred_noise - uncond_pred_noise)
|
||||||
|
# 4. Run one step of the ODE solver to estimate the next point x_prev on the
|
||||||
|
# augmented PF-ODE trajectory (solving backward in time)
|
||||||
|
# Note that the DDIM step depends on both the predicted x_0 and source noise eps_0.
|
||||||
x_prev = solver.ddim_step(pred_x0, pred_noise, index)
|
x_prev = solver.ddim_step(pred_x0, pred_noise, index)
|
||||||
|
|
||||||
# 20.4.12. Get target LCM prediction on x_prev, w, c, t_n
|
# 9. Get target LCM prediction on x_prev, w, c, t_n (timesteps)
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
with torch.autocast("cuda", dtype=weight_dtype):
|
with torch.autocast("cuda", dtype=weight_dtype):
|
||||||
target_noise_pred = target_unet(
|
target_noise_pred = target_unet(
|
||||||
@@ -1327,7 +1377,7 @@ def main(args):
|
|||||||
encoder_hidden_states=prompt_embeds.float(),
|
encoder_hidden_states=prompt_embeds.float(),
|
||||||
added_cond_kwargs=encoded_text,
|
added_cond_kwargs=encoded_text,
|
||||||
).sample
|
).sample
|
||||||
pred_x_0 = predicted_origin(
|
pred_x_0 = get_predicted_original_sample(
|
||||||
target_noise_pred,
|
target_noise_pred,
|
||||||
timesteps,
|
timesteps,
|
||||||
x_prev,
|
x_prev,
|
||||||
@@ -1337,7 +1387,7 @@ def main(args):
|
|||||||
)
|
)
|
||||||
target = c_skip * x_prev + c_out * pred_x_0
|
target = c_skip * x_prev + c_out * pred_x_0
|
||||||
|
|
||||||
# 20.4.13. Calculate loss
|
# 10. Calculate loss
|
||||||
if args.loss_type == "l2":
|
if args.loss_type == "l2":
|
||||||
loss = F.mse_loss(model_pred.float(), target.float(), reduction="mean")
|
loss = F.mse_loss(model_pred.float(), target.float(), reduction="mean")
|
||||||
elif args.loss_type == "huber":
|
elif args.loss_type == "huber":
|
||||||
@@ -1345,7 +1395,7 @@ def main(args):
|
|||||||
torch.sqrt((model_pred.float() - target.float()) ** 2 + args.huber_c**2) - args.huber_c
|
torch.sqrt((model_pred.float() - target.float()) ** 2 + args.huber_c**2) - args.huber_c
|
||||||
)
|
)
|
||||||
|
|
||||||
# 20.4.14. Backpropagate on the online student model (`unet`)
|
# 11. Backpropagate on the online student model (`unet`)
|
||||||
accelerator.backward(loss)
|
accelerator.backward(loss)
|
||||||
if accelerator.sync_gradients:
|
if accelerator.sync_gradients:
|
||||||
accelerator.clip_grad_norm_(unet.parameters(), args.max_grad_norm)
|
accelerator.clip_grad_norm_(unet.parameters(), args.max_grad_norm)
|
||||||
@@ -1355,7 +1405,7 @@ def main(args):
|
|||||||
|
|
||||||
# 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:
|
||||||
# 20.4.15. Make EMA update to target student model parameters
|
# 12. Make EMA update to target student model parameters (`target_unet`)
|
||||||
update_ema(target_unet.parameters(), unet.parameters(), args.ema_decay)
|
update_ema(target_unet.parameters(), unet.parameters(), args.ema_decay)
|
||||||
progress_bar.update(1)
|
progress_bar.update(1)
|
||||||
global_step += 1
|
global_step += 1
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ write_basic_config()
|
|||||||
```
|
```
|
||||||
|
|
||||||
When running `accelerate config`, if we specify torch compile mode to True there can be dramatic speedups.
|
When running `accelerate config`, if we specify torch compile mode to True there can be dramatic speedups.
|
||||||
|
Note also that we use PEFT library as backend for LoRA training, make sure to have `peft>=0.6.0` installed in your environment.
|
||||||
|
|
||||||
### Dog toy example
|
### Dog toy example
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ write_basic_config()
|
|||||||
```
|
```
|
||||||
|
|
||||||
When running `accelerate config`, if we specify torch compile mode to True there can be dramatic speedups.
|
When running `accelerate config`, if we specify torch compile mode to True there can be dramatic speedups.
|
||||||
|
Note also that we use PEFT library as backend for LoRA training, make sure to have `peft>=0.6.0` installed in your environment.
|
||||||
|
|
||||||
### Dog toy example
|
### Dog toy example
|
||||||
|
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ transformers>=4.25.1
|
|||||||
ftfy
|
ftfy
|
||||||
tensorboard
|
tensorboard
|
||||||
Jinja2
|
Jinja2
|
||||||
|
peft==0.7.0
|
||||||
@@ -4,3 +4,4 @@ transformers>=4.25.1
|
|||||||
ftfy
|
ftfy
|
||||||
tensorboard
|
tensorboard
|
||||||
Jinja2
|
Jinja2
|
||||||
|
peft==0.7.0
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import copy
|
import copy
|
||||||
import gc
|
import gc
|
||||||
import itertools
|
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
@@ -35,6 +34,8 @@ from accelerate.utils import ProjectConfiguration, set_seed
|
|||||||
from huggingface_hub import create_repo, upload_folder
|
from huggingface_hub import create_repo, upload_folder
|
||||||
from huggingface_hub.utils import insecure_hashlib
|
from huggingface_hub.utils import insecure_hashlib
|
||||||
from packaging import version
|
from packaging import version
|
||||||
|
from peft import LoraConfig
|
||||||
|
from peft.utils import get_peft_model_state_dict
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from PIL.ImageOps import exif_transpose
|
from PIL.ImageOps import exif_transpose
|
||||||
from torch.utils.data import Dataset
|
from torch.utils.data import Dataset
|
||||||
@@ -52,14 +53,7 @@ from diffusers import (
|
|||||||
UNet2DConditionModel,
|
UNet2DConditionModel,
|
||||||
)
|
)
|
||||||
from diffusers.loaders import LoraLoaderMixin
|
from diffusers.loaders import LoraLoaderMixin
|
||||||
from diffusers.models.attention_processor import (
|
|
||||||
AttnAddedKVProcessor,
|
|
||||||
AttnAddedKVProcessor2_0,
|
|
||||||
SlicedAttnAddedKVProcessor,
|
|
||||||
)
|
|
||||||
from diffusers.models.lora import LoRALinearLayer
|
|
||||||
from diffusers.optimization import get_scheduler
|
from diffusers.optimization import get_scheduler
|
||||||
from diffusers.training_utils import unet_lora_state_dict
|
|
||||||
from diffusers.utils import check_min_version, is_wandb_available
|
from diffusers.utils import check_min_version, is_wandb_available
|
||||||
from diffusers.utils.import_utils import is_xformers_available
|
from diffusers.utils.import_utils import is_xformers_available
|
||||||
|
|
||||||
@@ -864,79 +858,19 @@ def main(args):
|
|||||||
text_encoder.gradient_checkpointing_enable()
|
text_encoder.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
|
||||||
# It's important to realize here how many attention weights will be added and of which sizes
|
unet_lora_config = LoraConfig(
|
||||||
# The sizes of the attention layers consist only of two different variables:
|
r=args.rank,
|
||||||
# 1) - the "hidden_size", which is increased according to `unet.config.block_out_channels`.
|
init_lora_weights="gaussian",
|
||||||
# 2) - the "cross attention size", which is set to `unet.config.cross_attention_dim`.
|
target_modules=["to_k", "to_q", "to_v", "to_out.0", "add_k_proj", "add_v_proj"],
|
||||||
|
)
|
||||||
|
unet.add_adapter(unet_lora_config)
|
||||||
|
|
||||||
# Let's first see how many attention processors we will have to set.
|
# The text encoder comes from 🤗 transformers, we will also attach adapters to it.
|
||||||
# For Stable Diffusion, it should be equal to:
|
|
||||||
# - down blocks (2x attention layers) * (2x transformer layers) * (3x down blocks) = 12
|
|
||||||
# - mid blocks (2x attention layers) * (1x transformer layers) * (1x mid blocks) = 2
|
|
||||||
# - up blocks (2x attention layers) * (3x transformer layers) * (3x up blocks) = 18
|
|
||||||
# => 32 layers
|
|
||||||
|
|
||||||
# Set correct lora layers
|
|
||||||
unet_lora_parameters = []
|
|
||||||
for attn_processor_name, attn_processor in unet.attn_processors.items():
|
|
||||||
# Parse the attention module.
|
|
||||||
attn_module = unet
|
|
||||||
for n in attn_processor_name.split(".")[:-1]:
|
|
||||||
attn_module = getattr(attn_module, n)
|
|
||||||
|
|
||||||
# Set the `lora_layer` attribute of the attention-related matrices.
|
|
||||||
attn_module.to_q.set_lora_layer(
|
|
||||||
LoRALinearLayer(
|
|
||||||
in_features=attn_module.to_q.in_features, out_features=attn_module.to_q.out_features, rank=args.rank
|
|
||||||
)
|
|
||||||
)
|
|
||||||
attn_module.to_k.set_lora_layer(
|
|
||||||
LoRALinearLayer(
|
|
||||||
in_features=attn_module.to_k.in_features, out_features=attn_module.to_k.out_features, rank=args.rank
|
|
||||||
)
|
|
||||||
)
|
|
||||||
attn_module.to_v.set_lora_layer(
|
|
||||||
LoRALinearLayer(
|
|
||||||
in_features=attn_module.to_v.in_features, out_features=attn_module.to_v.out_features, rank=args.rank
|
|
||||||
)
|
|
||||||
)
|
|
||||||
attn_module.to_out[0].set_lora_layer(
|
|
||||||
LoRALinearLayer(
|
|
||||||
in_features=attn_module.to_out[0].in_features,
|
|
||||||
out_features=attn_module.to_out[0].out_features,
|
|
||||||
rank=args.rank,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Accumulate the LoRA params to optimize.
|
|
||||||
unet_lora_parameters.extend(attn_module.to_q.lora_layer.parameters())
|
|
||||||
unet_lora_parameters.extend(attn_module.to_k.lora_layer.parameters())
|
|
||||||
unet_lora_parameters.extend(attn_module.to_v.lora_layer.parameters())
|
|
||||||
unet_lora_parameters.extend(attn_module.to_out[0].lora_layer.parameters())
|
|
||||||
|
|
||||||
if isinstance(attn_processor, (AttnAddedKVProcessor, SlicedAttnAddedKVProcessor, AttnAddedKVProcessor2_0)):
|
|
||||||
attn_module.add_k_proj.set_lora_layer(
|
|
||||||
LoRALinearLayer(
|
|
||||||
in_features=attn_module.add_k_proj.in_features,
|
|
||||||
out_features=attn_module.add_k_proj.out_features,
|
|
||||||
rank=args.rank,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
attn_module.add_v_proj.set_lora_layer(
|
|
||||||
LoRALinearLayer(
|
|
||||||
in_features=attn_module.add_v_proj.in_features,
|
|
||||||
out_features=attn_module.add_v_proj.out_features,
|
|
||||||
rank=args.rank,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
unet_lora_parameters.extend(attn_module.add_k_proj.lora_layer.parameters())
|
|
||||||
unet_lora_parameters.extend(attn_module.add_v_proj.lora_layer.parameters())
|
|
||||||
|
|
||||||
# The text encoder comes from 🤗 transformers, so we cannot directly modify it.
|
|
||||||
# So, instead, we monkey-patch the forward calls of its attention-blocks.
|
|
||||||
if args.train_text_encoder:
|
if args.train_text_encoder:
|
||||||
# ensure that dtype is float32, even if rest of the model that isn't trained is loaded in fp16
|
text_lora_config = LoraConfig(
|
||||||
text_lora_parameters = LoraLoaderMixin._modify_text_encoder(text_encoder, dtype=torch.float32, rank=args.rank)
|
r=args.rank, init_lora_weights="gaussian", target_modules=["q_proj", "k_proj", "v_proj", "out_proj"]
|
||||||
|
)
|
||||||
|
text_encoder.add_adapter(text_lora_config)
|
||||||
|
|
||||||
# create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format
|
# create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format
|
||||||
def save_model_hook(models, weights, output_dir):
|
def save_model_hook(models, weights, output_dir):
|
||||||
@@ -946,11 +880,16 @@ def main(args):
|
|||||||
unet_lora_layers_to_save = None
|
unet_lora_layers_to_save = None
|
||||||
text_encoder_lora_layers_to_save = None
|
text_encoder_lora_layers_to_save = None
|
||||||
|
|
||||||
|
unet_lora_config = None
|
||||||
|
text_encoder_lora_config = None
|
||||||
|
|
||||||
for model in models:
|
for model in models:
|
||||||
if isinstance(model, type(accelerator.unwrap_model(unet))):
|
if isinstance(model, type(accelerator.unwrap_model(unet))):
|
||||||
unet_lora_layers_to_save = unet_lora_state_dict(model)
|
unet_lora_layers_to_save = get_peft_model_state_dict(model)
|
||||||
|
unet_lora_config = model.peft_config["default"]
|
||||||
elif isinstance(model, type(accelerator.unwrap_model(text_encoder))):
|
elif isinstance(model, type(accelerator.unwrap_model(text_encoder))):
|
||||||
text_encoder_lora_layers_to_save = text_encoder_lora_state_dict(model)
|
text_encoder_lora_layers_to_save = get_peft_model_state_dict(model)
|
||||||
|
text_encoder_lora_config = model.peft_config["default"]
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"unexpected save model: {model.__class__}")
|
raise ValueError(f"unexpected save model: {model.__class__}")
|
||||||
|
|
||||||
@@ -961,6 +900,8 @@ def main(args):
|
|||||||
output_dir,
|
output_dir,
|
||||||
unet_lora_layers=unet_lora_layers_to_save,
|
unet_lora_layers=unet_lora_layers_to_save,
|
||||||
text_encoder_lora_layers=text_encoder_lora_layers_to_save,
|
text_encoder_lora_layers=text_encoder_lora_layers_to_save,
|
||||||
|
unet_lora_config=unet_lora_config,
|
||||||
|
text_encoder_lora_config=text_encoder_lora_config,
|
||||||
)
|
)
|
||||||
|
|
||||||
def load_model_hook(models, input_dir):
|
def load_model_hook(models, input_dir):
|
||||||
@@ -977,10 +918,12 @@ def main(args):
|
|||||||
else:
|
else:
|
||||||
raise ValueError(f"unexpected save model: {model.__class__}")
|
raise ValueError(f"unexpected save model: {model.__class__}")
|
||||||
|
|
||||||
lora_state_dict, network_alphas = LoraLoaderMixin.lora_state_dict(input_dir)
|
lora_state_dict, network_alphas, metadata = LoraLoaderMixin.lora_state_dict(input_dir)
|
||||||
LoraLoaderMixin.load_lora_into_unet(lora_state_dict, network_alphas=network_alphas, unet=unet_)
|
LoraLoaderMixin.load_lora_into_unet(
|
||||||
|
lora_state_dict, network_alphas=network_alphas, unet=unet_, config=metadata
|
||||||
|
)
|
||||||
LoraLoaderMixin.load_lora_into_text_encoder(
|
LoraLoaderMixin.load_lora_into_text_encoder(
|
||||||
lora_state_dict, network_alphas=network_alphas, text_encoder=text_encoder_
|
lora_state_dict, network_alphas=network_alphas, text_encoder=text_encoder_, config=metadata
|
||||||
)
|
)
|
||||||
|
|
||||||
accelerator.register_save_state_pre_hook(save_model_hook)
|
accelerator.register_save_state_pre_hook(save_model_hook)
|
||||||
@@ -1010,11 +953,10 @@ def main(args):
|
|||||||
optimizer_class = torch.optim.AdamW
|
optimizer_class = torch.optim.AdamW
|
||||||
|
|
||||||
# Optimizer creation
|
# Optimizer creation
|
||||||
params_to_optimize = (
|
params_to_optimize = list(filter(lambda p: p.requires_grad, unet.parameters()))
|
||||||
itertools.chain(unet_lora_parameters, text_lora_parameters)
|
if args.train_text_encoder:
|
||||||
if args.train_text_encoder
|
params_to_optimize = params_to_optimize + list(filter(lambda p: p.requires_grad, text_encoder.parameters()))
|
||||||
else unet_lora_parameters
|
|
||||||
)
|
|
||||||
optimizer = optimizer_class(
|
optimizer = optimizer_class(
|
||||||
params_to_optimize,
|
params_to_optimize,
|
||||||
lr=args.learning_rate,
|
lr=args.learning_rate,
|
||||||
@@ -1257,12 +1199,7 @@ def main(args):
|
|||||||
|
|
||||||
accelerator.backward(loss)
|
accelerator.backward(loss)
|
||||||
if accelerator.sync_gradients:
|
if accelerator.sync_gradients:
|
||||||
params_to_clip = (
|
accelerator.clip_grad_norm_(params_to_optimize, args.max_grad_norm)
|
||||||
itertools.chain(unet_lora_parameters, text_lora_parameters)
|
|
||||||
if args.train_text_encoder
|
|
||||||
else unet_lora_parameters
|
|
||||||
)
|
|
||||||
accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm)
|
|
||||||
optimizer.step()
|
optimizer.step()
|
||||||
lr_scheduler.step()
|
lr_scheduler.step()
|
||||||
optimizer.zero_grad()
|
optimizer.zero_grad()
|
||||||
@@ -1385,19 +1322,24 @@ def main(args):
|
|||||||
if accelerator.is_main_process:
|
if accelerator.is_main_process:
|
||||||
unet = accelerator.unwrap_model(unet)
|
unet = accelerator.unwrap_model(unet)
|
||||||
unet = unet.to(torch.float32)
|
unet = unet.to(torch.float32)
|
||||||
unet_lora_layers = unet_lora_state_dict(unet)
|
|
||||||
|
|
||||||
if text_encoder is not None and args.train_text_encoder:
|
unet_lora_state_dict = get_peft_model_state_dict(unet)
|
||||||
|
unet_lora_config = unet.peft_config["default"]
|
||||||
|
|
||||||
|
if args.train_text_encoder:
|
||||||
text_encoder = accelerator.unwrap_model(text_encoder)
|
text_encoder = accelerator.unwrap_model(text_encoder)
|
||||||
text_encoder = text_encoder.to(torch.float32)
|
text_encoder_state_dict = get_peft_model_state_dict(text_encoder)
|
||||||
text_encoder_lora_layers = text_encoder_lora_state_dict(text_encoder)
|
text_encoder_lora_config = text_encoder.peft_config["default"]
|
||||||
else:
|
else:
|
||||||
text_encoder_lora_layers = None
|
text_encoder_state_dict = None
|
||||||
|
text_encoder_lora_config = None
|
||||||
|
|
||||||
LoraLoaderMixin.save_lora_weights(
|
LoraLoaderMixin.save_lora_weights(
|
||||||
save_directory=args.output_dir,
|
save_directory=args.output_dir,
|
||||||
unet_lora_layers=unet_lora_layers,
|
unet_lora_layers=unet_lora_state_dict,
|
||||||
text_encoder_lora_layers=text_encoder_lora_layers,
|
text_encoder_lora_layers=text_encoder_state_dict,
|
||||||
|
unet_lora_config=unet_lora_config,
|
||||||
|
text_encoder_lora_config=text_encoder_lora_config,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Final inference
|
# Final inference
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ from accelerate.utils import DistributedDataParallelKwargs, ProjectConfiguration
|
|||||||
from huggingface_hub import create_repo, upload_folder
|
from huggingface_hub import create_repo, upload_folder
|
||||||
from huggingface_hub.utils import insecure_hashlib
|
from huggingface_hub.utils import insecure_hashlib
|
||||||
from packaging import version
|
from packaging import version
|
||||||
|
from peft import LoraConfig
|
||||||
|
from peft.utils import get_peft_model_state_dict
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from PIL.ImageOps import exif_transpose
|
from PIL.ImageOps import exif_transpose
|
||||||
from torch.utils.data import Dataset
|
from torch.utils.data import Dataset
|
||||||
@@ -50,9 +52,8 @@ from diffusers import (
|
|||||||
UNet2DConditionModel,
|
UNet2DConditionModel,
|
||||||
)
|
)
|
||||||
from diffusers.loaders import LoraLoaderMixin
|
from diffusers.loaders import LoraLoaderMixin
|
||||||
from diffusers.models.lora import LoRALinearLayer
|
|
||||||
from diffusers.optimization import get_scheduler
|
from diffusers.optimization import get_scheduler
|
||||||
from diffusers.training_utils import compute_snr, unet_lora_state_dict
|
from diffusers.training_utils import compute_snr
|
||||||
from diffusers.utils import check_min_version, is_wandb_available
|
from diffusers.utils import check_min_version, is_wandb_available
|
||||||
from diffusers.utils.import_utils import is_xformers_available
|
from diffusers.utils.import_utils import is_xformers_available
|
||||||
|
|
||||||
@@ -1009,54 +1010,19 @@ def main(args):
|
|||||||
text_encoder_two.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
|
||||||
# Set correct lora layers
|
unet_lora_config = LoraConfig(
|
||||||
unet_lora_parameters = []
|
r=args.rank, init_lora_weights="gaussian", target_modules=["to_k", "to_q", "to_v", "to_out.0"]
|
||||||
for attn_processor_name, attn_processor in unet.attn_processors.items():
|
)
|
||||||
# Parse the attention module.
|
unet.add_adapter(unet_lora_config)
|
||||||
attn_module = unet
|
|
||||||
for n in attn_processor_name.split(".")[:-1]:
|
|
||||||
attn_module = getattr(attn_module, n)
|
|
||||||
|
|
||||||
# Set the `lora_layer` attribute of the attention-related matrices.
|
|
||||||
attn_module.to_q.set_lora_layer(
|
|
||||||
LoRALinearLayer(
|
|
||||||
in_features=attn_module.to_q.in_features, out_features=attn_module.to_q.out_features, rank=args.rank
|
|
||||||
)
|
|
||||||
)
|
|
||||||
attn_module.to_k.set_lora_layer(
|
|
||||||
LoRALinearLayer(
|
|
||||||
in_features=attn_module.to_k.in_features, out_features=attn_module.to_k.out_features, rank=args.rank
|
|
||||||
)
|
|
||||||
)
|
|
||||||
attn_module.to_v.set_lora_layer(
|
|
||||||
LoRALinearLayer(
|
|
||||||
in_features=attn_module.to_v.in_features, out_features=attn_module.to_v.out_features, rank=args.rank
|
|
||||||
)
|
|
||||||
)
|
|
||||||
attn_module.to_out[0].set_lora_layer(
|
|
||||||
LoRALinearLayer(
|
|
||||||
in_features=attn_module.to_out[0].in_features,
|
|
||||||
out_features=attn_module.to_out[0].out_features,
|
|
||||||
rank=args.rank,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Accumulate the LoRA params to optimize.
|
|
||||||
unet_lora_parameters.extend(attn_module.to_q.lora_layer.parameters())
|
|
||||||
unet_lora_parameters.extend(attn_module.to_k.lora_layer.parameters())
|
|
||||||
unet_lora_parameters.extend(attn_module.to_v.lora_layer.parameters())
|
|
||||||
unet_lora_parameters.extend(attn_module.to_out[0].lora_layer.parameters())
|
|
||||||
|
|
||||||
# The text encoder comes from 🤗 transformers, so we cannot directly modify it.
|
# The text encoder comes from 🤗 transformers, so we cannot directly modify it.
|
||||||
# So, instead, we monkey-patch the forward calls of its attention-blocks.
|
# So, instead, we monkey-patch the forward calls of its attention-blocks.
|
||||||
if args.train_text_encoder:
|
if args.train_text_encoder:
|
||||||
# ensure that dtype is float32, even if rest of the model that isn't trained is loaded in fp16
|
text_lora_config = LoraConfig(
|
||||||
text_lora_parameters_one = LoraLoaderMixin._modify_text_encoder(
|
r=args.rank, init_lora_weights="gaussian", target_modules=["q_proj", "k_proj", "v_proj", "out_proj"]
|
||||||
text_encoder_one, dtype=torch.float32, rank=args.rank
|
|
||||||
)
|
|
||||||
text_lora_parameters_two = LoraLoaderMixin._modify_text_encoder(
|
|
||||||
text_encoder_two, dtype=torch.float32, rank=args.rank
|
|
||||||
)
|
)
|
||||||
|
text_encoder_one.add_adapter(text_lora_config)
|
||||||
|
text_encoder_two.add_adapter(text_lora_config)
|
||||||
|
|
||||||
# create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format
|
# create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format
|
||||||
def save_model_hook(models, weights, output_dir):
|
def save_model_hook(models, weights, output_dir):
|
||||||
@@ -1067,13 +1033,20 @@ def main(args):
|
|||||||
text_encoder_one_lora_layers_to_save = None
|
text_encoder_one_lora_layers_to_save = None
|
||||||
text_encoder_two_lora_layers_to_save = None
|
text_encoder_two_lora_layers_to_save = None
|
||||||
|
|
||||||
|
unet_lora_config = None
|
||||||
|
text_encoder_one_lora_config = None
|
||||||
|
text_encoder_two_lora_config = None
|
||||||
|
|
||||||
for model in models:
|
for model in models:
|
||||||
if isinstance(model, type(accelerator.unwrap_model(unet))):
|
if isinstance(model, type(accelerator.unwrap_model(unet))):
|
||||||
unet_lora_layers_to_save = unet_lora_state_dict(model)
|
unet_lora_layers_to_save = get_peft_model_state_dict(model)
|
||||||
|
unet_lora_config = model.peft_config["default"]
|
||||||
elif isinstance(model, type(accelerator.unwrap_model(text_encoder_one))):
|
elif isinstance(model, type(accelerator.unwrap_model(text_encoder_one))):
|
||||||
text_encoder_one_lora_layers_to_save = text_encoder_lora_state_dict(model)
|
text_encoder_one_lora_layers_to_save = get_peft_model_state_dict(model)
|
||||||
|
text_encoder_one_lora_config = model.peft_config["default"]
|
||||||
elif isinstance(model, type(accelerator.unwrap_model(text_encoder_two))):
|
elif isinstance(model, type(accelerator.unwrap_model(text_encoder_two))):
|
||||||
text_encoder_two_lora_layers_to_save = text_encoder_lora_state_dict(model)
|
text_encoder_two_lora_layers_to_save = get_peft_model_state_dict(model)
|
||||||
|
text_encoder_two_lora_config = model.peft_config["default"]
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"unexpected save model: {model.__class__}")
|
raise ValueError(f"unexpected save model: {model.__class__}")
|
||||||
|
|
||||||
@@ -1085,6 +1058,9 @@ def main(args):
|
|||||||
unet_lora_layers=unet_lora_layers_to_save,
|
unet_lora_layers=unet_lora_layers_to_save,
|
||||||
text_encoder_lora_layers=text_encoder_one_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,
|
text_encoder_2_lora_layers=text_encoder_two_lora_layers_to_save,
|
||||||
|
unet_lora_config=unet_lora_config,
|
||||||
|
text_encoder_lora_config=text_encoder_one_lora_config,
|
||||||
|
text_encoder_2_lora_config=text_encoder_two_lora_config,
|
||||||
)
|
)
|
||||||
|
|
||||||
def load_model_hook(models, input_dir):
|
def load_model_hook(models, input_dir):
|
||||||
@@ -1104,17 +1080,19 @@ def main(args):
|
|||||||
else:
|
else:
|
||||||
raise ValueError(f"unexpected save model: {model.__class__}")
|
raise ValueError(f"unexpected save model: {model.__class__}")
|
||||||
|
|
||||||
lora_state_dict, network_alphas = LoraLoaderMixin.lora_state_dict(input_dir)
|
lora_state_dict, network_alphas, metadata = LoraLoaderMixin.lora_state_dict(input_dir)
|
||||||
LoraLoaderMixin.load_lora_into_unet(lora_state_dict, network_alphas=network_alphas, unet=unet_)
|
LoraLoaderMixin.load_lora_into_unet(
|
||||||
|
lora_state_dict, network_alphas=network_alphas, unet=unet_, config=metadata
|
||||||
|
)
|
||||||
|
|
||||||
text_encoder_state_dict = {k: v for k, v in lora_state_dict.items() if "text_encoder." in k}
|
text_encoder_state_dict = {k: v for k, v in lora_state_dict.items() if "text_encoder." in k}
|
||||||
LoraLoaderMixin.load_lora_into_text_encoder(
|
LoraLoaderMixin.load_lora_into_text_encoder(
|
||||||
text_encoder_state_dict, network_alphas=network_alphas, text_encoder=text_encoder_one_
|
text_encoder_state_dict, network_alphas=network_alphas, text_encoder=text_encoder_one_, config=metadata
|
||||||
)
|
)
|
||||||
|
|
||||||
text_encoder_2_state_dict = {k: v for k, v in lora_state_dict.items() if "text_encoder_2." in k}
|
text_encoder_2_state_dict = {k: v for k, v in lora_state_dict.items() if "text_encoder_2." in k}
|
||||||
LoraLoaderMixin.load_lora_into_text_encoder(
|
LoraLoaderMixin.load_lora_into_text_encoder(
|
||||||
text_encoder_2_state_dict, network_alphas=network_alphas, text_encoder=text_encoder_two_
|
text_encoder_2_state_dict, network_alphas=network_alphas, text_encoder=text_encoder_two_, config=metadata
|
||||||
)
|
)
|
||||||
|
|
||||||
accelerator.register_save_state_pre_hook(save_model_hook)
|
accelerator.register_save_state_pre_hook(save_model_hook)
|
||||||
@@ -1130,6 +1108,12 @@ def main(args):
|
|||||||
args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes
|
args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes
|
||||||
)
|
)
|
||||||
|
|
||||||
|
unet_lora_parameters = list(filter(lambda p: p.requires_grad, unet.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
|
||||||
unet_lora_parameters_with_lr = {"params": unet_lora_parameters, "lr": args.learning_rate}
|
unet_lora_parameters_with_lr = {"params": unet_lora_parameters, "lr": args.learning_rate}
|
||||||
if args.train_text_encoder:
|
if args.train_text_encoder:
|
||||||
@@ -1194,26 +1178,10 @@ def main(args):
|
|||||||
|
|
||||||
optimizer_class = prodigyopt.Prodigy
|
optimizer_class = prodigyopt.Prodigy
|
||||||
|
|
||||||
if args.learning_rate <= 0.1:
|
|
||||||
logger.warn(
|
|
||||||
"Learning rate is too low. When using prodigy, it's generally better to set learning rate around 1.0"
|
|
||||||
)
|
|
||||||
if args.train_text_encoder and args.text_encoder_lr:
|
|
||||||
logger.warn(
|
|
||||||
f"Learning rates were provided both for the unet and the text encoder- e.g. text_encoder_lr:"
|
|
||||||
f" {args.text_encoder_lr} and learning_rate: {args.learning_rate}. "
|
|
||||||
f"When using prodigy only learning_rate is used as the initial learning rate."
|
|
||||||
)
|
|
||||||
# changes the learning rate of text_encoder_parameters_one and text_encoder_parameters_two to be
|
|
||||||
# --learning_rate
|
|
||||||
params_to_optimize[1]["lr"] = args.learning_rate
|
|
||||||
params_to_optimize[2]["lr"] = args.learning_rate
|
|
||||||
|
|
||||||
optimizer = optimizer_class(
|
optimizer = optimizer_class(
|
||||||
params_to_optimize,
|
params_to_optimize,
|
||||||
lr=args.learning_rate,
|
lr=args.learning_rate,
|
||||||
betas=(args.adam_beta1, args.adam_beta2),
|
betas=(args.adam_beta1, args.adam_beta2),
|
||||||
beta3=args.prodigy_beta3,
|
|
||||||
weight_decay=args.adam_weight_decay,
|
weight_decay=args.adam_weight_decay,
|
||||||
eps=args.adam_epsilon,
|
eps=args.adam_epsilon,
|
||||||
decouple=args.prodigy_decouple,
|
decouple=args.prodigy_decouple,
|
||||||
@@ -1659,22 +1627,30 @@ def main(args):
|
|||||||
if accelerator.is_main_process:
|
if accelerator.is_main_process:
|
||||||
unet = accelerator.unwrap_model(unet)
|
unet = accelerator.unwrap_model(unet)
|
||||||
unet = unet.to(torch.float32)
|
unet = unet.to(torch.float32)
|
||||||
unet_lora_layers = unet_lora_state_dict(unet)
|
unet_lora_layers = get_peft_model_state_dict(unet)
|
||||||
|
unet_lora_config = unet.peft_config["default"]
|
||||||
|
|
||||||
if args.train_text_encoder:
|
if args.train_text_encoder:
|
||||||
text_encoder_one = accelerator.unwrap_model(text_encoder_one)
|
text_encoder_one = accelerator.unwrap_model(text_encoder_one)
|
||||||
text_encoder_lora_layers = text_encoder_lora_state_dict(text_encoder_one.to(torch.float32))
|
text_encoder_lora_layers = get_peft_model_state_dict(text_encoder_one.to(torch.float32))
|
||||||
text_encoder_two = accelerator.unwrap_model(text_encoder_two)
|
text_encoder_two = accelerator.unwrap_model(text_encoder_two)
|
||||||
text_encoder_2_lora_layers = text_encoder_lora_state_dict(text_encoder_two.to(torch.float32))
|
text_encoder_2_lora_layers = get_peft_model_state_dict(text_encoder_two.to(torch.float32))
|
||||||
|
text_encoder_one_lora_config = text_encoder_one.peft_config["default"]
|
||||||
|
text_encoder_two_lora_config = text_encoder_two.peft_config["default"]
|
||||||
else:
|
else:
|
||||||
text_encoder_lora_layers = None
|
text_encoder_lora_layers = None
|
||||||
text_encoder_2_lora_layers = None
|
text_encoder_2_lora_layers = None
|
||||||
|
text_encoder_one_lora_config = None
|
||||||
|
text_encoder_two_lora_config = None
|
||||||
|
|
||||||
StableDiffusionXLPipeline.save_lora_weights(
|
StableDiffusionXLPipeline.save_lora_weights(
|
||||||
save_directory=args.output_dir,
|
save_directory=args.output_dir,
|
||||||
unet_lora_layers=unet_lora_layers,
|
unet_lora_layers=unet_lora_layers,
|
||||||
text_encoder_lora_layers=text_encoder_lora_layers,
|
text_encoder_lora_layers=text_encoder_lora_layers,
|
||||||
text_encoder_2_lora_layers=text_encoder_2_lora_layers,
|
text_encoder_2_lora_layers=text_encoder_2_lora_layers,
|
||||||
|
unet_lora_config=unet_lora_config,
|
||||||
|
text_encoder_lora_config=text_encoder_one_lora_config,
|
||||||
|
text_encoder_2_lora_config=text_encoder_two_lora_config,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Final inference
|
# Final inference
|
||||||
|
|||||||
@@ -420,7 +420,7 @@ 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"
|
||||||
):
|
):
|
||||||
text_encoder_config = PretrainedConfig.from_pretrained(
|
text_encoder_config = PretrainedConfig.from_pretrained(
|
||||||
pretrained_model_name_or_path, subfolder=subfolder, revision=revision, use_auth_token=True
|
pretrained_model_name_or_path, subfolder=subfolder, revision=revision
|
||||||
)
|
)
|
||||||
model_class = text_encoder_config.architectures[0]
|
model_class = text_encoder_config.architectures[0]
|
||||||
|
|
||||||
@@ -975,7 +975,7 @@ def main(args):
|
|||||||
revision=args.revision,
|
revision=args.revision,
|
||||||
)
|
)
|
||||||
unet = UNet2DConditionModel.from_pretrained(
|
unet = UNet2DConditionModel.from_pretrained(
|
||||||
args.pretrained_model_name_or_path, subfolder="unet", revision=args.revision, use_auth_token=True
|
args.pretrained_model_name_or_path, subfolder="unet", revision=args.revision
|
||||||
)
|
)
|
||||||
|
|
||||||
if args.controlnet_model_name_or_path:
|
if args.controlnet_model_name_or_path:
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
## [Deprecated] Multi Token Textual Inversion
|
## [Deprecated] Multi Token Textual Inversion
|
||||||
|
|
||||||
**IMPORTART: This research project is deprecated. Multi Token Textual Inversion is now supported natively in [the officail textual inversion example](https://github.com/huggingface/diffusers/tree/main/examples/textual_inversion#running-locally-with-pytorch).**
|
**IMPORTART: This research project is deprecated. Multi Token Textual Inversion is now supported natively in [the official textual inversion example](https://github.com/huggingface/diffusers/tree/main/examples/textual_inversion#running-locally-with-pytorch).**
|
||||||
|
|
||||||
The author of this project is [Isamu Isozaki](https://github.com/isamu-isozaki) - please make sure to tag the author for issue and PRs as well as @patrickvonplaten.
|
The author of this project is [Isamu Isozaki](https://github.com/isamu-isozaki) - please make sure to tag the author for issue and PRs as well as @patrickvonplaten.
|
||||||
|
|
||||||
@@ -32,6 +32,8 @@ And initialize an [🤗Accelerate](https://github.com/huggingface/accelerate/) e
|
|||||||
accelerate config
|
accelerate config
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note also that we use PEFT library as backend for LoRA training, make sure to have `peft>=0.6.0` installed in your environment.
|
||||||
|
|
||||||
### Pokemon example
|
### Pokemon example
|
||||||
|
|
||||||
You need to accept the model license before downloading or using the weights. In this example we'll use model version `v1-4`, so you'll need to visit [its card](https://huggingface.co/CompVis/stable-diffusion-v1-4), read the license and tick the checkbox if you agree.
|
You need to accept the model license before downloading or using the weights. In this example we'll use model version `v1-4`, so you'll need to visit [its card](https://huggingface.co/CompVis/stable-diffusion-v1-4), read the license and tick the checkbox if you agree.
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ write_basic_config()
|
|||||||
```
|
```
|
||||||
|
|
||||||
When running `accelerate config`, if we specify torch compile mode to True there can be dramatic speedups.
|
When running `accelerate config`, if we specify torch compile mode to True there can be dramatic speedups.
|
||||||
|
Note also that we use PEFT library as backend for LoRA training, make sure to have `peft>=0.6.0` installed in your environment.
|
||||||
|
|
||||||
### Training
|
### Training
|
||||||
|
|
||||||
|
|||||||
@@ -5,3 +5,4 @@ datasets
|
|||||||
ftfy
|
ftfy
|
||||||
tensorboard
|
tensorboard
|
||||||
Jinja2
|
Jinja2
|
||||||
|
peft==0.7.0
|
||||||
@@ -5,3 +5,4 @@ ftfy
|
|||||||
tensorboard
|
tensorboard
|
||||||
Jinja2
|
Jinja2
|
||||||
datasets
|
datasets
|
||||||
|
peft==0.7.0
|
||||||
@@ -34,13 +34,14 @@ from accelerate.utils import ProjectConfiguration, set_seed
|
|||||||
from datasets import load_dataset
|
from datasets import load_dataset
|
||||||
from huggingface_hub import create_repo, upload_folder
|
from huggingface_hub import create_repo, upload_folder
|
||||||
from packaging import version
|
from packaging import version
|
||||||
|
from peft import LoraConfig
|
||||||
|
from peft.utils import get_peft_model_state_dict
|
||||||
from torchvision import transforms
|
from torchvision import transforms
|
||||||
from tqdm.auto import tqdm
|
from tqdm.auto import tqdm
|
||||||
from transformers import CLIPTextModel, CLIPTokenizer
|
from transformers import CLIPTextModel, CLIPTokenizer
|
||||||
|
|
||||||
import diffusers
|
import diffusers
|
||||||
from diffusers import AutoencoderKL, DDPMScheduler, DiffusionPipeline, UNet2DConditionModel
|
from diffusers import AutoencoderKL, DDPMScheduler, DiffusionPipeline, StableDiffusionPipeline, UNet2DConditionModel
|
||||||
from diffusers.models.lora import LoRALinearLayer
|
|
||||||
from diffusers.optimization import get_scheduler
|
from diffusers.optimization import get_scheduler
|
||||||
from diffusers.training_utils import compute_snr
|
from diffusers.training_utils import compute_snr
|
||||||
from diffusers.utils import check_min_version, is_wandb_available
|
from diffusers.utils import check_min_version, is_wandb_available
|
||||||
@@ -479,62 +480,20 @@ def main():
|
|||||||
elif accelerator.mixed_precision == "bf16":
|
elif accelerator.mixed_precision == "bf16":
|
||||||
weight_dtype = torch.bfloat16
|
weight_dtype = torch.bfloat16
|
||||||
|
|
||||||
|
# Freeze the unet parameters before adding adapters
|
||||||
|
for param in unet.parameters():
|
||||||
|
param.requires_grad_(False)
|
||||||
|
|
||||||
|
unet_lora_config = LoraConfig(
|
||||||
|
r=args.rank, init_lora_weights="gaussian", target_modules=["to_k", "to_q", "to_v", "to_out.0"]
|
||||||
|
)
|
||||||
|
|
||||||
# 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)
|
||||||
vae.to(accelerator.device, dtype=weight_dtype)
|
vae.to(accelerator.device, dtype=weight_dtype)
|
||||||
text_encoder.to(accelerator.device, dtype=weight_dtype)
|
text_encoder.to(accelerator.device, dtype=weight_dtype)
|
||||||
|
|
||||||
# now we will add new LoRA weights to the attention layers
|
unet.add_adapter(unet_lora_config)
|
||||||
# It's important to realize here how many attention weights will be added and of which sizes
|
|
||||||
# The sizes of the attention layers consist only of two different variables:
|
|
||||||
# 1) - the "hidden_size", which is increased according to `unet.config.block_out_channels`.
|
|
||||||
# 2) - the "cross attention size", which is set to `unet.config.cross_attention_dim`.
|
|
||||||
|
|
||||||
# Let's first see how many attention processors we will have to set.
|
|
||||||
# For Stable Diffusion, it should be equal to:
|
|
||||||
# - down blocks (2x attention layers) * (2x transformer layers) * (3x down blocks) = 12
|
|
||||||
# - mid blocks (2x attention layers) * (1x transformer layers) * (1x mid blocks) = 2
|
|
||||||
# - up blocks (2x attention layers) * (3x transformer layers) * (3x down blocks) = 18
|
|
||||||
# => 32 layers
|
|
||||||
|
|
||||||
# Set correct lora layers
|
|
||||||
unet_lora_parameters = []
|
|
||||||
for attn_processor_name, attn_processor in unet.attn_processors.items():
|
|
||||||
# Parse the attention module.
|
|
||||||
attn_module = unet
|
|
||||||
for n in attn_processor_name.split(".")[:-1]:
|
|
||||||
attn_module = getattr(attn_module, n)
|
|
||||||
|
|
||||||
# Set the `lora_layer` attribute of the attention-related matrices.
|
|
||||||
attn_module.to_q.set_lora_layer(
|
|
||||||
LoRALinearLayer(
|
|
||||||
in_features=attn_module.to_q.in_features, out_features=attn_module.to_q.out_features, rank=args.rank
|
|
||||||
)
|
|
||||||
)
|
|
||||||
attn_module.to_k.set_lora_layer(
|
|
||||||
LoRALinearLayer(
|
|
||||||
in_features=attn_module.to_k.in_features, out_features=attn_module.to_k.out_features, rank=args.rank
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
attn_module.to_v.set_lora_layer(
|
|
||||||
LoRALinearLayer(
|
|
||||||
in_features=attn_module.to_v.in_features, out_features=attn_module.to_v.out_features, rank=args.rank
|
|
||||||
)
|
|
||||||
)
|
|
||||||
attn_module.to_out[0].set_lora_layer(
|
|
||||||
LoRALinearLayer(
|
|
||||||
in_features=attn_module.to_out[0].in_features,
|
|
||||||
out_features=attn_module.to_out[0].out_features,
|
|
||||||
rank=args.rank,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Accumulate the LoRA params to optimize.
|
|
||||||
unet_lora_parameters.extend(attn_module.to_q.lora_layer.parameters())
|
|
||||||
unet_lora_parameters.extend(attn_module.to_k.lora_layer.parameters())
|
|
||||||
unet_lora_parameters.extend(attn_module.to_v.lora_layer.parameters())
|
|
||||||
unet_lora_parameters.extend(attn_module.to_out[0].lora_layer.parameters())
|
|
||||||
|
|
||||||
if args.enable_xformers_memory_efficient_attention:
|
if args.enable_xformers_memory_efficient_attention:
|
||||||
if is_xformers_available():
|
if is_xformers_available():
|
||||||
@@ -549,6 +508,8 @@ def main():
|
|||||||
else:
|
else:
|
||||||
raise ValueError("xformers is not available. Make sure it is installed correctly")
|
raise ValueError("xformers is not available. Make sure it is installed correctly")
|
||||||
|
|
||||||
|
lora_layers = filter(lambda p: p.requires_grad, unet.parameters())
|
||||||
|
|
||||||
# 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:
|
||||||
@@ -573,7 +534,7 @@ def main():
|
|||||||
optimizer_cls = torch.optim.AdamW
|
optimizer_cls = torch.optim.AdamW
|
||||||
|
|
||||||
optimizer = optimizer_cls(
|
optimizer = optimizer_cls(
|
||||||
unet_lora_parameters,
|
lora_layers,
|
||||||
lr=args.learning_rate,
|
lr=args.learning_rate,
|
||||||
betas=(args.adam_beta1, args.adam_beta2),
|
betas=(args.adam_beta1, args.adam_beta2),
|
||||||
weight_decay=args.adam_weight_decay,
|
weight_decay=args.adam_weight_decay,
|
||||||
@@ -700,8 +661,8 @@ def main():
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Prepare everything with our `accelerator`.
|
# Prepare everything with our `accelerator`.
|
||||||
unet_lora_parameters, optimizer, train_dataloader, lr_scheduler = accelerator.prepare(
|
unet, optimizer, train_dataloader, lr_scheduler = accelerator.prepare(
|
||||||
unet_lora_parameters, optimizer, train_dataloader, lr_scheduler
|
unet, 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.
|
||||||
@@ -833,7 +794,7 @@ def main():
|
|||||||
# Backpropagate
|
# Backpropagate
|
||||||
accelerator.backward(loss)
|
accelerator.backward(loss)
|
||||||
if accelerator.sync_gradients:
|
if accelerator.sync_gradients:
|
||||||
params_to_clip = unet_lora_parameters
|
params_to_clip = lora_layers
|
||||||
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()
|
||||||
lr_scheduler.step()
|
lr_scheduler.step()
|
||||||
@@ -870,6 +831,17 @@ def main():
|
|||||||
|
|
||||||
save_path = os.path.join(args.output_dir, f"checkpoint-{global_step}")
|
save_path = os.path.join(args.output_dir, f"checkpoint-{global_step}")
|
||||||
accelerator.save_state(save_path)
|
accelerator.save_state(save_path)
|
||||||
|
|
||||||
|
unet_lora_state_dict = get_peft_model_state_dict(unet)
|
||||||
|
unet_lora_config = unet.peft_config["default"]
|
||||||
|
|
||||||
|
StableDiffusionPipeline.save_lora_weights(
|
||||||
|
save_directory=save_path,
|
||||||
|
unet_lora_layers=unet_lora_state_dict,
|
||||||
|
unet_lora_config=unet_lora_config,
|
||||||
|
safe_serialization=True,
|
||||||
|
)
|
||||||
|
|
||||||
logger.info(f"Saved state to {save_path}")
|
logger.info(f"Saved state to {save_path}")
|
||||||
|
|
||||||
logs = {"step_loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0]}
|
logs = {"step_loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0]}
|
||||||
@@ -926,7 +898,15 @@ def main():
|
|||||||
accelerator.wait_for_everyone()
|
accelerator.wait_for_everyone()
|
||||||
if accelerator.is_main_process:
|
if accelerator.is_main_process:
|
||||||
unet = unet.to(torch.float32)
|
unet = unet.to(torch.float32)
|
||||||
unet.save_attn_procs(args.output_dir)
|
|
||||||
|
unet_lora_state_dict = get_peft_model_state_dict(unet)
|
||||||
|
unet_lora_config = unet.peft_config["default"]
|
||||||
|
StableDiffusionPipeline.save_lora_weights(
|
||||||
|
save_directory=args.output_dir,
|
||||||
|
unet_lora_layers=unet_lora_state_dict,
|
||||||
|
safe_serialization=True,
|
||||||
|
unet_lora_config=unet_lora_config,
|
||||||
|
)
|
||||||
|
|
||||||
if args.push_to_hub:
|
if args.push_to_hub:
|
||||||
save_model_card(
|
save_model_card(
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
"""Fine-tuning script for Stable Diffusion XL for text2image with support for LoRA."""
|
"""Fine-tuning script for Stable Diffusion XL for text2image with support for LoRA."""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import itertools
|
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
@@ -37,6 +36,8 @@ from accelerate.utils import DistributedDataParallelKwargs, ProjectConfiguration
|
|||||||
from datasets import load_dataset
|
from datasets import load_dataset
|
||||||
from huggingface_hub import create_repo, upload_folder
|
from huggingface_hub import create_repo, upload_folder
|
||||||
from packaging import version
|
from packaging import version
|
||||||
|
from peft import LoraConfig
|
||||||
|
from peft.utils import get_peft_model_state_dict
|
||||||
from torchvision import transforms
|
from torchvision import transforms
|
||||||
from torchvision.transforms.functional import crop
|
from torchvision.transforms.functional import crop
|
||||||
from tqdm.auto import tqdm
|
from tqdm.auto import tqdm
|
||||||
@@ -50,7 +51,6 @@ from diffusers import (
|
|||||||
UNet2DConditionModel,
|
UNet2DConditionModel,
|
||||||
)
|
)
|
||||||
from diffusers.loaders import LoraLoaderMixin
|
from diffusers.loaders import LoraLoaderMixin
|
||||||
from diffusers.models.lora import LoRALinearLayer
|
|
||||||
from diffusers.optimization import get_scheduler
|
from diffusers.optimization import get_scheduler
|
||||||
from diffusers.training_utils import compute_snr
|
from diffusers.training_utils import compute_snr
|
||||||
from diffusers.utils import check_min_version, is_wandb_available
|
from diffusers.utils import check_min_version, is_wandb_available
|
||||||
@@ -658,53 +658,20 @@ def main(args):
|
|||||||
|
|
||||||
# now we will add new LoRA weights to the attention layers
|
# now we will add new LoRA weights to the attention layers
|
||||||
# Set correct lora layers
|
# Set correct lora layers
|
||||||
unet_lora_parameters = []
|
unet_lora_config = LoraConfig(
|
||||||
for attn_processor_name, attn_processor in unet.attn_processors.items():
|
r=args.rank, init_lora_weights="gaussian", target_modules=["to_k", "to_q", "to_v", "to_out.0"]
|
||||||
# Parse the attention module.
|
)
|
||||||
attn_module = unet
|
|
||||||
for n in attn_processor_name.split(".")[:-1]:
|
|
||||||
attn_module = getattr(attn_module, n)
|
|
||||||
|
|
||||||
# Set the `lora_layer` attribute of the attention-related matrices.
|
unet.add_adapter(unet_lora_config)
|
||||||
attn_module.to_q.set_lora_layer(
|
|
||||||
LoRALinearLayer(
|
|
||||||
in_features=attn_module.to_q.in_features, out_features=attn_module.to_q.out_features, rank=args.rank
|
|
||||||
)
|
|
||||||
)
|
|
||||||
attn_module.to_k.set_lora_layer(
|
|
||||||
LoRALinearLayer(
|
|
||||||
in_features=attn_module.to_k.in_features, out_features=attn_module.to_k.out_features, rank=args.rank
|
|
||||||
)
|
|
||||||
)
|
|
||||||
attn_module.to_v.set_lora_layer(
|
|
||||||
LoRALinearLayer(
|
|
||||||
in_features=attn_module.to_v.in_features, out_features=attn_module.to_v.out_features, rank=args.rank
|
|
||||||
)
|
|
||||||
)
|
|
||||||
attn_module.to_out[0].set_lora_layer(
|
|
||||||
LoRALinearLayer(
|
|
||||||
in_features=attn_module.to_out[0].in_features,
|
|
||||||
out_features=attn_module.to_out[0].out_features,
|
|
||||||
rank=args.rank,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Accumulate the LoRA params to optimize.
|
# The text encoder comes from 🤗 transformers, we will also attach adapters to it.
|
||||||
unet_lora_parameters.extend(attn_module.to_q.lora_layer.parameters())
|
|
||||||
unet_lora_parameters.extend(attn_module.to_k.lora_layer.parameters())
|
|
||||||
unet_lora_parameters.extend(attn_module.to_v.lora_layer.parameters())
|
|
||||||
unet_lora_parameters.extend(attn_module.to_out[0].lora_layer.parameters())
|
|
||||||
|
|
||||||
# The text encoder comes from 🤗 transformers, so we cannot directly modify it.
|
|
||||||
# So, instead, we monkey-patch the forward calls of its attention-blocks.
|
|
||||||
if args.train_text_encoder:
|
if args.train_text_encoder:
|
||||||
# ensure that dtype is float32, even if rest of the model that isn't trained is loaded in fp16
|
# ensure that dtype is float32, even if rest of the model that isn't trained is loaded in fp16
|
||||||
text_lora_parameters_one = LoraLoaderMixin._modify_text_encoder(
|
text_lora_config = LoraConfig(
|
||||||
text_encoder_one, dtype=torch.float32, rank=args.rank
|
r=args.rank, init_lora_weights="gaussian", target_modules=["q_proj", "k_proj", "v_proj", "out_proj"]
|
||||||
)
|
|
||||||
text_lora_parameters_two = LoraLoaderMixin._modify_text_encoder(
|
|
||||||
text_encoder_two, dtype=torch.float32, rank=args.rank
|
|
||||||
)
|
)
|
||||||
|
text_encoder_one.add_adapter(text_lora_config)
|
||||||
|
text_encoder_two.add_adapter(text_lora_config)
|
||||||
|
|
||||||
# create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format
|
# create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format
|
||||||
def save_model_hook(models, weights, output_dir):
|
def save_model_hook(models, weights, output_dir):
|
||||||
@@ -715,13 +682,20 @@ def main(args):
|
|||||||
text_encoder_one_lora_layers_to_save = None
|
text_encoder_one_lora_layers_to_save = None
|
||||||
text_encoder_two_lora_layers_to_save = None
|
text_encoder_two_lora_layers_to_save = None
|
||||||
|
|
||||||
|
unet_lora_config = None
|
||||||
|
text_encoder_one_lora_config = None
|
||||||
|
text_encoder_two_lora_config = None
|
||||||
|
|
||||||
for model in models:
|
for model in models:
|
||||||
if isinstance(model, type(accelerator.unwrap_model(unet))):
|
if isinstance(model, type(accelerator.unwrap_model(unet))):
|
||||||
unet_lora_layers_to_save = unet_attn_processors_state_dict(model)
|
unet_lora_layers_to_save = get_peft_model_state_dict(model)
|
||||||
|
unet_lora_config = model.peft_config["default"]
|
||||||
elif isinstance(model, type(accelerator.unwrap_model(text_encoder_one))):
|
elif isinstance(model, type(accelerator.unwrap_model(text_encoder_one))):
|
||||||
text_encoder_one_lora_layers_to_save = text_encoder_lora_state_dict(model)
|
text_encoder_one_lora_layers_to_save = get_peft_model_state_dict(model)
|
||||||
|
text_encoder_one_lora_config = model.peft_config["default"]
|
||||||
elif isinstance(model, type(accelerator.unwrap_model(text_encoder_two))):
|
elif isinstance(model, type(accelerator.unwrap_model(text_encoder_two))):
|
||||||
text_encoder_two_lora_layers_to_save = text_encoder_lora_state_dict(model)
|
text_encoder_two_lora_layers_to_save = get_peft_model_state_dict(model)
|
||||||
|
text_encoder_two_lora_config = model.peft_config["default"]
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"unexpected save model: {model.__class__}")
|
raise ValueError(f"unexpected save model: {model.__class__}")
|
||||||
|
|
||||||
@@ -733,6 +707,9 @@ def main(args):
|
|||||||
unet_lora_layers=unet_lora_layers_to_save,
|
unet_lora_layers=unet_lora_layers_to_save,
|
||||||
text_encoder_lora_layers=text_encoder_one_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,
|
text_encoder_2_lora_layers=text_encoder_two_lora_layers_to_save,
|
||||||
|
unet_lora_config=unet_lora_config,
|
||||||
|
text_encoder_lora_config=text_encoder_one_lora_config,
|
||||||
|
text_encoder_2_lora_config=text_encoder_two_lora_config,
|
||||||
)
|
)
|
||||||
|
|
||||||
def load_model_hook(models, input_dir):
|
def load_model_hook(models, input_dir):
|
||||||
@@ -752,17 +729,19 @@ def main(args):
|
|||||||
else:
|
else:
|
||||||
raise ValueError(f"unexpected save model: {model.__class__}")
|
raise ValueError(f"unexpected save model: {model.__class__}")
|
||||||
|
|
||||||
lora_state_dict, network_alphas = LoraLoaderMixin.lora_state_dict(input_dir)
|
lora_state_dict, network_alphas, metadata = LoraLoaderMixin.lora_state_dict(input_dir)
|
||||||
LoraLoaderMixin.load_lora_into_unet(lora_state_dict, network_alphas=network_alphas, unet=unet_)
|
LoraLoaderMixin.load_lora_into_unet(
|
||||||
|
lora_state_dict, network_alphas=network_alphas, unet=unet_, config=metadata
|
||||||
|
)
|
||||||
|
|
||||||
text_encoder_state_dict = {k: v for k, v in lora_state_dict.items() if "text_encoder." in k}
|
text_encoder_state_dict = {k: v for k, v in lora_state_dict.items() if "text_encoder." in k}
|
||||||
LoraLoaderMixin.load_lora_into_text_encoder(
|
LoraLoaderMixin.load_lora_into_text_encoder(
|
||||||
text_encoder_state_dict, network_alphas=network_alphas, text_encoder=text_encoder_one_
|
text_encoder_state_dict, network_alphas=network_alphas, text_encoder=text_encoder_one_, config=metadata
|
||||||
)
|
)
|
||||||
|
|
||||||
text_encoder_2_state_dict = {k: v for k, v in lora_state_dict.items() if "text_encoder_2." in k}
|
text_encoder_2_state_dict = {k: v for k, v in lora_state_dict.items() if "text_encoder_2." in k}
|
||||||
LoraLoaderMixin.load_lora_into_text_encoder(
|
LoraLoaderMixin.load_lora_into_text_encoder(
|
||||||
text_encoder_2_state_dict, network_alphas=network_alphas, text_encoder=text_encoder_two_
|
text_encoder_2_state_dict, network_alphas=network_alphas, text_encoder=text_encoder_two_, config=metadata
|
||||||
)
|
)
|
||||||
|
|
||||||
accelerator.register_save_state_pre_hook(save_model_hook)
|
accelerator.register_save_state_pre_hook(save_model_hook)
|
||||||
@@ -792,11 +771,13 @@ def main(args):
|
|||||||
optimizer_class = torch.optim.AdamW
|
optimizer_class = torch.optim.AdamW
|
||||||
|
|
||||||
# Optimizer creation
|
# Optimizer creation
|
||||||
params_to_optimize = (
|
params_to_optimize = list(filter(lambda p: p.requires_grad, unet.parameters()))
|
||||||
itertools.chain(unet_lora_parameters, text_lora_parameters_one, text_lora_parameters_two)
|
if args.train_text_encoder:
|
||||||
if args.train_text_encoder
|
params_to_optimize = (
|
||||||
else unet_lora_parameters
|
params_to_optimize
|
||||||
)
|
+ list(filter(lambda p: p.requires_grad, text_encoder_one.parameters()))
|
||||||
|
+ list(filter(lambda p: p.requires_grad, text_encoder_two.parameters()))
|
||||||
|
)
|
||||||
optimizer = optimizer_class(
|
optimizer = optimizer_class(
|
||||||
params_to_optimize,
|
params_to_optimize,
|
||||||
lr=args.learning_rate,
|
lr=args.learning_rate,
|
||||||
@@ -1128,12 +1109,7 @@ def main(args):
|
|||||||
# Backpropagate
|
# Backpropagate
|
||||||
accelerator.backward(loss)
|
accelerator.backward(loss)
|
||||||
if accelerator.sync_gradients:
|
if accelerator.sync_gradients:
|
||||||
params_to_clip = (
|
accelerator.clip_grad_norm_(params_to_optimize, args.max_grad_norm)
|
||||||
itertools.chain(unet_lora_parameters, text_lora_parameters_one, text_lora_parameters_two)
|
|
||||||
if args.train_text_encoder
|
|
||||||
else unet_lora_parameters
|
|
||||||
)
|
|
||||||
accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm)
|
|
||||||
optimizer.step()
|
optimizer.step()
|
||||||
lr_scheduler.step()
|
lr_scheduler.step()
|
||||||
optimizer.zero_grad()
|
optimizer.zero_grad()
|
||||||
@@ -1229,22 +1205,32 @@ def main(args):
|
|||||||
accelerator.wait_for_everyone()
|
accelerator.wait_for_everyone()
|
||||||
if accelerator.is_main_process:
|
if accelerator.is_main_process:
|
||||||
unet = accelerator.unwrap_model(unet)
|
unet = accelerator.unwrap_model(unet)
|
||||||
unet_lora_layers = unet_attn_processors_state_dict(unet)
|
unet_lora_state_dict = get_peft_model_state_dict(unet)
|
||||||
|
unet_lora_config = unet.peft_config["default"]
|
||||||
|
|
||||||
if args.train_text_encoder:
|
if args.train_text_encoder:
|
||||||
text_encoder_one = accelerator.unwrap_model(text_encoder_one)
|
text_encoder_one = accelerator.unwrap_model(text_encoder_one)
|
||||||
text_encoder_lora_layers = text_encoder_lora_state_dict(text_encoder_one)
|
|
||||||
text_encoder_two = accelerator.unwrap_model(text_encoder_two)
|
text_encoder_two = accelerator.unwrap_model(text_encoder_two)
|
||||||
text_encoder_2_lora_layers = text_encoder_lora_state_dict(text_encoder_two)
|
|
||||||
|
text_encoder_lora_layers = get_peft_model_state_dict(text_encoder_one)
|
||||||
|
text_encoder_2_lora_layers = get_peft_model_state_dict(text_encoder_two)
|
||||||
|
|
||||||
|
text_encoder_one_lora_config = text_encoder_one.peft_config["default"]
|
||||||
|
text_encoder_two_lora_config = text_encoder_two.peft_config["default"]
|
||||||
else:
|
else:
|
||||||
text_encoder_lora_layers = None
|
text_encoder_lora_layers = None
|
||||||
text_encoder_2_lora_layers = None
|
text_encoder_2_lora_layers = None
|
||||||
|
text_encoder_one_lora_config = None
|
||||||
|
text_encoder_two_lora_config = None
|
||||||
|
|
||||||
StableDiffusionXLPipeline.save_lora_weights(
|
StableDiffusionXLPipeline.save_lora_weights(
|
||||||
save_directory=args.output_dir,
|
save_directory=args.output_dir,
|
||||||
unet_lora_layers=unet_lora_layers,
|
unet_lora_layers=unet_lora_state_dict,
|
||||||
text_encoder_lora_layers=text_encoder_lora_layers,
|
text_encoder_lora_layers=text_encoder_lora_layers,
|
||||||
text_encoder_2_lora_layers=text_encoder_2_lora_layers,
|
text_encoder_2_lora_layers=text_encoder_2_lora_layers,
|
||||||
|
unet_lora_config=unet_lora_config,
|
||||||
|
text_encoder_lora_config=text_encoder_one_lora_config,
|
||||||
|
text_encoder_2_lora_config=text_encoder_two_lora_config,
|
||||||
)
|
)
|
||||||
|
|
||||||
del unet
|
del unet
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ class DepsTableUpdateCommand(Command):
|
|||||||
extras = {}
|
extras = {}
|
||||||
extras["quality"] = deps_list("urllib3", "isort", "ruff", "hf-doc-builder")
|
extras["quality"] = deps_list("urllib3", "isort", "ruff", "hf-doc-builder")
|
||||||
extras["docs"] = deps_list("hf-doc-builder")
|
extras["docs"] = deps_list("hf-doc-builder")
|
||||||
extras["training"] = deps_list("accelerate", "datasets", "protobuf", "tensorboard", "Jinja2")
|
extras["training"] = deps_list("accelerate", "datasets", "protobuf", "tensorboard", "Jinja2", "peft")
|
||||||
extras["test"] = deps_list(
|
extras["test"] = deps_list(
|
||||||
"compel",
|
"compel",
|
||||||
"GitPython",
|
"GitPython",
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ else:
|
|||||||
"AutoencoderTiny",
|
"AutoencoderTiny",
|
||||||
"ConsistencyDecoderVAE",
|
"ConsistencyDecoderVAE",
|
||||||
"ControlNetModel",
|
"ControlNetModel",
|
||||||
|
"ControlNetXSModel",
|
||||||
"Kandinsky3UNet",
|
"Kandinsky3UNet",
|
||||||
"ModelMixin",
|
"ModelMixin",
|
||||||
"MotionAdapter",
|
"MotionAdapter",
|
||||||
@@ -250,6 +251,7 @@ else:
|
|||||||
"StableDiffusionControlNetImg2ImgPipeline",
|
"StableDiffusionControlNetImg2ImgPipeline",
|
||||||
"StableDiffusionControlNetInpaintPipeline",
|
"StableDiffusionControlNetInpaintPipeline",
|
||||||
"StableDiffusionControlNetPipeline",
|
"StableDiffusionControlNetPipeline",
|
||||||
|
"StableDiffusionControlNetXSPipeline",
|
||||||
"StableDiffusionDepth2ImgPipeline",
|
"StableDiffusionDepth2ImgPipeline",
|
||||||
"StableDiffusionDiffEditPipeline",
|
"StableDiffusionDiffEditPipeline",
|
||||||
"StableDiffusionGLIGENPipeline",
|
"StableDiffusionGLIGENPipeline",
|
||||||
@@ -273,6 +275,7 @@ else:
|
|||||||
"StableDiffusionXLControlNetImg2ImgPipeline",
|
"StableDiffusionXLControlNetImg2ImgPipeline",
|
||||||
"StableDiffusionXLControlNetInpaintPipeline",
|
"StableDiffusionXLControlNetInpaintPipeline",
|
||||||
"StableDiffusionXLControlNetPipeline",
|
"StableDiffusionXLControlNetPipeline",
|
||||||
|
"StableDiffusionXLControlNetXSPipeline",
|
||||||
"StableDiffusionXLImg2ImgPipeline",
|
"StableDiffusionXLImg2ImgPipeline",
|
||||||
"StableDiffusionXLInpaintPipeline",
|
"StableDiffusionXLInpaintPipeline",
|
||||||
"StableDiffusionXLInstructPix2PixPipeline",
|
"StableDiffusionXLInstructPix2PixPipeline",
|
||||||
@@ -454,6 +457,7 @@ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
|||||||
AutoencoderTiny,
|
AutoencoderTiny,
|
||||||
ConsistencyDecoderVAE,
|
ConsistencyDecoderVAE,
|
||||||
ControlNetModel,
|
ControlNetModel,
|
||||||
|
ControlNetXSModel,
|
||||||
Kandinsky3UNet,
|
Kandinsky3UNet,
|
||||||
ModelMixin,
|
ModelMixin,
|
||||||
MotionAdapter,
|
MotionAdapter,
|
||||||
@@ -603,6 +607,7 @@ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
|||||||
StableDiffusionControlNetImg2ImgPipeline,
|
StableDiffusionControlNetImg2ImgPipeline,
|
||||||
StableDiffusionControlNetInpaintPipeline,
|
StableDiffusionControlNetInpaintPipeline,
|
||||||
StableDiffusionControlNetPipeline,
|
StableDiffusionControlNetPipeline,
|
||||||
|
StableDiffusionControlNetXSPipeline,
|
||||||
StableDiffusionDepth2ImgPipeline,
|
StableDiffusionDepth2ImgPipeline,
|
||||||
StableDiffusionDiffEditPipeline,
|
StableDiffusionDiffEditPipeline,
|
||||||
StableDiffusionGLIGENPipeline,
|
StableDiffusionGLIGENPipeline,
|
||||||
@@ -626,6 +631,7 @@ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
|||||||
StableDiffusionXLControlNetImg2ImgPipeline,
|
StableDiffusionXLControlNetImg2ImgPipeline,
|
||||||
StableDiffusionXLControlNetInpaintPipeline,
|
StableDiffusionXLControlNetInpaintPipeline,
|
||||||
StableDiffusionXLControlNetPipeline,
|
StableDiffusionXLControlNetPipeline,
|
||||||
|
StableDiffusionXLControlNetXSPipeline,
|
||||||
StableDiffusionXLImg2ImgPipeline,
|
StableDiffusionXLImg2ImgPipeline,
|
||||||
StableDiffusionXLInpaintPipeline,
|
StableDiffusionXLInpaintPipeline,
|
||||||
StableDiffusionXLInstructPix2PixPipeline,
|
StableDiffusionXLInstructPix2PixPipeline,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ Usage example:
|
|||||||
|
|
||||||
import glob
|
import glob
|
||||||
import json
|
import json
|
||||||
|
import warnings
|
||||||
from argparse import ArgumentParser, Namespace
|
from argparse import ArgumentParser, Namespace
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
|
|
||||||
@@ -32,12 +33,12 @@ from . import BaseDiffusersCLICommand
|
|||||||
|
|
||||||
|
|
||||||
def conversion_command_factory(args: Namespace):
|
def conversion_command_factory(args: Namespace):
|
||||||
return FP16SafetensorsCommand(
|
if args.use_auth_token:
|
||||||
args.ckpt_id,
|
warnings.warn(
|
||||||
args.fp16,
|
"The `--use_auth_token` flag is deprecated and will be removed in a future version. Authentication is now"
|
||||||
args.use_safetensors,
|
" handled automatically if user is logged in."
|
||||||
args.use_auth_token,
|
)
|
||||||
)
|
return FP16SafetensorsCommand(args.ckpt_id, args.fp16, args.use_safetensors)
|
||||||
|
|
||||||
|
|
||||||
class FP16SafetensorsCommand(BaseDiffusersCLICommand):
|
class FP16SafetensorsCommand(BaseDiffusersCLICommand):
|
||||||
@@ -62,7 +63,7 @@ class FP16SafetensorsCommand(BaseDiffusersCLICommand):
|
|||||||
)
|
)
|
||||||
conversion_parser.set_defaults(func=conversion_command_factory)
|
conversion_parser.set_defaults(func=conversion_command_factory)
|
||||||
|
|
||||||
def __init__(self, ckpt_id: str, fp16: bool, use_safetensors: bool, use_auth_token: bool):
|
def __init__(self, ckpt_id: str, fp16: bool, use_safetensors: bool):
|
||||||
self.logger = logging.get_logger("diffusers-cli/fp16_safetensors")
|
self.logger = logging.get_logger("diffusers-cli/fp16_safetensors")
|
||||||
self.ckpt_id = ckpt_id
|
self.ckpt_id = ckpt_id
|
||||||
self.local_ckpt_dir = f"/tmp/{ckpt_id}"
|
self.local_ckpt_dir = f"/tmp/{ckpt_id}"
|
||||||
@@ -75,8 +76,6 @@ class FP16SafetensorsCommand(BaseDiffusersCLICommand):
|
|||||||
"When `use_safetensors` and `fp16` both are False, then this command is of no use."
|
"When `use_safetensors` and `fp16` both are False, then this command is of no use."
|
||||||
)
|
)
|
||||||
|
|
||||||
self.use_auth_token = use_auth_token
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if version.parse(huggingface_hub.__version__) < version.parse("0.9.0"):
|
if version.parse(huggingface_hub.__version__) < version.parse("0.9.0"):
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
@@ -87,7 +86,7 @@ class FP16SafetensorsCommand(BaseDiffusersCLICommand):
|
|||||||
from huggingface_hub import create_commit
|
from huggingface_hub import create_commit
|
||||||
from huggingface_hub._commit_api import CommitOperationAdd
|
from huggingface_hub._commit_api import CommitOperationAdd
|
||||||
|
|
||||||
model_index = hf_hub_download(repo_id=self.ckpt_id, filename="model_index.json", token=self.use_auth_token)
|
model_index = hf_hub_download(repo_id=self.ckpt_id, filename="model_index.json")
|
||||||
with open(model_index, "r") as f:
|
with open(model_index, "r") as f:
|
||||||
pipeline_class_name = json.load(f)["_class_name"]
|
pipeline_class_name = json.load(f)["_class_name"]
|
||||||
pipeline_class = getattr(import_module("diffusers"), pipeline_class_name)
|
pipeline_class = getattr(import_module("diffusers"), pipeline_class_name)
|
||||||
@@ -96,7 +95,7 @@ class FP16SafetensorsCommand(BaseDiffusersCLICommand):
|
|||||||
# Load the appropriate pipeline. We could have use `DiffusionPipeline`
|
# Load the appropriate pipeline. We could have use `DiffusionPipeline`
|
||||||
# here, but just to avoid any rough edge cases.
|
# here, but just to avoid any rough edge cases.
|
||||||
pipeline = pipeline_class.from_pretrained(
|
pipeline = pipeline_class.from_pretrained(
|
||||||
self.ckpt_id, torch_dtype=torch.float16 if self.fp16 else torch.float32, use_auth_token=self.use_auth_token
|
self.ckpt_id, torch_dtype=torch.float16 if self.fp16 else torch.float32
|
||||||
)
|
)
|
||||||
pipeline.save_pretrained(
|
pipeline.save_pretrained(
|
||||||
self.local_ckpt_dir,
|
self.local_ckpt_dir,
|
||||||
|
|||||||
@@ -27,12 +27,16 @@ from typing import Any, Dict, Tuple, Union
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from huggingface_hub import create_repo, hf_hub_download
|
from huggingface_hub import create_repo, hf_hub_download
|
||||||
from huggingface_hub.utils import EntryNotFoundError, RepositoryNotFoundError, RevisionNotFoundError
|
from huggingface_hub.utils import (
|
||||||
|
EntryNotFoundError,
|
||||||
|
RepositoryNotFoundError,
|
||||||
|
RevisionNotFoundError,
|
||||||
|
validate_hf_hub_args,
|
||||||
|
)
|
||||||
from requests import HTTPError
|
from requests import HTTPError
|
||||||
|
|
||||||
from . import __version__
|
from . import __version__
|
||||||
from .utils import (
|
from .utils import (
|
||||||
DIFFUSERS_CACHE,
|
|
||||||
HUGGINGFACE_CO_RESOLVE_ENDPOINT,
|
HUGGINGFACE_CO_RESOLVE_ENDPOINT,
|
||||||
DummyObject,
|
DummyObject,
|
||||||
deprecate,
|
deprecate,
|
||||||
@@ -275,6 +279,7 @@ class ConfigMixin:
|
|||||||
return cls.load_config(*args, **kwargs)
|
return cls.load_config(*args, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@validate_hf_hub_args
|
||||||
def load_config(
|
def load_config(
|
||||||
cls,
|
cls,
|
||||||
pretrained_model_name_or_path: Union[str, os.PathLike],
|
pretrained_model_name_or_path: Union[str, os.PathLike],
|
||||||
@@ -311,7 +316,7 @@ class ConfigMixin:
|
|||||||
local_files_only (`bool`, *optional*, defaults to `False`):
|
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
|
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.
|
won't be downloaded from the Hub.
|
||||||
use_auth_token (`str` or *bool*, *optional*):
|
token (`str` or *bool*, *optional*):
|
||||||
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
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.
|
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
||||||
revision (`str`, *optional*, defaults to `"main"`):
|
revision (`str`, *optional*, defaults to `"main"`):
|
||||||
@@ -329,11 +334,11 @@ class ConfigMixin:
|
|||||||
A dictionary of all the parameters stored in a JSON configuration file.
|
A dictionary of all the parameters stored in a JSON configuration file.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
force_download = kwargs.pop("force_download", False)
|
force_download = kwargs.pop("force_download", False)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", False)
|
local_files_only = kwargs.pop("local_files_only", False)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
_ = kwargs.pop("mirror", None)
|
_ = kwargs.pop("mirror", None)
|
||||||
@@ -376,7 +381,7 @@ class ConfigMixin:
|
|||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
user_agent=user_agent,
|
user_agent=user_agent,
|
||||||
subfolder=subfolder,
|
subfolder=subfolder,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
@@ -385,8 +390,7 @@ class ConfigMixin:
|
|||||||
raise EnvironmentError(
|
raise EnvironmentError(
|
||||||
f"{pretrained_model_name_or_path} is not a local folder and is not a valid model identifier"
|
f"{pretrained_model_name_or_path} is not a local folder and is not a valid model identifier"
|
||||||
" listed on 'https://huggingface.co/models'\nIf this is a private repository, make sure to pass a"
|
" listed on 'https://huggingface.co/models'\nIf this is a private repository, make sure to pass a"
|
||||||
" token having permission to this repo with `use_auth_token` or log in with `huggingface-cli"
|
" token having permission to this repo with `token` or log in with `huggingface-cli login`."
|
||||||
" login`."
|
|
||||||
)
|
)
|
||||||
except RevisionNotFoundError:
|
except RevisionNotFoundError:
|
||||||
raise EnvironmentError(
|
raise EnvironmentError(
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class VaeImageProcessor(ConfigMixin):
|
|||||||
self.config.do_convert_rgb = False
|
self.config.do_convert_rgb = False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def numpy_to_pil(images: np.ndarray) -> PIL.Image.Image:
|
def numpy_to_pil(images: np.ndarray) -> List[PIL.Image.Image]:
|
||||||
"""
|
"""
|
||||||
Convert a numpy image or a batch of images to a PIL image.
|
Convert a numpy image or a batch of images to a PIL image.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -15,11 +15,10 @@ import os
|
|||||||
from typing import Dict, Union
|
from typing import Dict, Union
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
|
from huggingface_hub.utils import validate_hf_hub_args
|
||||||
from safetensors import safe_open
|
from safetensors import safe_open
|
||||||
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
DIFFUSERS_CACHE,
|
|
||||||
HF_HUB_OFFLINE,
|
|
||||||
_get_model_file,
|
_get_model_file,
|
||||||
is_transformers_available,
|
is_transformers_available,
|
||||||
logging,
|
logging,
|
||||||
@@ -43,6 +42,7 @@ logger = logging.get_logger(__name__)
|
|||||||
class IPAdapterMixin:
|
class IPAdapterMixin:
|
||||||
"""Mixin for handling IP Adapters."""
|
"""Mixin for handling IP Adapters."""
|
||||||
|
|
||||||
|
@validate_hf_hub_args
|
||||||
def load_ip_adapter(
|
def load_ip_adapter(
|
||||||
self,
|
self,
|
||||||
pretrained_model_name_or_path_or_dict: Union[str, Dict[str, torch.Tensor]],
|
pretrained_model_name_or_path_or_dict: Union[str, Dict[str, torch.Tensor]],
|
||||||
@@ -77,7 +77,7 @@ class IPAdapterMixin:
|
|||||||
local_files_only (`bool`, *optional*, defaults to `False`):
|
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
|
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.
|
won't be downloaded from the Hub.
|
||||||
use_auth_token (`str` or *bool*, *optional*):
|
token (`str` or *bool*, *optional*):
|
||||||
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
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.
|
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
||||||
revision (`str`, *optional*, defaults to `"main"`):
|
revision (`str`, *optional*, defaults to `"main"`):
|
||||||
@@ -88,12 +88,12 @@ class IPAdapterMixin:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Load the main state dict first.
|
# Load the main state dict first.
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
force_download = kwargs.pop("force_download", False)
|
force_download = kwargs.pop("force_download", False)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", HF_HUB_OFFLINE)
|
local_files_only = kwargs.pop("local_files_only", None)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
|
|
||||||
user_agent = {
|
user_agent = {
|
||||||
@@ -110,7 +110,7 @@ class IPAdapterMixin:
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
subfolder=subfolder,
|
subfolder=subfolder,
|
||||||
user_agent=user_agent,
|
user_agent=user_agent,
|
||||||
|
|||||||
+152
-35
@@ -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 json
|
||||||
import os
|
import os
|
||||||
from contextlib import nullcontext
|
from contextlib import nullcontext
|
||||||
from typing import Callable, Dict, List, Optional, Union
|
from typing import Callable, Dict, List, Optional, Union
|
||||||
@@ -18,14 +19,14 @@ from typing import Callable, Dict, List, Optional, Union
|
|||||||
import safetensors
|
import safetensors
|
||||||
import torch
|
import torch
|
||||||
from huggingface_hub import model_info
|
from huggingface_hub import model_info
|
||||||
|
from huggingface_hub.constants import HF_HUB_OFFLINE
|
||||||
|
from huggingface_hub.utils import validate_hf_hub_args
|
||||||
from packaging import version
|
from packaging import version
|
||||||
from torch import nn
|
from torch import nn
|
||||||
|
|
||||||
from .. import __version__
|
from .. import __version__
|
||||||
from ..models.modeling_utils import _LOW_CPU_MEM_USAGE_DEFAULT, load_model_dict_into_meta
|
from ..models.modeling_utils import _LOW_CPU_MEM_USAGE_DEFAULT, load_model_dict_into_meta
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
DIFFUSERS_CACHE,
|
|
||||||
HF_HUB_OFFLINE,
|
|
||||||
USE_PEFT_BACKEND,
|
USE_PEFT_BACKEND,
|
||||||
_get_model_file,
|
_get_model_file,
|
||||||
convert_state_dict_to_diffusers,
|
convert_state_dict_to_diffusers,
|
||||||
@@ -103,7 +104,7 @@ class LoraLoaderMixin:
|
|||||||
`default_{i}` where i is the total number of adapters being loaded.
|
`default_{i}` where i is the total number of adapters being loaded.
|
||||||
"""
|
"""
|
||||||
# First, ensure that the checkpoint is a compatible one and can be successfully loaded.
|
# First, ensure that the checkpoint is a compatible one and can be successfully loaded.
|
||||||
state_dict, network_alphas = self.lora_state_dict(pretrained_model_name_or_path_or_dict, **kwargs)
|
state_dict, network_alphas, metadata = self.lora_state_dict(pretrained_model_name_or_path_or_dict, **kwargs)
|
||||||
|
|
||||||
is_correct_format = all("lora" in key for key in state_dict.keys())
|
is_correct_format = all("lora" in key for key in state_dict.keys())
|
||||||
if not is_correct_format:
|
if not is_correct_format:
|
||||||
@@ -114,6 +115,7 @@ class LoraLoaderMixin:
|
|||||||
self.load_lora_into_unet(
|
self.load_lora_into_unet(
|
||||||
state_dict,
|
state_dict,
|
||||||
network_alphas=network_alphas,
|
network_alphas=network_alphas,
|
||||||
|
config=metadata,
|
||||||
unet=getattr(self, self.unet_name) if not hasattr(self, "unet") else self.unet,
|
unet=getattr(self, self.unet_name) if not hasattr(self, "unet") else self.unet,
|
||||||
low_cpu_mem_usage=low_cpu_mem_usage,
|
low_cpu_mem_usage=low_cpu_mem_usage,
|
||||||
adapter_name=adapter_name,
|
adapter_name=adapter_name,
|
||||||
@@ -125,6 +127,7 @@ class LoraLoaderMixin:
|
|||||||
text_encoder=getattr(self, self.text_encoder_name)
|
text_encoder=getattr(self, self.text_encoder_name)
|
||||||
if not hasattr(self, "text_encoder")
|
if not hasattr(self, "text_encoder")
|
||||||
else self.text_encoder,
|
else self.text_encoder,
|
||||||
|
config=metadata,
|
||||||
lora_scale=self.lora_scale,
|
lora_scale=self.lora_scale,
|
||||||
low_cpu_mem_usage=low_cpu_mem_usage,
|
low_cpu_mem_usage=low_cpu_mem_usage,
|
||||||
adapter_name=adapter_name,
|
adapter_name=adapter_name,
|
||||||
@@ -132,6 +135,7 @@ class LoraLoaderMixin:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@validate_hf_hub_args
|
||||||
def lora_state_dict(
|
def lora_state_dict(
|
||||||
cls,
|
cls,
|
||||||
pretrained_model_name_or_path_or_dict: Union[str, Dict[str, torch.Tensor]],
|
pretrained_model_name_or_path_or_dict: Union[str, Dict[str, torch.Tensor]],
|
||||||
@@ -174,7 +178,7 @@ class LoraLoaderMixin:
|
|||||||
local_files_only (`bool`, *optional*, defaults to `False`):
|
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
|
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.
|
won't be downloaded from the Hub.
|
||||||
use_auth_token (`str` or *bool*, *optional*):
|
token (`str` or *bool*, *optional*):
|
||||||
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
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.
|
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
||||||
revision (`str`, *optional*, defaults to `"main"`):
|
revision (`str`, *optional*, defaults to `"main"`):
|
||||||
@@ -195,12 +199,12 @@ class LoraLoaderMixin:
|
|||||||
"""
|
"""
|
||||||
# Load the main state dict first which has the LoRA layers for either of
|
# Load the main state dict first which has the LoRA layers for either of
|
||||||
# UNet and text encoder or both.
|
# UNet and text encoder or both.
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
force_download = kwargs.pop("force_download", False)
|
force_download = kwargs.pop("force_download", False)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", HF_HUB_OFFLINE)
|
local_files_only = kwargs.pop("local_files_only", None)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
subfolder = kwargs.pop("subfolder", None)
|
subfolder = kwargs.pop("subfolder", None)
|
||||||
weight_name = kwargs.pop("weight_name", None)
|
weight_name = kwargs.pop("weight_name", None)
|
||||||
@@ -218,6 +222,7 @@ class LoraLoaderMixin:
|
|||||||
}
|
}
|
||||||
|
|
||||||
model_file = None
|
model_file = None
|
||||||
|
metadata = None
|
||||||
if not isinstance(pretrained_model_name_or_path_or_dict, dict):
|
if not isinstance(pretrained_model_name_or_path_or_dict, dict):
|
||||||
# Let's first try to load .safetensors weights
|
# Let's first try to load .safetensors weights
|
||||||
if (use_safetensors and weight_name is None) or (
|
if (use_safetensors and weight_name is None) or (
|
||||||
@@ -229,7 +234,9 @@ class LoraLoaderMixin:
|
|||||||
# determine `weight_name`.
|
# determine `weight_name`.
|
||||||
if weight_name is None:
|
if weight_name is None:
|
||||||
weight_name = cls._best_guess_weight_name(
|
weight_name = cls._best_guess_weight_name(
|
||||||
pretrained_model_name_or_path_or_dict, file_extension=".safetensors"
|
pretrained_model_name_or_path_or_dict,
|
||||||
|
file_extension=".safetensors",
|
||||||
|
local_files_only=local_files_only,
|
||||||
)
|
)
|
||||||
model_file = _get_model_file(
|
model_file = _get_model_file(
|
||||||
pretrained_model_name_or_path_or_dict,
|
pretrained_model_name_or_path_or_dict,
|
||||||
@@ -239,12 +246,14 @@ class LoraLoaderMixin:
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
subfolder=subfolder,
|
subfolder=subfolder,
|
||||||
user_agent=user_agent,
|
user_agent=user_agent,
|
||||||
)
|
)
|
||||||
state_dict = safetensors.torch.load_file(model_file, device="cpu")
|
state_dict = safetensors.torch.load_file(model_file, device="cpu")
|
||||||
|
with safetensors.safe_open(model_file, framework="pt", device="cpu") as f:
|
||||||
|
metadata = f.metadata()
|
||||||
except (IOError, safetensors.SafetensorError) as e:
|
except (IOError, safetensors.SafetensorError) as e:
|
||||||
if not allow_pickle:
|
if not allow_pickle:
|
||||||
raise e
|
raise e
|
||||||
@@ -255,7 +264,7 @@ class LoraLoaderMixin:
|
|||||||
if model_file is None:
|
if model_file is None:
|
||||||
if weight_name is None:
|
if weight_name is None:
|
||||||
weight_name = cls._best_guess_weight_name(
|
weight_name = cls._best_guess_weight_name(
|
||||||
pretrained_model_name_or_path_or_dict, file_extension=".bin"
|
pretrained_model_name_or_path_or_dict, file_extension=".bin", local_files_only=local_files_only
|
||||||
)
|
)
|
||||||
model_file = _get_model_file(
|
model_file = _get_model_file(
|
||||||
pretrained_model_name_or_path_or_dict,
|
pretrained_model_name_or_path_or_dict,
|
||||||
@@ -265,7 +274,7 @@ class LoraLoaderMixin:
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
subfolder=subfolder,
|
subfolder=subfolder,
|
||||||
user_agent=user_agent,
|
user_agent=user_agent,
|
||||||
@@ -291,10 +300,15 @@ class LoraLoaderMixin:
|
|||||||
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_kohya_lora_to_diffusers(state_dict)
|
||||||
|
|
||||||
return state_dict, network_alphas
|
return state_dict, network_alphas, metadata
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _best_guess_weight_name(cls, pretrained_model_name_or_path_or_dict, file_extension=".safetensors"):
|
def _best_guess_weight_name(
|
||||||
|
cls, pretrained_model_name_or_path_or_dict, file_extension=".safetensors", local_files_only=False
|
||||||
|
):
|
||||||
|
if local_files_only or HF_HUB_OFFLINE:
|
||||||
|
raise ValueError("When using the offline mode, you must specify a `weight_name`.")
|
||||||
|
|
||||||
targeted_files = []
|
targeted_files = []
|
||||||
|
|
||||||
if os.path.isfile(pretrained_model_name_or_path_or_dict):
|
if os.path.isfile(pretrained_model_name_or_path_or_dict):
|
||||||
@@ -362,7 +376,7 @@ class LoraLoaderMixin:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_lora_into_unet(
|
def load_lora_into_unet(
|
||||||
cls, state_dict, network_alphas, unet, low_cpu_mem_usage=None, adapter_name=None, _pipeline=None
|
cls, state_dict, network_alphas, unet, config=None, low_cpu_mem_usage=None, adapter_name=None, _pipeline=None
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
This will load the LoRA layers specified in `state_dict` into `unet`.
|
This will load the LoRA layers specified in `state_dict` into `unet`.
|
||||||
@@ -376,6 +390,8 @@ class LoraLoaderMixin:
|
|||||||
See `LoRALinearLayer` for more details.
|
See `LoRALinearLayer` for more details.
|
||||||
unet (`UNet2DConditionModel`):
|
unet (`UNet2DConditionModel`):
|
||||||
The UNet model to load the LoRA layers into.
|
The UNet model to load the LoRA layers into.
|
||||||
|
config: (`Dict`):
|
||||||
|
LoRA configuration parsed from the state dict.
|
||||||
low_cpu_mem_usage (`bool`, *optional*, defaults to `True` if torch version >= 1.9.0 else `False`):
|
low_cpu_mem_usage (`bool`, *optional*, defaults to `True` if torch version >= 1.9.0 else `False`):
|
||||||
Speed up model loading only loading the pretrained weights and not initializing the weights. This also
|
Speed up model loading only loading the pretrained weights and not initializing the weights. This also
|
||||||
tries to not use more than 1x model size in CPU memory (including peak memory) while loading the model.
|
tries to not use more than 1x model size in CPU memory (including peak memory) while loading the model.
|
||||||
@@ -435,7 +451,9 @@ class LoraLoaderMixin:
|
|||||||
if "lora_B" in key:
|
if "lora_B" in key:
|
||||||
rank[key] = val.shape[1]
|
rank[key] = val.shape[1]
|
||||||
|
|
||||||
lora_config_kwargs = get_peft_kwargs(rank, network_alphas, state_dict, is_unet=True)
|
if config is not None and isinstance(config, dict) and len(config) > 0:
|
||||||
|
config = json.loads(config["unet"])
|
||||||
|
lora_config_kwargs = get_peft_kwargs(rank, network_alphas, state_dict, config=config, is_unet=True)
|
||||||
lora_config = LoraConfig(**lora_config_kwargs)
|
lora_config = LoraConfig(**lora_config_kwargs)
|
||||||
|
|
||||||
# adapter_name
|
# adapter_name
|
||||||
@@ -476,6 +494,7 @@ class LoraLoaderMixin:
|
|||||||
network_alphas,
|
network_alphas,
|
||||||
text_encoder,
|
text_encoder,
|
||||||
prefix=None,
|
prefix=None,
|
||||||
|
config=None,
|
||||||
lora_scale=1.0,
|
lora_scale=1.0,
|
||||||
low_cpu_mem_usage=None,
|
low_cpu_mem_usage=None,
|
||||||
adapter_name=None,
|
adapter_name=None,
|
||||||
@@ -494,6 +513,8 @@ class LoraLoaderMixin:
|
|||||||
The text encoder model to load the LoRA layers into.
|
The text encoder model to load the LoRA layers into.
|
||||||
prefix (`str`):
|
prefix (`str`):
|
||||||
Expected prefix of the `text_encoder` in the `state_dict`.
|
Expected prefix of the `text_encoder` in the `state_dict`.
|
||||||
|
config (`Dict`):
|
||||||
|
LoRA configuration parsed from state dict.
|
||||||
lora_scale (`float`):
|
lora_scale (`float`):
|
||||||
How much to scale the output of the lora linear layer before it is added with the output of the regular
|
How much to scale the output of the lora linear layer before it is added with the output of the regular
|
||||||
lora layer.
|
lora layer.
|
||||||
@@ -567,10 +588,11 @@ class LoraLoaderMixin:
|
|||||||
if USE_PEFT_BACKEND:
|
if USE_PEFT_BACKEND:
|
||||||
from peft import LoraConfig
|
from peft import LoraConfig
|
||||||
|
|
||||||
|
if config is not None and len(config) > 0:
|
||||||
|
config = json.loads(config[prefix])
|
||||||
lora_config_kwargs = get_peft_kwargs(
|
lora_config_kwargs = get_peft_kwargs(
|
||||||
rank, network_alphas, text_encoder_lora_state_dict, is_unet=False
|
rank, network_alphas, text_encoder_lora_state_dict, config=config, is_unet=False
|
||||||
)
|
)
|
||||||
|
|
||||||
lora_config = LoraConfig(**lora_config_kwargs)
|
lora_config = LoraConfig(**lora_config_kwargs)
|
||||||
|
|
||||||
# adapter_name
|
# adapter_name
|
||||||
@@ -778,6 +800,8 @@ class LoraLoaderMixin:
|
|||||||
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, torch.nn.Module] = None,
|
text_encoder_lora_layers: Dict[str, torch.nn.Module] = None,
|
||||||
|
unet_lora_config=None,
|
||||||
|
text_encoder_lora_config=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,
|
||||||
@@ -805,21 +829,54 @@ class LoraLoaderMixin:
|
|||||||
safe_serialization (`bool`, *optional*, defaults to `True`):
|
safe_serialization (`bool`, *optional*, defaults to `True`):
|
||||||
Whether to save the model using `safetensors` or the traditional PyTorch way with `pickle`.
|
Whether to save the model using `safetensors` or the traditional PyTorch way with `pickle`.
|
||||||
"""
|
"""
|
||||||
|
if not USE_PEFT_BACKEND and not safe_serialization:
|
||||||
|
if unet_lora_config or text_encoder_lora_config:
|
||||||
|
raise ValueError(
|
||||||
|
"Without `peft`, passing `unet_lora_config` or `text_encoder_lora_config` is not possible. Please install `peft`."
|
||||||
|
)
|
||||||
|
elif USE_PEFT_BACKEND and safe_serialization:
|
||||||
|
from peft import LoraConfig
|
||||||
|
|
||||||
|
if not (unet_lora_layers or text_encoder_lora_layers):
|
||||||
|
raise ValueError("You must pass at least one of `unet_lora_layers` or `text_encoder_lora_layers`.")
|
||||||
|
|
||||||
state_dict = {}
|
state_dict = {}
|
||||||
|
metadata = {}
|
||||||
|
|
||||||
def pack_weights(layers, prefix):
|
def pack_weights(layers, prefix):
|
||||||
layers_weights = layers.state_dict() if isinstance(layers, torch.nn.Module) else layers
|
layers_weights = layers.state_dict() if isinstance(layers, torch.nn.Module) else layers
|
||||||
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 (unet_lora_layers or text_encoder_lora_layers):
|
def pack_metadata(config, prefix):
|
||||||
raise ValueError("You must pass at least one of `unet_lora_layers`, `text_encoder_lora_layers`.")
|
local_metadata = {}
|
||||||
|
if config is not None:
|
||||||
|
if isinstance(config, LoraConfig):
|
||||||
|
config = config.to_dict()
|
||||||
|
for key, value in config.items():
|
||||||
|
if isinstance(value, set):
|
||||||
|
config[key] = list(value)
|
||||||
|
|
||||||
|
config_as_string = json.dumps(config, indent=2, sort_keys=True)
|
||||||
|
local_metadata[prefix] = config_as_string
|
||||||
|
return local_metadata
|
||||||
|
|
||||||
if unet_lora_layers:
|
if unet_lora_layers:
|
||||||
state_dict.update(pack_weights(unet_lora_layers, "unet"))
|
prefix = "unet"
|
||||||
|
unet_state_dict = pack_weights(unet_lora_layers, prefix)
|
||||||
|
state_dict.update(unet_state_dict)
|
||||||
|
if unet_lora_config is not None:
|
||||||
|
unet_metadata = pack_metadata(unet_lora_config, prefix)
|
||||||
|
metadata.update(unet_metadata)
|
||||||
|
|
||||||
if text_encoder_lora_layers:
|
if text_encoder_lora_layers:
|
||||||
state_dict.update(pack_weights(text_encoder_lora_layers, "text_encoder"))
|
prefix = "text_encoder"
|
||||||
|
text_encoder_state_dict = pack_weights(text_encoder_lora_layers, "text_encoder")
|
||||||
|
state_dict.update(text_encoder_state_dict)
|
||||||
|
if text_encoder_lora_config is not None:
|
||||||
|
text_encoder_metadata = pack_metadata(text_encoder_lora_config, prefix)
|
||||||
|
metadata.update(text_encoder_metadata)
|
||||||
|
|
||||||
# Save the model
|
# Save the model
|
||||||
cls.write_lora_layers(
|
cls.write_lora_layers(
|
||||||
@@ -829,6 +886,7 @@ class LoraLoaderMixin:
|
|||||||
weight_name=weight_name,
|
weight_name=weight_name,
|
||||||
save_function=save_function,
|
save_function=save_function,
|
||||||
safe_serialization=safe_serialization,
|
safe_serialization=safe_serialization,
|
||||||
|
metadata=metadata,
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -839,7 +897,11 @@ class LoraLoaderMixin:
|
|||||||
weight_name: str,
|
weight_name: str,
|
||||||
save_function: Callable,
|
save_function: Callable,
|
||||||
safe_serialization: bool,
|
safe_serialization: bool,
|
||||||
|
metadata=None,
|
||||||
):
|
):
|
||||||
|
if not safe_serialization and isinstance(metadata, dict) and len(metadata) > 0:
|
||||||
|
raise ValueError("Passing `metadata` is not possible when `safe_serialization` is False.")
|
||||||
|
|
||||||
if os.path.isfile(save_directory):
|
if os.path.isfile(save_directory):
|
||||||
logger.error(f"Provided path ({save_directory}) should be a directory, not a file")
|
logger.error(f"Provided path ({save_directory}) should be a directory, not a file")
|
||||||
return
|
return
|
||||||
@@ -847,8 +909,10 @@ class LoraLoaderMixin:
|
|||||||
if save_function is None:
|
if save_function is None:
|
||||||
if safe_serialization:
|
if safe_serialization:
|
||||||
|
|
||||||
def save_function(weights, filename):
|
def save_function(weights, filename, metadata):
|
||||||
return safetensors.torch.save_file(weights, filename, metadata={"format": "pt"})
|
if metadata is None:
|
||||||
|
metadata = {"format": "pt"}
|
||||||
|
return safetensors.torch.save_file(weights, filename, metadata=metadata)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
save_function = torch.save
|
save_function = torch.save
|
||||||
@@ -861,7 +925,10 @@ class LoraLoaderMixin:
|
|||||||
else:
|
else:
|
||||||
weight_name = LORA_WEIGHT_NAME
|
weight_name = LORA_WEIGHT_NAME
|
||||||
|
|
||||||
save_function(state_dict, os.path.join(save_directory, weight_name))
|
if save_function != torch.save:
|
||||||
|
save_function(state_dict, os.path.join(save_directory, weight_name), metadata)
|
||||||
|
else:
|
||||||
|
save_function(state_dict, os.path.join(save_directory, weight_name))
|
||||||
logger.info(f"Model weights saved in {os.path.join(save_directory, weight_name)}")
|
logger.info(f"Model weights saved in {os.path.join(save_directory, weight_name)}")
|
||||||
|
|
||||||
def unload_lora_weights(self):
|
def unload_lora_weights(self):
|
||||||
@@ -1293,7 +1360,7 @@ class StableDiffusionXLLoraLoaderMixin(LoraLoaderMixin):
|
|||||||
# pipeline.
|
# pipeline.
|
||||||
|
|
||||||
# First, ensure that the checkpoint is a compatible one and can be successfully loaded.
|
# First, ensure that the checkpoint is a compatible one and can be successfully loaded.
|
||||||
state_dict, network_alphas = self.lora_state_dict(
|
state_dict, network_alphas, metadata = self.lora_state_dict(
|
||||||
pretrained_model_name_or_path_or_dict,
|
pretrained_model_name_or_path_or_dict,
|
||||||
unet_config=self.unet.config,
|
unet_config=self.unet.config,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
@@ -1303,7 +1370,12 @@ class StableDiffusionXLLoraLoaderMixin(LoraLoaderMixin):
|
|||||||
raise ValueError("Invalid LoRA checkpoint.")
|
raise ValueError("Invalid LoRA checkpoint.")
|
||||||
|
|
||||||
self.load_lora_into_unet(
|
self.load_lora_into_unet(
|
||||||
state_dict, network_alphas=network_alphas, unet=self.unet, adapter_name=adapter_name, _pipeline=self
|
state_dict,
|
||||||
|
network_alphas=network_alphas,
|
||||||
|
unet=self.unet,
|
||||||
|
config=metadata,
|
||||||
|
adapter_name=adapter_name,
|
||||||
|
_pipeline=self,
|
||||||
)
|
)
|
||||||
text_encoder_state_dict = {k: v for k, v in state_dict.items() if "text_encoder." in k}
|
text_encoder_state_dict = {k: v for k, v in state_dict.items() if "text_encoder." in k}
|
||||||
if len(text_encoder_state_dict) > 0:
|
if len(text_encoder_state_dict) > 0:
|
||||||
@@ -1311,6 +1383,7 @@ class StableDiffusionXLLoraLoaderMixin(LoraLoaderMixin):
|
|||||||
text_encoder_state_dict,
|
text_encoder_state_dict,
|
||||||
network_alphas=network_alphas,
|
network_alphas=network_alphas,
|
||||||
text_encoder=self.text_encoder,
|
text_encoder=self.text_encoder,
|
||||||
|
config=metadata,
|
||||||
prefix="text_encoder",
|
prefix="text_encoder",
|
||||||
lora_scale=self.lora_scale,
|
lora_scale=self.lora_scale,
|
||||||
adapter_name=adapter_name,
|
adapter_name=adapter_name,
|
||||||
@@ -1323,6 +1396,7 @@ class StableDiffusionXLLoraLoaderMixin(LoraLoaderMixin):
|
|||||||
text_encoder_2_state_dict,
|
text_encoder_2_state_dict,
|
||||||
network_alphas=network_alphas,
|
network_alphas=network_alphas,
|
||||||
text_encoder=self.text_encoder_2,
|
text_encoder=self.text_encoder_2,
|
||||||
|
config=metadata,
|
||||||
prefix="text_encoder_2",
|
prefix="text_encoder_2",
|
||||||
lora_scale=self.lora_scale,
|
lora_scale=self.lora_scale,
|
||||||
adapter_name=adapter_name,
|
adapter_name=adapter_name,
|
||||||
@@ -1336,6 +1410,9 @@ class StableDiffusionXLLoraLoaderMixin(LoraLoaderMixin):
|
|||||||
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,
|
||||||
text_encoder_2_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,
|
||||||
|
unet_lora_config=None,
|
||||||
|
text_encoder_lora_config=None,
|
||||||
|
text_encoder_2_lora_config=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,
|
||||||
@@ -1363,24 +1440,63 @@ class StableDiffusionXLLoraLoaderMixin(LoraLoaderMixin):
|
|||||||
safe_serialization (`bool`, *optional*, defaults to `True`):
|
safe_serialization (`bool`, *optional*, defaults to `True`):
|
||||||
Whether to save the model using `safetensors` or the traditional PyTorch way with `pickle`.
|
Whether to save the model using `safetensors` or the traditional PyTorch way with `pickle`.
|
||||||
"""
|
"""
|
||||||
state_dict = {}
|
if not USE_PEFT_BACKEND and not safe_serialization:
|
||||||
|
if unet_lora_config or text_encoder_lora_config or text_encoder_2_lora_config:
|
||||||
def pack_weights(layers, prefix):
|
raise ValueError(
|
||||||
layers_weights = layers.state_dict() if isinstance(layers, torch.nn.Module) else layers
|
"Without `peft`, passing `unet_lora_config` or `text_encoder_lora_config` or `text_encoder_2_lora_config` is not possible. Please install `peft`."
|
||||||
layers_state_dict = {f"{prefix}.{module_name}": param for module_name, param in layers_weights.items()}
|
)
|
||||||
return layers_state_dict
|
elif USE_PEFT_BACKEND and safe_serialization:
|
||||||
|
from peft import LoraConfig
|
||||||
|
|
||||||
if not (unet_lora_layers or text_encoder_lora_layers or text_encoder_2_lora_layers):
|
if not (unet_lora_layers or text_encoder_lora_layers or text_encoder_2_lora_layers):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"You must pass at least one of `unet_lora_layers`, `text_encoder_lora_layers` or `text_encoder_2_lora_layers`."
|
"You must pass at least one of `unet_lora_layers`, `text_encoder_lora_layers` or `text_encoder_2_lora_layers`."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
state_dict = {}
|
||||||
|
metadata = {}
|
||||||
|
|
||||||
|
def pack_weights(layers, prefix):
|
||||||
|
layers_weights = layers.state_dict() if isinstance(layers, torch.nn.Module) else layers
|
||||||
|
layers_state_dict = {f"{prefix}.{module_name}": param for module_name, param in layers_weights.items()}
|
||||||
|
|
||||||
|
return layers_state_dict
|
||||||
|
|
||||||
|
def pack_metadata(config, prefix):
|
||||||
|
local_metadata = {}
|
||||||
|
if config is not None:
|
||||||
|
if isinstance(config, LoraConfig):
|
||||||
|
config = config.to_dict()
|
||||||
|
for key, value in config.items():
|
||||||
|
if isinstance(value, set):
|
||||||
|
config[key] = list(value)
|
||||||
|
|
||||||
|
config_as_string = json.dumps(config, indent=2, sort_keys=True)
|
||||||
|
local_metadata[prefix] = config_as_string
|
||||||
|
return local_metadata
|
||||||
|
|
||||||
if unet_lora_layers:
|
if unet_lora_layers:
|
||||||
state_dict.update(pack_weights(unet_lora_layers, "unet"))
|
prefix = "unet"
|
||||||
|
unet_state_dict = pack_weights(unet_lora_layers, prefix)
|
||||||
|
state_dict.update(unet_state_dict)
|
||||||
|
if unet_lora_config is not None:
|
||||||
|
unet_metadata = pack_metadata(unet_lora_config, prefix)
|
||||||
|
metadata.update(unet_metadata)
|
||||||
|
|
||||||
if text_encoder_lora_layers and text_encoder_2_lora_layers:
|
if text_encoder_lora_layers and text_encoder_2_lora_layers:
|
||||||
state_dict.update(pack_weights(text_encoder_lora_layers, "text_encoder"))
|
prefix = "text_encoder"
|
||||||
state_dict.update(pack_weights(text_encoder_2_lora_layers, "text_encoder_2"))
|
text_encoder_state_dict = pack_weights(text_encoder_lora_layers, "text_encoder")
|
||||||
|
state_dict.update(text_encoder_state_dict)
|
||||||
|
if text_encoder_lora_config is not None:
|
||||||
|
text_encoder_metadata = pack_metadata(text_encoder_lora_config, prefix)
|
||||||
|
metadata.update(text_encoder_metadata)
|
||||||
|
|
||||||
|
prefix = "text_encoder_2"
|
||||||
|
text_encoder_2_state_dict = pack_weights(text_encoder_2_lora_layers, prefix)
|
||||||
|
state_dict.update(text_encoder_2_state_dict)
|
||||||
|
if text_encoder_2_lora_config is not None:
|
||||||
|
text_encoder_2_metadata = pack_metadata(text_encoder_2_lora_config, prefix)
|
||||||
|
metadata.update(text_encoder_2_metadata)
|
||||||
|
|
||||||
cls.write_lora_layers(
|
cls.write_lora_layers(
|
||||||
state_dict=state_dict,
|
state_dict=state_dict,
|
||||||
@@ -1389,6 +1505,7 @@ class StableDiffusionXLLoraLoaderMixin(LoraLoaderMixin):
|
|||||||
weight_name=weight_name,
|
weight_name=weight_name,
|
||||||
save_function=save_function,
|
save_function=save_function,
|
||||||
safe_serialization=safe_serialization,
|
safe_serialization=safe_serialization,
|
||||||
|
metadata=metadata,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _remove_text_encoder_monkey_patch(self):
|
def _remove_text_encoder_monkey_patch(self):
|
||||||
|
|||||||
@@ -18,10 +18,9 @@ from pathlib import Path
|
|||||||
import requests
|
import requests
|
||||||
import torch
|
import torch
|
||||||
from huggingface_hub import hf_hub_download
|
from huggingface_hub import hf_hub_download
|
||||||
|
from huggingface_hub.utils import validate_hf_hub_args
|
||||||
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
DIFFUSERS_CACHE,
|
|
||||||
HF_HUB_OFFLINE,
|
|
||||||
deprecate,
|
deprecate,
|
||||||
is_accelerate_available,
|
is_accelerate_available,
|
||||||
is_omegaconf_available,
|
is_omegaconf_available,
|
||||||
@@ -52,6 +51,7 @@ class FromSingleFileMixin:
|
|||||||
return cls.from_single_file(*args, **kwargs)
|
return cls.from_single_file(*args, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@validate_hf_hub_args
|
||||||
def from_single_file(cls, pretrained_model_link_or_path, **kwargs):
|
def from_single_file(cls, pretrained_model_link_or_path, **kwargs):
|
||||||
r"""
|
r"""
|
||||||
Instantiate a [`DiffusionPipeline`] from pretrained pipeline weights saved in the `.ckpt` or `.safetensors`
|
Instantiate a [`DiffusionPipeline`] from pretrained pipeline weights saved in the `.ckpt` or `.safetensors`
|
||||||
@@ -81,7 +81,7 @@ class FromSingleFileMixin:
|
|||||||
local_files_only (`bool`, *optional*, defaults to `False`):
|
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
|
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.
|
won't be downloaded from the Hub.
|
||||||
use_auth_token (`str` or *bool*, *optional*):
|
token (`str` or *bool*, *optional*):
|
||||||
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
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.
|
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
||||||
revision (`str`, *optional*, defaults to `"main"`):
|
revision (`str`, *optional*, defaults to `"main"`):
|
||||||
@@ -154,12 +154,12 @@ class FromSingleFileMixin:
|
|||||||
|
|
||||||
original_config_file = kwargs.pop("original_config_file", None)
|
original_config_file = kwargs.pop("original_config_file", None)
|
||||||
config_files = kwargs.pop("config_files", None)
|
config_files = kwargs.pop("config_files", None)
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
force_download = kwargs.pop("force_download", False)
|
force_download = kwargs.pop("force_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", HF_HUB_OFFLINE)
|
local_files_only = kwargs.pop("local_files_only", None)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
extract_ema = kwargs.pop("extract_ema", False)
|
extract_ema = kwargs.pop("extract_ema", False)
|
||||||
image_size = kwargs.pop("image_size", None)
|
image_size = kwargs.pop("image_size", None)
|
||||||
@@ -253,7 +253,7 @@ class FromSingleFileMixin:
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
force_download=force_download,
|
force_download=force_download,
|
||||||
)
|
)
|
||||||
@@ -293,6 +293,7 @@ class FromOriginalVAEMixin:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@validate_hf_hub_args
|
||||||
def from_single_file(cls, pretrained_model_link_or_path, **kwargs):
|
def from_single_file(cls, pretrained_model_link_or_path, **kwargs):
|
||||||
r"""
|
r"""
|
||||||
Instantiate a [`AutoencoderKL`] from pretrained ControlNet weights saved in the original `.ckpt` or
|
Instantiate a [`AutoencoderKL`] from pretrained ControlNet weights saved in the original `.ckpt` or
|
||||||
@@ -322,7 +323,7 @@ class FromOriginalVAEMixin:
|
|||||||
local_files_only (`bool`, *optional*, defaults to `False`):
|
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
|
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.
|
won't be downloaded from the Hub.
|
||||||
use_auth_token (`str` or *bool*, *optional*):
|
token (`str` or *bool*, *optional*):
|
||||||
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
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.
|
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
||||||
revision (`str`, *optional*, defaults to `"main"`):
|
revision (`str`, *optional*, defaults to `"main"`):
|
||||||
@@ -379,12 +380,12 @@ class FromOriginalVAEMixin:
|
|||||||
)
|
)
|
||||||
|
|
||||||
config_file = kwargs.pop("config_file", None)
|
config_file = kwargs.pop("config_file", None)
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
force_download = kwargs.pop("force_download", False)
|
force_download = kwargs.pop("force_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", HF_HUB_OFFLINE)
|
local_files_only = kwargs.pop("local_files_only", None)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
image_size = kwargs.pop("image_size", None)
|
image_size = kwargs.pop("image_size", None)
|
||||||
scaling_factor = kwargs.pop("scaling_factor", None)
|
scaling_factor = kwargs.pop("scaling_factor", None)
|
||||||
@@ -425,7 +426,7 @@ class FromOriginalVAEMixin:
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
force_download=force_download,
|
force_download=force_download,
|
||||||
)
|
)
|
||||||
@@ -490,6 +491,7 @@ class FromOriginalControlnetMixin:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@validate_hf_hub_args
|
||||||
def from_single_file(cls, pretrained_model_link_or_path, **kwargs):
|
def from_single_file(cls, pretrained_model_link_or_path, **kwargs):
|
||||||
r"""
|
r"""
|
||||||
Instantiate a [`ControlNetModel`] from pretrained ControlNet weights saved in the original `.ckpt` or
|
Instantiate a [`ControlNetModel`] from pretrained ControlNet weights saved in the original `.ckpt` or
|
||||||
@@ -519,7 +521,7 @@ class FromOriginalControlnetMixin:
|
|||||||
local_files_only (`bool`, *optional*, defaults to `False`):
|
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
|
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.
|
won't be downloaded from the Hub.
|
||||||
use_auth_token (`str` or *bool*, *optional*):
|
token (`str` or *bool*, *optional*):
|
||||||
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
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.
|
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
||||||
revision (`str`, *optional*, defaults to `"main"`):
|
revision (`str`, *optional*, defaults to `"main"`):
|
||||||
@@ -555,12 +557,12 @@ class FromOriginalControlnetMixin:
|
|||||||
from ..pipelines.stable_diffusion.convert_from_ckpt import download_controlnet_from_original_ckpt
|
from ..pipelines.stable_diffusion.convert_from_ckpt import download_controlnet_from_original_ckpt
|
||||||
|
|
||||||
config_file = kwargs.pop("config_file", None)
|
config_file = kwargs.pop("config_file", None)
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
force_download = kwargs.pop("force_download", False)
|
force_download = kwargs.pop("force_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", HF_HUB_OFFLINE)
|
local_files_only = kwargs.pop("local_files_only", None)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
num_in_channels = kwargs.pop("num_in_channels", None)
|
num_in_channels = kwargs.pop("num_in_channels", None)
|
||||||
use_linear_projection = kwargs.pop("use_linear_projection", None)
|
use_linear_projection = kwargs.pop("use_linear_projection", None)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
@@ -603,7 +605,7 @@ class FromOriginalControlnetMixin:
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
force_download=force_download,
|
force_download=force_download,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,16 +15,10 @@ from typing import Dict, List, Optional, Union
|
|||||||
|
|
||||||
import safetensors
|
import safetensors
|
||||||
import torch
|
import torch
|
||||||
|
from huggingface_hub.utils import validate_hf_hub_args
|
||||||
from torch import nn
|
from torch import nn
|
||||||
|
|
||||||
from ..utils import (
|
from ..utils import _get_model_file, is_accelerate_available, is_transformers_available, logging
|
||||||
DIFFUSERS_CACHE,
|
|
||||||
HF_HUB_OFFLINE,
|
|
||||||
_get_model_file,
|
|
||||||
is_accelerate_available,
|
|
||||||
is_transformers_available,
|
|
||||||
logging,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if is_transformers_available():
|
if is_transformers_available():
|
||||||
@@ -39,13 +33,14 @@ TEXT_INVERSION_NAME = "learned_embeds.bin"
|
|||||||
TEXT_INVERSION_NAME_SAFE = "learned_embeds.safetensors"
|
TEXT_INVERSION_NAME_SAFE = "learned_embeds.safetensors"
|
||||||
|
|
||||||
|
|
||||||
|
@validate_hf_hub_args
|
||||||
def load_textual_inversion_state_dicts(pretrained_model_name_or_paths, **kwargs):
|
def load_textual_inversion_state_dicts(pretrained_model_name_or_paths, **kwargs):
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
force_download = kwargs.pop("force_download", False)
|
force_download = kwargs.pop("force_download", False)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", HF_HUB_OFFLINE)
|
local_files_only = kwargs.pop("local_files_only", None)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
subfolder = kwargs.pop("subfolder", None)
|
subfolder = kwargs.pop("subfolder", None)
|
||||||
weight_name = kwargs.pop("weight_name", None)
|
weight_name = kwargs.pop("weight_name", None)
|
||||||
@@ -79,7 +74,7 @@ def load_textual_inversion_state_dicts(pretrained_model_name_or_paths, **kwargs)
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
subfolder=subfolder,
|
subfolder=subfolder,
|
||||||
user_agent=user_agent,
|
user_agent=user_agent,
|
||||||
@@ -100,7 +95,7 @@ def load_textual_inversion_state_dicts(pretrained_model_name_or_paths, **kwargs)
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
subfolder=subfolder,
|
subfolder=subfolder,
|
||||||
user_agent=user_agent,
|
user_agent=user_agent,
|
||||||
@@ -267,6 +262,7 @@ class TextualInversionLoaderMixin:
|
|||||||
|
|
||||||
return all_tokens, all_embeddings
|
return all_tokens, all_embeddings
|
||||||
|
|
||||||
|
@validate_hf_hub_args
|
||||||
def load_textual_inversion(
|
def load_textual_inversion(
|
||||||
self,
|
self,
|
||||||
pretrained_model_name_or_path: Union[str, List[str], Dict[str, torch.Tensor], List[Dict[str, torch.Tensor]]],
|
pretrained_model_name_or_path: Union[str, List[str], Dict[str, torch.Tensor], List[Dict[str, torch.Tensor]]],
|
||||||
@@ -320,7 +316,7 @@ class TextualInversionLoaderMixin:
|
|||||||
local_files_only (`bool`, *optional*, defaults to `False`):
|
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
|
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.
|
won't be downloaded from the Hub.
|
||||||
use_auth_token (`str` or *bool*, *optional*):
|
token (`str` or *bool*, *optional*):
|
||||||
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
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.
|
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
||||||
revision (`str`, *optional*, defaults to `"main"`):
|
revision (`str`, *optional*, defaults to `"main"`):
|
||||||
|
|||||||
@@ -19,13 +19,12 @@ from typing import Callable, Dict, List, Optional, Union
|
|||||||
import safetensors
|
import safetensors
|
||||||
import torch
|
import torch
|
||||||
import torch.nn.functional as F
|
import torch.nn.functional as F
|
||||||
|
from huggingface_hub.utils import validate_hf_hub_args
|
||||||
from torch import nn
|
from torch import nn
|
||||||
|
|
||||||
from ..models.embeddings import ImageProjection, Resampler
|
from ..models.embeddings import ImageProjection, MLPProjection, Resampler
|
||||||
from ..models.modeling_utils import _LOW_CPU_MEM_USAGE_DEFAULT, load_model_dict_into_meta
|
from ..models.modeling_utils import _LOW_CPU_MEM_USAGE_DEFAULT, load_model_dict_into_meta
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
DIFFUSERS_CACHE,
|
|
||||||
HF_HUB_OFFLINE,
|
|
||||||
USE_PEFT_BACKEND,
|
USE_PEFT_BACKEND,
|
||||||
_get_model_file,
|
_get_model_file,
|
||||||
delete_adapter_layers,
|
delete_adapter_layers,
|
||||||
@@ -62,6 +61,7 @@ class UNet2DConditionLoadersMixin:
|
|||||||
text_encoder_name = TEXT_ENCODER_NAME
|
text_encoder_name = TEXT_ENCODER_NAME
|
||||||
unet_name = UNET_NAME
|
unet_name = UNET_NAME
|
||||||
|
|
||||||
|
@validate_hf_hub_args
|
||||||
def load_attn_procs(self, pretrained_model_name_or_path_or_dict: Union[str, Dict[str, torch.Tensor]], **kwargs):
|
def load_attn_procs(self, pretrained_model_name_or_path_or_dict: Union[str, Dict[str, torch.Tensor]], **kwargs):
|
||||||
r"""
|
r"""
|
||||||
Load pretrained attention processor layers into [`UNet2DConditionModel`]. Attention processor layers have to be
|
Load pretrained attention processor layers into [`UNet2DConditionModel`]. Attention processor layers have to be
|
||||||
@@ -95,7 +95,7 @@ class UNet2DConditionLoadersMixin:
|
|||||||
local_files_only (`bool`, *optional*, defaults to `False`):
|
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
|
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.
|
won't be downloaded from the Hub.
|
||||||
use_auth_token (`str` or *bool*, *optional*):
|
token (`str` or *bool*, *optional*):
|
||||||
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
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.
|
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
||||||
low_cpu_mem_usage (`bool`, *optional*, defaults to `True` if torch version >= 1.9.0 else `False`):
|
low_cpu_mem_usage (`bool`, *optional*, defaults to `True` if torch version >= 1.9.0 else `False`):
|
||||||
@@ -130,12 +130,12 @@ class UNet2DConditionLoadersMixin:
|
|||||||
from ..models.attention_processor import CustomDiffusionAttnProcessor
|
from ..models.attention_processor import CustomDiffusionAttnProcessor
|
||||||
from ..models.lora import LoRACompatibleConv, LoRACompatibleLinear, LoRAConv2dLayer, LoRALinearLayer
|
from ..models.lora import LoRACompatibleConv, LoRACompatibleLinear, LoRAConv2dLayer, LoRALinearLayer
|
||||||
|
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
force_download = kwargs.pop("force_download", False)
|
force_download = kwargs.pop("force_download", False)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", HF_HUB_OFFLINE)
|
local_files_only = kwargs.pop("local_files_only", None)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
subfolder = kwargs.pop("subfolder", None)
|
subfolder = kwargs.pop("subfolder", None)
|
||||||
weight_name = kwargs.pop("weight_name", None)
|
weight_name = kwargs.pop("weight_name", None)
|
||||||
@@ -184,7 +184,7 @@ class UNet2DConditionLoadersMixin:
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
subfolder=subfolder,
|
subfolder=subfolder,
|
||||||
user_agent=user_agent,
|
user_agent=user_agent,
|
||||||
@@ -204,7 +204,7 @@ class UNet2DConditionLoadersMixin:
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
subfolder=subfolder,
|
subfolder=subfolder,
|
||||||
user_agent=user_agent,
|
user_agent=user_agent,
|
||||||
@@ -675,6 +675,9 @@ class UNet2DConditionLoadersMixin:
|
|||||||
if "proj.weight" in state_dict["image_proj"]:
|
if "proj.weight" in state_dict["image_proj"]:
|
||||||
# IP-Adapter
|
# IP-Adapter
|
||||||
num_image_text_embeds = 4
|
num_image_text_embeds = 4
|
||||||
|
elif "proj.3.weight" in state_dict["image_proj"]:
|
||||||
|
# IP-Adapter Full Face
|
||||||
|
num_image_text_embeds = 257 # 256 CLIP tokens + 1 CLS token
|
||||||
else:
|
else:
|
||||||
# IP-Adapter Plus
|
# IP-Adapter Plus
|
||||||
num_image_text_embeds = state_dict["image_proj"]["latents"].shape[1]
|
num_image_text_embeds = state_dict["image_proj"]["latents"].shape[1]
|
||||||
@@ -744,8 +747,32 @@ class UNet2DConditionLoadersMixin:
|
|||||||
"norm.bias": state_dict["image_proj"]["norm.bias"],
|
"norm.bias": state_dict["image_proj"]["norm.bias"],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
image_projection.load_state_dict(image_proj_state_dict)
|
image_projection.load_state_dict(image_proj_state_dict)
|
||||||
|
del image_proj_state_dict
|
||||||
|
|
||||||
|
elif "proj.3.weight" in state_dict["image_proj"]:
|
||||||
|
clip_embeddings_dim = state_dict["image_proj"]["proj.0.weight"].shape[0]
|
||||||
|
cross_attention_dim = state_dict["image_proj"]["proj.3.weight"].shape[0]
|
||||||
|
|
||||||
|
image_projection = MLPProjection(
|
||||||
|
cross_attention_dim=cross_attention_dim, image_embed_dim=clip_embeddings_dim
|
||||||
|
)
|
||||||
|
image_projection.to(dtype=self.dtype, device=self.device)
|
||||||
|
|
||||||
|
# load image projection layer weights
|
||||||
|
image_proj_state_dict = {}
|
||||||
|
image_proj_state_dict.update(
|
||||||
|
{
|
||||||
|
"ff.net.0.proj.weight": state_dict["image_proj"]["proj.0.weight"],
|
||||||
|
"ff.net.0.proj.bias": state_dict["image_proj"]["proj.0.bias"],
|
||||||
|
"ff.net.2.weight": state_dict["image_proj"]["proj.2.weight"],
|
||||||
|
"ff.net.2.bias": state_dict["image_proj"]["proj.2.bias"],
|
||||||
|
"norm.weight": state_dict["image_proj"]["proj.3.weight"],
|
||||||
|
"norm.bias": state_dict["image_proj"]["proj.3.bias"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
image_projection.load_state_dict(image_proj_state_dict)
|
||||||
|
del image_proj_state_dict
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# IP-Adapter Plus
|
# IP-Adapter Plus
|
||||||
|
|||||||
@@ -32,9 +32,10 @@ if is_torch_available():
|
|||||||
_import_structure["autoencoder_tiny"] = ["AutoencoderTiny"]
|
_import_structure["autoencoder_tiny"] = ["AutoencoderTiny"]
|
||||||
_import_structure["consistency_decoder_vae"] = ["ConsistencyDecoderVAE"]
|
_import_structure["consistency_decoder_vae"] = ["ConsistencyDecoderVAE"]
|
||||||
_import_structure["controlnet"] = ["ControlNetModel"]
|
_import_structure["controlnet"] = ["ControlNetModel"]
|
||||||
|
_import_structure["controlnetxs"] = ["ControlNetXSModel"]
|
||||||
_import_structure["dual_transformer_2d"] = ["DualTransformer2DModel"]
|
_import_structure["dual_transformer_2d"] = ["DualTransformer2DModel"]
|
||||||
_import_structure["modeling_utils"] = ["ModelMixin"]
|
|
||||||
_import_structure["embeddings"] = ["ImageProjection"]
|
_import_structure["embeddings"] = ["ImageProjection"]
|
||||||
|
_import_structure["modeling_utils"] = ["ModelMixin"]
|
||||||
_import_structure["prior_transformer"] = ["PriorTransformer"]
|
_import_structure["prior_transformer"] = ["PriorTransformer"]
|
||||||
_import_structure["t5_film_transformer"] = ["T5FilmDecoder"]
|
_import_structure["t5_film_transformer"] = ["T5FilmDecoder"]
|
||||||
_import_structure["transformer_2d"] = ["Transformer2DModel"]
|
_import_structure["transformer_2d"] = ["Transformer2DModel"]
|
||||||
@@ -63,6 +64,7 @@ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
|||||||
from .autoencoder_tiny import AutoencoderTiny
|
from .autoencoder_tiny import AutoencoderTiny
|
||||||
from .consistency_decoder_vae import ConsistencyDecoderVAE
|
from .consistency_decoder_vae import ConsistencyDecoderVAE
|
||||||
from .controlnet import ControlNetModel
|
from .controlnet import ControlNetModel
|
||||||
|
from .controlnetxs import ControlNetXSModel
|
||||||
from .dual_transformer_2d import DualTransformer2DModel
|
from .dual_transformer_2d import DualTransformer2DModel
|
||||||
from .embeddings import ImageProjection
|
from .embeddings import ImageProjection
|
||||||
from .modeling_utils import ModelMixin
|
from .modeling_utils import ModelMixin
|
||||||
|
|||||||
@@ -0,0 +1,977 @@
|
|||||||
|
# Copyright 2023 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 math
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||||
|
|
||||||
|
import torch
|
||||||
|
import torch.utils.checkpoint
|
||||||
|
from torch import nn
|
||||||
|
from torch.nn import functional as F
|
||||||
|
from torch.nn.modules.normalization import GroupNorm
|
||||||
|
|
||||||
|
from ..configuration_utils import ConfigMixin, register_to_config
|
||||||
|
from ..utils import BaseOutput, logging
|
||||||
|
from .attention_processor import (
|
||||||
|
AttentionProcessor,
|
||||||
|
)
|
||||||
|
from .autoencoder_kl import AutoencoderKL
|
||||||
|
from .lora import LoRACompatibleConv
|
||||||
|
from .modeling_utils import ModelMixin
|
||||||
|
from .unet_2d_blocks import (
|
||||||
|
CrossAttnDownBlock2D,
|
||||||
|
CrossAttnUpBlock2D,
|
||||||
|
DownBlock2D,
|
||||||
|
Downsample2D,
|
||||||
|
ResnetBlock2D,
|
||||||
|
Transformer2DModel,
|
||||||
|
UpBlock2D,
|
||||||
|
Upsample2D,
|
||||||
|
)
|
||||||
|
from .unet_2d_condition import UNet2DConditionModel
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ControlNetXSOutput(BaseOutput):
|
||||||
|
"""
|
||||||
|
The output of [`ControlNetXSModel`].
|
||||||
|
|
||||||
|
Args:
|
||||||
|
sample (`torch.FloatTensor` of shape `(batch_size, num_channels, height, width)`):
|
||||||
|
The output of the `ControlNetXSModel`. Unlike `ControlNetOutput` this is NOT to be added to the base model
|
||||||
|
output, but is already the final output.
|
||||||
|
"""
|
||||||
|
|
||||||
|
sample: torch.FloatTensor = None
|
||||||
|
|
||||||
|
|
||||||
|
# copied from diffusers.models.controlnet.ControlNetConditioningEmbedding
|
||||||
|
class ControlNetConditioningEmbedding(nn.Module):
|
||||||
|
"""
|
||||||
|
Quoting from https://arxiv.org/abs/2302.05543: "Stable Diffusion uses a pre-processing method similar to VQ-GAN
|
||||||
|
[11] to convert the entire dataset of 512 × 512 images into smaller 64 × 64 “latent images” for stabilized
|
||||||
|
training. This requires ControlNets to convert image-based conditions to 64 × 64 feature space to match the
|
||||||
|
convolution size. We use a tiny network E(·) of four convolution layers with 4 × 4 kernels and 2 × 2 strides
|
||||||
|
(activated by ReLU, channels are 16, 32, 64, 128, initialized with Gaussian weights, trained jointly with the full
|
||||||
|
model) to encode image-space conditions ... into feature maps ..."
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
conditioning_embedding_channels: int,
|
||||||
|
conditioning_channels: int = 3,
|
||||||
|
block_out_channels: Tuple[int, ...] = (16, 32, 96, 256),
|
||||||
|
):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.conv_in = nn.Conv2d(conditioning_channels, block_out_channels[0], kernel_size=3, padding=1)
|
||||||
|
|
||||||
|
self.blocks = nn.ModuleList([])
|
||||||
|
|
||||||
|
for i in range(len(block_out_channels) - 1):
|
||||||
|
channel_in = block_out_channels[i]
|
||||||
|
channel_out = block_out_channels[i + 1]
|
||||||
|
self.blocks.append(nn.Conv2d(channel_in, channel_in, kernel_size=3, padding=1))
|
||||||
|
self.blocks.append(nn.Conv2d(channel_in, channel_out, kernel_size=3, padding=1, stride=2))
|
||||||
|
|
||||||
|
self.conv_out = zero_module(
|
||||||
|
nn.Conv2d(block_out_channels[-1], conditioning_embedding_channels, kernel_size=3, padding=1)
|
||||||
|
)
|
||||||
|
|
||||||
|
def forward(self, conditioning):
|
||||||
|
embedding = self.conv_in(conditioning)
|
||||||
|
embedding = F.silu(embedding)
|
||||||
|
|
||||||
|
for block in self.blocks:
|
||||||
|
embedding = block(embedding)
|
||||||
|
embedding = F.silu(embedding)
|
||||||
|
|
||||||
|
embedding = self.conv_out(embedding)
|
||||||
|
|
||||||
|
return embedding
|
||||||
|
|
||||||
|
|
||||||
|
class ControlNetXSModel(ModelMixin, ConfigMixin):
|
||||||
|
r"""
|
||||||
|
A ControlNet-XS model
|
||||||
|
|
||||||
|
This model inherits from [`ModelMixin`] and [`ConfigMixin`]. Check the superclass documentation for it's generic
|
||||||
|
methods implemented for all models (such as downloading or saving).
|
||||||
|
|
||||||
|
Most of parameters for this model are passed into the [`UNet2DConditionModel`] it creates. Check the documentation
|
||||||
|
of [`UNet2DConditionModel`] for them.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
conditioning_channels (`int`, defaults to 3):
|
||||||
|
Number of channels of conditioning input (e.g. an image)
|
||||||
|
controlnet_conditioning_channel_order (`str`, defaults to `"rgb"`):
|
||||||
|
The channel order of conditional image. Will convert to `rgb` if it's `bgr`.
|
||||||
|
conditioning_embedding_out_channels (`tuple[int]`, defaults to `(16, 32, 96, 256)`):
|
||||||
|
The tuple of output channel for each block in the `controlnet_cond_embedding` layer.
|
||||||
|
time_embedding_input_dim (`int`, defaults to 320):
|
||||||
|
Dimension of input into time embedding. Needs to be same as in the base model.
|
||||||
|
time_embedding_dim (`int`, defaults to 1280):
|
||||||
|
Dimension of output from time embedding. Needs to be same as in the base model.
|
||||||
|
learn_embedding (`bool`, defaults to `False`):
|
||||||
|
Whether to use time embedding of the control model. If yes, the time embedding is a linear interpolation of
|
||||||
|
the time embeddings of the control and base model with interpolation parameter `time_embedding_mix**3`.
|
||||||
|
time_embedding_mix (`float`, defaults to 1.0):
|
||||||
|
Linear interpolation parameter used if `learn_embedding` is `True`. A value of 1.0 means only the
|
||||||
|
control model's time embedding will be used. A value of 0.0 means only the base model's time embedding will be used.
|
||||||
|
base_model_channel_sizes (`Dict[str, List[Tuple[int]]]`):
|
||||||
|
Channel sizes of each subblock of base model. Use `gather_subblock_sizes` on your base model to compute it.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def init_original(cls, base_model: UNet2DConditionModel, is_sdxl=True):
|
||||||
|
"""
|
||||||
|
Create a ControlNetXS model with the same parameters as in the original paper (https://github.com/vislearn/ControlNet-XS).
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
base_model (`UNet2DConditionModel`):
|
||||||
|
Base UNet model. Needs to be either StableDiffusion or StableDiffusion-XL.
|
||||||
|
is_sdxl (`bool`, defaults to `True`):
|
||||||
|
Whether passed `base_model` is a StableDiffusion-XL model.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_dim_attn_heads(base_model: UNet2DConditionModel, size_ratio: float, num_attn_heads: int):
|
||||||
|
"""
|
||||||
|
Currently, diffusers can only set the dimension of attention heads (see https://github.com/huggingface/diffusers/issues/2011#issuecomment-1547958131 for why).
|
||||||
|
The original ControlNet-XS model, however, define the number of attention heads.
|
||||||
|
That's why compute the dimensions needed to get the correct number of attention heads.
|
||||||
|
"""
|
||||||
|
block_out_channels = [int(size_ratio * c) for c in base_model.config.block_out_channels]
|
||||||
|
dim_attn_heads = [math.ceil(c / num_attn_heads) for c in block_out_channels]
|
||||||
|
return dim_attn_heads
|
||||||
|
|
||||||
|
if is_sdxl:
|
||||||
|
return ControlNetXSModel.from_unet(
|
||||||
|
base_model,
|
||||||
|
time_embedding_mix=0.95,
|
||||||
|
learn_embedding=True,
|
||||||
|
size_ratio=0.1,
|
||||||
|
conditioning_embedding_out_channels=(16, 32, 96, 256),
|
||||||
|
num_attention_heads=get_dim_attn_heads(base_model, 0.1, 64),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return ControlNetXSModel.from_unet(
|
||||||
|
base_model,
|
||||||
|
time_embedding_mix=1.0,
|
||||||
|
learn_embedding=True,
|
||||||
|
size_ratio=0.0125,
|
||||||
|
conditioning_embedding_out_channels=(16, 32, 96, 256),
|
||||||
|
num_attention_heads=get_dim_attn_heads(base_model, 0.0125, 8),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _gather_subblock_sizes(cls, unet: UNet2DConditionModel, base_or_control: str):
|
||||||
|
"""To create correctly sized connections between base and control model, we need to know
|
||||||
|
the input and output channels of each subblock.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
unet (`UNet2DConditionModel`):
|
||||||
|
Unet of which the subblock channels sizes are to be gathered.
|
||||||
|
base_or_control (`str`):
|
||||||
|
Needs to be either "base" or "control". If "base", decoder is also considered.
|
||||||
|
"""
|
||||||
|
if base_or_control not in ["base", "control"]:
|
||||||
|
raise ValueError("`base_or_control` needs to be either `base` or `control`")
|
||||||
|
|
||||||
|
channel_sizes = {"down": [], "mid": [], "up": []}
|
||||||
|
|
||||||
|
# input convolution
|
||||||
|
channel_sizes["down"].append((unet.conv_in.in_channels, unet.conv_in.out_channels))
|
||||||
|
|
||||||
|
# encoder blocks
|
||||||
|
for module in unet.down_blocks:
|
||||||
|
if isinstance(module, (CrossAttnDownBlock2D, DownBlock2D)):
|
||||||
|
for r in module.resnets:
|
||||||
|
channel_sizes["down"].append((r.in_channels, r.out_channels))
|
||||||
|
if module.downsamplers:
|
||||||
|
channel_sizes["down"].append(
|
||||||
|
(module.downsamplers[0].channels, module.downsamplers[0].out_channels)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Encountered unknown module of type {type(module)} while creating ControlNet-XS.")
|
||||||
|
|
||||||
|
# middle block
|
||||||
|
channel_sizes["mid"].append((unet.mid_block.resnets[0].in_channels, unet.mid_block.resnets[0].out_channels))
|
||||||
|
|
||||||
|
# decoder blocks
|
||||||
|
if base_or_control == "base":
|
||||||
|
for module in unet.up_blocks:
|
||||||
|
if isinstance(module, (CrossAttnUpBlock2D, UpBlock2D)):
|
||||||
|
for r in module.resnets:
|
||||||
|
channel_sizes["up"].append((r.in_channels, r.out_channels))
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Encountered unknown module of type {type(module)} while creating ControlNet-XS."
|
||||||
|
)
|
||||||
|
|
||||||
|
return channel_sizes
|
||||||
|
|
||||||
|
@register_to_config
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
conditioning_channels: int = 3,
|
||||||
|
conditioning_embedding_out_channels: Tuple[int] = (16, 32, 96, 256),
|
||||||
|
controlnet_conditioning_channel_order: str = "rgb",
|
||||||
|
time_embedding_input_dim: int = 320,
|
||||||
|
time_embedding_dim: int = 1280,
|
||||||
|
time_embedding_mix: float = 1.0,
|
||||||
|
learn_embedding: bool = False,
|
||||||
|
base_model_channel_sizes: Dict[str, List[Tuple[int]]] = {
|
||||||
|
"down": [
|
||||||
|
(4, 320),
|
||||||
|
(320, 320),
|
||||||
|
(320, 320),
|
||||||
|
(320, 320),
|
||||||
|
(320, 640),
|
||||||
|
(640, 640),
|
||||||
|
(640, 640),
|
||||||
|
(640, 1280),
|
||||||
|
(1280, 1280),
|
||||||
|
],
|
||||||
|
"mid": [(1280, 1280)],
|
||||||
|
"up": [
|
||||||
|
(2560, 1280),
|
||||||
|
(2560, 1280),
|
||||||
|
(1920, 1280),
|
||||||
|
(1920, 640),
|
||||||
|
(1280, 640),
|
||||||
|
(960, 640),
|
||||||
|
(960, 320),
|
||||||
|
(640, 320),
|
||||||
|
(640, 320),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
sample_size: Optional[int] = None,
|
||||||
|
down_block_types: Tuple[str] = (
|
||||||
|
"CrossAttnDownBlock2D",
|
||||||
|
"CrossAttnDownBlock2D",
|
||||||
|
"CrossAttnDownBlock2D",
|
||||||
|
"DownBlock2D",
|
||||||
|
),
|
||||||
|
up_block_types: Tuple[str] = ("UpBlock2D", "CrossAttnUpBlock2D", "CrossAttnUpBlock2D", "CrossAttnUpBlock2D"),
|
||||||
|
block_out_channels: Tuple[int] = (320, 640, 1280, 1280),
|
||||||
|
norm_num_groups: Optional[int] = 32,
|
||||||
|
cross_attention_dim: Union[int, Tuple[int]] = 1280,
|
||||||
|
transformer_layers_per_block: Union[int, Tuple[int], Tuple[Tuple]] = 1,
|
||||||
|
num_attention_heads: Optional[Union[int, Tuple[int]]] = 8,
|
||||||
|
upcast_attention: bool = False,
|
||||||
|
):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
# 1 - Create control unet
|
||||||
|
self.control_model = UNet2DConditionModel(
|
||||||
|
sample_size=sample_size,
|
||||||
|
down_block_types=down_block_types,
|
||||||
|
up_block_types=up_block_types,
|
||||||
|
block_out_channels=block_out_channels,
|
||||||
|
norm_num_groups=norm_num_groups,
|
||||||
|
cross_attention_dim=cross_attention_dim,
|
||||||
|
transformer_layers_per_block=transformer_layers_per_block,
|
||||||
|
attention_head_dim=num_attention_heads,
|
||||||
|
use_linear_projection=True,
|
||||||
|
upcast_attention=upcast_attention,
|
||||||
|
time_embedding_dim=time_embedding_dim,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 2 - Do model surgery on control model
|
||||||
|
# 2.1 - Allow to use the same time information as the base model
|
||||||
|
adjust_time_dims(self.control_model, time_embedding_input_dim, time_embedding_dim)
|
||||||
|
|
||||||
|
# 2.2 - Allow for information infusion from base model
|
||||||
|
|
||||||
|
# We concat the output of each base encoder subblocks to the input of the next control encoder subblock
|
||||||
|
# (We ignore the 1st element, as it represents the `conv_in`.)
|
||||||
|
extra_input_channels = [input_channels for input_channels, _ in base_model_channel_sizes["down"][1:]]
|
||||||
|
it_extra_input_channels = iter(extra_input_channels)
|
||||||
|
|
||||||
|
for b, block in enumerate(self.control_model.down_blocks):
|
||||||
|
for r in range(len(block.resnets)):
|
||||||
|
increase_block_input_in_encoder_resnet(
|
||||||
|
self.control_model, block_no=b, resnet_idx=r, by=next(it_extra_input_channels)
|
||||||
|
)
|
||||||
|
|
||||||
|
if block.downsamplers:
|
||||||
|
increase_block_input_in_encoder_downsampler(
|
||||||
|
self.control_model, block_no=b, by=next(it_extra_input_channels)
|
||||||
|
)
|
||||||
|
|
||||||
|
increase_block_input_in_mid_resnet(self.control_model, by=extra_input_channels[-1])
|
||||||
|
|
||||||
|
# 2.3 - Make group norms work with modified channel sizes
|
||||||
|
adjust_group_norms(self.control_model)
|
||||||
|
|
||||||
|
# 3 - Gather Channel Sizes
|
||||||
|
self.ch_inout_ctrl = ControlNetXSModel._gather_subblock_sizes(self.control_model, base_or_control="control")
|
||||||
|
self.ch_inout_base = base_model_channel_sizes
|
||||||
|
|
||||||
|
# 4 - Build connections between base and control model
|
||||||
|
self.down_zero_convs_out = nn.ModuleList([])
|
||||||
|
self.down_zero_convs_in = nn.ModuleList([])
|
||||||
|
self.middle_block_out = nn.ModuleList([])
|
||||||
|
self.middle_block_in = nn.ModuleList([])
|
||||||
|
self.up_zero_convs_out = nn.ModuleList([])
|
||||||
|
self.up_zero_convs_in = nn.ModuleList([])
|
||||||
|
|
||||||
|
for ch_io_base in self.ch_inout_base["down"]:
|
||||||
|
self.down_zero_convs_in.append(self._make_zero_conv(in_channels=ch_io_base[1], out_channels=ch_io_base[1]))
|
||||||
|
for i in range(len(self.ch_inout_ctrl["down"])):
|
||||||
|
self.down_zero_convs_out.append(
|
||||||
|
self._make_zero_conv(self.ch_inout_ctrl["down"][i][1], self.ch_inout_base["down"][i][1])
|
||||||
|
)
|
||||||
|
|
||||||
|
self.middle_block_out = self._make_zero_conv(
|
||||||
|
self.ch_inout_ctrl["mid"][-1][1], self.ch_inout_base["mid"][-1][1]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.up_zero_convs_out.append(
|
||||||
|
self._make_zero_conv(self.ch_inout_ctrl["down"][-1][1], self.ch_inout_base["mid"][-1][1])
|
||||||
|
)
|
||||||
|
for i in range(1, len(self.ch_inout_ctrl["down"])):
|
||||||
|
self.up_zero_convs_out.append(
|
||||||
|
self._make_zero_conv(self.ch_inout_ctrl["down"][-(i + 1)][1], self.ch_inout_base["up"][i - 1][1])
|
||||||
|
)
|
||||||
|
|
||||||
|
# 5 - Create conditioning hint embedding
|
||||||
|
self.controlnet_cond_embedding = ControlNetConditioningEmbedding(
|
||||||
|
conditioning_embedding_channels=block_out_channels[0],
|
||||||
|
block_out_channels=conditioning_embedding_out_channels,
|
||||||
|
conditioning_channels=conditioning_channels,
|
||||||
|
)
|
||||||
|
|
||||||
|
# In the mininal implementation setting, we only need the control model up to the mid block
|
||||||
|
del self.control_model.up_blocks
|
||||||
|
del self.control_model.conv_norm_out
|
||||||
|
del self.control_model.conv_out
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_unet(
|
||||||
|
cls,
|
||||||
|
unet: UNet2DConditionModel,
|
||||||
|
conditioning_channels: int = 3,
|
||||||
|
conditioning_embedding_out_channels: Tuple[int] = (16, 32, 96, 256),
|
||||||
|
controlnet_conditioning_channel_order: str = "rgb",
|
||||||
|
learn_embedding: bool = False,
|
||||||
|
time_embedding_mix: float = 1.0,
|
||||||
|
block_out_channels: Optional[Tuple[int]] = None,
|
||||||
|
size_ratio: Optional[float] = None,
|
||||||
|
num_attention_heads: Optional[Union[int, Tuple[int]]] = 8,
|
||||||
|
norm_num_groups: Optional[int] = None,
|
||||||
|
):
|
||||||
|
r"""
|
||||||
|
Instantiate a [`ControlNetXSModel`] from [`UNet2DConditionModel`].
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
unet (`UNet2DConditionModel`):
|
||||||
|
The UNet model we want to control. The dimensions of the ControlNetXSModel will be adapted to it.
|
||||||
|
conditioning_channels (`int`, defaults to 3):
|
||||||
|
Number of channels of conditioning input (e.g. an image)
|
||||||
|
conditioning_embedding_out_channels (`tuple[int]`, defaults to `(16, 32, 96, 256)`):
|
||||||
|
The tuple of output channel for each block in the `controlnet_cond_embedding` layer.
|
||||||
|
controlnet_conditioning_channel_order (`str`, defaults to `"rgb"`):
|
||||||
|
The channel order of conditional image. Will convert to `rgb` if it's `bgr`.
|
||||||
|
learn_embedding (`bool`, defaults to `False`):
|
||||||
|
Wether to use time embedding of the control model. If yes, the time embedding is a linear interpolation
|
||||||
|
of the time embeddings of the control and base model with interpolation parameter
|
||||||
|
`time_embedding_mix**3`.
|
||||||
|
time_embedding_mix (`float`, defaults to 1.0):
|
||||||
|
Linear interpolation parameter used if `learn_embedding` is `True`.
|
||||||
|
block_out_channels (`Tuple[int]`, *optional*):
|
||||||
|
Down blocks output channels in control model. Either this or `size_ratio` must be given.
|
||||||
|
size_ratio (float, *optional*):
|
||||||
|
When given, block_out_channels is set to a relative fraction of the base model's block_out_channels.
|
||||||
|
Either this or `block_out_channels` must be given.
|
||||||
|
num_attention_heads (`Union[int, Tuple[int]]`, *optional*):
|
||||||
|
The dimension of the attention heads. The naming seems a bit confusing and it is, see https://github.com/huggingface/diffusers/issues/2011#issuecomment-1547958131 for why.
|
||||||
|
norm_num_groups (int, *optional*, defaults to `None`):
|
||||||
|
The number of groups to use for the normalization of the control unet. If `None`,
|
||||||
|
`int(unet.config.norm_num_groups * size_ratio)` is taken.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Check input
|
||||||
|
fixed_size = block_out_channels is not None
|
||||||
|
relative_size = size_ratio is not None
|
||||||
|
if not (fixed_size ^ relative_size):
|
||||||
|
raise ValueError(
|
||||||
|
"Pass exactly one of `block_out_channels` (for absolute sizing) or `control_model_ratio` (for relative sizing)."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create model
|
||||||
|
if block_out_channels is None:
|
||||||
|
block_out_channels = [int(size_ratio * c) for c in unet.config.block_out_channels]
|
||||||
|
|
||||||
|
# Check that attention heads and group norms match channel sizes
|
||||||
|
# - attention heads
|
||||||
|
def attn_heads_match_channel_sizes(attn_heads, channel_sizes):
|
||||||
|
if isinstance(attn_heads, (tuple, list)):
|
||||||
|
return all(c % a == 0 for a, c in zip(attn_heads, channel_sizes))
|
||||||
|
else:
|
||||||
|
return all(c % attn_heads == 0 for c in channel_sizes)
|
||||||
|
|
||||||
|
num_attention_heads = num_attention_heads or unet.config.attention_head_dim
|
||||||
|
if not attn_heads_match_channel_sizes(num_attention_heads, block_out_channels):
|
||||||
|
raise ValueError(
|
||||||
|
f"The dimension of attention heads ({num_attention_heads}) must divide `block_out_channels` ({block_out_channels}). If you didn't set `num_attention_heads` the default settings don't match your model. Set `num_attention_heads` manually."
|
||||||
|
)
|
||||||
|
|
||||||
|
# - group norms
|
||||||
|
def group_norms_match_channel_sizes(num_groups, channel_sizes):
|
||||||
|
return all(c % num_groups == 0 for c in channel_sizes)
|
||||||
|
|
||||||
|
if norm_num_groups is None:
|
||||||
|
if group_norms_match_channel_sizes(unet.config.norm_num_groups, block_out_channels):
|
||||||
|
norm_num_groups = unet.config.norm_num_groups
|
||||||
|
else:
|
||||||
|
norm_num_groups = min(block_out_channels)
|
||||||
|
|
||||||
|
if group_norms_match_channel_sizes(norm_num_groups, block_out_channels):
|
||||||
|
print(
|
||||||
|
f"`norm_num_groups` was set to `min(block_out_channels)` (={norm_num_groups}) so it divides all block_out_channels` ({block_out_channels}). Set it explicitly to remove this information."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"`block_out_channels` ({block_out_channels}) don't match the base models `norm_num_groups` ({unet.config.norm_num_groups}). Setting `norm_num_groups` to `min(block_out_channels)` ({norm_num_groups}) didn't fix this. Pass `norm_num_groups` explicitly so it divides all block_out_channels."
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_time_emb_input_dim(unet: UNet2DConditionModel):
|
||||||
|
return unet.time_embedding.linear_1.in_features
|
||||||
|
|
||||||
|
def get_time_emb_dim(unet: UNet2DConditionModel):
|
||||||
|
return unet.time_embedding.linear_2.out_features
|
||||||
|
|
||||||
|
# Clone params from base unet if
|
||||||
|
# (i) it's required to build SD or SDXL, and
|
||||||
|
# (ii) it's not used for the time embedding (as time embedding of control model is never used), and
|
||||||
|
# (iii) it's not set further below anyway
|
||||||
|
to_keep = [
|
||||||
|
"cross_attention_dim",
|
||||||
|
"down_block_types",
|
||||||
|
"sample_size",
|
||||||
|
"transformer_layers_per_block",
|
||||||
|
"up_block_types",
|
||||||
|
"upcast_attention",
|
||||||
|
]
|
||||||
|
kwargs = {k: v for k, v in dict(unet.config).items() if k in to_keep}
|
||||||
|
kwargs.update(block_out_channels=block_out_channels)
|
||||||
|
kwargs.update(num_attention_heads=num_attention_heads)
|
||||||
|
kwargs.update(norm_num_groups=norm_num_groups)
|
||||||
|
|
||||||
|
# Add controlnetxs-specific params
|
||||||
|
kwargs.update(
|
||||||
|
conditioning_channels=conditioning_channels,
|
||||||
|
controlnet_conditioning_channel_order=controlnet_conditioning_channel_order,
|
||||||
|
time_embedding_input_dim=get_time_emb_input_dim(unet),
|
||||||
|
time_embedding_dim=get_time_emb_dim(unet),
|
||||||
|
time_embedding_mix=time_embedding_mix,
|
||||||
|
learn_embedding=learn_embedding,
|
||||||
|
base_model_channel_sizes=ControlNetXSModel._gather_subblock_sizes(unet, base_or_control="base"),
|
||||||
|
conditioning_embedding_out_channels=conditioning_embedding_out_channels,
|
||||||
|
)
|
||||||
|
|
||||||
|
return cls(**kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def attn_processors(self) -> Dict[str, AttentionProcessor]:
|
||||||
|
r"""
|
||||||
|
Returns:
|
||||||
|
`dict` of attention processors: A dictionary containing all attention processors used in the model with
|
||||||
|
indexed by its weight name.
|
||||||
|
"""
|
||||||
|
return self.control_model.attn_processors
|
||||||
|
|
||||||
|
def set_attn_processor(
|
||||||
|
self, processor: Union[AttentionProcessor, Dict[str, AttentionProcessor]], _remove_lora=False
|
||||||
|
):
|
||||||
|
r"""
|
||||||
|
Sets the attention processor to use to compute attention.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
processor (`dict` of `AttentionProcessor` or only `AttentionProcessor`):
|
||||||
|
The instantiated processor class or a dictionary of processor classes that will be set as the processor
|
||||||
|
for **all** `Attention` layers.
|
||||||
|
|
||||||
|
If `processor` is a dict, the key needs to define the path to the corresponding cross attention
|
||||||
|
processor. This is strongly recommended when setting trainable attention processors.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.control_model.set_attn_processor(processor, _remove_lora)
|
||||||
|
|
||||||
|
def set_default_attn_processor(self):
|
||||||
|
"""
|
||||||
|
Disables custom attention processors and sets the default attention implementation.
|
||||||
|
"""
|
||||||
|
self.control_model.set_default_attn_processor()
|
||||||
|
|
||||||
|
def set_attention_slice(self, slice_size):
|
||||||
|
r"""
|
||||||
|
Enable sliced attention computation.
|
||||||
|
|
||||||
|
When this option is enabled, the attention module splits the input tensor in slices to compute attention in
|
||||||
|
several steps. This is useful for saving some memory in exchange for a small decrease in speed.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
slice_size (`str` or `int` or `list(int)`, *optional*, defaults to `"auto"`):
|
||||||
|
When `"auto"`, input to the attention heads is halved, so attention is computed in two steps. If
|
||||||
|
`"max"`, maximum amount of memory is saved by running only one slice at a time. If a number is
|
||||||
|
provided, uses as many slices as `attention_head_dim // slice_size`. In this case, `attention_head_dim`
|
||||||
|
must be a multiple of `slice_size`.
|
||||||
|
"""
|
||||||
|
self.control_model.set_attention_slice(slice_size)
|
||||||
|
|
||||||
|
def _set_gradient_checkpointing(self, module, value=False):
|
||||||
|
if isinstance(module, (UNet2DConditionModel)):
|
||||||
|
if value:
|
||||||
|
module.enable_gradient_checkpointing()
|
||||||
|
else:
|
||||||
|
module.disable_gradient_checkpointing()
|
||||||
|
|
||||||
|
def forward(
|
||||||
|
self,
|
||||||
|
base_model: UNet2DConditionModel,
|
||||||
|
sample: torch.FloatTensor,
|
||||||
|
timestep: Union[torch.Tensor, float, int],
|
||||||
|
encoder_hidden_states: torch.Tensor,
|
||||||
|
controlnet_cond: torch.Tensor,
|
||||||
|
conditioning_scale: float = 1.0,
|
||||||
|
class_labels: Optional[torch.Tensor] = None,
|
||||||
|
timestep_cond: Optional[torch.Tensor] = None,
|
||||||
|
attention_mask: Optional[torch.Tensor] = None,
|
||||||
|
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
|
||||||
|
added_cond_kwargs: Optional[Dict[str, torch.Tensor]] = None,
|
||||||
|
return_dict: bool = True,
|
||||||
|
) -> Union[ControlNetXSOutput, Tuple]:
|
||||||
|
"""
|
||||||
|
The [`ControlNetModel`] forward method.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
base_model (`UNet2DConditionModel`):
|
||||||
|
The base unet model we want to control.
|
||||||
|
sample (`torch.FloatTensor`):
|
||||||
|
The noisy input tensor.
|
||||||
|
timestep (`Union[torch.Tensor, float, int]`):
|
||||||
|
The number of timesteps to denoise an input.
|
||||||
|
encoder_hidden_states (`torch.Tensor`):
|
||||||
|
The encoder hidden states.
|
||||||
|
controlnet_cond (`torch.FloatTensor`):
|
||||||
|
The conditional input tensor of shape `(batch_size, sequence_length, hidden_size)`.
|
||||||
|
conditioning_scale (`float`, defaults to `1.0`):
|
||||||
|
How much the control model affects the base model outputs.
|
||||||
|
class_labels (`torch.Tensor`, *optional*, defaults to `None`):
|
||||||
|
Optional class labels for conditioning. Their embeddings will be summed with the timestep embeddings.
|
||||||
|
timestep_cond (`torch.Tensor`, *optional*, defaults to `None`):
|
||||||
|
Additional conditional embeddings for timestep. If provided, the embeddings will be summed with the
|
||||||
|
timestep_embedding passed through the `self.time_embedding` layer to obtain the final timestep
|
||||||
|
embeddings.
|
||||||
|
attention_mask (`torch.Tensor`, *optional*, defaults to `None`):
|
||||||
|
An attention mask of shape `(batch, key_tokens)` is applied to `encoder_hidden_states`. If `1` the mask
|
||||||
|
is kept, otherwise if `0` it is discarded. Mask will be converted into a bias, which adds large
|
||||||
|
negative values to the attention scores corresponding to "discard" tokens.
|
||||||
|
added_cond_kwargs (`dict`):
|
||||||
|
Additional conditions for the Stable Diffusion XL UNet.
|
||||||
|
cross_attention_kwargs (`dict[str]`, *optional*, defaults to `None`):
|
||||||
|
A kwargs dictionary that if specified is passed along to the `AttnProcessor`.
|
||||||
|
return_dict (`bool`, defaults to `True`):
|
||||||
|
Whether or not to return a [`~models.controlnet.ControlNetOutput`] instead of a plain tuple.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
[`~models.controlnetxs.ControlNetXSOutput`] **or** `tuple`:
|
||||||
|
If `return_dict` is `True`, a [`~models.controlnetxs.ControlNetXSOutput`] is returned, otherwise a
|
||||||
|
tuple is returned where the first element is the sample tensor.
|
||||||
|
"""
|
||||||
|
# check channel order
|
||||||
|
channel_order = self.config.controlnet_conditioning_channel_order
|
||||||
|
|
||||||
|
if channel_order == "rgb":
|
||||||
|
# in rgb order by default
|
||||||
|
...
|
||||||
|
elif channel_order == "bgr":
|
||||||
|
controlnet_cond = torch.flip(controlnet_cond, dims=[1])
|
||||||
|
else:
|
||||||
|
raise ValueError(f"unknown `controlnet_conditioning_channel_order`: {channel_order}")
|
||||||
|
|
||||||
|
# scale control strength
|
||||||
|
n_connections = len(self.down_zero_convs_out) + 1 + len(self.up_zero_convs_out)
|
||||||
|
scale_list = torch.full((n_connections,), conditioning_scale)
|
||||||
|
|
||||||
|
# prepare attention_mask
|
||||||
|
if attention_mask is not None:
|
||||||
|
attention_mask = (1 - attention_mask.to(sample.dtype)) * -10000.0
|
||||||
|
attention_mask = attention_mask.unsqueeze(1)
|
||||||
|
|
||||||
|
# 1. time
|
||||||
|
timesteps = timestep
|
||||||
|
if not torch.is_tensor(timesteps):
|
||||||
|
# TODO: this requires sync between CPU and GPU. So try to pass timesteps as tensors if you can
|
||||||
|
# This would be a good case for the `match` statement (Python 3.10+)
|
||||||
|
is_mps = sample.device.type == "mps"
|
||||||
|
if isinstance(timestep, float):
|
||||||
|
dtype = torch.float32 if is_mps else torch.float64
|
||||||
|
else:
|
||||||
|
dtype = torch.int32 if is_mps else torch.int64
|
||||||
|
timesteps = torch.tensor([timesteps], dtype=dtype, device=sample.device)
|
||||||
|
elif len(timesteps.shape) == 0:
|
||||||
|
timesteps = timesteps[None].to(sample.device)
|
||||||
|
|
||||||
|
# broadcast to batch dimension in a way that's compatible with ONNX/Core ML
|
||||||
|
timesteps = timesteps.expand(sample.shape[0])
|
||||||
|
|
||||||
|
t_emb = base_model.time_proj(timesteps)
|
||||||
|
|
||||||
|
# timesteps does not contain any weights and will always return f32 tensors
|
||||||
|
# but time_embedding might actually be running in fp16. so we need to cast here.
|
||||||
|
# there might be better ways to encapsulate this.
|
||||||
|
t_emb = t_emb.to(dtype=sample.dtype)
|
||||||
|
|
||||||
|
if self.config.learn_embedding:
|
||||||
|
ctrl_temb = self.control_model.time_embedding(t_emb, timestep_cond)
|
||||||
|
base_temb = base_model.time_embedding(t_emb, timestep_cond)
|
||||||
|
interpolation_param = self.config.time_embedding_mix**0.3
|
||||||
|
|
||||||
|
temb = ctrl_temb * interpolation_param + base_temb * (1 - interpolation_param)
|
||||||
|
else:
|
||||||
|
temb = base_model.time_embedding(t_emb)
|
||||||
|
|
||||||
|
# added time & text embeddings
|
||||||
|
aug_emb = None
|
||||||
|
|
||||||
|
if base_model.class_embedding is not None:
|
||||||
|
if class_labels is None:
|
||||||
|
raise ValueError("class_labels should be provided when num_class_embeds > 0")
|
||||||
|
|
||||||
|
if base_model.config.class_embed_type == "timestep":
|
||||||
|
class_labels = base_model.time_proj(class_labels)
|
||||||
|
|
||||||
|
class_emb = base_model.class_embedding(class_labels).to(dtype=self.dtype)
|
||||||
|
temb = temb + class_emb
|
||||||
|
|
||||||
|
if base_model.config.addition_embed_type is not None:
|
||||||
|
if base_model.config.addition_embed_type == "text":
|
||||||
|
aug_emb = base_model.add_embedding(encoder_hidden_states)
|
||||||
|
elif base_model.config.addition_embed_type == "text_image":
|
||||||
|
raise NotImplementedError()
|
||||||
|
elif base_model.config.addition_embed_type == "text_time":
|
||||||
|
# SDXL - style
|
||||||
|
if "text_embeds" not in added_cond_kwargs:
|
||||||
|
raise ValueError(
|
||||||
|
f"{self.__class__} has the config param `addition_embed_type` set to 'text_time' which requires the keyword argument `text_embeds` to be passed in `added_cond_kwargs`"
|
||||||
|
)
|
||||||
|
text_embeds = added_cond_kwargs.get("text_embeds")
|
||||||
|
if "time_ids" not in added_cond_kwargs:
|
||||||
|
raise ValueError(
|
||||||
|
f"{self.__class__} has the config param `addition_embed_type` set to 'text_time' which requires the keyword argument `time_ids` to be passed in `added_cond_kwargs`"
|
||||||
|
)
|
||||||
|
time_ids = added_cond_kwargs.get("time_ids")
|
||||||
|
time_embeds = base_model.add_time_proj(time_ids.flatten())
|
||||||
|
time_embeds = time_embeds.reshape((text_embeds.shape[0], -1))
|
||||||
|
add_embeds = torch.concat([text_embeds, time_embeds], dim=-1)
|
||||||
|
add_embeds = add_embeds.to(temb.dtype)
|
||||||
|
aug_emb = base_model.add_embedding(add_embeds)
|
||||||
|
elif base_model.config.addition_embed_type == "image":
|
||||||
|
raise NotImplementedError()
|
||||||
|
elif base_model.config.addition_embed_type == "image_hint":
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
temb = temb + aug_emb if aug_emb is not None else temb
|
||||||
|
|
||||||
|
# text embeddings
|
||||||
|
cemb = encoder_hidden_states
|
||||||
|
|
||||||
|
# Preparation
|
||||||
|
guided_hint = self.controlnet_cond_embedding(controlnet_cond)
|
||||||
|
|
||||||
|
h_ctrl = h_base = sample
|
||||||
|
hs_base, hs_ctrl = [], []
|
||||||
|
it_down_convs_in, it_down_convs_out, it_dec_convs_in, it_up_convs_out = map(
|
||||||
|
iter, (self.down_zero_convs_in, self.down_zero_convs_out, self.up_zero_convs_in, self.up_zero_convs_out)
|
||||||
|
)
|
||||||
|
scales = iter(scale_list)
|
||||||
|
|
||||||
|
base_down_subblocks = to_sub_blocks(base_model.down_blocks)
|
||||||
|
ctrl_down_subblocks = to_sub_blocks(self.control_model.down_blocks)
|
||||||
|
base_mid_subblocks = to_sub_blocks([base_model.mid_block])
|
||||||
|
ctrl_mid_subblocks = to_sub_blocks([self.control_model.mid_block])
|
||||||
|
base_up_subblocks = to_sub_blocks(base_model.up_blocks)
|
||||||
|
|
||||||
|
# Cross Control
|
||||||
|
# 0 - conv in
|
||||||
|
h_base = base_model.conv_in(h_base)
|
||||||
|
h_ctrl = self.control_model.conv_in(h_ctrl)
|
||||||
|
if guided_hint is not None:
|
||||||
|
h_ctrl += guided_hint
|
||||||
|
h_base = h_base + next(it_down_convs_out)(h_ctrl) * next(scales) # D - add ctrl -> base
|
||||||
|
|
||||||
|
hs_base.append(h_base)
|
||||||
|
hs_ctrl.append(h_ctrl)
|
||||||
|
|
||||||
|
# 1 - down
|
||||||
|
for m_base, m_ctrl in zip(base_down_subblocks, ctrl_down_subblocks):
|
||||||
|
h_ctrl = torch.cat([h_ctrl, next(it_down_convs_in)(h_base)], dim=1) # A - concat base -> ctrl
|
||||||
|
h_base = m_base(h_base, temb, cemb, attention_mask, cross_attention_kwargs) # B - apply base subblock
|
||||||
|
h_ctrl = m_ctrl(h_ctrl, temb, cemb, attention_mask, cross_attention_kwargs) # C - apply ctrl subblock
|
||||||
|
h_base = h_base + next(it_down_convs_out)(h_ctrl) * next(scales) # D - add ctrl -> base
|
||||||
|
hs_base.append(h_base)
|
||||||
|
hs_ctrl.append(h_ctrl)
|
||||||
|
|
||||||
|
# 2 - mid
|
||||||
|
h_ctrl = torch.cat([h_ctrl, next(it_down_convs_in)(h_base)], dim=1) # A - concat base -> ctrl
|
||||||
|
for m_base, m_ctrl in zip(base_mid_subblocks, ctrl_mid_subblocks):
|
||||||
|
h_base = m_base(h_base, temb, cemb, attention_mask, cross_attention_kwargs) # B - apply base subblock
|
||||||
|
h_ctrl = m_ctrl(h_ctrl, temb, cemb, attention_mask, cross_attention_kwargs) # C - apply ctrl subblock
|
||||||
|
h_base = h_base + self.middle_block_out(h_ctrl) * next(scales) # D - add ctrl -> base
|
||||||
|
|
||||||
|
# 3 - up
|
||||||
|
for i, m_base in enumerate(base_up_subblocks):
|
||||||
|
h_base = h_base + next(it_up_convs_out)(hs_ctrl.pop()) * next(scales) # add info from ctrl encoder
|
||||||
|
h_base = torch.cat([h_base, hs_base.pop()], dim=1) # concat info from base encoder+ctrl encoder
|
||||||
|
h_base = m_base(h_base, temb, cemb, attention_mask, cross_attention_kwargs)
|
||||||
|
|
||||||
|
h_base = base_model.conv_norm_out(h_base)
|
||||||
|
h_base = base_model.conv_act(h_base)
|
||||||
|
h_base = base_model.conv_out(h_base)
|
||||||
|
|
||||||
|
if not return_dict:
|
||||||
|
return h_base
|
||||||
|
|
||||||
|
return ControlNetXSOutput(sample=h_base)
|
||||||
|
|
||||||
|
def _make_zero_conv(self, in_channels, out_channels=None):
|
||||||
|
# keep running track of channels sizes
|
||||||
|
self.in_channels = in_channels
|
||||||
|
self.out_channels = out_channels or in_channels
|
||||||
|
|
||||||
|
return zero_module(nn.Conv2d(in_channels, out_channels, 1, padding=0))
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
|
def _check_if_vae_compatible(self, vae: AutoencoderKL):
|
||||||
|
condition_downscale_factor = 2 ** (len(self.config.conditioning_embedding_out_channels) - 1)
|
||||||
|
vae_downscale_factor = 2 ** (len(vae.config.block_out_channels) - 1)
|
||||||
|
compatible = condition_downscale_factor == vae_downscale_factor
|
||||||
|
return compatible, condition_downscale_factor, vae_downscale_factor
|
||||||
|
|
||||||
|
|
||||||
|
class SubBlock(nn.ModuleList):
|
||||||
|
"""A SubBlock is the largest piece of either base or control model, that is executed independently of the other model respectively.
|
||||||
|
Before each subblock, information is concatted from base to control. And after each subblock, information is added from control to base.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, ms, *args, **kwargs):
|
||||||
|
if not is_iterable(ms):
|
||||||
|
ms = [ms]
|
||||||
|
super().__init__(ms, *args, **kwargs)
|
||||||
|
|
||||||
|
def forward(
|
||||||
|
self,
|
||||||
|
x: torch.Tensor,
|
||||||
|
temb: torch.Tensor,
|
||||||
|
cemb: torch.Tensor,
|
||||||
|
attention_mask: Optional[torch.Tensor] = None,
|
||||||
|
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
|
||||||
|
):
|
||||||
|
"""Iterate through children and pass correct information to each."""
|
||||||
|
for m in self:
|
||||||
|
if isinstance(m, ResnetBlock2D):
|
||||||
|
x = m(x, temb)
|
||||||
|
elif isinstance(m, Transformer2DModel):
|
||||||
|
x = m(x, cemb, attention_mask=attention_mask, cross_attention_kwargs=cross_attention_kwargs).sample
|
||||||
|
elif isinstance(m, Downsample2D):
|
||||||
|
x = m(x)
|
||||||
|
elif isinstance(m, Upsample2D):
|
||||||
|
x = m(x)
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Type of m is {type(m)} but should be `ResnetBlock2D`, `Transformer2DModel`, `Downsample2D` or `Upsample2D`"
|
||||||
|
)
|
||||||
|
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def adjust_time_dims(unet: UNet2DConditionModel, in_dim: int, out_dim: int):
|
||||||
|
unet.time_embedding.linear_1 = nn.Linear(in_dim, out_dim)
|
||||||
|
|
||||||
|
|
||||||
|
def increase_block_input_in_encoder_resnet(unet: UNet2DConditionModel, block_no, resnet_idx, by):
|
||||||
|
"""Increase channels sizes to allow for additional concatted information from base model"""
|
||||||
|
r = unet.down_blocks[block_no].resnets[resnet_idx]
|
||||||
|
old_norm1, old_conv1 = r.norm1, r.conv1
|
||||||
|
# norm
|
||||||
|
norm_args = "num_groups num_channels eps affine".split(" ")
|
||||||
|
for a in norm_args:
|
||||||
|
assert hasattr(old_norm1, a)
|
||||||
|
norm_kwargs = {a: getattr(old_norm1, a) for a in norm_args}
|
||||||
|
norm_kwargs["num_channels"] += by # surgery done here
|
||||||
|
# conv1
|
||||||
|
conv1_args = (
|
||||||
|
"in_channels out_channels kernel_size stride padding dilation groups bias padding_mode lora_layer".split(" ")
|
||||||
|
)
|
||||||
|
for a in conv1_args:
|
||||||
|
assert hasattr(old_conv1, a)
|
||||||
|
conv1_kwargs = {a: getattr(old_conv1, a) for a in conv1_args}
|
||||||
|
conv1_kwargs["bias"] = "bias" in conv1_kwargs # as param, bias is a boolean, but as attr, it's a tensor.
|
||||||
|
conv1_kwargs["in_channels"] += by # surgery done here
|
||||||
|
# conv_shortcut
|
||||||
|
# as we changed the input size of the block, the input and output sizes are likely different,
|
||||||
|
# therefore we need a conv_shortcut (simply adding won't work)
|
||||||
|
conv_shortcut_args_kwargs = {
|
||||||
|
"in_channels": conv1_kwargs["in_channels"],
|
||||||
|
"out_channels": conv1_kwargs["out_channels"],
|
||||||
|
# default arguments from resnet.__init__
|
||||||
|
"kernel_size": 1,
|
||||||
|
"stride": 1,
|
||||||
|
"padding": 0,
|
||||||
|
"bias": True,
|
||||||
|
}
|
||||||
|
# swap old with new modules
|
||||||
|
unet.down_blocks[block_no].resnets[resnet_idx].norm1 = GroupNorm(**norm_kwargs)
|
||||||
|
unet.down_blocks[block_no].resnets[resnet_idx].conv1 = LoRACompatibleConv(**conv1_kwargs)
|
||||||
|
unet.down_blocks[block_no].resnets[resnet_idx].conv_shortcut = LoRACompatibleConv(**conv_shortcut_args_kwargs)
|
||||||
|
unet.down_blocks[block_no].resnets[resnet_idx].in_channels += by # surgery done here
|
||||||
|
|
||||||
|
|
||||||
|
def increase_block_input_in_encoder_downsampler(unet: UNet2DConditionModel, block_no, by):
|
||||||
|
"""Increase channels sizes to allow for additional concatted information from base model"""
|
||||||
|
old_down = unet.down_blocks[block_no].downsamplers[0].conv
|
||||||
|
# conv1
|
||||||
|
args = "in_channels out_channels kernel_size stride padding dilation groups bias padding_mode lora_layer".split(
|
||||||
|
" "
|
||||||
|
)
|
||||||
|
for a in args:
|
||||||
|
assert hasattr(old_down, a)
|
||||||
|
kwargs = {a: getattr(old_down, a) for a in args}
|
||||||
|
kwargs["bias"] = "bias" in kwargs # as param, bias is a boolean, but as attr, it's a tensor.
|
||||||
|
kwargs["in_channels"] += by # surgery done here
|
||||||
|
# swap old with new modules
|
||||||
|
unet.down_blocks[block_no].downsamplers[0].conv = LoRACompatibleConv(**kwargs)
|
||||||
|
unet.down_blocks[block_no].downsamplers[0].channels += by # surgery done here
|
||||||
|
|
||||||
|
|
||||||
|
def increase_block_input_in_mid_resnet(unet: UNet2DConditionModel, by):
|
||||||
|
"""Increase channels sizes to allow for additional concatted information from base model"""
|
||||||
|
m = unet.mid_block.resnets[0]
|
||||||
|
old_norm1, old_conv1 = m.norm1, m.conv1
|
||||||
|
# norm
|
||||||
|
norm_args = "num_groups num_channels eps affine".split(" ")
|
||||||
|
for a in norm_args:
|
||||||
|
assert hasattr(old_norm1, a)
|
||||||
|
norm_kwargs = {a: getattr(old_norm1, a) for a in norm_args}
|
||||||
|
norm_kwargs["num_channels"] += by # surgery done here
|
||||||
|
# conv1
|
||||||
|
conv1_args = (
|
||||||
|
"in_channels out_channels kernel_size stride padding dilation groups bias padding_mode lora_layer".split(" ")
|
||||||
|
)
|
||||||
|
for a in conv1_args:
|
||||||
|
assert hasattr(old_conv1, a)
|
||||||
|
conv1_kwargs = {a: getattr(old_conv1, a) for a in conv1_args}
|
||||||
|
conv1_kwargs["bias"] = "bias" in conv1_kwargs # as param, bias is a boolean, but as attr, it's a tensor.
|
||||||
|
conv1_kwargs["in_channels"] += by # surgery done here
|
||||||
|
# conv_shortcut
|
||||||
|
# as we changed the input size of the block, the input and output sizes are likely different,
|
||||||
|
# therefore we need a conv_shortcut (simply adding won't work)
|
||||||
|
conv_shortcut_args_kwargs = {
|
||||||
|
"in_channels": conv1_kwargs["in_channels"],
|
||||||
|
"out_channels": conv1_kwargs["out_channels"],
|
||||||
|
# default arguments from resnet.__init__
|
||||||
|
"kernel_size": 1,
|
||||||
|
"stride": 1,
|
||||||
|
"padding": 0,
|
||||||
|
"bias": True,
|
||||||
|
}
|
||||||
|
# swap old with new modules
|
||||||
|
unet.mid_block.resnets[0].norm1 = GroupNorm(**norm_kwargs)
|
||||||
|
unet.mid_block.resnets[0].conv1 = LoRACompatibleConv(**conv1_kwargs)
|
||||||
|
unet.mid_block.resnets[0].conv_shortcut = LoRACompatibleConv(**conv_shortcut_args_kwargs)
|
||||||
|
unet.mid_block.resnets[0].in_channels += by # surgery done here
|
||||||
|
|
||||||
|
|
||||||
|
def adjust_group_norms(unet: UNet2DConditionModel, max_num_group: int = 32):
|
||||||
|
def find_denominator(number, start):
|
||||||
|
if start >= number:
|
||||||
|
return number
|
||||||
|
while start != 0:
|
||||||
|
residual = number % start
|
||||||
|
if residual == 0:
|
||||||
|
return start
|
||||||
|
start -= 1
|
||||||
|
|
||||||
|
for block in [*unet.down_blocks, unet.mid_block]:
|
||||||
|
# resnets
|
||||||
|
for r in block.resnets:
|
||||||
|
if r.norm1.num_groups < max_num_group:
|
||||||
|
r.norm1.num_groups = find_denominator(r.norm1.num_channels, start=max_num_group)
|
||||||
|
|
||||||
|
if r.norm2.num_groups < max_num_group:
|
||||||
|
r.norm2.num_groups = find_denominator(r.norm2.num_channels, start=max_num_group)
|
||||||
|
|
||||||
|
# transformers
|
||||||
|
if hasattr(block, "attentions"):
|
||||||
|
for a in block.attentions:
|
||||||
|
if a.norm.num_groups < max_num_group:
|
||||||
|
a.norm.num_groups = find_denominator(a.norm.num_channels, start=max_num_group)
|
||||||
|
|
||||||
|
|
||||||
|
def is_iterable(o):
|
||||||
|
if isinstance(o, str):
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
iter(o)
|
||||||
|
return True
|
||||||
|
except TypeError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def to_sub_blocks(blocks):
|
||||||
|
if not is_iterable(blocks):
|
||||||
|
blocks = [blocks]
|
||||||
|
|
||||||
|
sub_blocks = []
|
||||||
|
|
||||||
|
for b in blocks:
|
||||||
|
if hasattr(b, "resnets"):
|
||||||
|
if hasattr(b, "attentions") and b.attentions is not None:
|
||||||
|
for r, a in zip(b.resnets, b.attentions):
|
||||||
|
sub_blocks.append([r, a])
|
||||||
|
|
||||||
|
num_resnets = len(b.resnets)
|
||||||
|
num_attns = len(b.attentions)
|
||||||
|
|
||||||
|
if num_resnets > num_attns:
|
||||||
|
# we can have more resnets than attentions, so add each resnet as separate subblock
|
||||||
|
for i in range(num_attns, num_resnets):
|
||||||
|
sub_blocks.append([b.resnets[i]])
|
||||||
|
else:
|
||||||
|
for r in b.resnets:
|
||||||
|
sub_blocks.append([r])
|
||||||
|
|
||||||
|
# upsamplers are part of the same subblock
|
||||||
|
if hasattr(b, "upsamplers") and b.upsamplers is not None:
|
||||||
|
for u in b.upsamplers:
|
||||||
|
sub_blocks[-1].extend([u])
|
||||||
|
|
||||||
|
# downsamplers are own subblock
|
||||||
|
if hasattr(b, "downsamplers") and b.downsamplers is not None:
|
||||||
|
for d in b.downsamplers:
|
||||||
|
sub_blocks.append([d])
|
||||||
|
|
||||||
|
return list(map(SubBlock, sub_blocks))
|
||||||
|
|
||||||
|
|
||||||
|
def zero_module(module):
|
||||||
|
for p in module.parameters():
|
||||||
|
nn.init.zeros_(p)
|
||||||
|
return module
|
||||||
@@ -461,6 +461,18 @@ class ImageProjection(nn.Module):
|
|||||||
return image_embeds
|
return image_embeds
|
||||||
|
|
||||||
|
|
||||||
|
class MLPProjection(nn.Module):
|
||||||
|
def __init__(self, image_embed_dim=1024, cross_attention_dim=1024):
|
||||||
|
super().__init__()
|
||||||
|
from .attention import FeedForward
|
||||||
|
|
||||||
|
self.ff = FeedForward(image_embed_dim, cross_attention_dim, mult=1, activation_fn="gelu")
|
||||||
|
self.norm = nn.LayerNorm(cross_attention_dim)
|
||||||
|
|
||||||
|
def forward(self, image_embeds: torch.FloatTensor):
|
||||||
|
return self.norm(self.ff(image_embeds))
|
||||||
|
|
||||||
|
|
||||||
class CombinedTimestepLabelEmbeddings(nn.Module):
|
class CombinedTimestepLabelEmbeddings(nn.Module):
|
||||||
def __init__(self, num_classes, embedding_dim, class_dropout_prob=0.1):
|
def __init__(self, num_classes, embedding_dim, class_dropout_prob=0.1):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|||||||
@@ -24,13 +24,17 @@ from flax.core.frozen_dict import FrozenDict, unfreeze
|
|||||||
from flax.serialization import from_bytes, to_bytes
|
from flax.serialization import from_bytes, to_bytes
|
||||||
from flax.traverse_util import flatten_dict, unflatten_dict
|
from flax.traverse_util import flatten_dict, unflatten_dict
|
||||||
from huggingface_hub import create_repo, hf_hub_download
|
from huggingface_hub import create_repo, hf_hub_download
|
||||||
from huggingface_hub.utils import EntryNotFoundError, RepositoryNotFoundError, RevisionNotFoundError
|
from huggingface_hub.utils import (
|
||||||
|
EntryNotFoundError,
|
||||||
|
RepositoryNotFoundError,
|
||||||
|
RevisionNotFoundError,
|
||||||
|
validate_hf_hub_args,
|
||||||
|
)
|
||||||
from requests import HTTPError
|
from requests import HTTPError
|
||||||
|
|
||||||
from .. import __version__, is_torch_available
|
from .. import __version__, is_torch_available
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
CONFIG_NAME,
|
CONFIG_NAME,
|
||||||
DIFFUSERS_CACHE,
|
|
||||||
FLAX_WEIGHTS_NAME,
|
FLAX_WEIGHTS_NAME,
|
||||||
HUGGINGFACE_CO_RESOLVE_ENDPOINT,
|
HUGGINGFACE_CO_RESOLVE_ENDPOINT,
|
||||||
WEIGHTS_NAME,
|
WEIGHTS_NAME,
|
||||||
@@ -197,6 +201,7 @@ class FlaxModelMixin(PushToHubMixin):
|
|||||||
raise NotImplementedError(f"init_weights method has to be implemented for {self}")
|
raise NotImplementedError(f"init_weights method has to be implemented for {self}")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@validate_hf_hub_args
|
||||||
def from_pretrained(
|
def from_pretrained(
|
||||||
cls,
|
cls,
|
||||||
pretrained_model_name_or_path: Union[str, os.PathLike],
|
pretrained_model_name_or_path: Union[str, os.PathLike],
|
||||||
@@ -288,13 +293,13 @@ class FlaxModelMixin(PushToHubMixin):
|
|||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
config = kwargs.pop("config", None)
|
config = kwargs.pop("config", None)
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
force_download = kwargs.pop("force_download", False)
|
force_download = kwargs.pop("force_download", False)
|
||||||
from_pt = kwargs.pop("from_pt", False)
|
from_pt = kwargs.pop("from_pt", False)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", False)
|
local_files_only = kwargs.pop("local_files_only", False)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
subfolder = kwargs.pop("subfolder", None)
|
subfolder = kwargs.pop("subfolder", None)
|
||||||
|
|
||||||
@@ -314,7 +319,7 @@ class FlaxModelMixin(PushToHubMixin):
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
subfolder=subfolder,
|
subfolder=subfolder,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
@@ -359,7 +364,7 @@ class FlaxModelMixin(PushToHubMixin):
|
|||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
user_agent=user_agent,
|
user_agent=user_agent,
|
||||||
subfolder=subfolder,
|
subfolder=subfolder,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
@@ -369,7 +374,7 @@ class FlaxModelMixin(PushToHubMixin):
|
|||||||
raise EnvironmentError(
|
raise EnvironmentError(
|
||||||
f"{pretrained_model_name_or_path} is not a local folder and is not a valid model identifier "
|
f"{pretrained_model_name_or_path} is not a local folder and is not a valid model identifier "
|
||||||
"listed on 'https://huggingface.co/models'\nIf this is a private repository, make sure to pass a "
|
"listed on 'https://huggingface.co/models'\nIf this is a private repository, make sure to pass a "
|
||||||
"token having permission to this repo with `use_auth_token` or log in with `huggingface-cli "
|
"token having permission to this repo with `token` or log in with `huggingface-cli "
|
||||||
"login`."
|
"login`."
|
||||||
)
|
)
|
||||||
except RevisionNotFoundError:
|
except RevisionNotFoundError:
|
||||||
|
|||||||
@@ -25,14 +25,13 @@ from typing import Any, Callable, List, Optional, Tuple, Union
|
|||||||
import safetensors
|
import safetensors
|
||||||
import torch
|
import torch
|
||||||
from huggingface_hub import create_repo
|
from huggingface_hub import create_repo
|
||||||
|
from huggingface_hub.utils import validate_hf_hub_args
|
||||||
from torch import Tensor, nn
|
from torch import Tensor, nn
|
||||||
|
|
||||||
from .. import __version__
|
from .. import __version__
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
CONFIG_NAME,
|
CONFIG_NAME,
|
||||||
DIFFUSERS_CACHE,
|
|
||||||
FLAX_WEIGHTS_NAME,
|
FLAX_WEIGHTS_NAME,
|
||||||
HF_HUB_OFFLINE,
|
|
||||||
MIN_PEFT_VERSION,
|
MIN_PEFT_VERSION,
|
||||||
SAFETENSORS_WEIGHTS_NAME,
|
SAFETENSORS_WEIGHTS_NAME,
|
||||||
WEIGHTS_NAME,
|
WEIGHTS_NAME,
|
||||||
@@ -535,6 +534,7 @@ class ModelMixin(torch.nn.Module, PushToHubMixin):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@validate_hf_hub_args
|
||||||
def from_pretrained(cls, pretrained_model_name_or_path: Optional[Union[str, os.PathLike]], **kwargs):
|
def from_pretrained(cls, pretrained_model_name_or_path: Optional[Union[str, os.PathLike]], **kwargs):
|
||||||
r"""
|
r"""
|
||||||
Instantiate a pretrained PyTorch model from a pretrained model configuration.
|
Instantiate a pretrained PyTorch model from a pretrained model configuration.
|
||||||
@@ -571,7 +571,7 @@ class ModelMixin(torch.nn.Module, PushToHubMixin):
|
|||||||
local_files_only(`bool`, *optional*, defaults to `False`):
|
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
|
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.
|
won't be downloaded from the Hub.
|
||||||
use_auth_token (`str` or *bool*, *optional*):
|
token (`str` or *bool*, *optional*):
|
||||||
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
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.
|
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
||||||
revision (`str`, *optional*, defaults to `"main"`):
|
revision (`str`, *optional*, defaults to `"main"`):
|
||||||
@@ -640,15 +640,15 @@ class ModelMixin(torch.nn.Module, PushToHubMixin):
|
|||||||
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
|
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
ignore_mismatched_sizes = kwargs.pop("ignore_mismatched_sizes", False)
|
ignore_mismatched_sizes = kwargs.pop("ignore_mismatched_sizes", False)
|
||||||
force_download = kwargs.pop("force_download", False)
|
force_download = kwargs.pop("force_download", False)
|
||||||
from_flax = kwargs.pop("from_flax", False)
|
from_flax = kwargs.pop("from_flax", False)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
output_loading_info = kwargs.pop("output_loading_info", False)
|
output_loading_info = kwargs.pop("output_loading_info", False)
|
||||||
local_files_only = kwargs.pop("local_files_only", HF_HUB_OFFLINE)
|
local_files_only = kwargs.pop("local_files_only", None)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
torch_dtype = kwargs.pop("torch_dtype", None)
|
torch_dtype = kwargs.pop("torch_dtype", None)
|
||||||
subfolder = kwargs.pop("subfolder", None)
|
subfolder = kwargs.pop("subfolder", None)
|
||||||
@@ -718,7 +718,7 @@ class ModelMixin(torch.nn.Module, PushToHubMixin):
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
subfolder=subfolder,
|
subfolder=subfolder,
|
||||||
device_map=device_map,
|
device_map=device_map,
|
||||||
@@ -740,7 +740,7 @@ class ModelMixin(torch.nn.Module, PushToHubMixin):
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
subfolder=subfolder,
|
subfolder=subfolder,
|
||||||
user_agent=user_agent,
|
user_agent=user_agent,
|
||||||
@@ -763,7 +763,7 @@ class ModelMixin(torch.nn.Module, PushToHubMixin):
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
subfolder=subfolder,
|
subfolder=subfolder,
|
||||||
user_agent=user_agent,
|
user_agent=user_agent,
|
||||||
@@ -782,7 +782,7 @@ class ModelMixin(torch.nn.Module, PushToHubMixin):
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
subfolder=subfolder,
|
subfolder=subfolder,
|
||||||
user_agent=user_agent,
|
user_agent=user_agent,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from ..utils import (
|
|||||||
_dummy_objects = {}
|
_dummy_objects = {}
|
||||||
_import_structure = {
|
_import_structure = {
|
||||||
"controlnet": [],
|
"controlnet": [],
|
||||||
|
"controlnet_xs": [],
|
||||||
"latent_diffusion": [],
|
"latent_diffusion": [],
|
||||||
"stable_diffusion": [],
|
"stable_diffusion": [],
|
||||||
"stable_diffusion_xl": [],
|
"stable_diffusion_xl": [],
|
||||||
@@ -93,6 +94,12 @@ else:
|
|||||||
"StableDiffusionXLControlNetPipeline",
|
"StableDiffusionXLControlNetPipeline",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
_import_structure["controlnet_xs"].extend(
|
||||||
|
[
|
||||||
|
"StableDiffusionControlNetXSPipeline",
|
||||||
|
"StableDiffusionXLControlNetXSPipeline",
|
||||||
|
]
|
||||||
|
)
|
||||||
_import_structure["deepfloyd_if"] = [
|
_import_structure["deepfloyd_if"] = [
|
||||||
"IFImg2ImgPipeline",
|
"IFImg2ImgPipeline",
|
||||||
"IFImg2ImgSuperResolutionPipeline",
|
"IFImg2ImgSuperResolutionPipeline",
|
||||||
@@ -347,6 +354,10 @@ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
|||||||
StableDiffusionXLControlNetInpaintPipeline,
|
StableDiffusionXLControlNetInpaintPipeline,
|
||||||
StableDiffusionXLControlNetPipeline,
|
StableDiffusionXLControlNetPipeline,
|
||||||
)
|
)
|
||||||
|
from .controlnet_xs import (
|
||||||
|
StableDiffusionControlNetXSPipeline,
|
||||||
|
StableDiffusionXLControlNetXSPipeline,
|
||||||
|
)
|
||||||
from .deepfloyd_if import (
|
from .deepfloyd_if import (
|
||||||
IFImg2ImgPipeline,
|
IFImg2ImgPipeline,
|
||||||
IFImg2ImgSuperResolutionPipeline,
|
IFImg2ImgSuperResolutionPipeline,
|
||||||
|
|||||||
@@ -84,6 +84,12 @@ class AnimateDiffPipeline(DiffusionPipeline, TextualInversionLoaderMixin, IPAdap
|
|||||||
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
|
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
|
||||||
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
|
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
|
||||||
|
|
||||||
|
The pipeline also inherits the following loading methods:
|
||||||
|
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
||||||
|
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
|
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
|
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representations.
|
Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representations.
|
||||||
|
|||||||
@@ -16,8 +16,9 @@
|
|||||||
import inspect
|
import inspect
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from huggingface_hub.utils import validate_hf_hub_args
|
||||||
|
|
||||||
from ..configuration_utils import ConfigMixin
|
from ..configuration_utils import ConfigMixin
|
||||||
from ..utils import DIFFUSERS_CACHE
|
|
||||||
from .controlnet import (
|
from .controlnet import (
|
||||||
StableDiffusionControlNetImg2ImgPipeline,
|
StableDiffusionControlNetImg2ImgPipeline,
|
||||||
StableDiffusionControlNetInpaintPipeline,
|
StableDiffusionControlNetInpaintPipeline,
|
||||||
@@ -195,6 +196,7 @@ class AutoPipelineForText2Image(ConfigMixin):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@validate_hf_hub_args
|
||||||
def from_pretrained(cls, pretrained_model_or_path, **kwargs):
|
def from_pretrained(cls, pretrained_model_or_path, **kwargs):
|
||||||
r"""
|
r"""
|
||||||
Instantiates a text-to-image Pytorch diffusion pipeline from pretrained pipeline weight.
|
Instantiates a text-to-image Pytorch diffusion pipeline from pretrained pipeline weight.
|
||||||
@@ -246,7 +248,7 @@ class AutoPipelineForText2Image(ConfigMixin):
|
|||||||
local_files_only (`bool`, *optional*, defaults to `False`):
|
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
|
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.
|
won't be downloaded from the Hub.
|
||||||
use_auth_token (`str` or *bool*, *optional*):
|
token (`str` or *bool*, *optional*):
|
||||||
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
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.
|
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
||||||
revision (`str`, *optional*, defaults to `"main"`):
|
revision (`str`, *optional*, defaults to `"main"`):
|
||||||
@@ -310,11 +312,11 @@ class AutoPipelineForText2Image(ConfigMixin):
|
|||||||
>>> image = pipeline(prompt).images[0]
|
>>> image = pipeline(prompt).images[0]
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
force_download = kwargs.pop("force_download", False)
|
force_download = kwargs.pop("force_download", False)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", False)
|
local_files_only = kwargs.pop("local_files_only", False)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
|
|
||||||
@@ -323,7 +325,7 @@ class AutoPipelineForText2Image(ConfigMixin):
|
|||||||
"force_download": force_download,
|
"force_download": force_download,
|
||||||
"resume_download": resume_download,
|
"resume_download": resume_download,
|
||||||
"proxies": proxies,
|
"proxies": proxies,
|
||||||
"use_auth_token": use_auth_token,
|
"token": token,
|
||||||
"local_files_only": local_files_only,
|
"local_files_only": local_files_only,
|
||||||
"revision": revision,
|
"revision": revision,
|
||||||
}
|
}
|
||||||
@@ -466,6 +468,7 @@ class AutoPipelineForImage2Image(ConfigMixin):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@validate_hf_hub_args
|
||||||
def from_pretrained(cls, pretrained_model_or_path, **kwargs):
|
def from_pretrained(cls, pretrained_model_or_path, **kwargs):
|
||||||
r"""
|
r"""
|
||||||
Instantiates a image-to-image Pytorch diffusion pipeline from pretrained pipeline weight.
|
Instantiates a image-to-image Pytorch diffusion pipeline from pretrained pipeline weight.
|
||||||
@@ -518,7 +521,7 @@ class AutoPipelineForImage2Image(ConfigMixin):
|
|||||||
local_files_only (`bool`, *optional*, defaults to `False`):
|
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
|
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.
|
won't be downloaded from the Hub.
|
||||||
use_auth_token (`str` or *bool*, *optional*):
|
token (`str` or *bool*, *optional*):
|
||||||
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
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.
|
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
||||||
revision (`str`, *optional*, defaults to `"main"`):
|
revision (`str`, *optional*, defaults to `"main"`):
|
||||||
@@ -582,11 +585,11 @@ class AutoPipelineForImage2Image(ConfigMixin):
|
|||||||
>>> image = pipeline(prompt, image).images[0]
|
>>> image = pipeline(prompt, image).images[0]
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
force_download = kwargs.pop("force_download", False)
|
force_download = kwargs.pop("force_download", False)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", False)
|
local_files_only = kwargs.pop("local_files_only", False)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
|
|
||||||
@@ -595,7 +598,7 @@ class AutoPipelineForImage2Image(ConfigMixin):
|
|||||||
"force_download": force_download,
|
"force_download": force_download,
|
||||||
"resume_download": resume_download,
|
"resume_download": resume_download,
|
||||||
"proxies": proxies,
|
"proxies": proxies,
|
||||||
"use_auth_token": use_auth_token,
|
"token": token,
|
||||||
"local_files_only": local_files_only,
|
"local_files_only": local_files_only,
|
||||||
"revision": revision,
|
"revision": revision,
|
||||||
}
|
}
|
||||||
@@ -742,6 +745,7 @@ class AutoPipelineForInpainting(ConfigMixin):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@validate_hf_hub_args
|
||||||
def from_pretrained(cls, pretrained_model_or_path, **kwargs):
|
def from_pretrained(cls, pretrained_model_or_path, **kwargs):
|
||||||
r"""
|
r"""
|
||||||
Instantiates a inpainting Pytorch diffusion pipeline from pretrained pipeline weight.
|
Instantiates a inpainting Pytorch diffusion pipeline from pretrained pipeline weight.
|
||||||
@@ -793,7 +797,7 @@ class AutoPipelineForInpainting(ConfigMixin):
|
|||||||
local_files_only (`bool`, *optional*, defaults to `False`):
|
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
|
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.
|
won't be downloaded from the Hub.
|
||||||
use_auth_token (`str` or *bool*, *optional*):
|
token (`str` or *bool*, *optional*):
|
||||||
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
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.
|
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
||||||
revision (`str`, *optional*, defaults to `"main"`):
|
revision (`str`, *optional*, defaults to `"main"`):
|
||||||
@@ -857,11 +861,11 @@ class AutoPipelineForInpainting(ConfigMixin):
|
|||||||
>>> image = pipeline(prompt, image=init_image, mask_image=mask_image).images[0]
|
>>> image = pipeline(prompt, image=init_image, mask_image=mask_image).images[0]
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
force_download = kwargs.pop("force_download", False)
|
force_download = kwargs.pop("force_download", False)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", False)
|
local_files_only = kwargs.pop("local_files_only", False)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
|
|
||||||
@@ -870,7 +874,7 @@ class AutoPipelineForInpainting(ConfigMixin):
|
|||||||
"force_download": force_download,
|
"force_download": force_download,
|
||||||
"resume_download": resume_download,
|
"resume_download": resume_download,
|
||||||
"proxies": proxies,
|
"proxies": proxies,
|
||||||
"use_auth_token": use_auth_token,
|
"token": token,
|
||||||
"local_files_only": local_files_only,
|
"local_files_only": local_files_only,
|
||||||
"revision": revision,
|
"revision": revision,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,6 +147,9 @@ class StableDiffusionControlNetPipeline(
|
|||||||
|
|
||||||
The pipeline also inherits the following loading methods:
|
The pipeline also inherits the following loading methods:
|
||||||
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
||||||
|
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
|
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
|
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
||||||
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
|
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ import numpy as np
|
|||||||
import PIL.Image
|
import PIL.Image
|
||||||
import torch
|
import torch
|
||||||
import torch.nn.functional as F
|
import torch.nn.functional as F
|
||||||
from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer
|
from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer, CLIPVisionModelWithProjection
|
||||||
|
|
||||||
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
||||||
from ...loaders import FromSingleFileMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
from ...loaders import FromSingleFileMixin, IPAdapterMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
||||||
from ...models import AutoencoderKL, ControlNetModel, UNet2DConditionModel
|
from ...models import AutoencoderKL, ControlNetModel, UNet2DConditionModel
|
||||||
from ...models.lora import adjust_lora_scale_text_encoder
|
from ...models.lora import adjust_lora_scale_text_encoder
|
||||||
from ...schedulers import KarrasDiffusionSchedulers
|
from ...schedulers import KarrasDiffusionSchedulers
|
||||||
@@ -130,7 +130,7 @@ def prepare_image(image):
|
|||||||
|
|
||||||
|
|
||||||
class StableDiffusionControlNetImg2ImgPipeline(
|
class StableDiffusionControlNetImg2ImgPipeline(
|
||||||
DiffusionPipeline, TextualInversionLoaderMixin, LoraLoaderMixin, FromSingleFileMixin
|
DiffusionPipeline, TextualInversionLoaderMixin, LoraLoaderMixin, IPAdapterMixin, FromSingleFileMixin
|
||||||
):
|
):
|
||||||
r"""
|
r"""
|
||||||
Pipeline for image-to-image generation using Stable Diffusion with ControlNet guidance.
|
Pipeline for image-to-image generation using Stable Diffusion with ControlNet guidance.
|
||||||
@@ -140,6 +140,10 @@ class StableDiffusionControlNetImg2ImgPipeline(
|
|||||||
|
|
||||||
The pipeline also inherits the following loading methods:
|
The pipeline also inherits the following loading methods:
|
||||||
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
||||||
|
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
|
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
|
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
||||||
|
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
@@ -166,7 +170,7 @@ class StableDiffusionControlNetImg2ImgPipeline(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
model_cpu_offload_seq = "text_encoder->unet->vae"
|
model_cpu_offload_seq = "text_encoder->unet->vae"
|
||||||
_optional_components = ["safety_checker", "feature_extractor"]
|
_optional_components = ["safety_checker", "feature_extractor", "image_encoder"]
|
||||||
_exclude_from_cpu_offload = ["safety_checker"]
|
_exclude_from_cpu_offload = ["safety_checker"]
|
||||||
_callback_tensor_inputs = ["latents", "prompt_embeds", "negative_prompt_embeds"]
|
_callback_tensor_inputs = ["latents", "prompt_embeds", "negative_prompt_embeds"]
|
||||||
|
|
||||||
@@ -180,6 +184,7 @@ class StableDiffusionControlNetImg2ImgPipeline(
|
|||||||
scheduler: KarrasDiffusionSchedulers,
|
scheduler: KarrasDiffusionSchedulers,
|
||||||
safety_checker: StableDiffusionSafetyChecker,
|
safety_checker: StableDiffusionSafetyChecker,
|
||||||
feature_extractor: CLIPImageProcessor,
|
feature_extractor: CLIPImageProcessor,
|
||||||
|
image_encoder: CLIPVisionModelWithProjection = None,
|
||||||
requires_safety_checker: bool = True,
|
requires_safety_checker: bool = True,
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -212,6 +217,7 @@ class StableDiffusionControlNetImg2ImgPipeline(
|
|||||||
scheduler=scheduler,
|
scheduler=scheduler,
|
||||||
safety_checker=safety_checker,
|
safety_checker=safety_checker,
|
||||||
feature_extractor=feature_extractor,
|
feature_extractor=feature_extractor,
|
||||||
|
image_encoder=image_encoder,
|
||||||
)
|
)
|
||||||
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
|
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
|
||||||
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor, do_convert_rgb=True)
|
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor, do_convert_rgb=True)
|
||||||
@@ -468,6 +474,31 @@ class StableDiffusionControlNetImg2ImgPipeline(
|
|||||||
|
|
||||||
return prompt_embeds, negative_prompt_embeds
|
return prompt_embeds, negative_prompt_embeds
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.encode_image
|
||||||
|
def encode_image(self, image, device, num_images_per_prompt, output_hidden_states=None):
|
||||||
|
dtype = next(self.image_encoder.parameters()).dtype
|
||||||
|
|
||||||
|
if not isinstance(image, torch.Tensor):
|
||||||
|
image = self.feature_extractor(image, return_tensors="pt").pixel_values
|
||||||
|
|
||||||
|
image = image.to(device=device, dtype=dtype)
|
||||||
|
if output_hidden_states:
|
||||||
|
image_enc_hidden_states = self.image_encoder(image, output_hidden_states=True).hidden_states[-2]
|
||||||
|
image_enc_hidden_states = image_enc_hidden_states.repeat_interleave(num_images_per_prompt, dim=0)
|
||||||
|
uncond_image_enc_hidden_states = self.image_encoder(
|
||||||
|
torch.zeros_like(image), output_hidden_states=True
|
||||||
|
).hidden_states[-2]
|
||||||
|
uncond_image_enc_hidden_states = uncond_image_enc_hidden_states.repeat_interleave(
|
||||||
|
num_images_per_prompt, dim=0
|
||||||
|
)
|
||||||
|
return image_enc_hidden_states, uncond_image_enc_hidden_states
|
||||||
|
else:
|
||||||
|
image_embeds = self.image_encoder(image).image_embeds
|
||||||
|
image_embeds = image_embeds.repeat_interleave(num_images_per_prompt, dim=0)
|
||||||
|
uncond_image_embeds = torch.zeros_like(image_embeds)
|
||||||
|
|
||||||
|
return image_embeds, uncond_image_embeds
|
||||||
|
|
||||||
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.run_safety_checker
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.run_safety_checker
|
||||||
def run_safety_checker(self, image, device, dtype):
|
def run_safety_checker(self, image, device, dtype):
|
||||||
if self.safety_checker is None:
|
if self.safety_checker is None:
|
||||||
@@ -861,6 +892,7 @@ class StableDiffusionControlNetImg2ImgPipeline(
|
|||||||
latents: Optional[torch.FloatTensor] = None,
|
latents: Optional[torch.FloatTensor] = None,
|
||||||
prompt_embeds: Optional[torch.FloatTensor] = None,
|
prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
negative_prompt_embeds: Optional[torch.FloatTensor] = None,
|
negative_prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
|
ip_adapter_image: Optional[PipelineImageInput] = None,
|
||||||
output_type: Optional[str] = "pil",
|
output_type: Optional[str] = "pil",
|
||||||
return_dict: bool = True,
|
return_dict: bool = True,
|
||||||
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
|
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
|
||||||
@@ -922,6 +954,7 @@ class StableDiffusionControlNetImg2ImgPipeline(
|
|||||||
negative_prompt_embeds (`torch.FloatTensor`, *optional*):
|
negative_prompt_embeds (`torch.FloatTensor`, *optional*):
|
||||||
Pre-generated negative text embeddings. Can be used to easily tweak text inputs (prompt weighting). If
|
Pre-generated negative text embeddings. Can be used to easily tweak text inputs (prompt weighting). If
|
||||||
not provided, `negative_prompt_embeds` are generated from the `negative_prompt` input argument.
|
not provided, `negative_prompt_embeds` are generated from the `negative_prompt` input argument.
|
||||||
|
ip_adapter_image: (`PipelineImageInput`, *optional*): Optional image input to work with IP Adapters.
|
||||||
output_type (`str`, *optional*, defaults to `"pil"`):
|
output_type (`str`, *optional*, defaults to `"pil"`):
|
||||||
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
||||||
return_dict (`bool`, *optional*, defaults to `True`):
|
return_dict (`bool`, *optional*, defaults to `True`):
|
||||||
@@ -1053,6 +1086,11 @@ class StableDiffusionControlNetImg2ImgPipeline(
|
|||||||
if self.do_classifier_free_guidance:
|
if self.do_classifier_free_guidance:
|
||||||
prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds])
|
prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds])
|
||||||
|
|
||||||
|
if ip_adapter_image is not None:
|
||||||
|
image_embeds, negative_image_embeds = self.encode_image(ip_adapter_image, device, num_images_per_prompt)
|
||||||
|
if self.do_classifier_free_guidance:
|
||||||
|
image_embeds = torch.cat([negative_image_embeds, image_embeds])
|
||||||
|
|
||||||
# 4. Prepare image
|
# 4. Prepare image
|
||||||
image = self.image_processor.preprocess(image, height=height, width=width).to(dtype=torch.float32)
|
image = self.image_processor.preprocess(image, height=height, width=width).to(dtype=torch.float32)
|
||||||
|
|
||||||
@@ -1111,7 +1149,10 @@ class StableDiffusionControlNetImg2ImgPipeline(
|
|||||||
# 7. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline
|
# 7. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline
|
||||||
extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
|
extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
|
||||||
|
|
||||||
# 7.1 Create tensor stating which controlnets to keep
|
# 7.1 Add image embeds for IP-Adapter
|
||||||
|
added_cond_kwargs = {"image_embeds": image_embeds} if ip_adapter_image is not None else None
|
||||||
|
|
||||||
|
# 7.2 Create tensor stating which controlnets to keep
|
||||||
controlnet_keep = []
|
controlnet_keep = []
|
||||||
for i in range(len(timesteps)):
|
for i in range(len(timesteps)):
|
||||||
keeps = [
|
keeps = [
|
||||||
@@ -1171,6 +1212,7 @@ class StableDiffusionControlNetImg2ImgPipeline(
|
|||||||
cross_attention_kwargs=self.cross_attention_kwargs,
|
cross_attention_kwargs=self.cross_attention_kwargs,
|
||||||
down_block_additional_residuals=down_block_res_samples,
|
down_block_additional_residuals=down_block_res_samples,
|
||||||
mid_block_additional_residual=mid_block_res_sample,
|
mid_block_additional_residual=mid_block_res_sample,
|
||||||
|
added_cond_kwargs=added_cond_kwargs,
|
||||||
return_dict=False,
|
return_dict=False,
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
|
|||||||
@@ -251,6 +251,9 @@ class StableDiffusionControlNetInpaintPipeline(
|
|||||||
|
|
||||||
The pipeline also inherits the following loading methods:
|
The pipeline also inherits the following loading methods:
|
||||||
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
||||||
|
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
|
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
|
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
||||||
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
|
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
|
||||||
|
|
||||||
<Tip>
|
<Tip>
|
||||||
|
|||||||
@@ -148,12 +148,10 @@ class StableDiffusionXLControlNetInpaintPipeline(
|
|||||||
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods the
|
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods the
|
||||||
library implements for all the pipelines (such as downloading or saving, running on a particular device, etc.)
|
library implements for all the pipelines (such as downloading or saving, running on a particular device, etc.)
|
||||||
|
|
||||||
In addition the pipeline inherits the following loading methods:
|
The pipeline also inherits the following loading methods:
|
||||||
- *LoRA*: [`loaders.StableDiffusionXLLoraLoaderMixin.load_lora_weights`]
|
- [`~loaders.StableDiffusionXLLoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
- *Ckpt*: [`loaders.FromSingleFileMixin.from_single_file`]
|
- [`~loaders.StableDiffusionXLLoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
|
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
||||||
as well as the following saving methods:
|
|
||||||
- *LoRA*: [`loaders.StableDiffusionXLLoraLoaderMixin.save_lora_weights`]
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
|
|||||||
@@ -129,8 +129,10 @@ class StableDiffusionXLControlNetPipeline(
|
|||||||
|
|
||||||
The pipeline also inherits the following loading methods:
|
The pipeline also inherits the following loading methods:
|
||||||
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
||||||
- [`loaders.StableDiffusionXLLoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
- [`~loaders.StableDiffusionXLLoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
- [`loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
- [`~loaders.StableDiffusionXLLoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
|
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
||||||
|
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
|
|||||||
@@ -155,9 +155,10 @@ class StableDiffusionXLControlNetImg2ImgPipeline(
|
|||||||
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods the
|
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods the
|
||||||
library implements for all the pipelines (such as downloading or saving, running on a particular device, etc.)
|
library implements for all the pipelines (such as downloading or saving, running on a particular device, etc.)
|
||||||
|
|
||||||
In addition the pipeline inherits the following loading methods:
|
The pipeline also inherits the following loading methods:
|
||||||
- *Textual-Inversion*: [`loaders.TextualInversionLoaderMixin.load_textual_inversion`]
|
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
||||||
- *LoRA*: [`loaders.StableDiffusionXLLoraLoaderMixin.load_lora_weights`]
|
- [`~loaders.StableDiffusionXLLoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
|
- [`~loaders.StableDiffusionXLLoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from ...utils import (
|
||||||
|
DIFFUSERS_SLOW_IMPORT,
|
||||||
|
OptionalDependencyNotAvailable,
|
||||||
|
_LazyModule,
|
||||||
|
get_objects_from_module,
|
||||||
|
is_flax_available,
|
||||||
|
is_torch_available,
|
||||||
|
is_transformers_available,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
_dummy_objects = {}
|
||||||
|
_import_structure = {}
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not (is_transformers_available() and is_torch_available()):
|
||||||
|
raise OptionalDependencyNotAvailable()
|
||||||
|
except OptionalDependencyNotAvailable:
|
||||||
|
from ...utils import dummy_torch_and_transformers_objects # noqa F403
|
||||||
|
|
||||||
|
_dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
|
||||||
|
else:
|
||||||
|
_import_structure["pipeline_controlnet_xs"] = ["StableDiffusionControlNetXSPipeline"]
|
||||||
|
_import_structure["pipeline_controlnet_xs_sd_xl"] = ["StableDiffusionXLControlNetXSPipeline"]
|
||||||
|
try:
|
||||||
|
if not (is_transformers_available() and is_flax_available()):
|
||||||
|
raise OptionalDependencyNotAvailable()
|
||||||
|
except OptionalDependencyNotAvailable:
|
||||||
|
from ...utils import dummy_flax_and_transformers_objects # noqa F403
|
||||||
|
|
||||||
|
_dummy_objects.update(get_objects_from_module(dummy_flax_and_transformers_objects))
|
||||||
|
else:
|
||||||
|
pass # _import_structure["pipeline_flax_controlnet"] = ["FlaxStableDiffusionControlNetPipeline"]
|
||||||
|
|
||||||
|
|
||||||
|
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
||||||
|
try:
|
||||||
|
if not (is_transformers_available() and is_torch_available()):
|
||||||
|
raise OptionalDependencyNotAvailable()
|
||||||
|
|
||||||
|
except OptionalDependencyNotAvailable:
|
||||||
|
from ...utils.dummy_torch_and_transformers_objects import *
|
||||||
|
else:
|
||||||
|
from .pipeline_controlnet_xs import StableDiffusionControlNetXSPipeline
|
||||||
|
from .pipeline_controlnet_xs_sd_xl import StableDiffusionXLControlNetXSPipeline
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not (is_transformers_available() and is_flax_available()):
|
||||||
|
raise OptionalDependencyNotAvailable()
|
||||||
|
except OptionalDependencyNotAvailable:
|
||||||
|
from ...utils.dummy_flax_and_transformers_objects import * # noqa F403
|
||||||
|
else:
|
||||||
|
pass # from .pipeline_flax_controlnet import FlaxStableDiffusionControlNetPipeline
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.modules[__name__] = _LazyModule(
|
||||||
|
__name__,
|
||||||
|
globals()["__file__"],
|
||||||
|
_import_structure,
|
||||||
|
module_spec=__spec__,
|
||||||
|
)
|
||||||
|
for name, value in _dummy_objects.items():
|
||||||
|
setattr(sys.modules[__name__], name, value)
|
||||||
@@ -0,0 +1,946 @@
|
|||||||
|
# Copyright 2023 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 Any, Callable, Dict, List, Optional, Union
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import PIL.Image
|
||||||
|
import torch
|
||||||
|
import torch.nn.functional as F
|
||||||
|
from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer
|
||||||
|
|
||||||
|
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
||||||
|
from ...loaders import FromSingleFileMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
||||||
|
from ...models import AutoencoderKL, ControlNetXSModel, UNet2DConditionModel
|
||||||
|
from ...models.lora import adjust_lora_scale_text_encoder
|
||||||
|
from ...schedulers import KarrasDiffusionSchedulers
|
||||||
|
from ...utils import (
|
||||||
|
USE_PEFT_BACKEND,
|
||||||
|
deprecate,
|
||||||
|
logging,
|
||||||
|
replace_example_docstring,
|
||||||
|
scale_lora_layers,
|
||||||
|
unscale_lora_layers,
|
||||||
|
)
|
||||||
|
from ...utils.torch_utils import is_compiled_module, is_torch_version, randn_tensor
|
||||||
|
from ..pipeline_utils import DiffusionPipeline
|
||||||
|
from ..stable_diffusion.pipeline_output import StableDiffusionPipelineOutput
|
||||||
|
from ..stable_diffusion.safety_checker import StableDiffusionSafetyChecker
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
||||||
|
|
||||||
|
|
||||||
|
EXAMPLE_DOC_STRING = """
|
||||||
|
Examples:
|
||||||
|
```py
|
||||||
|
>>> # !pip install opencv-python transformers accelerate
|
||||||
|
>>> from diffusers import StableDiffusionControlNetXSPipeline, ControlNetXSModel
|
||||||
|
>>> from diffusers.utils import load_image
|
||||||
|
>>> import numpy as np
|
||||||
|
>>> import torch
|
||||||
|
|
||||||
|
>>> import cv2
|
||||||
|
>>> from PIL import Image
|
||||||
|
|
||||||
|
>>> prompt = "aerial view, a futuristic research complex in a bright foggy jungle, hard lighting"
|
||||||
|
>>> negative_prompt = "low quality, bad quality, sketches"
|
||||||
|
|
||||||
|
>>> # download an image
|
||||||
|
>>> image = load_image(
|
||||||
|
... "https://hf.co/datasets/hf-internal-testing/diffusers-images/resolve/main/sd_controlnet/hf-logo.png"
|
||||||
|
... )
|
||||||
|
|
||||||
|
>>> # initialize the models and pipeline
|
||||||
|
>>> controlnet_conditioning_scale = 0.5
|
||||||
|
>>> controlnet = ControlNetXSModel.from_pretrained(
|
||||||
|
... "UmerHA/ConrolNetXS-SD2.1-canny", torch_dtype=torch.float16
|
||||||
|
... )
|
||||||
|
>>> pipe = StableDiffusionControlNetXSPipeline.from_pretrained(
|
||||||
|
... "stabilityai/stable-diffusion-2-1", controlnet=controlnet, torch_dtype=torch.float16
|
||||||
|
... )
|
||||||
|
>>> pipe.enable_model_cpu_offload()
|
||||||
|
|
||||||
|
>>> # get canny image
|
||||||
|
>>> image = np.array(image)
|
||||||
|
>>> image = cv2.Canny(image, 100, 200)
|
||||||
|
>>> image = image[:, :, None]
|
||||||
|
>>> image = np.concatenate([image, image, image], axis=2)
|
||||||
|
>>> canny_image = Image.fromarray(image)
|
||||||
|
>>> # generate image
|
||||||
|
>>> image = pipe(
|
||||||
|
... prompt, controlnet_conditioning_scale=controlnet_conditioning_scale, image=canny_image
|
||||||
|
... ).images[0]
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class StableDiffusionControlNetXSPipeline(
|
||||||
|
DiffusionPipeline, TextualInversionLoaderMixin, LoraLoaderMixin, FromSingleFileMixin
|
||||||
|
):
|
||||||
|
r"""
|
||||||
|
Pipeline for text-to-image generation using Stable Diffusion with ControlNet-XS guidance.
|
||||||
|
|
||||||
|
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
|
||||||
|
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
|
||||||
|
|
||||||
|
The pipeline also inherits the following loading methods:
|
||||||
|
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
||||||
|
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
|
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
|
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
||||||
|
|
||||||
|
Args:
|
||||||
|
vae ([`AutoencoderKL`]):
|
||||||
|
Variational Auto-Encoder (VAE) model to encode and decode images to and from latent representations.
|
||||||
|
text_encoder ([`~transformers.CLIPTextModel`]):
|
||||||
|
Frozen text-encoder ([clip-vit-large-patch14](https://huggingface.co/openai/clip-vit-large-patch14)).
|
||||||
|
tokenizer ([`~transformers.CLIPTokenizer`]):
|
||||||
|
A `CLIPTokenizer` to tokenize text.
|
||||||
|
unet ([`UNet2DConditionModel`]):
|
||||||
|
A `UNet2DConditionModel` to denoise the encoded image latents.
|
||||||
|
controlnet ([`ControlNetXSModel`]):
|
||||||
|
Provides additional conditioning to the `unet` during the denoising process.
|
||||||
|
scheduler ([`SchedulerMixin`]):
|
||||||
|
A scheduler to be used in combination with `unet` to denoise the encoded image latents. Can be one of
|
||||||
|
[`DDIMScheduler`], [`LMSDiscreteScheduler`], or [`PNDMScheduler`].
|
||||||
|
safety_checker ([`StableDiffusionSafetyChecker`]):
|
||||||
|
Classification module that estimates whether generated images could be considered offensive or harmful.
|
||||||
|
Please refer to the [model card](https://huggingface.co/runwayml/stable-diffusion-v1-5) for more details
|
||||||
|
about a model's potential harms.
|
||||||
|
feature_extractor ([`~transformers.CLIPImageProcessor`]):
|
||||||
|
A `CLIPImageProcessor` to extract features from generated images; used as inputs to the `safety_checker`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
model_cpu_offload_seq = "text_encoder->unet->vae>controlnet"
|
||||||
|
_optional_components = ["safety_checker", "feature_extractor"]
|
||||||
|
_exclude_from_cpu_offload = ["safety_checker"]
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
vae: AutoencoderKL,
|
||||||
|
text_encoder: CLIPTextModel,
|
||||||
|
tokenizer: CLIPTokenizer,
|
||||||
|
unet: UNet2DConditionModel,
|
||||||
|
controlnet: ControlNetXSModel,
|
||||||
|
scheduler: KarrasDiffusionSchedulers,
|
||||||
|
safety_checker: StableDiffusionSafetyChecker,
|
||||||
|
feature_extractor: CLIPImageProcessor,
|
||||||
|
requires_safety_checker: bool = True,
|
||||||
|
):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
if safety_checker is None and requires_safety_checker:
|
||||||
|
logger.warning(
|
||||||
|
f"You have disabled the safety checker for {self.__class__} by passing `safety_checker=None`. Ensure"
|
||||||
|
" that you abide to the conditions of the Stable Diffusion license and do not expose unfiltered"
|
||||||
|
" results in services or applications open to the public. Both the diffusers team and Hugging Face"
|
||||||
|
" strongly recommend to keep the safety filter enabled in all public facing circumstances, disabling"
|
||||||
|
" it only for use-cases that involve analyzing network behavior or auditing its results. For more"
|
||||||
|
" information, please have a look at https://github.com/huggingface/diffusers/pull/254 ."
|
||||||
|
)
|
||||||
|
|
||||||
|
if safety_checker is not None and feature_extractor is None:
|
||||||
|
raise ValueError(
|
||||||
|
"Make sure to define a feature extractor when loading {self.__class__} if you want to use the safety"
|
||||||
|
" checker. If you do not want to use the safety checker, you can pass `'safety_checker=None'` instead."
|
||||||
|
)
|
||||||
|
|
||||||
|
vae_compatible, cnxs_condition_downsample_factor, vae_downsample_factor = controlnet._check_if_vae_compatible(
|
||||||
|
vae
|
||||||
|
)
|
||||||
|
if not vae_compatible:
|
||||||
|
raise ValueError(
|
||||||
|
f"The downsampling factors of the VAE ({vae_downsample_factor}) and the conditioning part of ControlNetXS model {cnxs_condition_downsample_factor} need to be equal. Consider building the ControlNetXS model with different `conditioning_block_sizes`."
|
||||||
|
)
|
||||||
|
|
||||||
|
self.register_modules(
|
||||||
|
vae=vae,
|
||||||
|
text_encoder=text_encoder,
|
||||||
|
tokenizer=tokenizer,
|
||||||
|
unet=unet,
|
||||||
|
controlnet=controlnet,
|
||||||
|
scheduler=scheduler,
|
||||||
|
safety_checker=safety_checker,
|
||||||
|
feature_extractor=feature_extractor,
|
||||||
|
)
|
||||||
|
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
|
||||||
|
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor, do_convert_rgb=True)
|
||||||
|
self.control_image_processor = VaeImageProcessor(
|
||||||
|
vae_scale_factor=self.vae_scale_factor, do_convert_rgb=True, do_normalize=False
|
||||||
|
)
|
||||||
|
self.register_to_config(requires_safety_checker=requires_safety_checker)
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.enable_vae_slicing
|
||||||
|
def enable_vae_slicing(self):
|
||||||
|
r"""
|
||||||
|
Enable sliced VAE decoding. When this option is enabled, the VAE will split the input tensor in slices to
|
||||||
|
compute decoding in several steps. This is useful to save some memory and allow larger batch sizes.
|
||||||
|
"""
|
||||||
|
self.vae.enable_slicing()
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.disable_vae_slicing
|
||||||
|
def disable_vae_slicing(self):
|
||||||
|
r"""
|
||||||
|
Disable sliced VAE decoding. If `enable_vae_slicing` was previously enabled, this method will go back to
|
||||||
|
computing decoding in one step.
|
||||||
|
"""
|
||||||
|
self.vae.disable_slicing()
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.enable_vae_tiling
|
||||||
|
def enable_vae_tiling(self):
|
||||||
|
r"""
|
||||||
|
Enable tiled VAE decoding. When this option is enabled, the VAE will split the input tensor into tiles to
|
||||||
|
compute decoding and encoding in several steps. This is useful for saving a large amount of memory and to allow
|
||||||
|
processing larger images.
|
||||||
|
"""
|
||||||
|
self.vae.enable_tiling()
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.disable_vae_tiling
|
||||||
|
def disable_vae_tiling(self):
|
||||||
|
r"""
|
||||||
|
Disable tiled VAE decoding. If `enable_vae_tiling` was previously enabled, this method will go back to
|
||||||
|
computing decoding in one step.
|
||||||
|
"""
|
||||||
|
self.vae.disable_tiling()
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline._encode_prompt
|
||||||
|
def _encode_prompt(
|
||||||
|
self,
|
||||||
|
prompt,
|
||||||
|
device,
|
||||||
|
num_images_per_prompt,
|
||||||
|
do_classifier_free_guidance,
|
||||||
|
negative_prompt=None,
|
||||||
|
prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
|
negative_prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
|
lora_scale: Optional[float] = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
deprecation_message = "`_encode_prompt()` is deprecated and it will be removed in a future version. Use `encode_prompt()` instead. Also, be aware that the output format changed from a concatenated tensor to a tuple."
|
||||||
|
deprecate("_encode_prompt()", "1.0.0", deprecation_message, standard_warn=False)
|
||||||
|
|
||||||
|
prompt_embeds_tuple = self.encode_prompt(
|
||||||
|
prompt=prompt,
|
||||||
|
device=device,
|
||||||
|
num_images_per_prompt=num_images_per_prompt,
|
||||||
|
do_classifier_free_guidance=do_classifier_free_guidance,
|
||||||
|
negative_prompt=negative_prompt,
|
||||||
|
prompt_embeds=prompt_embeds,
|
||||||
|
negative_prompt_embeds=negative_prompt_embeds,
|
||||||
|
lora_scale=lora_scale,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
# concatenate for backwards comp
|
||||||
|
prompt_embeds = torch.cat([prompt_embeds_tuple[1], prompt_embeds_tuple[0]])
|
||||||
|
|
||||||
|
return prompt_embeds
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.encode_prompt
|
||||||
|
def encode_prompt(
|
||||||
|
self,
|
||||||
|
prompt,
|
||||||
|
device,
|
||||||
|
num_images_per_prompt,
|
||||||
|
do_classifier_free_guidance,
|
||||||
|
negative_prompt=None,
|
||||||
|
prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
|
negative_prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
|
lora_scale: Optional[float] = None,
|
||||||
|
clip_skip: Optional[int] = None,
|
||||||
|
):
|
||||||
|
r"""
|
||||||
|
Encodes the prompt into text encoder hidden states.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prompt (`str` or `List[str]`, *optional*):
|
||||||
|
prompt to be encoded
|
||||||
|
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`).
|
||||||
|
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.
|
||||||
|
lora_scale (`float`, *optional*):
|
||||||
|
A LoRA scale that will be applied to all LoRA layers of the text encoder if LoRA layers are loaded.
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
# set lora scale so that monkey patched LoRA
|
||||||
|
# function of text encoder can correctly access it
|
||||||
|
if lora_scale is not None and isinstance(self, LoraLoaderMixin):
|
||||||
|
self._lora_scale = lora_scale
|
||||||
|
|
||||||
|
# dynamically adjust the LoRA scale
|
||||||
|
if not USE_PEFT_BACKEND:
|
||||||
|
adjust_lora_scale_text_encoder(self.text_encoder, lora_scale)
|
||||||
|
else:
|
||||||
|
scale_lora_layers(self.text_encoder, lora_scale)
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
if prompt_embeds is None:
|
||||||
|
# textual inversion: procecss multi-vector tokens if necessary
|
||||||
|
if isinstance(self, TextualInversionLoaderMixin):
|
||||||
|
prompt = self.maybe_convert_prompt(prompt, self.tokenizer)
|
||||||
|
|
||||||
|
text_inputs = self.tokenizer(
|
||||||
|
prompt,
|
||||||
|
padding="max_length",
|
||||||
|
max_length=self.tokenizer.model_max_length,
|
||||||
|
truncation=True,
|
||||||
|
return_tensors="pt",
|
||||||
|
)
|
||||||
|
text_input_ids = text_inputs.input_ids
|
||||||
|
untruncated_ids = self.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 = self.tokenizer.batch_decode(
|
||||||
|
untruncated_ids[:, self.tokenizer.model_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.model_max_length} tokens: {removed_text}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if hasattr(self.text_encoder.config, "use_attention_mask") and self.text_encoder.config.use_attention_mask:
|
||||||
|
attention_mask = text_inputs.attention_mask.to(device)
|
||||||
|
else:
|
||||||
|
attention_mask = None
|
||||||
|
|
||||||
|
if clip_skip is None:
|
||||||
|
prompt_embeds = self.text_encoder(text_input_ids.to(device), attention_mask=attention_mask)
|
||||||
|
prompt_embeds = prompt_embeds[0]
|
||||||
|
else:
|
||||||
|
prompt_embeds = self.text_encoder(
|
||||||
|
text_input_ids.to(device), attention_mask=attention_mask, output_hidden_states=True
|
||||||
|
)
|
||||||
|
# Access the `hidden_states` first, that contains a tuple of
|
||||||
|
# all the hidden states from the encoder layers. Then index into
|
||||||
|
# the tuple to access the hidden states from the desired layer.
|
||||||
|
prompt_embeds = prompt_embeds[-1][-(clip_skip + 1)]
|
||||||
|
# We also need to apply the final LayerNorm here to not mess with the
|
||||||
|
# representations. The `last_hidden_states` that we typically use for
|
||||||
|
# obtaining the final prompt representations passes through the LayerNorm
|
||||||
|
# layer.
|
||||||
|
prompt_embeds = self.text_encoder.text_model.final_layer_norm(prompt_embeds)
|
||||||
|
|
||||||
|
if self.text_encoder is not None:
|
||||||
|
prompt_embeds_dtype = self.text_encoder.dtype
|
||||||
|
elif self.unet is not None:
|
||||||
|
prompt_embeds_dtype = self.unet.dtype
|
||||||
|
else:
|
||||||
|
prompt_embeds_dtype = prompt_embeds.dtype
|
||||||
|
|
||||||
|
prompt_embeds = prompt_embeds.to(dtype=prompt_embeds_dtype, device=device)
|
||||||
|
|
||||||
|
bs_embed, 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(bs_embed * num_images_per_prompt, seq_len, -1)
|
||||||
|
|
||||||
|
# get unconditional embeddings for classifier free guidance
|
||||||
|
if do_classifier_free_guidance and negative_prompt_embeds is None:
|
||||||
|
uncond_tokens: List[str]
|
||||||
|
if negative_prompt is None:
|
||||||
|
uncond_tokens = [""] * batch_size
|
||||||
|
elif 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 isinstance(negative_prompt, str):
|
||||||
|
uncond_tokens = [negative_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`."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
uncond_tokens = negative_prompt
|
||||||
|
|
||||||
|
# textual inversion: procecss multi-vector tokens if necessary
|
||||||
|
if isinstance(self, TextualInversionLoaderMixin):
|
||||||
|
uncond_tokens = self.maybe_convert_prompt(uncond_tokens, self.tokenizer)
|
||||||
|
|
||||||
|
max_length = prompt_embeds.shape[1]
|
||||||
|
uncond_input = self.tokenizer(
|
||||||
|
uncond_tokens,
|
||||||
|
padding="max_length",
|
||||||
|
max_length=max_length,
|
||||||
|
truncation=True,
|
||||||
|
return_tensors="pt",
|
||||||
|
)
|
||||||
|
|
||||||
|
if hasattr(self.text_encoder.config, "use_attention_mask") and self.text_encoder.config.use_attention_mask:
|
||||||
|
attention_mask = uncond_input.attention_mask.to(device)
|
||||||
|
else:
|
||||||
|
attention_mask = None
|
||||||
|
|
||||||
|
negative_prompt_embeds = self.text_encoder(
|
||||||
|
uncond_input.input_ids.to(device),
|
||||||
|
attention_mask=attention_mask,
|
||||||
|
)
|
||||||
|
negative_prompt_embeds = negative_prompt_embeds[0]
|
||||||
|
|
||||||
|
if do_classifier_free_guidance:
|
||||||
|
# duplicate unconditional embeddings for each generation per prompt, using mps friendly method
|
||||||
|
seq_len = negative_prompt_embeds.shape[1]
|
||||||
|
|
||||||
|
negative_prompt_embeds = negative_prompt_embeds.to(dtype=prompt_embeds_dtype, device=device)
|
||||||
|
|
||||||
|
negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_images_per_prompt, 1)
|
||||||
|
negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
|
||||||
|
|
||||||
|
if isinstance(self, LoraLoaderMixin) and USE_PEFT_BACKEND:
|
||||||
|
# Retrieve the original scale by scaling back the LoRA layers
|
||||||
|
unscale_lora_layers(self.text_encoder, lora_scale)
|
||||||
|
|
||||||
|
return prompt_embeds, negative_prompt_embeds
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.run_safety_checker
|
||||||
|
def run_safety_checker(self, image, device, dtype):
|
||||||
|
if self.safety_checker is None:
|
||||||
|
has_nsfw_concept = None
|
||||||
|
else:
|
||||||
|
if torch.is_tensor(image):
|
||||||
|
feature_extractor_input = self.image_processor.postprocess(image, output_type="pil")
|
||||||
|
else:
|
||||||
|
feature_extractor_input = self.image_processor.numpy_to_pil(image)
|
||||||
|
safety_checker_input = self.feature_extractor(feature_extractor_input, return_tensors="pt").to(device)
|
||||||
|
image, has_nsfw_concept = self.safety_checker(
|
||||||
|
images=image, clip_input=safety_checker_input.pixel_values.to(dtype)
|
||||||
|
)
|
||||||
|
return image, has_nsfw_concept
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.decode_latents
|
||||||
|
def decode_latents(self, latents):
|
||||||
|
deprecation_message = "The decode_latents method is deprecated and will be removed in 1.0.0. Please use VaeImageProcessor.postprocess(...) instead"
|
||||||
|
deprecate("decode_latents", "1.0.0", deprecation_message, standard_warn=False)
|
||||||
|
|
||||||
|
latents = 1 / self.vae.config.scaling_factor * latents
|
||||||
|
image = self.vae.decode(latents, return_dict=False)[0]
|
||||||
|
image = (image / 2 + 0.5).clamp(0, 1)
|
||||||
|
# we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
|
||||||
|
image = image.cpu().permute(0, 2, 3, 1).float().numpy()
|
||||||
|
return image
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_extra_step_kwargs
|
||||||
|
def prepare_extra_step_kwargs(self, generator, eta):
|
||||||
|
# prepare extra kwargs for the scheduler step, since not all schedulers have the same signature
|
||||||
|
# eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers.
|
||||||
|
# eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502
|
||||||
|
# and should be between [0, 1]
|
||||||
|
|
||||||
|
accepts_eta = "eta" in set(inspect.signature(self.scheduler.step).parameters.keys())
|
||||||
|
extra_step_kwargs = {}
|
||||||
|
if accepts_eta:
|
||||||
|
extra_step_kwargs["eta"] = eta
|
||||||
|
|
||||||
|
# check if the scheduler accepts generator
|
||||||
|
accepts_generator = "generator" in set(inspect.signature(self.scheduler.step).parameters.keys())
|
||||||
|
if accepts_generator:
|
||||||
|
extra_step_kwargs["generator"] = generator
|
||||||
|
return extra_step_kwargs
|
||||||
|
|
||||||
|
def check_inputs(
|
||||||
|
self,
|
||||||
|
prompt,
|
||||||
|
image,
|
||||||
|
callback_steps,
|
||||||
|
negative_prompt=None,
|
||||||
|
prompt_embeds=None,
|
||||||
|
negative_prompt_embeds=None,
|
||||||
|
controlnet_conditioning_scale=1.0,
|
||||||
|
control_guidance_start=0.0,
|
||||||
|
control_guidance_end=1.0,
|
||||||
|
):
|
||||||
|
if (callback_steps is None) or (
|
||||||
|
callback_steps is not None and (not isinstance(callback_steps, int) or callback_steps <= 0)
|
||||||
|
):
|
||||||
|
raise ValueError(
|
||||||
|
f"`callback_steps` has to be a positive integer but is {callback_steps} of type"
|
||||||
|
f" {type(callback_steps)}."
|
||||||
|
)
|
||||||
|
|
||||||
|
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 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)}")
|
||||||
|
|
||||||
|
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."
|
||||||
|
)
|
||||||
|
|
||||||
|
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}."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check `image`
|
||||||
|
is_compiled = hasattr(F, "scaled_dot_product_attention") and isinstance(
|
||||||
|
self.controlnet, torch._dynamo.eval_frame.OptimizedModule
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
isinstance(self.controlnet, ControlNetXSModel)
|
||||||
|
or is_compiled
|
||||||
|
and isinstance(self.controlnet._orig_mod, ControlNetXSModel)
|
||||||
|
):
|
||||||
|
self.check_image(image, prompt, prompt_embeds)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
# Check `controlnet_conditioning_scale`
|
||||||
|
if (
|
||||||
|
isinstance(self.controlnet, ControlNetXSModel)
|
||||||
|
or is_compiled
|
||||||
|
and isinstance(self.controlnet._orig_mod, ControlNetXSModel)
|
||||||
|
):
|
||||||
|
if not isinstance(controlnet_conditioning_scale, float):
|
||||||
|
raise TypeError("For single controlnet: `controlnet_conditioning_scale` must be type `float`.")
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
start, end = control_guidance_start, control_guidance_end
|
||||||
|
if start >= end:
|
||||||
|
raise ValueError(
|
||||||
|
f"control guidance start: {start} cannot be larger or equal to control guidance end: {end}."
|
||||||
|
)
|
||||||
|
if start < 0.0:
|
||||||
|
raise ValueError(f"control guidance start: {start} can't be smaller than 0.")
|
||||||
|
if end > 1.0:
|
||||||
|
raise ValueError(f"control guidance end: {end} can't be larger than 1.0.")
|
||||||
|
|
||||||
|
def check_image(self, image, prompt, prompt_embeds):
|
||||||
|
image_is_pil = isinstance(image, PIL.Image.Image)
|
||||||
|
image_is_tensor = isinstance(image, torch.Tensor)
|
||||||
|
image_is_np = isinstance(image, np.ndarray)
|
||||||
|
image_is_pil_list = isinstance(image, list) and isinstance(image[0], PIL.Image.Image)
|
||||||
|
image_is_tensor_list = isinstance(image, list) and isinstance(image[0], torch.Tensor)
|
||||||
|
image_is_np_list = isinstance(image, list) and isinstance(image[0], np.ndarray)
|
||||||
|
|
||||||
|
if (
|
||||||
|
not image_is_pil
|
||||||
|
and not image_is_tensor
|
||||||
|
and not image_is_np
|
||||||
|
and not image_is_pil_list
|
||||||
|
and not image_is_tensor_list
|
||||||
|
and not image_is_np_list
|
||||||
|
):
|
||||||
|
raise TypeError(
|
||||||
|
f"image must be passed and be one of PIL image, numpy array, torch tensor, list of PIL images, list of numpy arrays or list of torch tensors, but is {type(image)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if image_is_pil:
|
||||||
|
image_batch_size = 1
|
||||||
|
else:
|
||||||
|
image_batch_size = len(image)
|
||||||
|
|
||||||
|
if prompt is not None and isinstance(prompt, str):
|
||||||
|
prompt_batch_size = 1
|
||||||
|
elif prompt is not None and isinstance(prompt, list):
|
||||||
|
prompt_batch_size = len(prompt)
|
||||||
|
elif prompt_embeds is not None:
|
||||||
|
prompt_batch_size = prompt_embeds.shape[0]
|
||||||
|
|
||||||
|
if image_batch_size != 1 and image_batch_size != prompt_batch_size:
|
||||||
|
raise ValueError(
|
||||||
|
f"If image batch size is not 1, image batch size must be same as prompt batch size. image batch size: {image_batch_size}, prompt batch size: {prompt_batch_size}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def prepare_image(
|
||||||
|
self,
|
||||||
|
image,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
batch_size,
|
||||||
|
num_images_per_prompt,
|
||||||
|
device,
|
||||||
|
dtype,
|
||||||
|
do_classifier_free_guidance=False,
|
||||||
|
):
|
||||||
|
image = self.control_image_processor.preprocess(image, height=height, width=width).to(dtype=torch.float32)
|
||||||
|
image_batch_size = image.shape[0]
|
||||||
|
|
||||||
|
if image_batch_size == 1:
|
||||||
|
repeat_by = batch_size
|
||||||
|
else:
|
||||||
|
# image batch size is the same as prompt batch size
|
||||||
|
repeat_by = num_images_per_prompt
|
||||||
|
|
||||||
|
image = image.repeat_interleave(repeat_by, dim=0)
|
||||||
|
|
||||||
|
image = image.to(device=device, dtype=dtype)
|
||||||
|
|
||||||
|
if do_classifier_free_guidance:
|
||||||
|
image = torch.cat([image] * 2)
|
||||||
|
|
||||||
|
return image
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_latents
|
||||||
|
def prepare_latents(self, batch_size, num_channels_latents, height, width, dtype, device, generator, latents=None):
|
||||||
|
shape = (batch_size, num_channels_latents, height // self.vae_scale_factor, width // self.vae_scale_factor)
|
||||||
|
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."
|
||||||
|
)
|
||||||
|
|
||||||
|
if latents is None:
|
||||||
|
latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
|
||||||
|
else:
|
||||||
|
latents = latents.to(device)
|
||||||
|
|
||||||
|
# scale the initial noise by the standard deviation required by the scheduler
|
||||||
|
latents = latents * self.scheduler.init_noise_sigma
|
||||||
|
return latents
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.enable_freeu
|
||||||
|
def enable_freeu(self, s1: float, s2: float, b1: float, b2: float):
|
||||||
|
r"""Enables the FreeU mechanism as in https://arxiv.org/abs/2309.11497.
|
||||||
|
|
||||||
|
The suffixes after the scaling factors represent the stages where they are being applied.
|
||||||
|
|
||||||
|
Please refer to the [official repository](https://github.com/ChenyangSi/FreeU) for combinations of the values
|
||||||
|
that are known to work well for different pipelines such as Stable Diffusion v1, v2, and Stable Diffusion XL.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
s1 (`float`):
|
||||||
|
Scaling factor for stage 1 to attenuate the contributions of the skip features. This is done to
|
||||||
|
mitigate "oversmoothing effect" in the enhanced denoising process.
|
||||||
|
s2 (`float`):
|
||||||
|
Scaling factor for stage 2 to attenuate the contributions of the skip features. This is done to
|
||||||
|
mitigate "oversmoothing effect" in the enhanced denoising process.
|
||||||
|
b1 (`float`): Scaling factor for stage 1 to amplify the contributions of backbone features.
|
||||||
|
b2 (`float`): Scaling factor for stage 2 to amplify the contributions of backbone features.
|
||||||
|
"""
|
||||||
|
if not hasattr(self, "unet"):
|
||||||
|
raise ValueError("The pipeline must have `unet` for using FreeU.")
|
||||||
|
self.unet.enable_freeu(s1=s1, s2=s2, b1=b1, b2=b2)
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.disable_freeu
|
||||||
|
def disable_freeu(self):
|
||||||
|
"""Disables the FreeU mechanism if enabled."""
|
||||||
|
self.unet.disable_freeu()
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
|
@replace_example_docstring(EXAMPLE_DOC_STRING)
|
||||||
|
def __call__(
|
||||||
|
self,
|
||||||
|
prompt: Union[str, List[str]] = None,
|
||||||
|
image: PipelineImageInput = None,
|
||||||
|
height: Optional[int] = None,
|
||||||
|
width: Optional[int] = None,
|
||||||
|
num_inference_steps: int = 50,
|
||||||
|
guidance_scale: float = 7.5,
|
||||||
|
negative_prompt: Optional[Union[str, List[str]]] = None,
|
||||||
|
num_images_per_prompt: Optional[int] = 1,
|
||||||
|
eta: float = 0.0,
|
||||||
|
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,
|
||||||
|
output_type: Optional[str] = "pil",
|
||||||
|
return_dict: bool = True,
|
||||||
|
callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None,
|
||||||
|
callback_steps: int = 1,
|
||||||
|
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
|
||||||
|
controlnet_conditioning_scale: Union[float, List[float]] = 1.0,
|
||||||
|
control_guidance_start: float = 0.0,
|
||||||
|
control_guidance_end: float = 1.0,
|
||||||
|
clip_skip: Optional[int] = None,
|
||||||
|
):
|
||||||
|
r"""
|
||||||
|
The call function to the pipeline for generation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prompt (`str` or `List[str]`, *optional*):
|
||||||
|
The prompt or prompts to guide image generation. If not defined, you need to pass `prompt_embeds`.
|
||||||
|
image (`torch.FloatTensor`, `PIL.Image.Image`, `np.ndarray`, `List[torch.FloatTensor]`, `List[PIL.Image.Image]`, `List[np.ndarray]`,
|
||||||
|
`List[List[torch.FloatTensor]]`, `List[List[np.ndarray]]` or `List[List[PIL.Image.Image]]`):
|
||||||
|
The ControlNet input condition to provide guidance to the `unet` for generation. If the type is
|
||||||
|
specified as `torch.FloatTensor`, it is passed to ControlNet as is. `PIL.Image.Image` can also be
|
||||||
|
accepted as an image. The dimensions of the output image defaults to `image`'s dimensions. If height
|
||||||
|
and/or width are passed, `image` is resized accordingly. If multiple ControlNets are specified in
|
||||||
|
`init`, images must be passed as a list such that each element of the list can be correctly batched for
|
||||||
|
input to a single ControlNet.
|
||||||
|
height (`int`, *optional*, defaults to `self.unet.config.sample_size * self.vae_scale_factor`):
|
||||||
|
The height in pixels of the generated image.
|
||||||
|
width (`int`, *optional*, defaults to `self.unet.config.sample_size * self.vae_scale_factor`):
|
||||||
|
The width in pixels of the generated image.
|
||||||
|
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.
|
||||||
|
guidance_scale (`float`, *optional*, defaults to 7.5):
|
||||||
|
A higher guidance scale value encourages the model to generate images closely linked to the text
|
||||||
|
`prompt` at the expense of lower image quality. Guidance scale is enabled when `guidance_scale > 1`.
|
||||||
|
negative_prompt (`str` or `List[str]`, *optional*):
|
||||||
|
The prompt or prompts to guide what to not include in image generation. If not defined, you need to
|
||||||
|
pass `negative_prompt_embeds` instead. Ignored when not using guidance (`guidance_scale < 1`).
|
||||||
|
num_images_per_prompt (`int`, *optional*, defaults to 1):
|
||||||
|
The number of images to generate per prompt.
|
||||||
|
eta (`float`, *optional*, defaults to 0.0):
|
||||||
|
Corresponds to parameter eta (η) from the [DDIM](https://arxiv.org/abs/2010.02502) paper. Only applies
|
||||||
|
to the [`~schedulers.DDIMScheduler`], and is ignored in other schedulers.
|
||||||
|
generator (`torch.Generator` or `List[torch.Generator]`, *optional*):
|
||||||
|
A [`torch.Generator`](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 is 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 (prompt weighting). If not
|
||||||
|
provided, text embeddings are generated from the `prompt` input argument.
|
||||||
|
negative_prompt_embeds (`torch.FloatTensor`, *optional*):
|
||||||
|
Pre-generated negative text embeddings. Can be used to easily tweak text inputs (prompt weighting). If
|
||||||
|
not provided, `negative_prompt_embeds` are generated from the `negative_prompt` input argument.
|
||||||
|
output_type (`str`, *optional*, defaults to `"pil"`):
|
||||||
|
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
||||||
|
return_dict (`bool`, *optional*, defaults to `True`):
|
||||||
|
Whether or not to return a [`~pipelines.stable_diffusion.StableDiffusionPipelineOutput`] instead of a
|
||||||
|
plain tuple.
|
||||||
|
callback (`Callable`, *optional*):
|
||||||
|
A function that calls every `callback_steps` steps during inference. The function is called with the
|
||||||
|
following arguments: `callback(step: int, timestep: int, latents: torch.FloatTensor)`.
|
||||||
|
callback_steps (`int`, *optional*, defaults to 1):
|
||||||
|
The frequency at which the `callback` function is called. If not specified, the callback is called at
|
||||||
|
every step.
|
||||||
|
cross_attention_kwargs (`dict`, *optional*):
|
||||||
|
A kwargs dictionary that if specified is passed along to the [`AttentionProcessor`] as defined in
|
||||||
|
[`self.processor`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
|
||||||
|
controlnet_conditioning_scale (`float` or `List[float]`, *optional*, defaults to 1.0):
|
||||||
|
The outputs of the ControlNet are multiplied by `controlnet_conditioning_scale` before they are added
|
||||||
|
to the residual in the original `unet`. If multiple ControlNets are specified in `init`, you can set
|
||||||
|
the corresponding scale as a list.
|
||||||
|
control_guidance_start (`float` or `List[float]`, *optional*, defaults to 0.0):
|
||||||
|
The percentage of total steps at which the ControlNet starts applying.
|
||||||
|
control_guidance_end (`float` or `List[float]`, *optional*, defaults to 1.0):
|
||||||
|
The percentage of total steps at which the ControlNet stops applying.
|
||||||
|
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.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
[`~pipelines.stable_diffusion.StableDiffusionPipelineOutput`] or `tuple`:
|
||||||
|
If `return_dict` is `True`, [`~pipelines.stable_diffusion.StableDiffusionPipelineOutput`] is returned,
|
||||||
|
otherwise a `tuple` is returned where the first element is a list with the generated images and the
|
||||||
|
second element is a list of `bool`s indicating whether the corresponding generated image contains
|
||||||
|
"not-safe-for-work" (nsfw) content.
|
||||||
|
"""
|
||||||
|
controlnet = self.controlnet._orig_mod if is_compiled_module(self.controlnet) else self.controlnet
|
||||||
|
|
||||||
|
# 1. Check inputs. Raise error if not correct
|
||||||
|
self.check_inputs(
|
||||||
|
prompt,
|
||||||
|
image,
|
||||||
|
callback_steps,
|
||||||
|
negative_prompt,
|
||||||
|
prompt_embeds,
|
||||||
|
negative_prompt_embeds,
|
||||||
|
controlnet_conditioning_scale,
|
||||||
|
control_guidance_start,
|
||||||
|
control_guidance_end,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# 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.
|
||||||
|
do_classifier_free_guidance = guidance_scale > 1.0
|
||||||
|
|
||||||
|
# 3. Encode input prompt
|
||||||
|
text_encoder_lora_scale = (
|
||||||
|
cross_attention_kwargs.get("scale", None) if cross_attention_kwargs is not None else None
|
||||||
|
)
|
||||||
|
prompt_embeds, negative_prompt_embeds = self.encode_prompt(
|
||||||
|
prompt,
|
||||||
|
device,
|
||||||
|
num_images_per_prompt,
|
||||||
|
do_classifier_free_guidance,
|
||||||
|
negative_prompt,
|
||||||
|
prompt_embeds=prompt_embeds,
|
||||||
|
negative_prompt_embeds=negative_prompt_embeds,
|
||||||
|
lora_scale=text_encoder_lora_scale,
|
||||||
|
clip_skip=clip_skip,
|
||||||
|
)
|
||||||
|
# For classifier free guidance, we need to do two forward passes.
|
||||||
|
# Here we concatenate the unconditional and text embeddings into a single batch
|
||||||
|
# to avoid doing two forward passes
|
||||||
|
if do_classifier_free_guidance:
|
||||||
|
prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds])
|
||||||
|
|
||||||
|
# 4. Prepare image
|
||||||
|
if isinstance(controlnet, ControlNetXSModel):
|
||||||
|
image = self.prepare_image(
|
||||||
|
image=image,
|
||||||
|
width=width,
|
||||||
|
height=height,
|
||||||
|
batch_size=batch_size * num_images_per_prompt,
|
||||||
|
num_images_per_prompt=num_images_per_prompt,
|
||||||
|
device=device,
|
||||||
|
dtype=controlnet.dtype,
|
||||||
|
do_classifier_free_guidance=do_classifier_free_guidance,
|
||||||
|
)
|
||||||
|
height, width = image.shape[-2:]
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
# 5. Prepare timesteps
|
||||||
|
self.scheduler.set_timesteps(num_inference_steps, device=device)
|
||||||
|
timesteps = self.scheduler.timesteps
|
||||||
|
|
||||||
|
# 6. Prepare latent variables
|
||||||
|
num_channels_latents = self.unet.config.in_channels
|
||||||
|
latents = self.prepare_latents(
|
||||||
|
batch_size * num_images_per_prompt,
|
||||||
|
num_channels_latents,
|
||||||
|
height,
|
||||||
|
width,
|
||||||
|
prompt_embeds.dtype,
|
||||||
|
device,
|
||||||
|
generator,
|
||||||
|
latents,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 7. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline
|
||||||
|
extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
|
||||||
|
|
||||||
|
# 8. Denoising loop
|
||||||
|
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
|
||||||
|
is_unet_compiled = is_compiled_module(self.unet)
|
||||||
|
is_controlnet_compiled = is_compiled_module(self.controlnet)
|
||||||
|
is_torch_higher_equal_2_1 = is_torch_version(">=", "2.1")
|
||||||
|
with self.progress_bar(total=num_inference_steps) as progress_bar:
|
||||||
|
for i, t in enumerate(timesteps):
|
||||||
|
# Relevant thread:
|
||||||
|
# https://dev-discuss.pytorch.org/t/cudagraphs-in-pytorch-2-0/1428
|
||||||
|
if (is_unet_compiled and is_controlnet_compiled) and is_torch_higher_equal_2_1:
|
||||||
|
torch._inductor.cudagraph_mark_step_begin()
|
||||||
|
# expand the latents if we are doing classifier free guidance
|
||||||
|
latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents
|
||||||
|
latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)
|
||||||
|
|
||||||
|
# predict the noise residual
|
||||||
|
dont_control = (
|
||||||
|
i / len(timesteps) < control_guidance_start or (i + 1) / len(timesteps) > control_guidance_end
|
||||||
|
)
|
||||||
|
if dont_control:
|
||||||
|
noise_pred = self.unet(
|
||||||
|
sample=latent_model_input,
|
||||||
|
timestep=t,
|
||||||
|
encoder_hidden_states=prompt_embeds,
|
||||||
|
cross_attention_kwargs=cross_attention_kwargs,
|
||||||
|
return_dict=True,
|
||||||
|
).sample
|
||||||
|
else:
|
||||||
|
noise_pred = self.controlnet(
|
||||||
|
base_model=self.unet,
|
||||||
|
sample=latent_model_input,
|
||||||
|
timestep=t,
|
||||||
|
encoder_hidden_states=prompt_embeds,
|
||||||
|
controlnet_cond=image,
|
||||||
|
conditioning_scale=controlnet_conditioning_scale,
|
||||||
|
cross_attention_kwargs=cross_attention_kwargs,
|
||||||
|
return_dict=True,
|
||||||
|
).sample
|
||||||
|
|
||||||
|
# perform guidance
|
||||||
|
if do_classifier_free_guidance:
|
||||||
|
noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
|
||||||
|
noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)
|
||||||
|
|
||||||
|
latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs, return_dict=False)[0]
|
||||||
|
|
||||||
|
# call the callback, if provided
|
||||||
|
if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
|
||||||
|
progress_bar.update()
|
||||||
|
if callback is not None and i % callback_steps == 0:
|
||||||
|
step_idx = i // getattr(self.scheduler, "order", 1)
|
||||||
|
callback(step_idx, t, latents)
|
||||||
|
|
||||||
|
# If we do sequential model offloading, let's offload unet and controlnet
|
||||||
|
# manually for max memory savings
|
||||||
|
if hasattr(self, "final_offload_hook") and self.final_offload_hook is not None:
|
||||||
|
self.unet.to("cpu")
|
||||||
|
self.controlnet.to("cpu")
|
||||||
|
torch.cuda.empty_cache()
|
||||||
|
|
||||||
|
if not output_type == "latent":
|
||||||
|
image = self.vae.decode(latents / self.vae.config.scaling_factor, return_dict=False, generator=generator)[
|
||||||
|
0
|
||||||
|
]
|
||||||
|
image, has_nsfw_concept = self.run_safety_checker(image, device, prompt_embeds.dtype)
|
||||||
|
else:
|
||||||
|
image = latents
|
||||||
|
has_nsfw_concept = None
|
||||||
|
|
||||||
|
if has_nsfw_concept is None:
|
||||||
|
do_denormalize = [True] * image.shape[0]
|
||||||
|
else:
|
||||||
|
do_denormalize = [not has_nsfw for has_nsfw in has_nsfw_concept]
|
||||||
|
|
||||||
|
image = self.image_processor.postprocess(image, output_type=output_type, do_denormalize=do_denormalize)
|
||||||
|
|
||||||
|
# Offload all models
|
||||||
|
self.maybe_free_model_hooks()
|
||||||
|
|
||||||
|
if not return_dict:
|
||||||
|
return (image, has_nsfw_concept)
|
||||||
|
|
||||||
|
return StableDiffusionPipelineOutput(images=image, nsfw_content_detected=has_nsfw_concept)
|
||||||
File diff suppressed because it is too large
Load Diff
+46
-5
@@ -20,11 +20,11 @@ from typing import Any, Callable, Dict, List, Optional, Union
|
|||||||
|
|
||||||
import PIL.Image
|
import PIL.Image
|
||||||
import torch
|
import torch
|
||||||
from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer
|
from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer, CLIPVisionModelWithProjection
|
||||||
|
|
||||||
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
||||||
from ...loaders import FromSingleFileMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
from ...loaders import FromSingleFileMixin, IPAdapterMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
||||||
from ...models import AutoencoderKL, UNet2DConditionModel
|
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
||||||
from ...models.lora import adjust_lora_scale_text_encoder
|
from ...models.lora import adjust_lora_scale_text_encoder
|
||||||
from ...schedulers import LCMScheduler
|
from ...schedulers import LCMScheduler
|
||||||
from ...utils import (
|
from ...utils import (
|
||||||
@@ -129,7 +129,7 @@ EXAMPLE_DOC_STRING = """
|
|||||||
|
|
||||||
|
|
||||||
class LatentConsistencyModelImg2ImgPipeline(
|
class LatentConsistencyModelImg2ImgPipeline(
|
||||||
DiffusionPipeline, TextualInversionLoaderMixin, LoraLoaderMixin, FromSingleFileMixin
|
DiffusionPipeline, TextualInversionLoaderMixin, IPAdapterMixin, LoraLoaderMixin, FromSingleFileMixin
|
||||||
):
|
):
|
||||||
r"""
|
r"""
|
||||||
Pipeline for image-to-image generation using a latent consistency model.
|
Pipeline for image-to-image generation using a latent consistency model.
|
||||||
@@ -142,6 +142,7 @@ class LatentConsistencyModelImg2ImgPipeline(
|
|||||||
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
||||||
|
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
@@ -166,7 +167,7 @@ class LatentConsistencyModelImg2ImgPipeline(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
model_cpu_offload_seq = "text_encoder->unet->vae"
|
model_cpu_offload_seq = "text_encoder->unet->vae"
|
||||||
_optional_components = ["safety_checker", "feature_extractor"]
|
_optional_components = ["safety_checker", "feature_extractor", "image_encoder"]
|
||||||
_exclude_from_cpu_offload = ["safety_checker"]
|
_exclude_from_cpu_offload = ["safety_checker"]
|
||||||
_callback_tensor_inputs = ["latents", "denoised", "prompt_embeds", "w_embedding"]
|
_callback_tensor_inputs = ["latents", "denoised", "prompt_embeds", "w_embedding"]
|
||||||
|
|
||||||
@@ -179,6 +180,7 @@ class LatentConsistencyModelImg2ImgPipeline(
|
|||||||
scheduler: LCMScheduler,
|
scheduler: LCMScheduler,
|
||||||
safety_checker: StableDiffusionSafetyChecker,
|
safety_checker: StableDiffusionSafetyChecker,
|
||||||
feature_extractor: CLIPImageProcessor,
|
feature_extractor: CLIPImageProcessor,
|
||||||
|
image_encoder: Optional[CLIPVisionModelWithProjection] = None,
|
||||||
requires_safety_checker: bool = True,
|
requires_safety_checker: bool = True,
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -191,6 +193,7 @@ class LatentConsistencyModelImg2ImgPipeline(
|
|||||||
scheduler=scheduler,
|
scheduler=scheduler,
|
||||||
safety_checker=safety_checker,
|
safety_checker=safety_checker,
|
||||||
feature_extractor=feature_extractor,
|
feature_extractor=feature_extractor,
|
||||||
|
image_encoder=image_encoder,
|
||||||
)
|
)
|
||||||
|
|
||||||
if safety_checker is None and requires_safety_checker:
|
if safety_checker is None and requires_safety_checker:
|
||||||
@@ -449,6 +452,31 @@ class LatentConsistencyModelImg2ImgPipeline(
|
|||||||
|
|
||||||
return prompt_embeds, negative_prompt_embeds
|
return prompt_embeds, negative_prompt_embeds
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.encode_image
|
||||||
|
def encode_image(self, image, device, num_images_per_prompt, output_hidden_states=None):
|
||||||
|
dtype = next(self.image_encoder.parameters()).dtype
|
||||||
|
|
||||||
|
if not isinstance(image, torch.Tensor):
|
||||||
|
image = self.feature_extractor(image, return_tensors="pt").pixel_values
|
||||||
|
|
||||||
|
image = image.to(device=device, dtype=dtype)
|
||||||
|
if output_hidden_states:
|
||||||
|
image_enc_hidden_states = self.image_encoder(image, output_hidden_states=True).hidden_states[-2]
|
||||||
|
image_enc_hidden_states = image_enc_hidden_states.repeat_interleave(num_images_per_prompt, dim=0)
|
||||||
|
uncond_image_enc_hidden_states = self.image_encoder(
|
||||||
|
torch.zeros_like(image), output_hidden_states=True
|
||||||
|
).hidden_states[-2]
|
||||||
|
uncond_image_enc_hidden_states = uncond_image_enc_hidden_states.repeat_interleave(
|
||||||
|
num_images_per_prompt, dim=0
|
||||||
|
)
|
||||||
|
return image_enc_hidden_states, uncond_image_enc_hidden_states
|
||||||
|
else:
|
||||||
|
image_embeds = self.image_encoder(image).image_embeds
|
||||||
|
image_embeds = image_embeds.repeat_interleave(num_images_per_prompt, dim=0)
|
||||||
|
uncond_image_embeds = torch.zeros_like(image_embeds)
|
||||||
|
|
||||||
|
return image_embeds, uncond_image_embeds
|
||||||
|
|
||||||
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.run_safety_checker
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.run_safety_checker
|
||||||
def run_safety_checker(self, image, device, dtype):
|
def run_safety_checker(self, image, device, dtype):
|
||||||
if self.safety_checker is None:
|
if self.safety_checker is None:
|
||||||
@@ -647,6 +675,7 @@ class LatentConsistencyModelImg2ImgPipeline(
|
|||||||
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
|
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
|
||||||
latents: Optional[torch.FloatTensor] = None,
|
latents: Optional[torch.FloatTensor] = None,
|
||||||
prompt_embeds: Optional[torch.FloatTensor] = None,
|
prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
|
ip_adapter_image: Optional[PipelineImageInput] = None,
|
||||||
output_type: Optional[str] = "pil",
|
output_type: Optional[str] = "pil",
|
||||||
return_dict: bool = True,
|
return_dict: bool = True,
|
||||||
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
|
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
|
||||||
@@ -695,6 +724,8 @@ class LatentConsistencyModelImg2ImgPipeline(
|
|||||||
prompt_embeds (`torch.FloatTensor`, *optional*):
|
prompt_embeds (`torch.FloatTensor`, *optional*):
|
||||||
Pre-generated text embeddings. Can be used to easily tweak text inputs (prompt weighting). If not
|
Pre-generated text embeddings. Can be used to easily tweak text inputs (prompt weighting). If not
|
||||||
provided, text embeddings are generated from the `prompt` input argument.
|
provided, text embeddings are generated from the `prompt` input argument.
|
||||||
|
ip_adapter_image: (`PipelineImageInput`, *optional*):
|
||||||
|
Optional image input to work with IP Adapters.
|
||||||
output_type (`str`, *optional*, defaults to `"pil"`):
|
output_type (`str`, *optional*, defaults to `"pil"`):
|
||||||
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
||||||
return_dict (`bool`, *optional*, defaults to `True`):
|
return_dict (`bool`, *optional*, defaults to `True`):
|
||||||
@@ -758,6 +789,12 @@ class LatentConsistencyModelImg2ImgPipeline(
|
|||||||
device = self._execution_device
|
device = self._execution_device
|
||||||
# do_classifier_free_guidance = guidance_scale > 1.0
|
# do_classifier_free_guidance = guidance_scale > 1.0
|
||||||
|
|
||||||
|
if ip_adapter_image is not None:
|
||||||
|
output_hidden_state = False if isinstance(self.unet.encoder_hid_proj, ImageProjection) else True
|
||||||
|
image_embeds, negative_image_embeds = self.encode_image(
|
||||||
|
ip_adapter_image, device, num_images_per_prompt, output_hidden_state
|
||||||
|
)
|
||||||
|
|
||||||
# 3. Encode input prompt
|
# 3. Encode input prompt
|
||||||
lora_scale = (
|
lora_scale = (
|
||||||
self.cross_attention_kwargs.get("scale", None) if self.cross_attention_kwargs is not None else None
|
self.cross_attention_kwargs.get("scale", None) if self.cross_attention_kwargs is not None else None
|
||||||
@@ -815,6 +852,9 @@ class LatentConsistencyModelImg2ImgPipeline(
|
|||||||
# 7. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline
|
# 7. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline
|
||||||
extra_step_kwargs = self.prepare_extra_step_kwargs(generator, None)
|
extra_step_kwargs = self.prepare_extra_step_kwargs(generator, None)
|
||||||
|
|
||||||
|
# 7.1 Add image embeds for IP-Adapter
|
||||||
|
added_cond_kwargs = {"image_embeds": image_embeds} if ip_adapter_image is not None else None
|
||||||
|
|
||||||
# 8. LCM Multistep Sampling Loop
|
# 8. LCM Multistep Sampling Loop
|
||||||
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
|
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
|
||||||
self._num_timesteps = len(timesteps)
|
self._num_timesteps = len(timesteps)
|
||||||
@@ -829,6 +869,7 @@ class LatentConsistencyModelImg2ImgPipeline(
|
|||||||
timestep_cond=w_embedding,
|
timestep_cond=w_embedding,
|
||||||
encoder_hidden_states=prompt_embeds,
|
encoder_hidden_states=prompt_embeds,
|
||||||
cross_attention_kwargs=self.cross_attention_kwargs,
|
cross_attention_kwargs=self.cross_attention_kwargs,
|
||||||
|
added_cond_kwargs=added_cond_kwargs,
|
||||||
return_dict=False,
|
return_dict=False,
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
|
|||||||
+47
-6
@@ -19,11 +19,11 @@ import inspect
|
|||||||
from typing import Any, Callable, Dict, List, Optional, Union
|
from typing import Any, Callable, Dict, List, Optional, Union
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer
|
from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer, CLIPVisionModelWithProjection
|
||||||
|
|
||||||
from ...image_processor import VaeImageProcessor
|
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
||||||
from ...loaders import FromSingleFileMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
from ...loaders import FromSingleFileMixin, IPAdapterMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
||||||
from ...models import AutoencoderKL, UNet2DConditionModel
|
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
||||||
from ...models.lora import adjust_lora_scale_text_encoder
|
from ...models.lora import adjust_lora_scale_text_encoder
|
||||||
from ...schedulers import LCMScheduler
|
from ...schedulers import LCMScheduler
|
||||||
from ...utils import (
|
from ...utils import (
|
||||||
@@ -107,7 +107,7 @@ def retrieve_timesteps(
|
|||||||
|
|
||||||
|
|
||||||
class LatentConsistencyModelPipeline(
|
class LatentConsistencyModelPipeline(
|
||||||
DiffusionPipeline, TextualInversionLoaderMixin, LoraLoaderMixin, FromSingleFileMixin
|
DiffusionPipeline, TextualInversionLoaderMixin, IPAdapterMixin, LoraLoaderMixin, FromSingleFileMixin
|
||||||
):
|
):
|
||||||
r"""
|
r"""
|
||||||
Pipeline for text-to-image generation using a latent consistency model.
|
Pipeline for text-to-image generation using a latent consistency model.
|
||||||
@@ -120,6 +120,7 @@ class LatentConsistencyModelPipeline(
|
|||||||
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
||||||
|
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
@@ -144,7 +145,7 @@ class LatentConsistencyModelPipeline(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
model_cpu_offload_seq = "text_encoder->unet->vae"
|
model_cpu_offload_seq = "text_encoder->unet->vae"
|
||||||
_optional_components = ["safety_checker", "feature_extractor"]
|
_optional_components = ["safety_checker", "feature_extractor", "image_encoder"]
|
||||||
_exclude_from_cpu_offload = ["safety_checker"]
|
_exclude_from_cpu_offload = ["safety_checker"]
|
||||||
_callback_tensor_inputs = ["latents", "denoised", "prompt_embeds", "w_embedding"]
|
_callback_tensor_inputs = ["latents", "denoised", "prompt_embeds", "w_embedding"]
|
||||||
|
|
||||||
@@ -157,6 +158,7 @@ class LatentConsistencyModelPipeline(
|
|||||||
scheduler: LCMScheduler,
|
scheduler: LCMScheduler,
|
||||||
safety_checker: StableDiffusionSafetyChecker,
|
safety_checker: StableDiffusionSafetyChecker,
|
||||||
feature_extractor: CLIPImageProcessor,
|
feature_extractor: CLIPImageProcessor,
|
||||||
|
image_encoder: Optional[CLIPVisionModelWithProjection] = None,
|
||||||
requires_safety_checker: bool = True,
|
requires_safety_checker: bool = True,
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -185,6 +187,7 @@ class LatentConsistencyModelPipeline(
|
|||||||
scheduler=scheduler,
|
scheduler=scheduler,
|
||||||
safety_checker=safety_checker,
|
safety_checker=safety_checker,
|
||||||
feature_extractor=feature_extractor,
|
feature_extractor=feature_extractor,
|
||||||
|
image_encoder=image_encoder,
|
||||||
)
|
)
|
||||||
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
|
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
|
||||||
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor)
|
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor)
|
||||||
@@ -433,6 +436,31 @@ class LatentConsistencyModelPipeline(
|
|||||||
|
|
||||||
return prompt_embeds, negative_prompt_embeds
|
return prompt_embeds, negative_prompt_embeds
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.encode_image
|
||||||
|
def encode_image(self, image, device, num_images_per_prompt, output_hidden_states=None):
|
||||||
|
dtype = next(self.image_encoder.parameters()).dtype
|
||||||
|
|
||||||
|
if not isinstance(image, torch.Tensor):
|
||||||
|
image = self.feature_extractor(image, return_tensors="pt").pixel_values
|
||||||
|
|
||||||
|
image = image.to(device=device, dtype=dtype)
|
||||||
|
if output_hidden_states:
|
||||||
|
image_enc_hidden_states = self.image_encoder(image, output_hidden_states=True).hidden_states[-2]
|
||||||
|
image_enc_hidden_states = image_enc_hidden_states.repeat_interleave(num_images_per_prompt, dim=0)
|
||||||
|
uncond_image_enc_hidden_states = self.image_encoder(
|
||||||
|
torch.zeros_like(image), output_hidden_states=True
|
||||||
|
).hidden_states[-2]
|
||||||
|
uncond_image_enc_hidden_states = uncond_image_enc_hidden_states.repeat_interleave(
|
||||||
|
num_images_per_prompt, dim=0
|
||||||
|
)
|
||||||
|
return image_enc_hidden_states, uncond_image_enc_hidden_states
|
||||||
|
else:
|
||||||
|
image_embeds = self.image_encoder(image).image_embeds
|
||||||
|
image_embeds = image_embeds.repeat_interleave(num_images_per_prompt, dim=0)
|
||||||
|
uncond_image_embeds = torch.zeros_like(image_embeds)
|
||||||
|
|
||||||
|
return image_embeds, uncond_image_embeds
|
||||||
|
|
||||||
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.run_safety_checker
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.run_safety_checker
|
||||||
def run_safety_checker(self, image, device, dtype):
|
def run_safety_checker(self, image, device, dtype):
|
||||||
if self.safety_checker is None:
|
if self.safety_checker is None:
|
||||||
@@ -581,6 +609,7 @@ class LatentConsistencyModelPipeline(
|
|||||||
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
|
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
|
||||||
latents: Optional[torch.FloatTensor] = None,
|
latents: Optional[torch.FloatTensor] = None,
|
||||||
prompt_embeds: Optional[torch.FloatTensor] = None,
|
prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
|
ip_adapter_image: Optional[PipelineImageInput] = None,
|
||||||
output_type: Optional[str] = "pil",
|
output_type: Optional[str] = "pil",
|
||||||
return_dict: bool = True,
|
return_dict: bool = True,
|
||||||
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
|
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
|
||||||
@@ -629,6 +658,8 @@ class LatentConsistencyModelPipeline(
|
|||||||
prompt_embeds (`torch.FloatTensor`, *optional*):
|
prompt_embeds (`torch.FloatTensor`, *optional*):
|
||||||
Pre-generated text embeddings. Can be used to easily tweak text inputs (prompt weighting). If not
|
Pre-generated text embeddings. Can be used to easily tweak text inputs (prompt weighting). If not
|
||||||
provided, text embeddings are generated from the `prompt` input argument.
|
provided, text embeddings are generated from the `prompt` input argument.
|
||||||
|
ip_adapter_image: (`PipelineImageInput`, *optional*):
|
||||||
|
Optional image input to work with IP Adapters.
|
||||||
output_type (`str`, *optional*, defaults to `"pil"`):
|
output_type (`str`, *optional*, defaults to `"pil"`):
|
||||||
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
||||||
return_dict (`bool`, *optional*, defaults to `True`):
|
return_dict (`bool`, *optional*, defaults to `True`):
|
||||||
@@ -697,6 +728,12 @@ class LatentConsistencyModelPipeline(
|
|||||||
device = self._execution_device
|
device = self._execution_device
|
||||||
# do_classifier_free_guidance = guidance_scale > 1.0
|
# do_classifier_free_guidance = guidance_scale > 1.0
|
||||||
|
|
||||||
|
if ip_adapter_image is not None:
|
||||||
|
output_hidden_state = False if isinstance(self.unet.encoder_hid_proj, ImageProjection) else True
|
||||||
|
image_embeds, negative_image_embeds = self.encode_image(
|
||||||
|
ip_adapter_image, device, num_images_per_prompt, output_hidden_state
|
||||||
|
)
|
||||||
|
|
||||||
# 3. Encode input prompt
|
# 3. Encode input prompt
|
||||||
lora_scale = (
|
lora_scale = (
|
||||||
self.cross_attention_kwargs.get("scale", None) if self.cross_attention_kwargs is not None else None
|
self.cross_attention_kwargs.get("scale", None) if self.cross_attention_kwargs is not None else None
|
||||||
@@ -748,6 +785,9 @@ class LatentConsistencyModelPipeline(
|
|||||||
# 7. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline
|
# 7. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline
|
||||||
extra_step_kwargs = self.prepare_extra_step_kwargs(generator, None)
|
extra_step_kwargs = self.prepare_extra_step_kwargs(generator, None)
|
||||||
|
|
||||||
|
# 7.1 Add image embeds for IP-Adapter
|
||||||
|
added_cond_kwargs = {"image_embeds": image_embeds} if ip_adapter_image is not None else None
|
||||||
|
|
||||||
# 8. LCM MultiStep Sampling Loop:
|
# 8. LCM MultiStep Sampling Loop:
|
||||||
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
|
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
|
||||||
self._num_timesteps = len(timesteps)
|
self._num_timesteps = len(timesteps)
|
||||||
@@ -762,6 +802,7 @@ class LatentConsistencyModelPipeline(
|
|||||||
timestep_cond=w_embedding,
|
timestep_cond=w_embedding,
|
||||||
encoder_hidden_states=prompt_embeds,
|
encoder_hidden_states=prompt_embeds,
|
||||||
cross_attention_kwargs=self.cross_attention_kwargs,
|
cross_attention_kwargs=self.cross_attention_kwargs,
|
||||||
|
added_cond_kwargs=added_cond_kwargs,
|
||||||
return_dict=False,
|
return_dict=False,
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ from typing import Optional, Union
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from huggingface_hub import hf_hub_download
|
from huggingface_hub import hf_hub_download
|
||||||
|
from huggingface_hub.utils import validate_hf_hub_args
|
||||||
|
|
||||||
from ..utils import ONNX_EXTERNAL_WEIGHTS_NAME, ONNX_WEIGHTS_NAME, is_onnx_available, logging
|
from ..utils import ONNX_EXTERNAL_WEIGHTS_NAME, ONNX_WEIGHTS_NAME, is_onnx_available, logging
|
||||||
|
|
||||||
@@ -130,10 +131,11 @@ class OnnxRuntimeModel:
|
|||||||
self._save_pretrained(save_directory, **kwargs)
|
self._save_pretrained(save_directory, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@validate_hf_hub_args
|
||||||
def _from_pretrained(
|
def _from_pretrained(
|
||||||
cls,
|
cls,
|
||||||
model_id: Union[str, Path],
|
model_id: Union[str, Path],
|
||||||
use_auth_token: Optional[Union[bool, str, None]] = None,
|
token: Optional[Union[bool, str, None]] = None,
|
||||||
revision: Optional[Union[str, None]] = None,
|
revision: Optional[Union[str, None]] = None,
|
||||||
force_download: bool = False,
|
force_download: bool = False,
|
||||||
cache_dir: Optional[str] = None,
|
cache_dir: Optional[str] = None,
|
||||||
@@ -148,7 +150,7 @@ class OnnxRuntimeModel:
|
|||||||
Arguments:
|
Arguments:
|
||||||
model_id (`str` or `Path`):
|
model_id (`str` or `Path`):
|
||||||
Directory from which to load
|
Directory from which to load
|
||||||
use_auth_token (`str` or `bool`):
|
token (`str` or `bool`):
|
||||||
Is needed to load models from a private or gated repository
|
Is needed to load models from a private or gated repository
|
||||||
revision (`str`):
|
revision (`str`):
|
||||||
Revision is the specific model version to use. It can be a branch name, a tag name, or a commit id
|
Revision is the specific model version to use. It can be a branch name, a tag name, or a commit id
|
||||||
@@ -179,7 +181,7 @@ class OnnxRuntimeModel:
|
|||||||
model_cache_path = hf_hub_download(
|
model_cache_path = hf_hub_download(
|
||||||
repo_id=model_id,
|
repo_id=model_id,
|
||||||
filename=model_file_name,
|
filename=model_file_name,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
cache_dir=cache_dir,
|
cache_dir=cache_dir,
|
||||||
force_download=force_download,
|
force_download=force_download,
|
||||||
@@ -190,11 +192,12 @@ class OnnxRuntimeModel:
|
|||||||
return cls(model=model, **kwargs)
|
return cls(model=model, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@validate_hf_hub_args
|
||||||
def from_pretrained(
|
def from_pretrained(
|
||||||
cls,
|
cls,
|
||||||
model_id: Union[str, Path],
|
model_id: Union[str, Path],
|
||||||
force_download: bool = True,
|
force_download: bool = True,
|
||||||
use_auth_token: Optional[str] = None,
|
token: Optional[str] = None,
|
||||||
cache_dir: Optional[str] = None,
|
cache_dir: Optional[str] = None,
|
||||||
**model_kwargs,
|
**model_kwargs,
|
||||||
):
|
):
|
||||||
@@ -207,6 +210,6 @@ class OnnxRuntimeModel:
|
|||||||
revision=revision,
|
revision=revision,
|
||||||
cache_dir=cache_dir,
|
cache_dir=cache_dir,
|
||||||
force_download=force_download,
|
force_download=force_download,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
**model_kwargs,
|
**model_kwargs,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import numpy as np
|
|||||||
import PIL.Image
|
import PIL.Image
|
||||||
from flax.core.frozen_dict import FrozenDict
|
from flax.core.frozen_dict import FrozenDict
|
||||||
from huggingface_hub import create_repo, snapshot_download
|
from huggingface_hub import create_repo, snapshot_download
|
||||||
|
from huggingface_hub.utils import validate_hf_hub_args
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from tqdm.auto import tqdm
|
from tqdm.auto import tqdm
|
||||||
|
|
||||||
@@ -32,7 +33,6 @@ from ..models.modeling_flax_utils import FLAX_WEIGHTS_NAME, FlaxModelMixin
|
|||||||
from ..schedulers.scheduling_utils_flax import SCHEDULER_CONFIG_NAME, FlaxSchedulerMixin
|
from ..schedulers.scheduling_utils_flax import SCHEDULER_CONFIG_NAME, FlaxSchedulerMixin
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
CONFIG_NAME,
|
CONFIG_NAME,
|
||||||
DIFFUSERS_CACHE,
|
|
||||||
BaseOutput,
|
BaseOutput,
|
||||||
PushToHubMixin,
|
PushToHubMixin,
|
||||||
http_user_agent,
|
http_user_agent,
|
||||||
@@ -227,6 +227,7 @@ class FlaxDiffusionPipeline(ConfigMixin, PushToHubMixin):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@validate_hf_hub_args
|
||||||
def from_pretrained(cls, pretrained_model_name_or_path: Optional[Union[str, os.PathLike]], **kwargs):
|
def from_pretrained(cls, pretrained_model_name_or_path: Optional[Union[str, os.PathLike]], **kwargs):
|
||||||
r"""
|
r"""
|
||||||
Instantiate a Flax-based diffusion pipeline from pretrained pipeline weights.
|
Instantiate a Flax-based diffusion pipeline from pretrained pipeline weights.
|
||||||
@@ -264,7 +265,7 @@ class FlaxDiffusionPipeline(ConfigMixin, PushToHubMixin):
|
|||||||
local_files_only (`bool`, *optional*, defaults to `False`):
|
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
|
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.
|
won't be downloaded from the Hub.
|
||||||
use_auth_token (`str` or *bool*, *optional*):
|
token (`str` or *bool*, *optional*):
|
||||||
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
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.
|
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
||||||
revision (`str`, *optional*, defaults to `"main"`):
|
revision (`str`, *optional*, defaults to `"main"`):
|
||||||
@@ -314,11 +315,11 @@ class FlaxDiffusionPipeline(ConfigMixin, PushToHubMixin):
|
|||||||
>>> dpm_params["scheduler"] = dpmpp_state
|
>>> dpm_params["scheduler"] = dpmpp_state
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", False)
|
local_files_only = kwargs.pop("local_files_only", False)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
from_pt = kwargs.pop("from_pt", False)
|
from_pt = kwargs.pop("from_pt", False)
|
||||||
use_memory_efficient_attention = kwargs.pop("use_memory_efficient_attention", False)
|
use_memory_efficient_attention = kwargs.pop("use_memory_efficient_attention", False)
|
||||||
@@ -334,7 +335,7 @@ class FlaxDiffusionPipeline(ConfigMixin, PushToHubMixin):
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
)
|
)
|
||||||
# make sure we only download sub-folders and `diffusers` filenames
|
# make sure we only download sub-folders and `diffusers` filenames
|
||||||
@@ -365,7 +366,7 @@ class FlaxDiffusionPipeline(ConfigMixin, PushToHubMixin):
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
allow_patterns=allow_patterns,
|
allow_patterns=allow_patterns,
|
||||||
ignore_patterns=ignore_patterns,
|
ignore_patterns=ignore_patterns,
|
||||||
|
|||||||
@@ -28,7 +28,14 @@ from typing import Any, Callable, Dict, List, Optional, Union
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import PIL.Image
|
import PIL.Image
|
||||||
import torch
|
import torch
|
||||||
from huggingface_hub import ModelCard, create_repo, hf_hub_download, model_info, snapshot_download
|
from huggingface_hub import (
|
||||||
|
ModelCard,
|
||||||
|
create_repo,
|
||||||
|
hf_hub_download,
|
||||||
|
model_info,
|
||||||
|
snapshot_download,
|
||||||
|
)
|
||||||
|
from huggingface_hub.utils import validate_hf_hub_args
|
||||||
from packaging import version
|
from packaging import version
|
||||||
from requests.exceptions import HTTPError
|
from requests.exceptions import HTTPError
|
||||||
from tqdm.auto import tqdm
|
from tqdm.auto import tqdm
|
||||||
@@ -40,8 +47,6 @@ from ..schedulers.scheduling_utils import SCHEDULER_CONFIG_NAME
|
|||||||
from ..utils import (
|
from ..utils import (
|
||||||
CONFIG_NAME,
|
CONFIG_NAME,
|
||||||
DEPRECATED_REVISION_ARGS,
|
DEPRECATED_REVISION_ARGS,
|
||||||
DIFFUSERS_CACHE,
|
|
||||||
HF_HUB_OFFLINE,
|
|
||||||
SAFETENSORS_WEIGHTS_NAME,
|
SAFETENSORS_WEIGHTS_NAME,
|
||||||
WEIGHTS_NAME,
|
WEIGHTS_NAME,
|
||||||
BaseOutput,
|
BaseOutput,
|
||||||
@@ -249,10 +254,11 @@ def variant_compatible_siblings(filenames, variant=None) -> Union[List[os.PathLi
|
|||||||
return usable_filenames, variant_filenames
|
return usable_filenames, variant_filenames
|
||||||
|
|
||||||
|
|
||||||
def warn_deprecated_model_variant(pretrained_model_name_or_path, use_auth_token, variant, revision, model_filenames):
|
@validate_hf_hub_args
|
||||||
|
def warn_deprecated_model_variant(pretrained_model_name_or_path, token, variant, revision, model_filenames):
|
||||||
info = model_info(
|
info = model_info(
|
||||||
pretrained_model_name_or_path,
|
pretrained_model_name_or_path,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=None,
|
revision=None,
|
||||||
)
|
)
|
||||||
filenames = {sibling.rfilename for sibling in info.siblings}
|
filenames = {sibling.rfilename for sibling in info.siblings}
|
||||||
@@ -375,7 +381,6 @@ def _get_pipeline_class(
|
|||||||
custom_pipeline,
|
custom_pipeline,
|
||||||
module_file=file_name,
|
module_file=file_name,
|
||||||
class_name=class_name,
|
class_name=class_name,
|
||||||
repo_id=repo_id,
|
|
||||||
cache_dir=cache_dir,
|
cache_dir=cache_dir,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
)
|
)
|
||||||
@@ -909,6 +914,7 @@ class DiffusionPipeline(ConfigMixin, PushToHubMixin):
|
|||||||
return torch.float32
|
return torch.float32
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@validate_hf_hub_args
|
||||||
def from_pretrained(cls, pretrained_model_name_or_path: Optional[Union[str, os.PathLike]], **kwargs):
|
def from_pretrained(cls, pretrained_model_name_or_path: Optional[Union[str, os.PathLike]], **kwargs):
|
||||||
r"""
|
r"""
|
||||||
Instantiate a PyTorch diffusion pipeline from pretrained pipeline weights.
|
Instantiate a PyTorch diffusion pipeline from pretrained pipeline weights.
|
||||||
@@ -976,7 +982,7 @@ class DiffusionPipeline(ConfigMixin, PushToHubMixin):
|
|||||||
local_files_only (`bool`, *optional*, defaults to `False`):
|
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
|
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.
|
won't be downloaded from the Hub.
|
||||||
use_auth_token (`str` or *bool*, *optional*):
|
token (`str` or *bool*, *optional*):
|
||||||
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
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.
|
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
||||||
revision (`str`, *optional*, defaults to `"main"`):
|
revision (`str`, *optional*, defaults to `"main"`):
|
||||||
@@ -1056,12 +1062,12 @@ class DiffusionPipeline(ConfigMixin, PushToHubMixin):
|
|||||||
>>> pipeline.scheduler = scheduler
|
>>> pipeline.scheduler = scheduler
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
force_download = kwargs.pop("force_download", False)
|
force_download = kwargs.pop("force_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", HF_HUB_OFFLINE)
|
local_files_only = kwargs.pop("local_files_only", None)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
from_flax = kwargs.pop("from_flax", False)
|
from_flax = kwargs.pop("from_flax", False)
|
||||||
torch_dtype = kwargs.pop("torch_dtype", None)
|
torch_dtype = kwargs.pop("torch_dtype", None)
|
||||||
@@ -1094,7 +1100,7 @@ class DiffusionPipeline(ConfigMixin, PushToHubMixin):
|
|||||||
force_download=force_download,
|
force_download=force_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
from_flax=from_flax,
|
from_flax=from_flax,
|
||||||
use_safetensors=use_safetensors,
|
use_safetensors=use_safetensors,
|
||||||
@@ -1299,7 +1305,7 @@ class DiffusionPipeline(ConfigMixin, PushToHubMixin):
|
|||||||
"force_download": force_download,
|
"force_download": force_download,
|
||||||
"proxies": proxies,
|
"proxies": proxies,
|
||||||
"local_files_only": local_files_only,
|
"local_files_only": local_files_only,
|
||||||
"use_auth_token": use_auth_token,
|
"token": token,
|
||||||
"revision": revision,
|
"revision": revision,
|
||||||
"torch_dtype": torch_dtype,
|
"torch_dtype": torch_dtype,
|
||||||
"custom_pipeline": custom_pipeline,
|
"custom_pipeline": custom_pipeline,
|
||||||
@@ -1529,6 +1535,7 @@ class DiffusionPipeline(ConfigMixin, PushToHubMixin):
|
|||||||
cpu_offload(model, device, offload_buffers=offload_buffers)
|
cpu_offload(model, device, offload_buffers=offload_buffers)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@validate_hf_hub_args
|
||||||
def download(cls, pretrained_model_name, **kwargs) -> Union[str, os.PathLike]:
|
def download(cls, pretrained_model_name, **kwargs) -> Union[str, os.PathLike]:
|
||||||
r"""
|
r"""
|
||||||
Download and cache a PyTorch diffusion pipeline from pretrained pipeline weights.
|
Download and cache a PyTorch diffusion pipeline from pretrained pipeline weights.
|
||||||
@@ -1576,7 +1583,7 @@ class DiffusionPipeline(ConfigMixin, PushToHubMixin):
|
|||||||
local_files_only (`bool`, *optional*, defaults to `False`):
|
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
|
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.
|
won't be downloaded from the Hub.
|
||||||
use_auth_token (`str` or *bool*, *optional*):
|
token (`str` or *bool*, *optional*):
|
||||||
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
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.
|
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
||||||
revision (`str`, *optional*, defaults to `"main"`):
|
revision (`str`, *optional*, defaults to `"main"`):
|
||||||
@@ -1619,12 +1626,12 @@ class DiffusionPipeline(ConfigMixin, PushToHubMixin):
|
|||||||
</Tip>
|
</Tip>
|
||||||
|
|
||||||
"""
|
"""
|
||||||
cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)
|
cache_dir = kwargs.pop("cache_dir", None)
|
||||||
resume_download = kwargs.pop("resume_download", False)
|
resume_download = kwargs.pop("resume_download", False)
|
||||||
force_download = kwargs.pop("force_download", False)
|
force_download = kwargs.pop("force_download", False)
|
||||||
proxies = kwargs.pop("proxies", None)
|
proxies = kwargs.pop("proxies", None)
|
||||||
local_files_only = kwargs.pop("local_files_only", HF_HUB_OFFLINE)
|
local_files_only = kwargs.pop("local_files_only", None)
|
||||||
use_auth_token = kwargs.pop("use_auth_token", None)
|
token = kwargs.pop("token", None)
|
||||||
revision = kwargs.pop("revision", None)
|
revision = kwargs.pop("revision", None)
|
||||||
from_flax = kwargs.pop("from_flax", False)
|
from_flax = kwargs.pop("from_flax", False)
|
||||||
custom_pipeline = kwargs.pop("custom_pipeline", None)
|
custom_pipeline = kwargs.pop("custom_pipeline", None)
|
||||||
@@ -1646,11 +1653,7 @@ class DiffusionPipeline(ConfigMixin, PushToHubMixin):
|
|||||||
model_info_call_error: Optional[Exception] = None
|
model_info_call_error: Optional[Exception] = None
|
||||||
if not local_files_only:
|
if not local_files_only:
|
||||||
try:
|
try:
|
||||||
info = model_info(
|
info = model_info(pretrained_model_name, token=token, revision=revision)
|
||||||
pretrained_model_name,
|
|
||||||
use_auth_token=use_auth_token,
|
|
||||||
revision=revision,
|
|
||||||
)
|
|
||||||
except HTTPError as e:
|
except HTTPError as e:
|
||||||
logger.warn(f"Couldn't connect to the Hub: {e}.\nWill try to load from local cache.")
|
logger.warn(f"Couldn't connect to the Hub: {e}.\nWill try to load from local cache.")
|
||||||
local_files_only = True
|
local_files_only = True
|
||||||
@@ -1665,7 +1668,7 @@ class DiffusionPipeline(ConfigMixin, PushToHubMixin):
|
|||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
force_download=force_download,
|
force_download=force_download,
|
||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
)
|
)
|
||||||
|
|
||||||
config_dict = cls._dict_from_json_file(config_file)
|
config_dict = cls._dict_from_json_file(config_file)
|
||||||
@@ -1715,9 +1718,7 @@ class DiffusionPipeline(ConfigMixin, PushToHubMixin):
|
|||||||
if revision in DEPRECATED_REVISION_ARGS and version.parse(
|
if revision in DEPRECATED_REVISION_ARGS and version.parse(
|
||||||
version.parse(__version__).base_version
|
version.parse(__version__).base_version
|
||||||
) >= version.parse("0.22.0"):
|
) >= version.parse("0.22.0"):
|
||||||
warn_deprecated_model_variant(
|
warn_deprecated_model_variant(pretrained_model_name, token, variant, revision, model_filenames)
|
||||||
pretrained_model_name, use_auth_token, variant, revision, model_filenames
|
|
||||||
)
|
|
||||||
|
|
||||||
model_folder_names = {os.path.split(f)[0] for f in model_filenames if os.path.split(f)[0] in folder_names}
|
model_folder_names = {os.path.split(f)[0] for f in model_filenames if os.path.split(f)[0] in folder_names}
|
||||||
|
|
||||||
@@ -1859,7 +1860,7 @@ class DiffusionPipeline(ConfigMixin, PushToHubMixin):
|
|||||||
resume_download=resume_download,
|
resume_download=resume_download,
|
||||||
proxies=proxies,
|
proxies=proxies,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
use_auth_token=use_auth_token,
|
token=token,
|
||||||
revision=revision,
|
revision=revision,
|
||||||
allow_patterns=allow_patterns,
|
allow_patterns=allow_patterns,
|
||||||
ignore_patterns=ignore_patterns,
|
ignore_patterns=ignore_patterns,
|
||||||
@@ -1883,7 +1884,7 @@ class DiffusionPipeline(ConfigMixin, PushToHubMixin):
|
|||||||
"force_download": force_download,
|
"force_download": force_download,
|
||||||
"proxies": proxies,
|
"proxies": proxies,
|
||||||
"local_files_only": local_files_only,
|
"local_files_only": local_files_only,
|
||||||
"use_auth_token": use_auth_token,
|
"token": token,
|
||||||
"variant": variant,
|
"variant": variant,
|
||||||
"use_safetensors": use_safetensors,
|
"use_safetensors": use_safetensors,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ if is_onnx_available():
|
|||||||
from ..onnx_utils import OnnxRuntimeModel
|
from ..onnx_utils import OnnxRuntimeModel
|
||||||
|
|
||||||
from ..pipeline_utils import AudioPipelineOutput, DiffusionPipeline
|
from ..pipeline_utils import AudioPipelineOutput, DiffusionPipeline
|
||||||
from .continous_encoder import SpectrogramContEncoder
|
from .continuous_encoder import SpectrogramContEncoder
|
||||||
from .notes_encoder import SpectrogramNotesEncoder
|
from .notes_encoder import SpectrogramNotesEncoder
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -143,6 +143,11 @@ class CycleDiffusionPipeline(DiffusionPipeline, TextualInversionLoaderMixin, Lor
|
|||||||
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
|
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
|
||||||
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
|
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
|
||||||
|
|
||||||
|
The pipeline also inherits the following loading methods:
|
||||||
|
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
||||||
|
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
|
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
Variational Auto-Encoder (VAE) model to encode and decode images to and from latent representations.
|
Variational Auto-Encoder (VAE) model to encode and decode images to and from latent representations.
|
||||||
|
|||||||
+3
@@ -177,6 +177,9 @@ class StableDiffusionAttendAndExcitePipeline(DiffusionPipeline, TextualInversion
|
|||||||
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
|
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
|
||||||
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
|
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
|
||||||
|
|
||||||
|
The pipeline also inherits the following loading methods:
|
||||||
|
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
Variational Auto-Encoder (VAE) model to encode and decode images to and from latent representations.
|
Variational Auto-Encoder (VAE) model to encode and decode images to and from latent representations.
|
||||||
|
|||||||
@@ -232,6 +232,7 @@ class StableDiffusionInpaintPipeline(
|
|||||||
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
|
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
|
||||||
|
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`, `AsymmetricAutoencoderKL`]):
|
vae ([`AutoencoderKL`, `AsymmetricAutoencoderKL`]):
|
||||||
|
|||||||
+57
-7
@@ -18,11 +18,11 @@ from typing import Callable, Dict, List, Optional, Union
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import PIL.Image
|
import PIL.Image
|
||||||
import torch
|
import torch
|
||||||
from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer
|
from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer, CLIPVisionModelWithProjection
|
||||||
|
|
||||||
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
||||||
from ...loaders import LoraLoaderMixin, TextualInversionLoaderMixin
|
from ...loaders import IPAdapterMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
||||||
from ...models import AutoencoderKL, UNet2DConditionModel
|
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
||||||
from ...schedulers import KarrasDiffusionSchedulers
|
from ...schedulers import KarrasDiffusionSchedulers
|
||||||
from ...utils import PIL_INTERPOLATION, deprecate, logging
|
from ...utils import PIL_INTERPOLATION, deprecate, logging
|
||||||
from ...utils.torch_utils import randn_tensor
|
from ...utils.torch_utils import randn_tensor
|
||||||
@@ -72,7 +72,9 @@ def retrieve_latents(
|
|||||||
raise AttributeError("Could not access latents of provided encoder_output")
|
raise AttributeError("Could not access latents of provided encoder_output")
|
||||||
|
|
||||||
|
|
||||||
class StableDiffusionInstructPix2PixPipeline(DiffusionPipeline, TextualInversionLoaderMixin, LoraLoaderMixin):
|
class StableDiffusionInstructPix2PixPipeline(
|
||||||
|
DiffusionPipeline, TextualInversionLoaderMixin, LoraLoaderMixin, IPAdapterMixin
|
||||||
|
):
|
||||||
r"""
|
r"""
|
||||||
Pipeline for pixel-level image editing by following text instructions (based on Stable Diffusion).
|
Pipeline for pixel-level image editing by following text instructions (based on Stable Diffusion).
|
||||||
|
|
||||||
@@ -83,6 +85,7 @@ class StableDiffusionInstructPix2PixPipeline(DiffusionPipeline, TextualInversion
|
|||||||
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
||||||
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
|
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
@@ -105,7 +108,7 @@ class StableDiffusionInstructPix2PixPipeline(DiffusionPipeline, TextualInversion
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
model_cpu_offload_seq = "text_encoder->unet->vae"
|
model_cpu_offload_seq = "text_encoder->unet->vae"
|
||||||
_optional_components = ["safety_checker", "feature_extractor"]
|
_optional_components = ["safety_checker", "feature_extractor", "image_encoder"]
|
||||||
_exclude_from_cpu_offload = ["safety_checker"]
|
_exclude_from_cpu_offload = ["safety_checker"]
|
||||||
_callback_tensor_inputs = ["latents", "prompt_embeds", "image_latents"]
|
_callback_tensor_inputs = ["latents", "prompt_embeds", "image_latents"]
|
||||||
|
|
||||||
@@ -118,6 +121,7 @@ class StableDiffusionInstructPix2PixPipeline(DiffusionPipeline, TextualInversion
|
|||||||
scheduler: KarrasDiffusionSchedulers,
|
scheduler: KarrasDiffusionSchedulers,
|
||||||
safety_checker: StableDiffusionSafetyChecker,
|
safety_checker: StableDiffusionSafetyChecker,
|
||||||
feature_extractor: CLIPImageProcessor,
|
feature_extractor: CLIPImageProcessor,
|
||||||
|
image_encoder: Optional[CLIPVisionModelWithProjection] = None,
|
||||||
requires_safety_checker: bool = True,
|
requires_safety_checker: bool = True,
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -146,6 +150,7 @@ class StableDiffusionInstructPix2PixPipeline(DiffusionPipeline, TextualInversion
|
|||||||
scheduler=scheduler,
|
scheduler=scheduler,
|
||||||
safety_checker=safety_checker,
|
safety_checker=safety_checker,
|
||||||
feature_extractor=feature_extractor,
|
feature_extractor=feature_extractor,
|
||||||
|
image_encoder=image_encoder,
|
||||||
)
|
)
|
||||||
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
|
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
|
||||||
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor)
|
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor)
|
||||||
@@ -166,6 +171,7 @@ class StableDiffusionInstructPix2PixPipeline(DiffusionPipeline, TextualInversion
|
|||||||
latents: Optional[torch.FloatTensor] = None,
|
latents: Optional[torch.FloatTensor] = None,
|
||||||
prompt_embeds: Optional[torch.FloatTensor] = None,
|
prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
negative_prompt_embeds: Optional[torch.FloatTensor] = None,
|
negative_prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
|
ip_adapter_image: Optional[PipelineImageInput] = None,
|
||||||
output_type: Optional[str] = "pil",
|
output_type: Optional[str] = "pil",
|
||||||
return_dict: bool = True,
|
return_dict: bool = True,
|
||||||
callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
|
callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
|
||||||
@@ -213,6 +219,8 @@ class StableDiffusionInstructPix2PixPipeline(DiffusionPipeline, TextualInversion
|
|||||||
negative_prompt_embeds (`torch.FloatTensor`, *optional*):
|
negative_prompt_embeds (`torch.FloatTensor`, *optional*):
|
||||||
Pre-generated negative text embeddings. Can be used to easily tweak text inputs (prompt weighting). If
|
Pre-generated negative text embeddings. Can be used to easily tweak text inputs (prompt weighting). If
|
||||||
not provided, `negative_prompt_embeds` are generated from the `negative_prompt` input argument.
|
not provided, `negative_prompt_embeds` are generated from the `negative_prompt` input argument.
|
||||||
|
ip_adapter_image: (`PipelineImageInput`, *optional*):
|
||||||
|
Optional image input to work with IP Adapters.
|
||||||
output_type (`str`, *optional*, defaults to `"pil"`):
|
output_type (`str`, *optional*, defaults to `"pil"`):
|
||||||
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
||||||
return_dict (`bool`, *optional*, defaults to `True`):
|
return_dict (`bool`, *optional*, defaults to `True`):
|
||||||
@@ -293,6 +301,16 @@ class StableDiffusionInstructPix2PixPipeline(DiffusionPipeline, TextualInversion
|
|||||||
self._guidance_scale = guidance_scale
|
self._guidance_scale = guidance_scale
|
||||||
self._image_guidance_scale = image_guidance_scale
|
self._image_guidance_scale = image_guidance_scale
|
||||||
|
|
||||||
|
device = self._execution_device
|
||||||
|
|
||||||
|
if ip_adapter_image is not None:
|
||||||
|
output_hidden_state = False if isinstance(self.unet.encoder_hid_proj, ImageProjection) else True
|
||||||
|
image_embeds, negative_image_embeds = self.encode_image(
|
||||||
|
ip_adapter_image, device, num_images_per_prompt, output_hidden_state
|
||||||
|
)
|
||||||
|
if self.do_classifier_free_guidance:
|
||||||
|
image_embeds = torch.cat([image_embeds, negative_image_embeds, negative_image_embeds])
|
||||||
|
|
||||||
if image is None:
|
if image is None:
|
||||||
raise ValueError("`image` input cannot be undefined.")
|
raise ValueError("`image` input cannot be undefined.")
|
||||||
|
|
||||||
@@ -367,6 +385,9 @@ class StableDiffusionInstructPix2PixPipeline(DiffusionPipeline, TextualInversion
|
|||||||
# 8. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline
|
# 8. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline
|
||||||
extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
|
extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
|
||||||
|
|
||||||
|
# 8.1 Add image embeds for IP-Adapter
|
||||||
|
added_cond_kwargs = {"image_embeds": image_embeds} if ip_adapter_image is not None else None
|
||||||
|
|
||||||
# 9. Denoising loop
|
# 9. Denoising loop
|
||||||
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
|
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
|
||||||
self._num_timesteps = len(timesteps)
|
self._num_timesteps = len(timesteps)
|
||||||
@@ -383,7 +404,11 @@ class StableDiffusionInstructPix2PixPipeline(DiffusionPipeline, TextualInversion
|
|||||||
|
|
||||||
# predict the noise residual
|
# predict the noise residual
|
||||||
noise_pred = self.unet(
|
noise_pred = self.unet(
|
||||||
scaled_latent_model_input, t, encoder_hidden_states=prompt_embeds, return_dict=False
|
scaled_latent_model_input,
|
||||||
|
t,
|
||||||
|
encoder_hidden_states=prompt_embeds,
|
||||||
|
added_cond_kwargs=added_cond_kwargs,
|
||||||
|
return_dict=False,
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
# Hack:
|
# Hack:
|
||||||
@@ -598,11 +623,36 @@ class StableDiffusionInstructPix2PixPipeline(DiffusionPipeline, TextualInversion
|
|||||||
# For classifier free guidance, we need to do two forward passes.
|
# For classifier free guidance, we need to do two forward passes.
|
||||||
# Here we concatenate the unconditional and text embeddings into a single batch
|
# Here we concatenate the unconditional and text embeddings into a single batch
|
||||||
# to avoid doing two forward passes
|
# to avoid doing two forward passes
|
||||||
# pix2pix has two negative embeddings, and unlike in other pipelines latents are ordered [prompt_embeds, negative_prompt_embeds, negative_prompt_embeds]
|
# pix2pix has two negative embeddings, and unlike in other pipelines latents are ordered [prompt_embeds, negative_prompt_embeds, negative_prompt_embeds]
|
||||||
prompt_embeds = torch.cat([prompt_embeds, negative_prompt_embeds, negative_prompt_embeds])
|
prompt_embeds = torch.cat([prompt_embeds, negative_prompt_embeds, negative_prompt_embeds])
|
||||||
|
|
||||||
return prompt_embeds
|
return prompt_embeds
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.encode_image
|
||||||
|
def encode_image(self, image, device, num_images_per_prompt, output_hidden_states=None):
|
||||||
|
dtype = next(self.image_encoder.parameters()).dtype
|
||||||
|
|
||||||
|
if not isinstance(image, torch.Tensor):
|
||||||
|
image = self.feature_extractor(image, return_tensors="pt").pixel_values
|
||||||
|
|
||||||
|
image = image.to(device=device, dtype=dtype)
|
||||||
|
if output_hidden_states:
|
||||||
|
image_enc_hidden_states = self.image_encoder(image, output_hidden_states=True).hidden_states[-2]
|
||||||
|
image_enc_hidden_states = image_enc_hidden_states.repeat_interleave(num_images_per_prompt, dim=0)
|
||||||
|
uncond_image_enc_hidden_states = self.image_encoder(
|
||||||
|
torch.zeros_like(image), output_hidden_states=True
|
||||||
|
).hidden_states[-2]
|
||||||
|
uncond_image_enc_hidden_states = uncond_image_enc_hidden_states.repeat_interleave(
|
||||||
|
num_images_per_prompt, dim=0
|
||||||
|
)
|
||||||
|
return image_enc_hidden_states, uncond_image_enc_hidden_states
|
||||||
|
else:
|
||||||
|
image_embeds = self.image_encoder(image).image_embeds
|
||||||
|
image_embeds = image_embeds.repeat_interleave(num_images_per_prompt, dim=0)
|
||||||
|
uncond_image_embeds = torch.zeros_like(image_embeds)
|
||||||
|
|
||||||
|
return image_embeds, uncond_image_embeds
|
||||||
|
|
||||||
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.run_safety_checker
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.run_safety_checker
|
||||||
def run_safety_checker(self, image, device, dtype):
|
def run_safety_checker(self, image, device, dtype):
|
||||||
if self.safety_checker is None:
|
if self.safety_checker is None:
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ class StableDiffusionKDiffusionPipeline(DiffusionPipeline, TextualInversionLoade
|
|||||||
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods the
|
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods the
|
||||||
library implements for all the pipelines (such as downloading or saving, running on a particular device, etc.)
|
library implements for all the pipelines (such as downloading or saving, running on a particular device, etc.)
|
||||||
|
|
||||||
|
The pipeline also inherits the following loading methods:
|
||||||
|
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
||||||
|
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
|
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
|
|
||||||
<Tip warning={true}>
|
<Tip warning={true}>
|
||||||
|
|
||||||
This is an experimental pipeline and is likely to change in the future.
|
This is an experimental pipeline and is likely to change in the future.
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ class StableDiffusionLatentUpscalePipeline(DiffusionPipeline, FromSingleFileMixi
|
|||||||
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
|
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
|
||||||
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
|
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
|
||||||
|
|
||||||
|
The pipeline also inherits the following loading methods:
|
||||||
|
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
Variational Auto-Encoder (VAE) model to encode and decode images to and from latent representations.
|
Variational Auto-Encoder (VAE) model to encode and decode images to and from latent representations.
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ from typing import Any, Callable, Dict, List, Optional, Union
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import PIL.Image
|
import PIL.Image
|
||||||
import torch
|
import torch
|
||||||
from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer
|
from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer, CLIPVisionModelWithProjection
|
||||||
|
|
||||||
from ...image_processor import VaeImageProcessorLDM3D
|
from ...image_processor import PipelineImageInput, VaeImageProcessorLDM3D
|
||||||
from ...loaders import FromSingleFileMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
from ...loaders import FromSingleFileMixin, IPAdapterMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
||||||
from ...models import AutoencoderKL, UNet2DConditionModel
|
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
||||||
from ...models.lora import adjust_lora_scale_text_encoder
|
from ...models.lora import adjust_lora_scale_text_encoder
|
||||||
from ...schedulers import KarrasDiffusionSchedulers
|
from ...schedulers import KarrasDiffusionSchedulers
|
||||||
from ...utils import (
|
from ...utils import (
|
||||||
@@ -82,7 +82,7 @@ class LDM3DPipelineOutput(BaseOutput):
|
|||||||
|
|
||||||
|
|
||||||
class StableDiffusionLDM3DPipeline(
|
class StableDiffusionLDM3DPipeline(
|
||||||
DiffusionPipeline, TextualInversionLoaderMixin, LoraLoaderMixin, FromSingleFileMixin
|
DiffusionPipeline, TextualInversionLoaderMixin, IPAdapterMixin, LoraLoaderMixin, FromSingleFileMixin
|
||||||
):
|
):
|
||||||
r"""
|
r"""
|
||||||
Pipeline for text-to-image and 3D generation using LDM3D.
|
Pipeline for text-to-image and 3D generation using LDM3D.
|
||||||
@@ -95,6 +95,7 @@ class StableDiffusionLDM3DPipeline(
|
|||||||
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
||||||
|
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
@@ -117,7 +118,7 @@ class StableDiffusionLDM3DPipeline(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
model_cpu_offload_seq = "text_encoder->unet->vae"
|
model_cpu_offload_seq = "text_encoder->unet->vae"
|
||||||
_optional_components = ["safety_checker", "feature_extractor"]
|
_optional_components = ["safety_checker", "feature_extractor", "image_encoder"]
|
||||||
_exclude_from_cpu_offload = ["safety_checker"]
|
_exclude_from_cpu_offload = ["safety_checker"]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -129,6 +130,7 @@ class StableDiffusionLDM3DPipeline(
|
|||||||
scheduler: KarrasDiffusionSchedulers,
|
scheduler: KarrasDiffusionSchedulers,
|
||||||
safety_checker: StableDiffusionSafetyChecker,
|
safety_checker: StableDiffusionSafetyChecker,
|
||||||
feature_extractor: CLIPImageProcessor,
|
feature_extractor: CLIPImageProcessor,
|
||||||
|
image_encoder: Optional[CLIPVisionModelWithProjection],
|
||||||
requires_safety_checker: bool = True,
|
requires_safety_checker: bool = True,
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -157,6 +159,7 @@ class StableDiffusionLDM3DPipeline(
|
|||||||
scheduler=scheduler,
|
scheduler=scheduler,
|
||||||
safety_checker=safety_checker,
|
safety_checker=safety_checker,
|
||||||
feature_extractor=feature_extractor,
|
feature_extractor=feature_extractor,
|
||||||
|
image_encoder=image_encoder,
|
||||||
)
|
)
|
||||||
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
|
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
|
||||||
self.image_processor = VaeImageProcessorLDM3D(vae_scale_factor=self.vae_scale_factor)
|
self.image_processor = VaeImageProcessorLDM3D(vae_scale_factor=self.vae_scale_factor)
|
||||||
@@ -410,6 +413,31 @@ class StableDiffusionLDM3DPipeline(
|
|||||||
|
|
||||||
return prompt_embeds, negative_prompt_embeds
|
return prompt_embeds, negative_prompt_embeds
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.encode_image
|
||||||
|
def encode_image(self, image, device, num_images_per_prompt, output_hidden_states=None):
|
||||||
|
dtype = next(self.image_encoder.parameters()).dtype
|
||||||
|
|
||||||
|
if not isinstance(image, torch.Tensor):
|
||||||
|
image = self.feature_extractor(image, return_tensors="pt").pixel_values
|
||||||
|
|
||||||
|
image = image.to(device=device, dtype=dtype)
|
||||||
|
if output_hidden_states:
|
||||||
|
image_enc_hidden_states = self.image_encoder(image, output_hidden_states=True).hidden_states[-2]
|
||||||
|
image_enc_hidden_states = image_enc_hidden_states.repeat_interleave(num_images_per_prompt, dim=0)
|
||||||
|
uncond_image_enc_hidden_states = self.image_encoder(
|
||||||
|
torch.zeros_like(image), output_hidden_states=True
|
||||||
|
).hidden_states[-2]
|
||||||
|
uncond_image_enc_hidden_states = uncond_image_enc_hidden_states.repeat_interleave(
|
||||||
|
num_images_per_prompt, dim=0
|
||||||
|
)
|
||||||
|
return image_enc_hidden_states, uncond_image_enc_hidden_states
|
||||||
|
else:
|
||||||
|
image_embeds = self.image_encoder(image).image_embeds
|
||||||
|
image_embeds = image_embeds.repeat_interleave(num_images_per_prompt, dim=0)
|
||||||
|
uncond_image_embeds = torch.zeros_like(image_embeds)
|
||||||
|
|
||||||
|
return image_embeds, uncond_image_embeds
|
||||||
|
|
||||||
def run_safety_checker(self, image, device, dtype):
|
def run_safety_checker(self, image, device, dtype):
|
||||||
if self.safety_checker is None:
|
if self.safety_checker is None:
|
||||||
has_nsfw_concept = None
|
has_nsfw_concept = None
|
||||||
@@ -529,6 +557,7 @@ class StableDiffusionLDM3DPipeline(
|
|||||||
latents: Optional[torch.FloatTensor] = None,
|
latents: Optional[torch.FloatTensor] = None,
|
||||||
prompt_embeds: Optional[torch.FloatTensor] = None,
|
prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
negative_prompt_embeds: Optional[torch.FloatTensor] = None,
|
negative_prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
|
ip_adapter_image: Optional[PipelineImageInput] = None,
|
||||||
output_type: Optional[str] = "pil",
|
output_type: Optional[str] = "pil",
|
||||||
return_dict: bool = True,
|
return_dict: bool = True,
|
||||||
callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None,
|
callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None,
|
||||||
@@ -573,6 +602,8 @@ class StableDiffusionLDM3DPipeline(
|
|||||||
negative_prompt_embeds (`torch.FloatTensor`, *optional*):
|
negative_prompt_embeds (`torch.FloatTensor`, *optional*):
|
||||||
Pre-generated negative text embeddings. Can be used to easily tweak text inputs (prompt weighting). If
|
Pre-generated negative text embeddings. Can be used to easily tweak text inputs (prompt weighting). If
|
||||||
not provided, `negative_prompt_embeds` are generated from the `negative_prompt` input argument.
|
not provided, `negative_prompt_embeds` are generated from the `negative_prompt` input argument.
|
||||||
|
ip_adapter_image: (`PipelineImageInput`, *optional*):
|
||||||
|
Optional image input to work with IP Adapters.
|
||||||
output_type (`str`, *optional*, defaults to `"pil"`):
|
output_type (`str`, *optional*, defaults to `"pil"`):
|
||||||
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
||||||
return_dict (`bool`, *optional*, defaults to `True`):
|
return_dict (`bool`, *optional*, defaults to `True`):
|
||||||
@@ -622,6 +653,14 @@ class StableDiffusionLDM3DPipeline(
|
|||||||
# corresponds to doing no classifier free guidance.
|
# corresponds to doing no classifier free guidance.
|
||||||
do_classifier_free_guidance = guidance_scale > 1.0
|
do_classifier_free_guidance = guidance_scale > 1.0
|
||||||
|
|
||||||
|
if ip_adapter_image is not None:
|
||||||
|
output_hidden_state = False if isinstance(self.unet.encoder_hid_proj, ImageProjection) else True
|
||||||
|
image_embeds, negative_image_embeds = self.encode_image(
|
||||||
|
ip_adapter_image, device, num_images_per_prompt, output_hidden_state
|
||||||
|
)
|
||||||
|
if do_classifier_free_guidance:
|
||||||
|
image_embeds = torch.cat([negative_image_embeds, image_embeds])
|
||||||
|
|
||||||
# 3. Encode input prompt
|
# 3. Encode input prompt
|
||||||
prompt_embeds, negative_prompt_embeds = self.encode_prompt(
|
prompt_embeds, negative_prompt_embeds = self.encode_prompt(
|
||||||
prompt,
|
prompt,
|
||||||
@@ -659,6 +698,9 @@ class StableDiffusionLDM3DPipeline(
|
|||||||
# 6. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline
|
# 6. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline
|
||||||
extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
|
extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
|
||||||
|
|
||||||
|
# 6.1 Add image embeds for IP-Adapter
|
||||||
|
added_cond_kwargs = {"image_embeds": image_embeds} if ip_adapter_image is not None else None
|
||||||
|
|
||||||
# 7. Denoising loop
|
# 7. Denoising loop
|
||||||
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
|
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
|
||||||
with self.progress_bar(total=num_inference_steps) as progress_bar:
|
with self.progress_bar(total=num_inference_steps) as progress_bar:
|
||||||
@@ -673,6 +715,7 @@ class StableDiffusionLDM3DPipeline(
|
|||||||
t,
|
t,
|
||||||
encoder_hidden_states=prompt_embeds,
|
encoder_hidden_states=prompt_embeds,
|
||||||
cross_attention_kwargs=cross_attention_kwargs,
|
cross_attention_kwargs=cross_attention_kwargs,
|
||||||
|
added_cond_kwargs=added_cond_kwargs,
|
||||||
return_dict=False,
|
return_dict=False,
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ class StableDiffusionModelEditingPipeline(DiffusionPipeline, TextualInversionLoa
|
|||||||
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
|
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
|
||||||
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
|
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
|
||||||
|
|
||||||
|
The pipeline also inherits the following loading methods:
|
||||||
|
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
||||||
|
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
|
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
Variational Auto-Encoder (VAE) model to encode and decode images to and from latent representations.
|
Variational Auto-Encoder (VAE) model to encode and decode images to and from latent representations.
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ import inspect
|
|||||||
from typing import Any, Callable, Dict, List, Optional, Union
|
from typing import Any, Callable, Dict, List, Optional, Union
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer
|
from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer, CLIPVisionModelWithProjection
|
||||||
|
|
||||||
from ...image_processor import VaeImageProcessor
|
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
||||||
from ...loaders import LoraLoaderMixin, TextualInversionLoaderMixin
|
from ...loaders import IPAdapterMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
||||||
from ...models import AutoencoderKL, UNet2DConditionModel
|
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
||||||
from ...models.lora import adjust_lora_scale_text_encoder
|
from ...models.lora import adjust_lora_scale_text_encoder
|
||||||
from ...schedulers import DDIMScheduler
|
from ...schedulers import DDIMScheduler
|
||||||
from ...utils import (
|
from ...utils import (
|
||||||
@@ -59,13 +59,19 @@ EXAMPLE_DOC_STRING = """
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class StableDiffusionPanoramaPipeline(DiffusionPipeline, TextualInversionLoaderMixin, LoraLoaderMixin):
|
class StableDiffusionPanoramaPipeline(DiffusionPipeline, TextualInversionLoaderMixin, LoraLoaderMixin, IPAdapterMixin):
|
||||||
r"""
|
r"""
|
||||||
Pipeline for text-to-image generation using MultiDiffusion.
|
Pipeline for text-to-image generation using MultiDiffusion.
|
||||||
|
|
||||||
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
|
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
|
||||||
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
|
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
|
||||||
|
|
||||||
|
The pipeline also inherits the following loading methods:
|
||||||
|
- [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
||||||
|
- [`~loaders.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
|
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
|
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
Variational Auto-Encoder (VAE) model to encode and decode images to and from latent representations.
|
Variational Auto-Encoder (VAE) model to encode and decode images to and from latent representations.
|
||||||
@@ -87,7 +93,7 @@ class StableDiffusionPanoramaPipeline(DiffusionPipeline, TextualInversionLoaderM
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
model_cpu_offload_seq = "text_encoder->unet->vae"
|
model_cpu_offload_seq = "text_encoder->unet->vae"
|
||||||
_optional_components = ["safety_checker", "feature_extractor"]
|
_optional_components = ["safety_checker", "feature_extractor", "image_encoder"]
|
||||||
_exclude_from_cpu_offload = ["safety_checker"]
|
_exclude_from_cpu_offload = ["safety_checker"]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -99,6 +105,7 @@ class StableDiffusionPanoramaPipeline(DiffusionPipeline, TextualInversionLoaderM
|
|||||||
scheduler: DDIMScheduler,
|
scheduler: DDIMScheduler,
|
||||||
safety_checker: StableDiffusionSafetyChecker,
|
safety_checker: StableDiffusionSafetyChecker,
|
||||||
feature_extractor: CLIPImageProcessor,
|
feature_extractor: CLIPImageProcessor,
|
||||||
|
image_encoder: Optional[CLIPVisionModelWithProjection] = None,
|
||||||
requires_safety_checker: bool = True,
|
requires_safety_checker: bool = True,
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -127,6 +134,7 @@ class StableDiffusionPanoramaPipeline(DiffusionPipeline, TextualInversionLoaderM
|
|||||||
scheduler=scheduler,
|
scheduler=scheduler,
|
||||||
safety_checker=safety_checker,
|
safety_checker=safety_checker,
|
||||||
feature_extractor=feature_extractor,
|
feature_extractor=feature_extractor,
|
||||||
|
image_encoder=image_encoder,
|
||||||
)
|
)
|
||||||
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
|
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
|
||||||
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor)
|
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor)
|
||||||
@@ -363,6 +371,31 @@ class StableDiffusionPanoramaPipeline(DiffusionPipeline, TextualInversionLoaderM
|
|||||||
|
|
||||||
return prompt_embeds, negative_prompt_embeds
|
return prompt_embeds, negative_prompt_embeds
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.encode_image
|
||||||
|
def encode_image(self, image, device, num_images_per_prompt, output_hidden_states=None):
|
||||||
|
dtype = next(self.image_encoder.parameters()).dtype
|
||||||
|
|
||||||
|
if not isinstance(image, torch.Tensor):
|
||||||
|
image = self.feature_extractor(image, return_tensors="pt").pixel_values
|
||||||
|
|
||||||
|
image = image.to(device=device, dtype=dtype)
|
||||||
|
if output_hidden_states:
|
||||||
|
image_enc_hidden_states = self.image_encoder(image, output_hidden_states=True).hidden_states[-2]
|
||||||
|
image_enc_hidden_states = image_enc_hidden_states.repeat_interleave(num_images_per_prompt, dim=0)
|
||||||
|
uncond_image_enc_hidden_states = self.image_encoder(
|
||||||
|
torch.zeros_like(image), output_hidden_states=True
|
||||||
|
).hidden_states[-2]
|
||||||
|
uncond_image_enc_hidden_states = uncond_image_enc_hidden_states.repeat_interleave(
|
||||||
|
num_images_per_prompt, dim=0
|
||||||
|
)
|
||||||
|
return image_enc_hidden_states, uncond_image_enc_hidden_states
|
||||||
|
else:
|
||||||
|
image_embeds = self.image_encoder(image).image_embeds
|
||||||
|
image_embeds = image_embeds.repeat_interleave(num_images_per_prompt, dim=0)
|
||||||
|
uncond_image_embeds = torch.zeros_like(image_embeds)
|
||||||
|
|
||||||
|
return image_embeds, uncond_image_embeds
|
||||||
|
|
||||||
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.run_safety_checker
|
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.run_safety_checker
|
||||||
def run_safety_checker(self, image, device, dtype):
|
def run_safety_checker(self, image, device, dtype):
|
||||||
if self.safety_checker is None:
|
if self.safety_checker is None:
|
||||||
@@ -529,6 +562,7 @@ class StableDiffusionPanoramaPipeline(DiffusionPipeline, TextualInversionLoaderM
|
|||||||
latents: Optional[torch.FloatTensor] = None,
|
latents: Optional[torch.FloatTensor] = None,
|
||||||
prompt_embeds: Optional[torch.FloatTensor] = None,
|
prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
negative_prompt_embeds: Optional[torch.FloatTensor] = None,
|
negative_prompt_embeds: Optional[torch.FloatTensor] = None,
|
||||||
|
ip_adapter_image: Optional[PipelineImageInput] = None,
|
||||||
output_type: Optional[str] = "pil",
|
output_type: Optional[str] = "pil",
|
||||||
return_dict: bool = True,
|
return_dict: bool = True,
|
||||||
callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None,
|
callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None,
|
||||||
@@ -578,6 +612,8 @@ class StableDiffusionPanoramaPipeline(DiffusionPipeline, TextualInversionLoaderM
|
|||||||
negative_prompt_embeds (`torch.FloatTensor`, *optional*):
|
negative_prompt_embeds (`torch.FloatTensor`, *optional*):
|
||||||
Pre-generated negative text embeddings. Can be used to easily tweak text inputs (prompt weighting). If
|
Pre-generated negative text embeddings. Can be used to easily tweak text inputs (prompt weighting). If
|
||||||
not provided, `negative_prompt_embeds` are generated from the `negative_prompt` input argument.
|
not provided, `negative_prompt_embeds` are generated from the `negative_prompt` input argument.
|
||||||
|
ip_adapter_image: (`PipelineImageInput`, *optional*):
|
||||||
|
Optional image input to work with IP Adapters.
|
||||||
output_type (`str`, *optional*, defaults to `"pil"`):
|
output_type (`str`, *optional*, defaults to `"pil"`):
|
||||||
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
||||||
return_dict (`bool`, *optional*, defaults to `True`):
|
return_dict (`bool`, *optional*, defaults to `True`):
|
||||||
@@ -632,6 +668,14 @@ class StableDiffusionPanoramaPipeline(DiffusionPipeline, TextualInversionLoaderM
|
|||||||
# corresponds to doing no classifier free guidance.
|
# corresponds to doing no classifier free guidance.
|
||||||
do_classifier_free_guidance = guidance_scale > 1.0
|
do_classifier_free_guidance = guidance_scale > 1.0
|
||||||
|
|
||||||
|
if ip_adapter_image is not None:
|
||||||
|
output_hidden_state = False if isinstance(self.unet.encoder_hid_proj, ImageProjection) else True
|
||||||
|
image_embeds, negative_image_embeds = self.encode_image(
|
||||||
|
ip_adapter_image, device, num_images_per_prompt, output_hidden_state
|
||||||
|
)
|
||||||
|
if do_classifier_free_guidance:
|
||||||
|
image_embeds = torch.cat([negative_image_embeds, image_embeds])
|
||||||
|
|
||||||
# 3. Encode input prompt
|
# 3. Encode input prompt
|
||||||
text_encoder_lora_scale = (
|
text_encoder_lora_scale = (
|
||||||
cross_attention_kwargs.get("scale", None) if cross_attention_kwargs is not None else None
|
cross_attention_kwargs.get("scale", None) if cross_attention_kwargs is not None else None
|
||||||
@@ -681,6 +725,9 @@ class StableDiffusionPanoramaPipeline(DiffusionPipeline, TextualInversionLoaderM
|
|||||||
# 7. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline
|
# 7. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline
|
||||||
extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
|
extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
|
||||||
|
|
||||||
|
# 7.1 Add image embeds for IP-Adapter
|
||||||
|
added_cond_kwargs = {"image_embeds": image_embeds} if ip_adapter_image is not None else None
|
||||||
|
|
||||||
# 8. Denoising loop
|
# 8. Denoising loop
|
||||||
# Each denoising step also includes refinement of the latents with respect to the
|
# Each denoising step also includes refinement of the latents with respect to the
|
||||||
# views.
|
# views.
|
||||||
@@ -743,6 +790,7 @@ class StableDiffusionPanoramaPipeline(DiffusionPipeline, TextualInversionLoaderM
|
|||||||
t,
|
t,
|
||||||
encoder_hidden_states=prompt_embeds_input,
|
encoder_hidden_states=prompt_embeds_input,
|
||||||
cross_attention_kwargs=cross_attention_kwargs,
|
cross_attention_kwargs=cross_attention_kwargs,
|
||||||
|
added_cond_kwargs=added_cond_kwargs,
|
||||||
).sample
|
).sample
|
||||||
|
|
||||||
# perform guidance
|
# perform guidance
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ class Pix2PixZeroAttnProcessor:
|
|||||||
|
|
||||||
class StableDiffusionPix2PixZeroPipeline(DiffusionPipeline):
|
class StableDiffusionPix2PixZeroPipeline(DiffusionPipeline):
|
||||||
r"""
|
r"""
|
||||||
Pipeline for pixel-levl image editing using Pix2Pix Zero. Based on Stable Diffusion.
|
Pipeline for pixel-level image editing using Pix2Pix Zero. Based on Stable Diffusion.
|
||||||
|
|
||||||
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods the
|
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods the
|
||||||
library implements for all the pipelines (such as downloading or saving, running on a particular device, etc.)
|
library implements for all the pipelines (such as downloading or saving, running on a particular device, etc.)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user