Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 993907f561 | |||
| 6976cab7ca | |||
| fcbed3fa79 | |||
| b98b314b7a | |||
| 74558ff65b | |||
| 49644babd3 | |||
| 56b3b21693 | |||
| 9cef07da5a | |||
| 2d94c7838e | |||
| a81334e3f0 | |||
| d704a730cd | |||
| 49db233b35 | |||
| 93ea26f272 | |||
| f5dfe2a8b0 | |||
| 4836cfad98 | |||
| 1ccbfbb663 | |||
| 29dfe22a8e | |||
| 56806cdbfd | |||
| 8ccc76ab37 | |||
| c46711e895 | |||
| 1d686bac81 | |||
| 0a401b95b7 | |||
| 664e931bcb | |||
| 88bdd97ccd | |||
| 08b453e382 | |||
| 2a111bc9fe | |||
| 16e6997f0d |
@@ -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
|
||||||
@@ -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,316 @@
|
|||||||
|
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,
|
||||||
|
)
|
||||||
|
|
||||||
|
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=self.lora_id, 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 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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -49,12 +49,12 @@ make_image_grid([original_image, mask_image, image], rows=1, cols=3)
|
|||||||
|
|
||||||
## AsymmetricAutoencoderKL
|
## AsymmetricAutoencoderKL
|
||||||
|
|
||||||
[[autodoc]] models.autoencoder_asym_kl.AsymmetricAutoencoderKL
|
[[autodoc]] models.autoencoders.autoencoder_asym_kl.AsymmetricAutoencoderKL
|
||||||
|
|
||||||
## AutoencoderKLOutput
|
## AutoencoderKLOutput
|
||||||
|
|
||||||
[[autodoc]] models.autoencoder_kl.AutoencoderKLOutput
|
[[autodoc]] models.autoencoders.autoencoder_kl.AutoencoderKLOutput
|
||||||
|
|
||||||
## DecoderOutput
|
## DecoderOutput
|
||||||
|
|
||||||
[[autodoc]] models.vae.DecoderOutput
|
[[autodoc]] models.autoencoders.vae.DecoderOutput
|
||||||
|
|||||||
@@ -54,4 +54,4 @@ image
|
|||||||
|
|
||||||
## AutoencoderTinyOutput
|
## AutoencoderTinyOutput
|
||||||
|
|
||||||
[[autodoc]] models.autoencoder_tiny.AutoencoderTinyOutput
|
[[autodoc]] models.autoencoders.autoencoder_tiny.AutoencoderTinyOutput
|
||||||
|
|||||||
@@ -36,11 +36,11 @@ model = AutoencoderKL.from_single_file(url)
|
|||||||
|
|
||||||
## AutoencoderKLOutput
|
## AutoencoderKLOutput
|
||||||
|
|
||||||
[[autodoc]] models.autoencoder_kl.AutoencoderKLOutput
|
[[autodoc]] models.autoencoders.autoencoder_kl.AutoencoderKLOutput
|
||||||
|
|
||||||
## DecoderOutput
|
## DecoderOutput
|
||||||
|
|
||||||
[[autodoc]] models.vae.DecoderOutput
|
[[autodoc]] models.autoencoders.vae.DecoderOutput
|
||||||
|
|
||||||
## FlaxAutoencoderKL
|
## FlaxAutoencoderKL
|
||||||
|
|
||||||
|
|||||||
@@ -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 \
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ accelerate launch train_unconditional.py \
|
|||||||
If you're training with more than one GPU, add the `--multi_gpu` parameter to the training command:
|
If you're training with more than one GPU, add the `--multi_gpu` parameter to the training command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
accelerate launch --mixed_precision="fp16" --multi_gpu train_unconditional.py \
|
accelerate launch --multi_gpu train_unconditional.py \
|
||||||
--dataset_name="huggan/flowers-102-categories" \
|
--dataset_name="huggan/flowers-102-categories" \
|
||||||
--output_dir="ddpm-ema-flowers-64" \
|
--output_dir="ddpm-ema-flowers-64" \
|
||||||
--mixed_precision="fp16" \
|
--mixed_precision="fp16" \
|
||||||
|
|||||||
+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 = ""
|
||||||
@@ -135,8 +139,8 @@ 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():
|
||||||
@@ -2008,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,6 +48,7 @@ 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) |
|
| 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) |
|
||||||
@@ -2930,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(
|
||||||
@@ -2962,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
|
||||||
@@ -2986,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")
|
||||||
|
|
||||||
|
```
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -64,39 +64,6 @@ check_min_version("0.25.0.dev0")
|
|||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# TODO: This function should be removed once training scripts are rewritten in PEFT
|
|
||||||
def text_encoder_lora_state_dict(text_encoder):
|
|
||||||
state_dict = {}
|
|
||||||
|
|
||||||
def text_encoder_attn_modules(text_encoder):
|
|
||||||
from transformers import CLIPTextModel, CLIPTextModelWithProjection
|
|
||||||
|
|
||||||
attn_modules = []
|
|
||||||
|
|
||||||
if isinstance(text_encoder, (CLIPTextModel, CLIPTextModelWithProjection)):
|
|
||||||
for i, layer in enumerate(text_encoder.text_model.encoder.layers):
|
|
||||||
name = f"text_model.encoder.layers.{i}.self_attn"
|
|
||||||
mod = layer.self_attn
|
|
||||||
attn_modules.append((name, mod))
|
|
||||||
|
|
||||||
return attn_modules
|
|
||||||
|
|
||||||
for name, module in text_encoder_attn_modules(text_encoder):
|
|
||||||
for k, v in module.q_proj.lora_linear_layer.state_dict().items():
|
|
||||||
state_dict[f"{name}.q_proj.lora_linear_layer.{k}"] = v
|
|
||||||
|
|
||||||
for k, v in module.k_proj.lora_linear_layer.state_dict().items():
|
|
||||||
state_dict[f"{name}.k_proj.lora_linear_layer.{k}"] = v
|
|
||||||
|
|
||||||
for k, v in module.v_proj.lora_linear_layer.state_dict().items():
|
|
||||||
state_dict[f"{name}.v_proj.lora_linear_layer.{k}"] = v
|
|
||||||
|
|
||||||
for k, v in module.out_proj.lora_linear_layer.state_dict().items():
|
|
||||||
state_dict[f"{name}.out_proj.lora_linear_layer.{k}"] = v
|
|
||||||
|
|
||||||
return state_dict
|
|
||||||
|
|
||||||
|
|
||||||
def save_model_card(
|
def save_model_card(
|
||||||
repo_id: str,
|
repo_id: str,
|
||||||
images=None,
|
images=None,
|
||||||
|
|||||||
@@ -64,39 +64,6 @@ check_min_version("0.25.0.dev0")
|
|||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# TODO: This function should be removed once training scripts are rewritten in PEFT
|
|
||||||
def text_encoder_lora_state_dict(text_encoder):
|
|
||||||
state_dict = {}
|
|
||||||
|
|
||||||
def text_encoder_attn_modules(text_encoder):
|
|
||||||
from transformers import CLIPTextModel, CLIPTextModelWithProjection
|
|
||||||
|
|
||||||
attn_modules = []
|
|
||||||
|
|
||||||
if isinstance(text_encoder, (CLIPTextModel, CLIPTextModelWithProjection)):
|
|
||||||
for i, layer in enumerate(text_encoder.text_model.encoder.layers):
|
|
||||||
name = f"text_model.encoder.layers.{i}.self_attn"
|
|
||||||
mod = layer.self_attn
|
|
||||||
attn_modules.append((name, mod))
|
|
||||||
|
|
||||||
return attn_modules
|
|
||||||
|
|
||||||
for name, module in text_encoder_attn_modules(text_encoder):
|
|
||||||
for k, v in module.q_proj.lora_linear_layer.state_dict().items():
|
|
||||||
state_dict[f"{name}.q_proj.lora_linear_layer.{k}"] = v
|
|
||||||
|
|
||||||
for k, v in module.k_proj.lora_linear_layer.state_dict().items():
|
|
||||||
state_dict[f"{name}.k_proj.lora_linear_layer.{k}"] = v
|
|
||||||
|
|
||||||
for k, v in module.v_proj.lora_linear_layer.state_dict().items():
|
|
||||||
state_dict[f"{name}.v_proj.lora_linear_layer.{k}"] = v
|
|
||||||
|
|
||||||
for k, v in module.out_proj.lora_linear_layer.state_dict().items():
|
|
||||||
state_dict[f"{name}.out_proj.lora_linear_layer.{k}"] = v
|
|
||||||
|
|
||||||
return state_dict
|
|
||||||
|
|
||||||
|
|
||||||
def save_model_card(
|
def save_model_card(
|
||||||
repo_id: str,
|
repo_id: str,
|
||||||
images=None,
|
images=None,
|
||||||
|
|||||||
+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.
|
||||||
|
|
||||||
@@ -101,8 +101,8 @@ accelerate launch --mixed_precision="fp16" train_text_to_image.py \
|
|||||||
|
|
||||||
Once the training is finished the model will be saved in the `output_dir` specified in the command. In this example it's `sd-pokemon-model`. To load the fine-tuned model for inference just pass that path to `StableDiffusionPipeline`
|
Once the training is finished the model will be saved in the `output_dir` specified in the command. In this example it's `sd-pokemon-model`. To load the fine-tuned model for inference just pass that path to `StableDiffusionPipeline`
|
||||||
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
import torch
|
||||||
from diffusers import StableDiffusionPipeline
|
from diffusers import StableDiffusionPipeline
|
||||||
|
|
||||||
model_path = "path_to_saved_model"
|
model_path = "path_to_saved_model"
|
||||||
@@ -114,12 +114,13 @@ image.save("yoda-pokemon.png")
|
|||||||
```
|
```
|
||||||
|
|
||||||
Checkpoints only save the unet, so to run inference from a checkpoint, just load the unet
|
Checkpoints only save the unet, so to run inference from a checkpoint, just load the unet
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
import torch
|
||||||
from diffusers import StableDiffusionPipeline, UNet2DConditionModel
|
from diffusers import StableDiffusionPipeline, UNet2DConditionModel
|
||||||
|
|
||||||
model_path = "path_to_saved_model"
|
model_path = "path_to_saved_model"
|
||||||
|
unet = UNet2DConditionModel.from_pretrained(model_path + "/checkpoint-<N>/unet", torch_dtype=torch.float16)
|
||||||
unet = UNet2DConditionModel.from_pretrained(model_path + "/checkpoint-<N>/unet")
|
|
||||||
|
|
||||||
pipe = StableDiffusionPipeline.from_pretrained("<initial model>", unet=unet, torch_dtype=torch.float16)
|
pipe = StableDiffusionPipeline.from_pretrained("<initial model>", unet=unet, torch_dtype=torch.float16)
|
||||||
pipe.to("cuda")
|
pipe.to("cuda")
|
||||||
|
|||||||
@@ -54,39 +54,6 @@ check_min_version("0.25.0.dev0")
|
|||||||
logger = get_logger(__name__, log_level="INFO")
|
logger = get_logger(__name__, log_level="INFO")
|
||||||
|
|
||||||
|
|
||||||
# TODO: This function should be removed once training scripts are rewritten in PEFT
|
|
||||||
def text_encoder_lora_state_dict(text_encoder):
|
|
||||||
state_dict = {}
|
|
||||||
|
|
||||||
def text_encoder_attn_modules(text_encoder):
|
|
||||||
from transformers import CLIPTextModel, CLIPTextModelWithProjection
|
|
||||||
|
|
||||||
attn_modules = []
|
|
||||||
|
|
||||||
if isinstance(text_encoder, (CLIPTextModel, CLIPTextModelWithProjection)):
|
|
||||||
for i, layer in enumerate(text_encoder.text_model.encoder.layers):
|
|
||||||
name = f"text_model.encoder.layers.{i}.self_attn"
|
|
||||||
mod = layer.self_attn
|
|
||||||
attn_modules.append((name, mod))
|
|
||||||
|
|
||||||
return attn_modules
|
|
||||||
|
|
||||||
for name, module in text_encoder_attn_modules(text_encoder):
|
|
||||||
for k, v in module.q_proj.lora_linear_layer.state_dict().items():
|
|
||||||
state_dict[f"{name}.q_proj.lora_linear_layer.{k}"] = v
|
|
||||||
|
|
||||||
for k, v in module.k_proj.lora_linear_layer.state_dict().items():
|
|
||||||
state_dict[f"{name}.k_proj.lora_linear_layer.{k}"] = v
|
|
||||||
|
|
||||||
for k, v in module.v_proj.lora_linear_layer.state_dict().items():
|
|
||||||
state_dict[f"{name}.v_proj.lora_linear_layer.{k}"] = v
|
|
||||||
|
|
||||||
for k, v in module.out_proj.lora_linear_layer.state_dict().items():
|
|
||||||
state_dict[f"{name}.out_proj.lora_linear_layer.{k}"] = v
|
|
||||||
|
|
||||||
return state_dict
|
|
||||||
|
|
||||||
|
|
||||||
def save_model_card(repo_id: str, images=None, base_model=str, dataset_name=str, repo_folder=None):
|
def save_model_card(repo_id: str, images=None, base_model=str, dataset_name=str, repo_folder=None):
|
||||||
img_str = ""
|
img_str = ""
|
||||||
for i, image in enumerate(images):
|
for i, image in enumerate(images):
|
||||||
|
|||||||
@@ -63,39 +63,6 @@ check_min_version("0.25.0.dev0")
|
|||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# TODO: This function should be removed once training scripts are rewritten in PEFT
|
|
||||||
def text_encoder_lora_state_dict(text_encoder):
|
|
||||||
state_dict = {}
|
|
||||||
|
|
||||||
def text_encoder_attn_modules(text_encoder):
|
|
||||||
from transformers import CLIPTextModel, CLIPTextModelWithProjection
|
|
||||||
|
|
||||||
attn_modules = []
|
|
||||||
|
|
||||||
if isinstance(text_encoder, (CLIPTextModel, CLIPTextModelWithProjection)):
|
|
||||||
for i, layer in enumerate(text_encoder.text_model.encoder.layers):
|
|
||||||
name = f"text_model.encoder.layers.{i}.self_attn"
|
|
||||||
mod = layer.self_attn
|
|
||||||
attn_modules.append((name, mod))
|
|
||||||
|
|
||||||
return attn_modules
|
|
||||||
|
|
||||||
for name, module in text_encoder_attn_modules(text_encoder):
|
|
||||||
for k, v in module.q_proj.lora_linear_layer.state_dict().items():
|
|
||||||
state_dict[f"{name}.q_proj.lora_linear_layer.{k}"] = v
|
|
||||||
|
|
||||||
for k, v in module.k_proj.lora_linear_layer.state_dict().items():
|
|
||||||
state_dict[f"{name}.k_proj.lora_linear_layer.{k}"] = v
|
|
||||||
|
|
||||||
for k, v in module.v_proj.lora_linear_layer.state_dict().items():
|
|
||||||
state_dict[f"{name}.v_proj.lora_linear_layer.{k}"] = v
|
|
||||||
|
|
||||||
for k, v in module.out_proj.lora_linear_layer.state_dict().items():
|
|
||||||
state_dict[f"{name}.out_proj.lora_linear_layer.{k}"] = v
|
|
||||||
|
|
||||||
return state_dict
|
|
||||||
|
|
||||||
|
|
||||||
def save_model_card(
|
def save_model_card(
|
||||||
repo_id: str,
|
repo_id: str,
|
||||||
images=None,
|
images=None,
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ from safetensors.torch import load_file as stl
|
|||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
from diffusers import AutoencoderKL, ConsistencyDecoderVAE, DiffusionPipeline, StableDiffusionPipeline, UNet2DModel
|
from diffusers import AutoencoderKL, ConsistencyDecoderVAE, DiffusionPipeline, StableDiffusionPipeline, UNet2DModel
|
||||||
|
from diffusers.models.autoencoders.vae import Encoder
|
||||||
from diffusers.models.embeddings import TimestepEmbedding
|
from diffusers.models.embeddings import TimestepEmbedding
|
||||||
from diffusers.models.unet_2d_blocks import ResnetDownsampleBlock2D, ResnetUpsampleBlock2D, UNetMidBlock2D
|
from diffusers.models.unet_2d_blocks import ResnetDownsampleBlock2D, ResnetUpsampleBlock2D, UNetMidBlock2D
|
||||||
from diffusers.models.vae import Encoder
|
|
||||||
|
|
||||||
|
|
||||||
args = ArgumentParser()
|
args = ArgumentParser()
|
||||||
|
|||||||
@@ -159,6 +159,14 @@ vae_conversion_map_attn = [
|
|||||||
("proj_out.", "proj_attn."),
|
("proj_out.", "proj_attn."),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# This is probably not the most ideal solution, but it does work.
|
||||||
|
vae_extra_conversion_map = [
|
||||||
|
("to_q", "q"),
|
||||||
|
("to_k", "k"),
|
||||||
|
("to_v", "v"),
|
||||||
|
("to_out.0", "proj_out"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def reshape_weight_for_sd(w):
|
def reshape_weight_for_sd(w):
|
||||||
# convert HF linear weights to SD conv2d weights
|
# convert HF linear weights to SD conv2d weights
|
||||||
@@ -178,11 +186,20 @@ def convert_vae_state_dict(vae_state_dict):
|
|||||||
mapping[k] = v
|
mapping[k] = v
|
||||||
new_state_dict = {v: vae_state_dict[k] for k, v in mapping.items()}
|
new_state_dict = {v: vae_state_dict[k] for k, v in mapping.items()}
|
||||||
weights_to_convert = ["q", "k", "v", "proj_out"]
|
weights_to_convert = ["q", "k", "v", "proj_out"]
|
||||||
|
keys_to_rename = {}
|
||||||
for k, v in new_state_dict.items():
|
for k, v in new_state_dict.items():
|
||||||
for weight_name in weights_to_convert:
|
for weight_name in weights_to_convert:
|
||||||
if f"mid.attn_1.{weight_name}.weight" in k:
|
if f"mid.attn_1.{weight_name}.weight" in k:
|
||||||
print(f"Reshaping {k} for SD format")
|
print(f"Reshaping {k} for SD format")
|
||||||
new_state_dict[k] = reshape_weight_for_sd(v)
|
new_state_dict[k] = reshape_weight_for_sd(v)
|
||||||
|
for weight_name, real_weight_name in vae_extra_conversion_map:
|
||||||
|
if f"mid.attn_1.{weight_name}.weight" in k or f"mid.attn_1.{weight_name}.bias" in k:
|
||||||
|
keys_to_rename[k] = k.replace(weight_name, real_weight_name)
|
||||||
|
for k, v in keys_to_rename.items():
|
||||||
|
if k in new_state_dict:
|
||||||
|
print(f"Renaming {k} to {v}")
|
||||||
|
new_state_dict[v] = reshape_weight_for_sd(new_state_dict[k])
|
||||||
|
del new_state_dict[k]
|
||||||
return new_state_dict
|
return new_state_dict
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ 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 huggingface_hub.utils import validate_hf_hub_args
|
||||||
from packaging import version
|
from packaging import version
|
||||||
from torch import nn
|
from torch import nn
|
||||||
@@ -229,7 +230,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,
|
||||||
@@ -255,7 +258,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,
|
||||||
@@ -294,7 +297,12 @@ class LoraLoaderMixin:
|
|||||||
return state_dict, network_alphas
|
return state_dict, network_alphas
|
||||||
|
|
||||||
@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):
|
||||||
|
|||||||
@@ -169,10 +169,12 @@ class FromSingleFileMixin:
|
|||||||
load_safety_checker = kwargs.pop("load_safety_checker", True)
|
load_safety_checker = kwargs.pop("load_safety_checker", True)
|
||||||
prediction_type = kwargs.pop("prediction_type", None)
|
prediction_type = kwargs.pop("prediction_type", None)
|
||||||
text_encoder = kwargs.pop("text_encoder", None)
|
text_encoder = kwargs.pop("text_encoder", None)
|
||||||
|
text_encoder_2 = kwargs.pop("text_encoder_2", None)
|
||||||
vae = kwargs.pop("vae", None)
|
vae = kwargs.pop("vae", None)
|
||||||
controlnet = kwargs.pop("controlnet", None)
|
controlnet = kwargs.pop("controlnet", None)
|
||||||
adapter = kwargs.pop("adapter", None)
|
adapter = kwargs.pop("adapter", None)
|
||||||
tokenizer = kwargs.pop("tokenizer", None)
|
tokenizer = kwargs.pop("tokenizer", None)
|
||||||
|
tokenizer_2 = kwargs.pop("tokenizer_2", None)
|
||||||
|
|
||||||
torch_dtype = kwargs.pop("torch_dtype", None)
|
torch_dtype = kwargs.pop("torch_dtype", None)
|
||||||
|
|
||||||
@@ -274,8 +276,10 @@ class FromSingleFileMixin:
|
|||||||
load_safety_checker=load_safety_checker,
|
load_safety_checker=load_safety_checker,
|
||||||
prediction_type=prediction_type,
|
prediction_type=prediction_type,
|
||||||
text_encoder=text_encoder,
|
text_encoder=text_encoder,
|
||||||
|
text_encoder_2=text_encoder_2,
|
||||||
vae=vae,
|
vae=vae,
|
||||||
tokenizer=tokenizer,
|
tokenizer=tokenizer,
|
||||||
|
tokenizer_2=tokenizer_2,
|
||||||
original_config_file=original_config_file,
|
original_config_file=original_config_file,
|
||||||
config_files=config_files,
|
config_files=config_files,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
|
|||||||
@@ -26,11 +26,11 @@ _import_structure = {}
|
|||||||
|
|
||||||
if is_torch_available():
|
if is_torch_available():
|
||||||
_import_structure["adapter"] = ["MultiAdapter", "T2IAdapter"]
|
_import_structure["adapter"] = ["MultiAdapter", "T2IAdapter"]
|
||||||
_import_structure["autoencoder_asym_kl"] = ["AsymmetricAutoencoderKL"]
|
_import_structure["autoencoders.autoencoder_asym_kl"] = ["AsymmetricAutoencoderKL"]
|
||||||
_import_structure["autoencoder_kl"] = ["AutoencoderKL"]
|
_import_structure["autoencoders.autoencoder_kl"] = ["AutoencoderKL"]
|
||||||
_import_structure["autoencoder_kl_temporal_decoder"] = ["AutoencoderKLTemporalDecoder"]
|
_import_structure["autoencoders.autoencoder_kl_temporal_decoder"] = ["AutoencoderKLTemporalDecoder"]
|
||||||
_import_structure["autoencoder_tiny"] = ["AutoencoderTiny"]
|
_import_structure["autoencoders.autoencoder_tiny"] = ["AutoencoderTiny"]
|
||||||
_import_structure["consistency_decoder_vae"] = ["ConsistencyDecoderVAE"]
|
_import_structure["autoencoders.consistency_decoder_vae"] = ["ConsistencyDecoderVAE"]
|
||||||
_import_structure["controlnet"] = ["ControlNetModel"]
|
_import_structure["controlnet"] = ["ControlNetModel"]
|
||||||
_import_structure["controlnetxs"] = ["ControlNetXSModel"]
|
_import_structure["controlnetxs"] = ["ControlNetXSModel"]
|
||||||
_import_structure["dual_transformer_2d"] = ["DualTransformer2DModel"]
|
_import_structure["dual_transformer_2d"] = ["DualTransformer2DModel"]
|
||||||
@@ -58,11 +58,13 @@ if is_flax_available():
|
|||||||
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
||||||
if is_torch_available():
|
if is_torch_available():
|
||||||
from .adapter import MultiAdapter, T2IAdapter
|
from .adapter import MultiAdapter, T2IAdapter
|
||||||
from .autoencoder_asym_kl import AsymmetricAutoencoderKL
|
from .autoencoders import (
|
||||||
from .autoencoder_kl import AutoencoderKL
|
AsymmetricAutoencoderKL,
|
||||||
from .autoencoder_kl_temporal_decoder import AutoencoderKLTemporalDecoder
|
AutoencoderKL,
|
||||||
from .autoencoder_tiny import AutoencoderTiny
|
AutoencoderKLTemporalDecoder,
|
||||||
from .consistency_decoder_vae import ConsistencyDecoderVAE
|
AutoencoderTiny,
|
||||||
|
ConsistencyDecoderVAE,
|
||||||
|
)
|
||||||
from .controlnet import ControlNetModel
|
from .controlnet import ControlNetModel
|
||||||
from .controlnetxs import ControlNetXSModel
|
from .controlnetxs import ControlNetXSModel
|
||||||
from .dual_transformer_2d import DualTransformer2DModel
|
from .dual_transformer_2d import DualTransformer2DModel
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from .autoencoder_asym_kl import AsymmetricAutoencoderKL
|
||||||
|
from .autoencoder_kl import AutoencoderKL
|
||||||
|
from .autoencoder_kl_temporal_decoder import AutoencoderKLTemporalDecoder
|
||||||
|
from .autoencoder_tiny import AutoencoderTiny
|
||||||
|
from .consistency_decoder_vae import ConsistencyDecoderVAE
|
||||||
+4
-4
@@ -16,10 +16,10 @@ from typing import Optional, Tuple, Union
|
|||||||
import torch
|
import torch
|
||||||
import torch.nn as nn
|
import torch.nn as nn
|
||||||
|
|
||||||
from ..configuration_utils import ConfigMixin, register_to_config
|
from ...configuration_utils import ConfigMixin, register_to_config
|
||||||
from ..utils.accelerate_utils import apply_forward_hook
|
from ...utils.accelerate_utils import apply_forward_hook
|
||||||
from .modeling_outputs import AutoencoderKLOutput
|
from ..modeling_outputs import AutoencoderKLOutput
|
||||||
from .modeling_utils import ModelMixin
|
from ..modeling_utils import ModelMixin
|
||||||
from .vae import DecoderOutput, DiagonalGaussianDistribution, Encoder, MaskConditionDecoder
|
from .vae import DecoderOutput, DiagonalGaussianDistribution, Encoder, MaskConditionDecoder
|
||||||
|
|
||||||
|
|
||||||
+6
-6
@@ -16,10 +16,10 @@ from typing import Dict, Optional, Tuple, Union
|
|||||||
import torch
|
import torch
|
||||||
import torch.nn as nn
|
import torch.nn as nn
|
||||||
|
|
||||||
from ..configuration_utils import ConfigMixin, register_to_config
|
from ...configuration_utils import ConfigMixin, register_to_config
|
||||||
from ..loaders import FromOriginalVAEMixin
|
from ...loaders import FromOriginalVAEMixin
|
||||||
from ..utils.accelerate_utils import apply_forward_hook
|
from ...utils.accelerate_utils import apply_forward_hook
|
||||||
from .attention_processor import (
|
from ..attention_processor import (
|
||||||
ADDED_KV_ATTENTION_PROCESSORS,
|
ADDED_KV_ATTENTION_PROCESSORS,
|
||||||
CROSS_ATTENTION_PROCESSORS,
|
CROSS_ATTENTION_PROCESSORS,
|
||||||
Attention,
|
Attention,
|
||||||
@@ -27,8 +27,8 @@ from .attention_processor import (
|
|||||||
AttnAddedKVProcessor,
|
AttnAddedKVProcessor,
|
||||||
AttnProcessor,
|
AttnProcessor,
|
||||||
)
|
)
|
||||||
from .modeling_outputs import AutoencoderKLOutput
|
from ..modeling_outputs import AutoencoderKLOutput
|
||||||
from .modeling_utils import ModelMixin
|
from ..modeling_utils import ModelMixin
|
||||||
from .vae import Decoder, DecoderOutput, DiagonalGaussianDistribution, Encoder
|
from .vae import Decoder, DecoderOutput, DiagonalGaussianDistribution, Encoder
|
||||||
|
|
||||||
|
|
||||||
+8
-8
@@ -16,14 +16,14 @@ from typing import Dict, Optional, Tuple, Union
|
|||||||
import torch
|
import torch
|
||||||
import torch.nn as nn
|
import torch.nn as nn
|
||||||
|
|
||||||
from ..configuration_utils import ConfigMixin, register_to_config
|
from ...configuration_utils import ConfigMixin, register_to_config
|
||||||
from ..loaders import FromOriginalVAEMixin
|
from ...loaders import FromOriginalVAEMixin
|
||||||
from ..utils import is_torch_version
|
from ...utils import is_torch_version
|
||||||
from ..utils.accelerate_utils import apply_forward_hook
|
from ...utils.accelerate_utils import apply_forward_hook
|
||||||
from .attention_processor import CROSS_ATTENTION_PROCESSORS, AttentionProcessor, AttnProcessor
|
from ..attention_processor import CROSS_ATTENTION_PROCESSORS, AttentionProcessor, AttnProcessor
|
||||||
from .modeling_outputs import AutoencoderKLOutput
|
from ..modeling_outputs import AutoencoderKLOutput
|
||||||
from .modeling_utils import ModelMixin
|
from ..modeling_utils import ModelMixin
|
||||||
from .unet_3d_blocks import MidBlockTemporalDecoder, UpBlockTemporalDecoder
|
from ..unet_3d_blocks import MidBlockTemporalDecoder, UpBlockTemporalDecoder
|
||||||
from .vae import DecoderOutput, DiagonalGaussianDistribution, Encoder
|
from .vae import DecoderOutput, DiagonalGaussianDistribution, Encoder
|
||||||
|
|
||||||
|
|
||||||
+4
-4
@@ -18,10 +18,10 @@ from typing import Optional, Tuple, Union
|
|||||||
|
|
||||||
import torch
|
import torch
|
||||||
|
|
||||||
from ..configuration_utils import ConfigMixin, register_to_config
|
from ...configuration_utils import ConfigMixin, register_to_config
|
||||||
from ..utils import BaseOutput
|
from ...utils import BaseOutput
|
||||||
from ..utils.accelerate_utils import apply_forward_hook
|
from ...utils.accelerate_utils import apply_forward_hook
|
||||||
from .modeling_utils import ModelMixin
|
from ..modeling_utils import ModelMixin
|
||||||
from .vae import DecoderOutput, DecoderTiny, EncoderTiny
|
from .vae import DecoderOutput, DecoderTiny, EncoderTiny
|
||||||
|
|
||||||
|
|
||||||
+14
-14
@@ -18,20 +18,20 @@ import torch
|
|||||||
import torch.nn.functional as F
|
import torch.nn.functional as F
|
||||||
from torch import nn
|
from torch import nn
|
||||||
|
|
||||||
from ..configuration_utils import ConfigMixin, register_to_config
|
from ...configuration_utils import ConfigMixin, register_to_config
|
||||||
from ..schedulers import ConsistencyDecoderScheduler
|
from ...schedulers import ConsistencyDecoderScheduler
|
||||||
from ..utils import BaseOutput
|
from ...utils import BaseOutput
|
||||||
from ..utils.accelerate_utils import apply_forward_hook
|
from ...utils.accelerate_utils import apply_forward_hook
|
||||||
from ..utils.torch_utils import randn_tensor
|
from ...utils.torch_utils import randn_tensor
|
||||||
from .attention_processor import (
|
from ..attention_processor import (
|
||||||
ADDED_KV_ATTENTION_PROCESSORS,
|
ADDED_KV_ATTENTION_PROCESSORS,
|
||||||
CROSS_ATTENTION_PROCESSORS,
|
CROSS_ATTENTION_PROCESSORS,
|
||||||
AttentionProcessor,
|
AttentionProcessor,
|
||||||
AttnAddedKVProcessor,
|
AttnAddedKVProcessor,
|
||||||
AttnProcessor,
|
AttnProcessor,
|
||||||
)
|
)
|
||||||
from .modeling_utils import ModelMixin
|
from ..modeling_utils import ModelMixin
|
||||||
from .unet_2d import UNet2DModel
|
from ..unet_2d import UNet2DModel
|
||||||
from .vae import DecoderOutput, DiagonalGaussianDistribution, Encoder
|
from .vae import DecoderOutput, DiagonalGaussianDistribution, Encoder
|
||||||
|
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ class ConsistencyDecoderVAE(ModelMixin, ConfigMixin):
|
|||||||
self.use_slicing = False
|
self.use_slicing = False
|
||||||
self.use_tiling = False
|
self.use_tiling = False
|
||||||
|
|
||||||
# Copied from diffusers.models.autoencoder_kl.AutoencoderKL.enable_tiling
|
# Copied from diffusers.models.autoencoders.autoencoder_kl.AutoencoderKL.enable_tiling
|
||||||
def enable_tiling(self, use_tiling: bool = True):
|
def enable_tiling(self, use_tiling: bool = True):
|
||||||
r"""
|
r"""
|
||||||
Enable tiled VAE decoding. When this option is enabled, the VAE will split the input tensor into tiles to
|
Enable tiled VAE decoding. When this option is enabled, the VAE will split the input tensor into tiles to
|
||||||
@@ -162,7 +162,7 @@ class ConsistencyDecoderVAE(ModelMixin, ConfigMixin):
|
|||||||
"""
|
"""
|
||||||
self.use_tiling = use_tiling
|
self.use_tiling = use_tiling
|
||||||
|
|
||||||
# Copied from diffusers.models.autoencoder_kl.AutoencoderKL.disable_tiling
|
# Copied from diffusers.models.autoencoders.autoencoder_kl.AutoencoderKL.disable_tiling
|
||||||
def disable_tiling(self):
|
def disable_tiling(self):
|
||||||
r"""
|
r"""
|
||||||
Disable tiled VAE decoding. If `enable_tiling` was previously enabled, this method will go back to computing
|
Disable tiled VAE decoding. If `enable_tiling` was previously enabled, this method will go back to computing
|
||||||
@@ -170,7 +170,7 @@ class ConsistencyDecoderVAE(ModelMixin, ConfigMixin):
|
|||||||
"""
|
"""
|
||||||
self.enable_tiling(False)
|
self.enable_tiling(False)
|
||||||
|
|
||||||
# Copied from diffusers.models.autoencoder_kl.AutoencoderKL.enable_slicing
|
# Copied from diffusers.models.autoencoders.autoencoder_kl.AutoencoderKL.enable_slicing
|
||||||
def enable_slicing(self):
|
def enable_slicing(self):
|
||||||
r"""
|
r"""
|
||||||
Enable sliced VAE decoding. When this option is enabled, the VAE will split the input tensor in slices to
|
Enable sliced VAE decoding. When this option is enabled, the VAE will split the input tensor in slices to
|
||||||
@@ -178,7 +178,7 @@ class ConsistencyDecoderVAE(ModelMixin, ConfigMixin):
|
|||||||
"""
|
"""
|
||||||
self.use_slicing = True
|
self.use_slicing = True
|
||||||
|
|
||||||
# Copied from diffusers.models.autoencoder_kl.AutoencoderKL.disable_slicing
|
# Copied from diffusers.models.autoencoders.autoencoder_kl.AutoencoderKL.disable_slicing
|
||||||
def disable_slicing(self):
|
def disable_slicing(self):
|
||||||
r"""
|
r"""
|
||||||
Disable sliced VAE decoding. If `enable_slicing` was previously enabled, this method will go back to computing
|
Disable sliced VAE decoding. If `enable_slicing` was previously enabled, this method will go back to computing
|
||||||
@@ -333,14 +333,14 @@ class ConsistencyDecoderVAE(ModelMixin, ConfigMixin):
|
|||||||
|
|
||||||
return DecoderOutput(sample=x_0)
|
return DecoderOutput(sample=x_0)
|
||||||
|
|
||||||
# Copied from diffusers.models.autoencoder_kl.AutoencoderKL.blend_v
|
# Copied from diffusers.models.autoencoders.autoencoder_kl.AutoencoderKL.blend_v
|
||||||
def blend_v(self, a: torch.Tensor, b: torch.Tensor, blend_extent: int) -> torch.Tensor:
|
def blend_v(self, a: torch.Tensor, b: torch.Tensor, blend_extent: int) -> torch.Tensor:
|
||||||
blend_extent = min(a.shape[2], b.shape[2], blend_extent)
|
blend_extent = min(a.shape[2], b.shape[2], blend_extent)
|
||||||
for y in range(blend_extent):
|
for y in range(blend_extent):
|
||||||
b[:, :, y, :] = a[:, :, -blend_extent + y, :] * (1 - y / blend_extent) + b[:, :, y, :] * (y / blend_extent)
|
b[:, :, y, :] = a[:, :, -blend_extent + y, :] * (1 - y / blend_extent) + b[:, :, y, :] * (y / blend_extent)
|
||||||
return b
|
return b
|
||||||
|
|
||||||
# Copied from diffusers.models.autoencoder_kl.AutoencoderKL.blend_h
|
# Copied from diffusers.models.autoencoders.autoencoder_kl.AutoencoderKL.blend_h
|
||||||
def blend_h(self, a: torch.Tensor, b: torch.Tensor, blend_extent: int) -> torch.Tensor:
|
def blend_h(self, a: torch.Tensor, b: torch.Tensor, blend_extent: int) -> torch.Tensor:
|
||||||
blend_extent = min(a.shape[3], b.shape[3], blend_extent)
|
blend_extent = min(a.shape[3], b.shape[3], blend_extent)
|
||||||
for x in range(blend_extent):
|
for x in range(blend_extent):
|
||||||
@@ -18,11 +18,11 @@ import numpy as np
|
|||||||
import torch
|
import torch
|
||||||
import torch.nn as nn
|
import torch.nn as nn
|
||||||
|
|
||||||
from ..utils import BaseOutput, is_torch_version
|
from ...utils import BaseOutput, is_torch_version
|
||||||
from ..utils.torch_utils import randn_tensor
|
from ...utils.torch_utils import randn_tensor
|
||||||
from .activations import get_activation
|
from ..activations import get_activation
|
||||||
from .attention_processor import SpatialNorm
|
from ..attention_processor import SpatialNorm
|
||||||
from .unet_2d_blocks import (
|
from ..unet_2d_blocks import (
|
||||||
AutoencoderTinyBlock,
|
AutoencoderTinyBlock,
|
||||||
UNetMidBlock2D,
|
UNetMidBlock2D,
|
||||||
get_down_block,
|
get_down_block,
|
||||||
@@ -26,7 +26,7 @@ from ..utils import BaseOutput, logging
|
|||||||
from .attention_processor import (
|
from .attention_processor import (
|
||||||
AttentionProcessor,
|
AttentionProcessor,
|
||||||
)
|
)
|
||||||
from .autoencoder_kl import AutoencoderKL
|
from .autoencoders import AutoencoderKL
|
||||||
from .lora import LoRACompatibleConv
|
from .lora import LoRACompatibleConv
|
||||||
from .modeling_utils import ModelMixin
|
from .modeling_utils import ModelMixin
|
||||||
from .unet_2d_blocks import (
|
from .unet_2d_blocks import (
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import torch.nn as nn
|
|||||||
from ..configuration_utils import ConfigMixin, register_to_config
|
from ..configuration_utils import ConfigMixin, register_to_config
|
||||||
from ..utils import BaseOutput
|
from ..utils import BaseOutput
|
||||||
from ..utils.accelerate_utils import apply_forward_hook
|
from ..utils.accelerate_utils import apply_forward_hook
|
||||||
|
from .autoencoders.vae import Decoder, DecoderOutput, Encoder, VectorQuantizer
|
||||||
from .modeling_utils import ModelMixin
|
from .modeling_utils import ModelMixin
|
||||||
from .vae import Decoder, DecoderOutput, Encoder, VectorQuantizer
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ from ...configuration_utils import FrozenDict
|
|||||||
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
||||||
from ...loaders import FromSingleFileMixin, IPAdapterMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
from ...loaders import FromSingleFileMixin, IPAdapterMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
||||||
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
||||||
|
from ...models.attention_processor import FusedAttnProcessor2_0
|
||||||
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 (
|
||||||
@@ -655,6 +656,65 @@ class AltDiffusionPipeline(
|
|||||||
"""Disables the FreeU mechanism if enabled."""
|
"""Disables the FreeU mechanism if enabled."""
|
||||||
self.unet.disable_freeu()
|
self.unet.disable_freeu()
|
||||||
|
|
||||||
|
def fuse_qkv_projections(self, unet: bool = True, vae: bool = True):
|
||||||
|
"""
|
||||||
|
Enables fused QKV projections. For self-attention modules, all projection matrices (i.e., query,
|
||||||
|
key, value) are fused. For cross-attention modules, key and value projection matrices are fused.
|
||||||
|
|
||||||
|
<Tip warning={true}>
|
||||||
|
|
||||||
|
This API is 🧪 experimental.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
unet (`bool`, defaults to `True`): To apply fusion on the UNet.
|
||||||
|
vae (`bool`, defaults to `True`): To apply fusion on the VAE.
|
||||||
|
"""
|
||||||
|
self.fusing_unet = False
|
||||||
|
self.fusing_vae = False
|
||||||
|
|
||||||
|
if unet:
|
||||||
|
self.fusing_unet = True
|
||||||
|
self.unet.fuse_qkv_projections()
|
||||||
|
self.unet.set_attn_processor(FusedAttnProcessor2_0())
|
||||||
|
|
||||||
|
if vae:
|
||||||
|
if not isinstance(self.vae, AutoencoderKL):
|
||||||
|
raise ValueError("`fuse_qkv_projections()` is only supported for the VAE of type `AutoencoderKL`.")
|
||||||
|
|
||||||
|
self.fusing_vae = True
|
||||||
|
self.vae.fuse_qkv_projections()
|
||||||
|
self.vae.set_attn_processor(FusedAttnProcessor2_0())
|
||||||
|
|
||||||
|
def unfuse_qkv_projections(self, unet: bool = True, vae: bool = True):
|
||||||
|
"""Disable QKV projection fusion if enabled.
|
||||||
|
|
||||||
|
<Tip warning={true}>
|
||||||
|
|
||||||
|
This API is 🧪 experimental.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
unet (`bool`, defaults to `True`): To apply fusion on the UNet.
|
||||||
|
vae (`bool`, defaults to `True`): To apply fusion on the VAE.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if unet:
|
||||||
|
if not self.fusing_unet:
|
||||||
|
logger.warning("The UNet was not initially fused for QKV projections. Doing nothing.")
|
||||||
|
else:
|
||||||
|
self.unet.unfuse_qkv_projections()
|
||||||
|
self.fusing_unet = False
|
||||||
|
|
||||||
|
if vae:
|
||||||
|
if not self.fusing_vae:
|
||||||
|
logger.warning("The VAE was not initially fused for QKV projections. Doing nothing.")
|
||||||
|
else:
|
||||||
|
self.vae.unfuse_qkv_projections()
|
||||||
|
self.fusing_vae = False
|
||||||
|
|
||||||
def get_guidance_scale_embedding(self, w, embedding_dim=512, dtype=torch.float32):
|
def get_guidance_scale_embedding(self, w, embedding_dim=512, dtype=torch.float32):
|
||||||
"""
|
"""
|
||||||
See https://github.com/google-research/vdm/blob/dc27b98a554f65cdc654b800da5aa1846545d41b/model_vdm.py#L298
|
See https://github.com/google-research/vdm/blob/dc27b98a554f65cdc654b800da5aa1846545d41b/model_vdm.py#L298
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ from ...configuration_utils import FrozenDict
|
|||||||
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
||||||
from ...loaders import FromSingleFileMixin, IPAdapterMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
from ...loaders import FromSingleFileMixin, IPAdapterMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
||||||
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
||||||
|
from ...models.attention_processor import FusedAttnProcessor2_0
|
||||||
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 (
|
||||||
@@ -715,6 +716,65 @@ class AltDiffusionImg2ImgPipeline(
|
|||||||
"""Disables the FreeU mechanism if enabled."""
|
"""Disables the FreeU mechanism if enabled."""
|
||||||
self.unet.disable_freeu()
|
self.unet.disable_freeu()
|
||||||
|
|
||||||
|
def fuse_qkv_projections(self, unet: bool = True, vae: bool = True):
|
||||||
|
"""
|
||||||
|
Enables fused QKV projections. For self-attention modules, all projection matrices (i.e., query,
|
||||||
|
key, value) are fused. For cross-attention modules, key and value projection matrices are fused.
|
||||||
|
|
||||||
|
<Tip warning={true}>
|
||||||
|
|
||||||
|
This API is 🧪 experimental.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
unet (`bool`, defaults to `True`): To apply fusion on the UNet.
|
||||||
|
vae (`bool`, defaults to `True`): To apply fusion on the VAE.
|
||||||
|
"""
|
||||||
|
self.fusing_unet = False
|
||||||
|
self.fusing_vae = False
|
||||||
|
|
||||||
|
if unet:
|
||||||
|
self.fusing_unet = True
|
||||||
|
self.unet.fuse_qkv_projections()
|
||||||
|
self.unet.set_attn_processor(FusedAttnProcessor2_0())
|
||||||
|
|
||||||
|
if vae:
|
||||||
|
if not isinstance(self.vae, AutoencoderKL):
|
||||||
|
raise ValueError("`fuse_qkv_projections()` is only supported for the VAE of type `AutoencoderKL`.")
|
||||||
|
|
||||||
|
self.fusing_vae = True
|
||||||
|
self.vae.fuse_qkv_projections()
|
||||||
|
self.vae.set_attn_processor(FusedAttnProcessor2_0())
|
||||||
|
|
||||||
|
def unfuse_qkv_projections(self, unet: bool = True, vae: bool = True):
|
||||||
|
"""Disable QKV projection fusion if enabled.
|
||||||
|
|
||||||
|
<Tip warning={true}>
|
||||||
|
|
||||||
|
This API is 🧪 experimental.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
unet (`bool`, defaults to `True`): To apply fusion on the UNet.
|
||||||
|
vae (`bool`, defaults to `True`): To apply fusion on the VAE.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if unet:
|
||||||
|
if not self.fusing_unet:
|
||||||
|
logger.warning("The UNet was not initially fused for QKV projections. Doing nothing.")
|
||||||
|
else:
|
||||||
|
self.unet.unfuse_qkv_projections()
|
||||||
|
self.fusing_unet = False
|
||||||
|
|
||||||
|
if vae:
|
||||||
|
if not self.fusing_vae:
|
||||||
|
logger.warning("The VAE was not initially fused for QKV projections. Doing nothing.")
|
||||||
|
else:
|
||||||
|
self.vae.unfuse_qkv_projections()
|
||||||
|
self.fusing_vae = False
|
||||||
|
|
||||||
def get_guidance_scale_embedding(self, w, embedding_dim=512, dtype=torch.float32):
|
def get_guidance_scale_embedding(self, w, embedding_dim=512, dtype=torch.float32):
|
||||||
"""
|
"""
|
||||||
See https://github.com/google-research/vdm/blob/dc27b98a554f65cdc654b800da5aa1846545d41b/model_vdm.py#L298
|
See https://github.com/google-research/vdm/blob/dc27b98a554f65cdc654b800da5aa1846545d41b/model_vdm.py#L298
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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`]):
|
||||||
|
|||||||
@@ -98,7 +98,9 @@ class StableDiffusionControlNetXSPipeline(
|
|||||||
|
|
||||||
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.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
- [`~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:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
|
|||||||
@@ -102,8 +102,9 @@ class StableDiffusionXLControlNetXSPipeline(
|
|||||||
|
|
||||||
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
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
|
|||||||
+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]
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1153,7 +1153,9 @@ def download_from_original_stable_diffusion_ckpt(
|
|||||||
vae_path=None,
|
vae_path=None,
|
||||||
vae=None,
|
vae=None,
|
||||||
text_encoder=None,
|
text_encoder=None,
|
||||||
|
text_encoder_2=None,
|
||||||
tokenizer=None,
|
tokenizer=None,
|
||||||
|
tokenizer_2=None,
|
||||||
config_files=None,
|
config_files=None,
|
||||||
) -> DiffusionPipeline:
|
) -> DiffusionPipeline:
|
||||||
"""
|
"""
|
||||||
@@ -1232,7 +1234,9 @@ def download_from_original_stable_diffusion_ckpt(
|
|||||||
StableDiffusionInpaintPipeline,
|
StableDiffusionInpaintPipeline,
|
||||||
StableDiffusionPipeline,
|
StableDiffusionPipeline,
|
||||||
StableDiffusionUpscalePipeline,
|
StableDiffusionUpscalePipeline,
|
||||||
|
StableDiffusionXLControlNetInpaintPipeline,
|
||||||
StableDiffusionXLImg2ImgPipeline,
|
StableDiffusionXLImg2ImgPipeline,
|
||||||
|
StableDiffusionXLInpaintPipeline,
|
||||||
StableDiffusionXLPipeline,
|
StableDiffusionXLPipeline,
|
||||||
StableUnCLIPImg2ImgPipeline,
|
StableUnCLIPImg2ImgPipeline,
|
||||||
StableUnCLIPPipeline,
|
StableUnCLIPPipeline,
|
||||||
@@ -1339,7 +1343,11 @@ def download_from_original_stable_diffusion_ckpt(
|
|||||||
else:
|
else:
|
||||||
pipeline_class = StableDiffusionXLPipeline if model_type == "SDXL" else StableDiffusionXLImg2ImgPipeline
|
pipeline_class = StableDiffusionXLPipeline if model_type == "SDXL" else StableDiffusionXLImg2ImgPipeline
|
||||||
|
|
||||||
if num_in_channels is None and pipeline_class == StableDiffusionInpaintPipeline:
|
if num_in_channels is None and pipeline_class in [
|
||||||
|
StableDiffusionInpaintPipeline,
|
||||||
|
StableDiffusionXLInpaintPipeline,
|
||||||
|
StableDiffusionXLControlNetInpaintPipeline,
|
||||||
|
]:
|
||||||
num_in_channels = 9
|
num_in_channels = 9
|
||||||
if num_in_channels is None and pipeline_class == StableDiffusionUpscalePipeline:
|
if num_in_channels is None and pipeline_class == StableDiffusionUpscalePipeline:
|
||||||
num_in_channels = 7
|
num_in_channels = 7
|
||||||
@@ -1686,7 +1694,9 @@ def download_from_original_stable_diffusion_ckpt(
|
|||||||
feature_extractor=feature_extractor,
|
feature_extractor=feature_extractor,
|
||||||
)
|
)
|
||||||
elif model_type in ["SDXL", "SDXL-Refiner"]:
|
elif model_type in ["SDXL", "SDXL-Refiner"]:
|
||||||
if model_type == "SDXL":
|
is_refiner = model_type == "SDXL-Refiner"
|
||||||
|
|
||||||
|
if (is_refiner is False) and (tokenizer is None):
|
||||||
try:
|
try:
|
||||||
tokenizer = CLIPTokenizer.from_pretrained(
|
tokenizer = CLIPTokenizer.from_pretrained(
|
||||||
"openai/clip-vit-large-patch14", local_files_only=local_files_only
|
"openai/clip-vit-large-patch14", local_files_only=local_files_only
|
||||||
@@ -1695,7 +1705,11 @@ def download_from_original_stable_diffusion_ckpt(
|
|||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"With local_files_only set to {local_files_only}, you must first locally save the tokenizer in the following path: 'openai/clip-vit-large-patch14'."
|
f"With local_files_only set to {local_files_only}, you must first locally save the tokenizer in the following path: 'openai/clip-vit-large-patch14'."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (is_refiner is False) and (text_encoder is None):
|
||||||
text_encoder = convert_ldm_clip_checkpoint(checkpoint, local_files_only=local_files_only)
|
text_encoder = convert_ldm_clip_checkpoint(checkpoint, local_files_only=local_files_only)
|
||||||
|
|
||||||
|
if tokenizer_2 is None:
|
||||||
try:
|
try:
|
||||||
tokenizer_2 = CLIPTokenizer.from_pretrained(
|
tokenizer_2 = CLIPTokenizer.from_pretrained(
|
||||||
"laion/CLIP-ViT-bigG-14-laion2B-39B-b160k", pad_token="!", local_files_only=local_files_only
|
"laion/CLIP-ViT-bigG-14-laion2B-39B-b160k", pad_token="!", local_files_only=local_files_only
|
||||||
@@ -1705,95 +1719,69 @@ def download_from_original_stable_diffusion_ckpt(
|
|||||||
f"With local_files_only set to {local_files_only}, you must first locally save the tokenizer in the following path: 'laion/CLIP-ViT-bigG-14-laion2B-39B-b160k' with `pad_token` set to '!'."
|
f"With local_files_only set to {local_files_only}, you must first locally save the tokenizer in the following path: 'laion/CLIP-ViT-bigG-14-laion2B-39B-b160k' with `pad_token` set to '!'."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if text_encoder_2 is None:
|
||||||
config_name = "laion/CLIP-ViT-bigG-14-laion2B-39B-b160k"
|
config_name = "laion/CLIP-ViT-bigG-14-laion2B-39B-b160k"
|
||||||
config_kwargs = {"projection_dim": 1280}
|
config_kwargs = {"projection_dim": 1280}
|
||||||
|
prefix = "conditioner.embedders.0.model." if is_refiner else "conditioner.embedders.1.model."
|
||||||
|
|
||||||
text_encoder_2 = convert_open_clip_checkpoint(
|
text_encoder_2 = convert_open_clip_checkpoint(
|
||||||
checkpoint,
|
checkpoint,
|
||||||
config_name,
|
config_name,
|
||||||
prefix="conditioner.embedders.1.model.",
|
prefix=prefix,
|
||||||
has_projection=True,
|
has_projection=True,
|
||||||
local_files_only=local_files_only,
|
local_files_only=local_files_only,
|
||||||
**config_kwargs,
|
**config_kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
if is_accelerate_available(): # SBM Now move model to cpu.
|
if is_accelerate_available(): # SBM Now move model to cpu.
|
||||||
if model_type in ["SDXL", "SDXL-Refiner"]:
|
for param_name, param in converted_unet_checkpoint.items():
|
||||||
for param_name, param in converted_unet_checkpoint.items():
|
set_module_tensor_to_device(unet, param_name, "cpu", value=param)
|
||||||
set_module_tensor_to_device(unet, param_name, "cpu", value=param)
|
|
||||||
|
|
||||||
if controlnet:
|
if controlnet:
|
||||||
pipe = pipeline_class(
|
pipe = pipeline_class(
|
||||||
vae=vae,
|
|
||||||
text_encoder=text_encoder,
|
|
||||||
tokenizer=tokenizer,
|
|
||||||
text_encoder_2=text_encoder_2,
|
|
||||||
tokenizer_2=tokenizer_2,
|
|
||||||
unet=unet,
|
|
||||||
controlnet=controlnet,
|
|
||||||
scheduler=scheduler,
|
|
||||||
force_zeros_for_empty_prompt=True,
|
|
||||||
)
|
|
||||||
elif adapter:
|
|
||||||
pipe = pipeline_class(
|
|
||||||
vae=vae,
|
|
||||||
text_encoder=text_encoder,
|
|
||||||
tokenizer=tokenizer,
|
|
||||||
text_encoder_2=text_encoder_2,
|
|
||||||
tokenizer_2=tokenizer_2,
|
|
||||||
unet=unet,
|
|
||||||
adapter=adapter,
|
|
||||||
scheduler=scheduler,
|
|
||||||
force_zeros_for_empty_prompt=True,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
pipe = pipeline_class(
|
|
||||||
vae=vae,
|
|
||||||
text_encoder=text_encoder,
|
|
||||||
tokenizer=tokenizer,
|
|
||||||
text_encoder_2=text_encoder_2,
|
|
||||||
tokenizer_2=tokenizer_2,
|
|
||||||
unet=unet,
|
|
||||||
scheduler=scheduler,
|
|
||||||
force_zeros_for_empty_prompt=True,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
tokenizer = None
|
|
||||||
text_encoder = None
|
|
||||||
try:
|
|
||||||
tokenizer_2 = CLIPTokenizer.from_pretrained(
|
|
||||||
"laion/CLIP-ViT-bigG-14-laion2B-39B-b160k", pad_token="!", local_files_only=local_files_only
|
|
||||||
)
|
|
||||||
except Exception:
|
|
||||||
raise ValueError(
|
|
||||||
f"With local_files_only set to {local_files_only}, you must first locally save the tokenizer in the following path: 'laion/CLIP-ViT-bigG-14-laion2B-39B-b160k' with `pad_token` set to '!'."
|
|
||||||
)
|
|
||||||
config_name = "laion/CLIP-ViT-bigG-14-laion2B-39B-b160k"
|
|
||||||
config_kwargs = {"projection_dim": 1280}
|
|
||||||
text_encoder_2 = convert_open_clip_checkpoint(
|
|
||||||
checkpoint,
|
|
||||||
config_name,
|
|
||||||
prefix="conditioner.embedders.0.model.",
|
|
||||||
has_projection=True,
|
|
||||||
local_files_only=local_files_only,
|
|
||||||
**config_kwargs,
|
|
||||||
)
|
|
||||||
|
|
||||||
if is_accelerate_available(): # SBM Now move model to cpu.
|
|
||||||
if model_type in ["SDXL", "SDXL-Refiner"]:
|
|
||||||
for param_name, param in converted_unet_checkpoint.items():
|
|
||||||
set_module_tensor_to_device(unet, param_name, "cpu", value=param)
|
|
||||||
|
|
||||||
pipe = StableDiffusionXLImg2ImgPipeline(
|
|
||||||
vae=vae,
|
vae=vae,
|
||||||
text_encoder=text_encoder,
|
text_encoder=text_encoder,
|
||||||
tokenizer=tokenizer,
|
tokenizer=tokenizer,
|
||||||
text_encoder_2=text_encoder_2,
|
text_encoder_2=text_encoder_2,
|
||||||
tokenizer_2=tokenizer_2,
|
tokenizer_2=tokenizer_2,
|
||||||
unet=unet,
|
unet=unet,
|
||||||
|
controlnet=controlnet,
|
||||||
scheduler=scheduler,
|
scheduler=scheduler,
|
||||||
requires_aesthetics_score=True,
|
force_zeros_for_empty_prompt=True,
|
||||||
force_zeros_for_empty_prompt=False,
|
|
||||||
)
|
)
|
||||||
|
elif adapter:
|
||||||
|
pipe = pipeline_class(
|
||||||
|
vae=vae,
|
||||||
|
text_encoder=text_encoder,
|
||||||
|
tokenizer=tokenizer,
|
||||||
|
text_encoder_2=text_encoder_2,
|
||||||
|
tokenizer_2=tokenizer_2,
|
||||||
|
unet=unet,
|
||||||
|
adapter=adapter,
|
||||||
|
scheduler=scheduler,
|
||||||
|
force_zeros_for_empty_prompt=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
pipeline_kwargs = {
|
||||||
|
"vae": vae,
|
||||||
|
"text_encoder": text_encoder,
|
||||||
|
"tokenizer": tokenizer,
|
||||||
|
"text_encoder_2": text_encoder_2,
|
||||||
|
"tokenizer_2": tokenizer_2,
|
||||||
|
"unet": unet,
|
||||||
|
"scheduler": scheduler,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pipeline_class == StableDiffusionXLImg2ImgPipeline) or (
|
||||||
|
pipeline_class == StableDiffusionXLInpaintPipeline
|
||||||
|
):
|
||||||
|
pipeline_kwargs.update({"requires_aesthetics_score": is_refiner})
|
||||||
|
|
||||||
|
if is_refiner:
|
||||||
|
pipeline_kwargs.update({"force_zeros_for_empty_prompt": False})
|
||||||
|
|
||||||
|
pipe = pipeline_class(**pipeline_kwargs)
|
||||||
else:
|
else:
|
||||||
text_config = create_ldm_bert_config(original_config)
|
text_config = create_ldm_bert_config(original_config)
|
||||||
text_model = convert_ldm_bert_checkpoint(checkpoint, text_config)
|
text_model = convert_ldm_bert_checkpoint(checkpoint, text_config)
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ from ...configuration_utils import FrozenDict
|
|||||||
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
||||||
from ...loaders import FromSingleFileMixin, IPAdapterMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
from ...loaders import FromSingleFileMixin, IPAdapterMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
||||||
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
||||||
|
from ...models.attention_processor import FusedAttnProcessor2_0
|
||||||
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 (
|
||||||
@@ -650,6 +651,67 @@ class StableDiffusionPipeline(
|
|||||||
"""Disables the FreeU mechanism if enabled."""
|
"""Disables the FreeU mechanism if enabled."""
|
||||||
self.unet.disable_freeu()
|
self.unet.disable_freeu()
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion_xl.pipeline_stable_diffusion_xl.StableDiffusionXLPipeline.fuse_qkv_projections
|
||||||
|
def fuse_qkv_projections(self, unet: bool = True, vae: bool = True):
|
||||||
|
"""
|
||||||
|
Enables fused QKV projections. For self-attention modules, all projection matrices (i.e., query,
|
||||||
|
key, value) are fused. For cross-attention modules, key and value projection matrices are fused.
|
||||||
|
|
||||||
|
<Tip warning={true}>
|
||||||
|
|
||||||
|
This API is 🧪 experimental.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
unet (`bool`, defaults to `True`): To apply fusion on the UNet.
|
||||||
|
vae (`bool`, defaults to `True`): To apply fusion on the VAE.
|
||||||
|
"""
|
||||||
|
self.fusing_unet = False
|
||||||
|
self.fusing_vae = False
|
||||||
|
|
||||||
|
if unet:
|
||||||
|
self.fusing_unet = True
|
||||||
|
self.unet.fuse_qkv_projections()
|
||||||
|
self.unet.set_attn_processor(FusedAttnProcessor2_0())
|
||||||
|
|
||||||
|
if vae:
|
||||||
|
if not isinstance(self.vae, AutoencoderKL):
|
||||||
|
raise ValueError("`fuse_qkv_projections()` is only supported for the VAE of type `AutoencoderKL`.")
|
||||||
|
|
||||||
|
self.fusing_vae = True
|
||||||
|
self.vae.fuse_qkv_projections()
|
||||||
|
self.vae.set_attn_processor(FusedAttnProcessor2_0())
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion_xl.pipeline_stable_diffusion_xl.StableDiffusionXLPipeline.unfuse_qkv_projections
|
||||||
|
def unfuse_qkv_projections(self, unet: bool = True, vae: bool = True):
|
||||||
|
"""Disable QKV projection fusion if enabled.
|
||||||
|
|
||||||
|
<Tip warning={true}>
|
||||||
|
|
||||||
|
This API is 🧪 experimental.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
unet (`bool`, defaults to `True`): To apply fusion on the UNet.
|
||||||
|
vae (`bool`, defaults to `True`): To apply fusion on the VAE.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if unet:
|
||||||
|
if not self.fusing_unet:
|
||||||
|
logger.warning("The UNet was not initially fused for QKV projections. Doing nothing.")
|
||||||
|
else:
|
||||||
|
self.unet.unfuse_qkv_projections()
|
||||||
|
self.fusing_unet = False
|
||||||
|
|
||||||
|
if vae:
|
||||||
|
if not self.fusing_vae:
|
||||||
|
logger.warning("The VAE was not initially fused for QKV projections. Doing nothing.")
|
||||||
|
else:
|
||||||
|
self.vae.unfuse_qkv_projections()
|
||||||
|
self.fusing_vae = False
|
||||||
|
|
||||||
# Copied from diffusers.pipelines.latent_consistency_models.pipeline_latent_consistency_text2img.LatentConsistencyModelPipeline.get_guidance_scale_embedding
|
# Copied from diffusers.pipelines.latent_consistency_models.pipeline_latent_consistency_text2img.LatentConsistencyModelPipeline.get_guidance_scale_embedding
|
||||||
def get_guidance_scale_embedding(self, w, embedding_dim=512, dtype=torch.float32):
|
def get_guidance_scale_embedding(self, w, embedding_dim=512, dtype=torch.float32):
|
||||||
"""
|
"""
|
||||||
|
|||||||
+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.
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ from ...configuration_utils import FrozenDict
|
|||||||
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
||||||
from ...loaders import FromSingleFileMixin, IPAdapterMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
from ...loaders import FromSingleFileMixin, IPAdapterMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
||||||
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
||||||
|
from ...models.attention_processor import FusedAttnProcessor2_0
|
||||||
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 (
|
||||||
@@ -718,6 +719,67 @@ class StableDiffusionImg2ImgPipeline(
|
|||||||
"""Disables the FreeU mechanism if enabled."""
|
"""Disables the FreeU mechanism if enabled."""
|
||||||
self.unet.disable_freeu()
|
self.unet.disable_freeu()
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion_xl.pipeline_stable_diffusion_xl.StableDiffusionXLPipeline.fuse_qkv_projections
|
||||||
|
def fuse_qkv_projections(self, unet: bool = True, vae: bool = True):
|
||||||
|
"""
|
||||||
|
Enables fused QKV projections. For self-attention modules, all projection matrices (i.e., query,
|
||||||
|
key, value) are fused. For cross-attention modules, key and value projection matrices are fused.
|
||||||
|
|
||||||
|
<Tip warning={true}>
|
||||||
|
|
||||||
|
This API is 🧪 experimental.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
unet (`bool`, defaults to `True`): To apply fusion on the UNet.
|
||||||
|
vae (`bool`, defaults to `True`): To apply fusion on the VAE.
|
||||||
|
"""
|
||||||
|
self.fusing_unet = False
|
||||||
|
self.fusing_vae = False
|
||||||
|
|
||||||
|
if unet:
|
||||||
|
self.fusing_unet = True
|
||||||
|
self.unet.fuse_qkv_projections()
|
||||||
|
self.unet.set_attn_processor(FusedAttnProcessor2_0())
|
||||||
|
|
||||||
|
if vae:
|
||||||
|
if not isinstance(self.vae, AutoencoderKL):
|
||||||
|
raise ValueError("`fuse_qkv_projections()` is only supported for the VAE of type `AutoencoderKL`.")
|
||||||
|
|
||||||
|
self.fusing_vae = True
|
||||||
|
self.vae.fuse_qkv_projections()
|
||||||
|
self.vae.set_attn_processor(FusedAttnProcessor2_0())
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion_xl.pipeline_stable_diffusion_xl.StableDiffusionXLPipeline.unfuse_qkv_projections
|
||||||
|
def unfuse_qkv_projections(self, unet: bool = True, vae: bool = True):
|
||||||
|
"""Disable QKV projection fusion if enabled.
|
||||||
|
|
||||||
|
<Tip warning={true}>
|
||||||
|
|
||||||
|
This API is 🧪 experimental.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
unet (`bool`, defaults to `True`): To apply fusion on the UNet.
|
||||||
|
vae (`bool`, defaults to `True`): To apply fusion on the VAE.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if unet:
|
||||||
|
if not self.fusing_unet:
|
||||||
|
logger.warning("The UNet was not initially fused for QKV projections. Doing nothing.")
|
||||||
|
else:
|
||||||
|
self.unet.unfuse_qkv_projections()
|
||||||
|
self.fusing_unet = False
|
||||||
|
|
||||||
|
if vae:
|
||||||
|
if not self.fusing_vae:
|
||||||
|
logger.warning("The VAE was not initially fused for QKV projections. Doing nothing.")
|
||||||
|
else:
|
||||||
|
self.vae.unfuse_qkv_projections()
|
||||||
|
self.fusing_vae = False
|
||||||
|
|
||||||
# Copied from diffusers.pipelines.latent_consistency_models.pipeline_latent_consistency_text2img.LatentConsistencyModelPipeline.get_guidance_scale_embedding
|
# Copied from diffusers.pipelines.latent_consistency_models.pipeline_latent_consistency_text2img.LatentConsistencyModelPipeline.get_guidance_scale_embedding
|
||||||
def get_guidance_scale_embedding(self, w, embedding_dim=512, dtype=torch.float32):
|
def get_guidance_scale_embedding(self, w, embedding_dim=512, dtype=torch.float32):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ from ...configuration_utils import FrozenDict
|
|||||||
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
||||||
from ...loaders import FromSingleFileMixin, IPAdapterMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
from ...loaders import FromSingleFileMixin, IPAdapterMixin, LoraLoaderMixin, TextualInversionLoaderMixin
|
||||||
from ...models import AsymmetricAutoencoderKL, AutoencoderKL, ImageProjection, UNet2DConditionModel
|
from ...models import AsymmetricAutoencoderKL, AutoencoderKL, ImageProjection, UNet2DConditionModel
|
||||||
|
from ...models.attention_processor import FusedAttnProcessor2_0
|
||||||
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 USE_PEFT_BACKEND, deprecate, logging, scale_lora_layers, unscale_lora_layers
|
from ...utils import USE_PEFT_BACKEND, deprecate, logging, scale_lora_layers, unscale_lora_layers
|
||||||
@@ -232,6 +233,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`]):
|
||||||
@@ -843,6 +845,67 @@ class StableDiffusionInpaintPipeline(
|
|||||||
"""Disables the FreeU mechanism if enabled."""
|
"""Disables the FreeU mechanism if enabled."""
|
||||||
self.unet.disable_freeu()
|
self.unet.disable_freeu()
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion_xl.pipeline_stable_diffusion_xl.StableDiffusionXLPipeline.fuse_qkv_projections
|
||||||
|
def fuse_qkv_projections(self, unet: bool = True, vae: bool = True):
|
||||||
|
"""
|
||||||
|
Enables fused QKV projections. For self-attention modules, all projection matrices (i.e., query,
|
||||||
|
key, value) are fused. For cross-attention modules, key and value projection matrices are fused.
|
||||||
|
|
||||||
|
<Tip warning={true}>
|
||||||
|
|
||||||
|
This API is 🧪 experimental.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
unet (`bool`, defaults to `True`): To apply fusion on the UNet.
|
||||||
|
vae (`bool`, defaults to `True`): To apply fusion on the VAE.
|
||||||
|
"""
|
||||||
|
self.fusing_unet = False
|
||||||
|
self.fusing_vae = False
|
||||||
|
|
||||||
|
if unet:
|
||||||
|
self.fusing_unet = True
|
||||||
|
self.unet.fuse_qkv_projections()
|
||||||
|
self.unet.set_attn_processor(FusedAttnProcessor2_0())
|
||||||
|
|
||||||
|
if vae:
|
||||||
|
if not isinstance(self.vae, AutoencoderKL):
|
||||||
|
raise ValueError("`fuse_qkv_projections()` is only supported for the VAE of type `AutoencoderKL`.")
|
||||||
|
|
||||||
|
self.fusing_vae = True
|
||||||
|
self.vae.fuse_qkv_projections()
|
||||||
|
self.vae.set_attn_processor(FusedAttnProcessor2_0())
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion_xl.pipeline_stable_diffusion_xl.StableDiffusionXLPipeline.unfuse_qkv_projections
|
||||||
|
def unfuse_qkv_projections(self, unet: bool = True, vae: bool = True):
|
||||||
|
"""Disable QKV projection fusion if enabled.
|
||||||
|
|
||||||
|
<Tip warning={true}>
|
||||||
|
|
||||||
|
This API is 🧪 experimental.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
unet (`bool`, defaults to `True`): To apply fusion on the UNet.
|
||||||
|
vae (`bool`, defaults to `True`): To apply fusion on the VAE.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if unet:
|
||||||
|
if not self.fusing_unet:
|
||||||
|
logger.warning("The UNet was not initially fused for QKV projections. Doing nothing.")
|
||||||
|
else:
|
||||||
|
self.unet.unfuse_qkv_projections()
|
||||||
|
self.fusing_unet = False
|
||||||
|
|
||||||
|
if vae:
|
||||||
|
if not self.fusing_vae:
|
||||||
|
logger.warning("The VAE was not initially fused for QKV projections. Doing nothing.")
|
||||||
|
else:
|
||||||
|
self.vae.unfuse_qkv_projections()
|
||||||
|
self.fusing_vae = False
|
||||||
|
|
||||||
# Copied from diffusers.pipelines.latent_consistency_models.pipeline_latent_consistency_text2img.LatentConsistencyModelPipeline.get_guidance_scale_embedding
|
# Copied from diffusers.pipelines.latent_consistency_models.pipeline_latent_consistency_text2img.LatentConsistencyModelPipeline.get_guidance_scale_embedding
|
||||||
def get_guidance_scale_embedding(self, w, embedding_dim=512, dtype=torch.float32):
|
def get_guidance_scale_embedding(self, w, embedding_dim=512, dtype=torch.float32):
|
||||||
"""
|
"""
|
||||||
|
|||||||
+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.)
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ from typing import Any, Callable, Dict, List, Optional, Union
|
|||||||
|
|
||||||
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 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 KarrasDiffusionSchedulers
|
from ...schedulers import KarrasDiffusionSchedulers
|
||||||
from ...utils import (
|
from ...utils import (
|
||||||
@@ -98,13 +98,17 @@ class CrossAttnStoreProcessor:
|
|||||||
|
|
||||||
|
|
||||||
# Modified to get self-attention guidance scale in this paper (https://arxiv.org/pdf/2210.00939.pdf) as an input
|
# Modified to get self-attention guidance scale in this paper (https://arxiv.org/pdf/2210.00939.pdf) as an input
|
||||||
class StableDiffusionSAGPipeline(DiffusionPipeline, TextualInversionLoaderMixin):
|
class StableDiffusionSAGPipeline(DiffusionPipeline, TextualInversionLoaderMixin, IPAdapterMixin):
|
||||||
r"""
|
r"""
|
||||||
Pipeline for text-to-image generation using Stable Diffusion.
|
Pipeline for text-to-image generation using Stable Diffusion.
|
||||||
|
|
||||||
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.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.
|
||||||
@@ -126,7 +130,7 @@ class StableDiffusionSAGPipeline(DiffusionPipeline, TextualInversionLoaderMixin)
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
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__(
|
||||||
@@ -138,6 +142,7 @@ class StableDiffusionSAGPipeline(DiffusionPipeline, TextualInversionLoaderMixin)
|
|||||||
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__()
|
||||||
@@ -150,6 +155,7 @@ class StableDiffusionSAGPipeline(DiffusionPipeline, TextualInversionLoaderMixin)
|
|||||||
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)
|
||||||
@@ -386,6 +392,31 @@ class StableDiffusionSAGPipeline(DiffusionPipeline, TextualInversionLoaderMixin)
|
|||||||
|
|
||||||
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:
|
||||||
@@ -519,6 +550,7 @@ class StableDiffusionSAGPipeline(DiffusionPipeline, TextualInversionLoaderMixin)
|
|||||||
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,
|
||||||
@@ -565,6 +597,8 @@ class StableDiffusionSAGPipeline(DiffusionPipeline, TextualInversionLoaderMixin)
|
|||||||
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`):
|
||||||
@@ -618,6 +652,14 @@ class StableDiffusionSAGPipeline(DiffusionPipeline, TextualInversionLoaderMixin)
|
|||||||
# `sag_scale = 0` means no self-attention guidance
|
# `sag_scale = 0` means no self-attention guidance
|
||||||
do_self_attention_guidance = sag_scale > 0.0
|
do_self_attention_guidance = sag_scale > 0.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,
|
||||||
@@ -655,6 +697,10 @@ class StableDiffusionSAGPipeline(DiffusionPipeline, TextualInversionLoaderMixin)
|
|||||||
# 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
|
||||||
|
added_uncond_kwargs = {"image_embeds": negative_image_embeds} if ip_adapter_image is not None else None
|
||||||
|
|
||||||
# 7. Denoising loop
|
# 7. Denoising loop
|
||||||
store_processor = CrossAttnStoreProcessor()
|
store_processor = CrossAttnStoreProcessor()
|
||||||
self.unet.mid_block.attentions[0].transformer_blocks[0].attn1.processor = store_processor
|
self.unet.mid_block.attentions[0].transformer_blocks[0].attn1.processor = store_processor
|
||||||
@@ -680,6 +726,7 @@ class StableDiffusionSAGPipeline(DiffusionPipeline, TextualInversionLoaderMixin)
|
|||||||
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,
|
||||||
).sample
|
).sample
|
||||||
|
|
||||||
# perform guidance
|
# perform guidance
|
||||||
@@ -703,7 +750,12 @@ class StableDiffusionSAGPipeline(DiffusionPipeline, TextualInversionLoaderMixin)
|
|||||||
)
|
)
|
||||||
uncond_emb, _ = prompt_embeds.chunk(2)
|
uncond_emb, _ = prompt_embeds.chunk(2)
|
||||||
# forward and give guidance
|
# forward and give guidance
|
||||||
degraded_pred = self.unet(degraded_latents, t, encoder_hidden_states=uncond_emb).sample
|
degraded_pred = self.unet(
|
||||||
|
degraded_latents,
|
||||||
|
t,
|
||||||
|
encoder_hidden_states=uncond_emb,
|
||||||
|
added_cond_kwargs=added_uncond_kwargs,
|
||||||
|
).sample
|
||||||
noise_pred += sag_scale * (noise_pred_uncond - degraded_pred)
|
noise_pred += sag_scale * (noise_pred_uncond - degraded_pred)
|
||||||
else:
|
else:
|
||||||
# DDIM-like prediction of x0
|
# DDIM-like prediction of x0
|
||||||
@@ -715,7 +767,12 @@ class StableDiffusionSAGPipeline(DiffusionPipeline, TextualInversionLoaderMixin)
|
|||||||
pred_x0, cond_attn, map_size, t, self.pred_epsilon(latents, noise_pred, t)
|
pred_x0, cond_attn, map_size, t, self.pred_epsilon(latents, noise_pred, t)
|
||||||
)
|
)
|
||||||
# forward and give guidance
|
# forward and give guidance
|
||||||
degraded_pred = self.unet(degraded_latents, t, encoder_hidden_states=prompt_embeds).sample
|
degraded_pred = self.unet(
|
||||||
|
degraded_latents,
|
||||||
|
t,
|
||||||
|
encoder_hidden_states=prompt_embeds,
|
||||||
|
added_cond_kwargs=added_cond_kwargs,
|
||||||
|
).sample
|
||||||
noise_pred += sag_scale * (noise_pred - degraded_pred)
|
noise_pred += sag_scale * (noise_pred - degraded_pred)
|
||||||
|
|
||||||
# compute the previous noisy sample x_t -> x_t-1
|
# compute the previous noisy sample x_t -> x_t-1
|
||||||
|
|||||||
@@ -76,6 +76,12 @@ class StableDiffusionUpscalePipeline(
|
|||||||
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.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.
|
||||||
|
|||||||
@@ -65,6 +65,11 @@ class StableUnCLIPPipeline(DiffusionPipeline, TextualInversionLoaderMixin, LoraL
|
|||||||
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:
|
||||||
prior_tokenizer ([`CLIPTokenizer`]):
|
prior_tokenizer ([`CLIPTokenizer`]):
|
||||||
A [`CLIPTokenizer`].
|
A [`CLIPTokenizer`].
|
||||||
|
|||||||
@@ -76,6 +76,11 @@ class StableUnCLIPImg2ImgPipeline(DiffusionPipeline, TextualInversionLoaderMixin
|
|||||||
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:
|
||||||
feature_extractor ([`CLIPImageProcessor`]):
|
feature_extractor ([`CLIPImageProcessor`]):
|
||||||
Feature extractor for image pre-processing before being encoded.
|
Feature extractor for image pre-processing before being encoded.
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ from typing import Callable, List, Optional, Union
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import torch
|
import torch
|
||||||
from packaging import version
|
from packaging import version
|
||||||
from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer
|
from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer, CLIPVisionModelWithProjection
|
||||||
|
|
||||||
from ...configuration_utils import FrozenDict
|
from ...configuration_utils import FrozenDict
|
||||||
from ...models import AutoencoderKL, UNet2DConditionModel
|
from ...image_processor import PipelineImageInput
|
||||||
|
from ...loaders import IPAdapterMixin
|
||||||
|
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
||||||
from ...schedulers import KarrasDiffusionSchedulers
|
from ...schedulers import KarrasDiffusionSchedulers
|
||||||
from ...utils import deprecate, logging
|
from ...utils import deprecate, logging
|
||||||
from ...utils.torch_utils import randn_tensor
|
from ...utils.torch_utils import randn_tensor
|
||||||
@@ -20,13 +22,16 @@ from .safety_checker import SafeStableDiffusionSafetyChecker
|
|||||||
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
||||||
|
|
||||||
|
|
||||||
class StableDiffusionPipelineSafe(DiffusionPipeline):
|
class StableDiffusionPipelineSafe(DiffusionPipeline, IPAdapterMixin):
|
||||||
r"""
|
r"""
|
||||||
Pipeline based on the [`StableDiffusionPipeline`] for text-to-image generation using Safe Latent Diffusion.
|
Pipeline based on the [`StableDiffusionPipeline`] for text-to-image generation using Safe Latent Diffusion.
|
||||||
|
|
||||||
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.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.
|
||||||
@@ -48,7 +53,7 @@ class StableDiffusionPipelineSafe(DiffusionPipeline):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
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"]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -59,6 +64,7 @@ class StableDiffusionPipelineSafe(DiffusionPipeline):
|
|||||||
scheduler: KarrasDiffusionSchedulers,
|
scheduler: KarrasDiffusionSchedulers,
|
||||||
safety_checker: SafeStableDiffusionSafetyChecker,
|
safety_checker: SafeStableDiffusionSafetyChecker,
|
||||||
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__()
|
||||||
@@ -140,6 +146,7 @@ class StableDiffusionPipelineSafe(DiffusionPipeline):
|
|||||||
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._safety_text_concept = safety_concept
|
self._safety_text_concept = safety_concept
|
||||||
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)
|
||||||
@@ -467,6 +474,31 @@ class StableDiffusionPipelineSafe(DiffusionPipeline):
|
|||||||
noise_guidance = noise_guidance - noise_guidance_safety
|
noise_guidance = noise_guidance - noise_guidance_safety
|
||||||
return noise_guidance, safety_momentum
|
return noise_guidance, safety_momentum
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
@torch.no_grad()
|
@torch.no_grad()
|
||||||
def __call__(
|
def __call__(
|
||||||
self,
|
self,
|
||||||
@@ -480,6 +512,7 @@ class StableDiffusionPipelineSafe(DiffusionPipeline):
|
|||||||
eta: float = 0.0,
|
eta: float = 0.0,
|
||||||
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,
|
||||||
|
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,
|
||||||
@@ -521,6 +554,8 @@ class StableDiffusionPipelineSafe(DiffusionPipeline):
|
|||||||
Pre-generated noisy latents sampled from a Gaussian distribution, to be used as inputs for image
|
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
|
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`.
|
tensor is generated by sampling using the supplied random `generator`.
|
||||||
|
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`):
|
||||||
@@ -560,10 +595,11 @@ class StableDiffusionPipelineSafe(DiffusionPipeline):
|
|||||||
```py
|
```py
|
||||||
import torch
|
import torch
|
||||||
from diffusers import StableDiffusionPipelineSafe
|
from diffusers import StableDiffusionPipelineSafe
|
||||||
|
from diffusers.pipelines.stable_diffusion_safe import SafetyConfig
|
||||||
|
|
||||||
pipeline = StableDiffusionPipelineSafe.from_pretrained(
|
pipeline = StableDiffusionPipelineSafe.from_pretrained(
|
||||||
"AIML-TUDA/stable-diffusion-safe", torch_dtype=torch.float16
|
"AIML-TUDA/stable-diffusion-safe", torch_dtype=torch.float16
|
||||||
)
|
).to("cuda")
|
||||||
prompt = "the four horsewomen of the apocalypse, painting by tom of finland, gaston bussiere, craig mullins, j. c. leyendecker"
|
prompt = "the four horsewomen of the apocalypse, painting by tom of finland, gaston bussiere, craig mullins, j. c. leyendecker"
|
||||||
image = pipeline(prompt=prompt, **SafetyConfig.MEDIUM).images[0]
|
image = pipeline(prompt=prompt, **SafetyConfig.MEDIUM).images[0]
|
||||||
```
|
```
|
||||||
@@ -588,6 +624,17 @@ class StableDiffusionPipelineSafe(DiffusionPipeline):
|
|||||||
if not enable_safety_guidance:
|
if not enable_safety_guidance:
|
||||||
warnings.warn("Safety checker disabled!")
|
warnings.warn("Safety checker disabled!")
|
||||||
|
|
||||||
|
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:
|
||||||
|
if enable_safety_guidance:
|
||||||
|
image_embeds = torch.cat([negative_image_embeds, image_embeds, image_embeds])
|
||||||
|
else:
|
||||||
|
image_embeds = torch.cat([negative_image_embeds, image_embeds])
|
||||||
|
|
||||||
# 3. Encode input prompt
|
# 3. Encode input prompt
|
||||||
prompt_embeds = self._encode_prompt(
|
prompt_embeds = self._encode_prompt(
|
||||||
prompt, device, num_images_per_prompt, do_classifier_free_guidance, negative_prompt, enable_safety_guidance
|
prompt, device, num_images_per_prompt, do_classifier_free_guidance, negative_prompt, enable_safety_guidance
|
||||||
@@ -613,6 +660,9 @@ class StableDiffusionPipelineSafe(DiffusionPipeline):
|
|||||||
# 6. Prepare extra step kwargs.
|
# 6. Prepare extra step kwargs.
|
||||||
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
|
||||||
|
|
||||||
safety_momentum = None
|
safety_momentum = None
|
||||||
|
|
||||||
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
|
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
|
||||||
@@ -627,7 +677,9 @@ class StableDiffusionPipelineSafe(DiffusionPipeline):
|
|||||||
latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)
|
latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)
|
||||||
|
|
||||||
# predict the noise residual
|
# predict the noise residual
|
||||||
noise_pred = self.unet(latent_model_input, t, encoder_hidden_states=prompt_embeds).sample
|
noise_pred = self.unet(
|
||||||
|
latent_model_input, t, encoder_hidden_states=prompt_embeds, added_cond_kwargs=added_cond_kwargs
|
||||||
|
).sample
|
||||||
|
|
||||||
# perform guidance
|
# perform guidance
|
||||||
if do_classifier_free_guidance:
|
if do_classifier_free_guidance:
|
||||||
|
|||||||
@@ -159,12 +159,12 @@ class StableDiffusionXLPipeline(
|
|||||||
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.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
||||||
- *Ckpt*: [`loaders.FromSingleFileMixin.from_single_file`]
|
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
||||||
|
- [`~loaders.StableDiffusionXLLoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
as well as the following saving methods:
|
- [`~loaders.StableDiffusionXLLoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
- *LoRA*: [`loaders.StableDiffusionXLLoraLoaderMixin.save_lora_weights`]
|
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
|
|||||||
+68
-6
@@ -35,6 +35,7 @@ from ...loaders import (
|
|||||||
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
||||||
from ...models.attention_processor import (
|
from ...models.attention_processor import (
|
||||||
AttnProcessor2_0,
|
AttnProcessor2_0,
|
||||||
|
FusedAttnProcessor2_0,
|
||||||
LoRAAttnProcessor2_0,
|
LoRAAttnProcessor2_0,
|
||||||
LoRAXFormersAttnProcessor,
|
LoRAXFormersAttnProcessor,
|
||||||
XFormersAttnProcessor,
|
XFormersAttnProcessor,
|
||||||
@@ -176,12 +177,12 @@ class StableDiffusionXLImg2ImgPipeline(
|
|||||||
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.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
||||||
- *Ckpt*: [`loaders.FromSingleFileMixin.from_single_file`]
|
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
||||||
|
- [`~loaders.StableDiffusionXLLoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
as well as the following saving methods:
|
- [`~loaders.StableDiffusionXLLoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
- *LoRA*: [`loaders.StableDiffusionXLLoraLoaderMixin.save_lora_weights`]
|
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
@@ -864,6 +865,67 @@ class StableDiffusionXLImg2ImgPipeline(
|
|||||||
"""Disables the FreeU mechanism if enabled."""
|
"""Disables the FreeU mechanism if enabled."""
|
||||||
self.unet.disable_freeu()
|
self.unet.disable_freeu()
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion_xl.pipeline_stable_diffusion_xl.StableDiffusionXLPipeline.fuse_qkv_projections
|
||||||
|
def fuse_qkv_projections(self, unet: bool = True, vae: bool = True):
|
||||||
|
"""
|
||||||
|
Enables fused QKV projections. For self-attention modules, all projection matrices (i.e., query,
|
||||||
|
key, value) are fused. For cross-attention modules, key and value projection matrices are fused.
|
||||||
|
|
||||||
|
<Tip warning={true}>
|
||||||
|
|
||||||
|
This API is 🧪 experimental.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
unet (`bool`, defaults to `True`): To apply fusion on the UNet.
|
||||||
|
vae (`bool`, defaults to `True`): To apply fusion on the VAE.
|
||||||
|
"""
|
||||||
|
self.fusing_unet = False
|
||||||
|
self.fusing_vae = False
|
||||||
|
|
||||||
|
if unet:
|
||||||
|
self.fusing_unet = True
|
||||||
|
self.unet.fuse_qkv_projections()
|
||||||
|
self.unet.set_attn_processor(FusedAttnProcessor2_0())
|
||||||
|
|
||||||
|
if vae:
|
||||||
|
if not isinstance(self.vae, AutoencoderKL):
|
||||||
|
raise ValueError("`fuse_qkv_projections()` is only supported for the VAE of type `AutoencoderKL`.")
|
||||||
|
|
||||||
|
self.fusing_vae = True
|
||||||
|
self.vae.fuse_qkv_projections()
|
||||||
|
self.vae.set_attn_processor(FusedAttnProcessor2_0())
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion_xl.pipeline_stable_diffusion_xl.StableDiffusionXLPipeline.unfuse_qkv_projections
|
||||||
|
def unfuse_qkv_projections(self, unet: bool = True, vae: bool = True):
|
||||||
|
"""Disable QKV projection fusion if enabled.
|
||||||
|
|
||||||
|
<Tip warning={true}>
|
||||||
|
|
||||||
|
This API is 🧪 experimental.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
unet (`bool`, defaults to `True`): To apply fusion on the UNet.
|
||||||
|
vae (`bool`, defaults to `True`): To apply fusion on the VAE.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if unet:
|
||||||
|
if not self.fusing_unet:
|
||||||
|
logger.warning("The UNet was not initially fused for QKV projections. Doing nothing.")
|
||||||
|
else:
|
||||||
|
self.unet.unfuse_qkv_projections()
|
||||||
|
self.fusing_unet = False
|
||||||
|
|
||||||
|
if vae:
|
||||||
|
if not self.fusing_vae:
|
||||||
|
logger.warning("The VAE was not initially fused for QKV projections. Doing nothing.")
|
||||||
|
else:
|
||||||
|
self.vae.unfuse_qkv_projections()
|
||||||
|
self.fusing_vae = False
|
||||||
|
|
||||||
# Copied from diffusers.pipelines.latent_consistency_models.pipeline_latent_consistency_text2img.LatentConsistencyModelPipeline.get_guidance_scale_embedding
|
# Copied from diffusers.pipelines.latent_consistency_models.pipeline_latent_consistency_text2img.LatentConsistencyModelPipeline.get_guidance_scale_embedding
|
||||||
def get_guidance_scale_embedding(self, w, embedding_dim=512, dtype=torch.float32):
|
def get_guidance_scale_embedding(self, w, embedding_dim=512, dtype=torch.float32):
|
||||||
"""
|
"""
|
||||||
|
|||||||
+68
-6
@@ -36,6 +36,7 @@ from ...loaders import (
|
|||||||
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
from ...models import AutoencoderKL, ImageProjection, UNet2DConditionModel
|
||||||
from ...models.attention_processor import (
|
from ...models.attention_processor import (
|
||||||
AttnProcessor2_0,
|
AttnProcessor2_0,
|
||||||
|
FusedAttnProcessor2_0,
|
||||||
LoRAAttnProcessor2_0,
|
LoRAAttnProcessor2_0,
|
||||||
LoRAXFormersAttnProcessor,
|
LoRAXFormersAttnProcessor,
|
||||||
XFormersAttnProcessor,
|
XFormersAttnProcessor,
|
||||||
@@ -321,12 +322,12 @@ class StableDiffusionXLInpaintPipeline(
|
|||||||
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.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
||||||
- *Ckpt*: [`loaders.FromSingleFileMixin.from_single_file`]
|
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
||||||
|
- [`~loaders.StableDiffusionXLLoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
as well as the following saving methods:
|
- [`~loaders.StableDiffusionXLLoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
- *LoRA*: [`loaders.StableDiffusionXLLoraLoaderMixin.save_lora_weights`]
|
- [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
@@ -1084,6 +1085,67 @@ class StableDiffusionXLInpaintPipeline(
|
|||||||
"""Disables the FreeU mechanism if enabled."""
|
"""Disables the FreeU mechanism if enabled."""
|
||||||
self.unet.disable_freeu()
|
self.unet.disable_freeu()
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion_xl.pipeline_stable_diffusion_xl.StableDiffusionXLPipeline.fuse_qkv_projections
|
||||||
|
def fuse_qkv_projections(self, unet: bool = True, vae: bool = True):
|
||||||
|
"""
|
||||||
|
Enables fused QKV projections. For self-attention modules, all projection matrices (i.e., query,
|
||||||
|
key, value) are fused. For cross-attention modules, key and value projection matrices are fused.
|
||||||
|
|
||||||
|
<Tip warning={true}>
|
||||||
|
|
||||||
|
This API is 🧪 experimental.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
unet (`bool`, defaults to `True`): To apply fusion on the UNet.
|
||||||
|
vae (`bool`, defaults to `True`): To apply fusion on the VAE.
|
||||||
|
"""
|
||||||
|
self.fusing_unet = False
|
||||||
|
self.fusing_vae = False
|
||||||
|
|
||||||
|
if unet:
|
||||||
|
self.fusing_unet = True
|
||||||
|
self.unet.fuse_qkv_projections()
|
||||||
|
self.unet.set_attn_processor(FusedAttnProcessor2_0())
|
||||||
|
|
||||||
|
if vae:
|
||||||
|
if not isinstance(self.vae, AutoencoderKL):
|
||||||
|
raise ValueError("`fuse_qkv_projections()` is only supported for the VAE of type `AutoencoderKL`.")
|
||||||
|
|
||||||
|
self.fusing_vae = True
|
||||||
|
self.vae.fuse_qkv_projections()
|
||||||
|
self.vae.set_attn_processor(FusedAttnProcessor2_0())
|
||||||
|
|
||||||
|
# Copied from diffusers.pipelines.stable_diffusion_xl.pipeline_stable_diffusion_xl.StableDiffusionXLPipeline.unfuse_qkv_projections
|
||||||
|
def unfuse_qkv_projections(self, unet: bool = True, vae: bool = True):
|
||||||
|
"""Disable QKV projection fusion if enabled.
|
||||||
|
|
||||||
|
<Tip warning={true}>
|
||||||
|
|
||||||
|
This API is 🧪 experimental.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
unet (`bool`, defaults to `True`): To apply fusion on the UNet.
|
||||||
|
vae (`bool`, defaults to `True`): To apply fusion on the VAE.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if unet:
|
||||||
|
if not self.fusing_unet:
|
||||||
|
logger.warning("The UNet was not initially fused for QKV projections. Doing nothing.")
|
||||||
|
else:
|
||||||
|
self.unet.unfuse_qkv_projections()
|
||||||
|
self.fusing_unet = False
|
||||||
|
|
||||||
|
if vae:
|
||||||
|
if not self.fusing_vae:
|
||||||
|
logger.warning("The VAE was not initially fused for QKV projections. Doing nothing.")
|
||||||
|
else:
|
||||||
|
self.vae.unfuse_qkv_projections()
|
||||||
|
self.fusing_vae = False
|
||||||
|
|
||||||
# Copied from diffusers.pipelines.latent_consistency_models.pipeline_latent_consistency_text2img.LatentConsistencyModelPipeline.get_guidance_scale_embedding
|
# Copied from diffusers.pipelines.latent_consistency_models.pipeline_latent_consistency_text2img.LatentConsistencyModelPipeline.get_guidance_scale_embedding
|
||||||
def get_guidance_scale_embedding(self, w, embedding_dim=512, dtype=torch.float32):
|
def get_guidance_scale_embedding(self, w, embedding_dim=512, dtype=torch.float32):
|
||||||
"""
|
"""
|
||||||
|
|||||||
+5
-5
@@ -126,11 +126,11 @@ class StableDiffusionXLInstructPix2PixPipeline(
|
|||||||
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.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
|
||||||
|
- [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
||||||
as well as the following saving methods:
|
- [`~loaders.StableDiffusionXLLoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
- *LoRA*: [`loaders.StableDiffusionXLLoraLoaderMixin.save_lora_weights`]
|
- [`~loaders.StableDiffusionXLLoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vae ([`AutoencoderKL`]):
|
vae ([`AutoencoderKL`]):
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ from ...image_processor import VaeImageProcessor
|
|||||||
from ...models import AutoencoderKLTemporalDecoder, UNetSpatioTemporalConditionModel
|
from ...models import AutoencoderKLTemporalDecoder, UNetSpatioTemporalConditionModel
|
||||||
from ...schedulers import EulerDiscreteScheduler
|
from ...schedulers import EulerDiscreteScheduler
|
||||||
from ...utils import BaseOutput, logging
|
from ...utils import BaseOutput, logging
|
||||||
from ...utils.torch_utils import randn_tensor
|
from ...utils.torch_utils import is_compiled_module, randn_tensor
|
||||||
from ..pipeline_utils import DiffusionPipeline
|
from ..pipeline_utils import DiffusionPipeline
|
||||||
|
|
||||||
|
|
||||||
@@ -211,7 +211,8 @@ class StableVideoDiffusionPipeline(DiffusionPipeline):
|
|||||||
|
|
||||||
latents = 1 / self.vae.config.scaling_factor * latents
|
latents = 1 / self.vae.config.scaling_factor * latents
|
||||||
|
|
||||||
accepts_num_frames = "num_frames" in set(inspect.signature(self.vae.forward).parameters.keys())
|
forward_vae_fn = self.vae._orig_mod.forward if is_compiled_module(self.vae) else self.vae.forward
|
||||||
|
accepts_num_frames = "num_frames" in set(inspect.signature(forward_vae_fn).parameters.keys())
|
||||||
|
|
||||||
# decode decode_chunk_size frames at a time to avoid OOM
|
# decode decode_chunk_size frames at a time to avoid OOM
|
||||||
frames = []
|
frames = []
|
||||||
|
|||||||
@@ -178,6 +178,12 @@ class StableDiffusionXLAdapterPipeline(
|
|||||||
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.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
|
||||||
|
- [`~loaders.StableDiffusionXLLoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
|
- [`~loaders.StableDiffusionXLLoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
adapter ([`T2IAdapter`] or [`MultiAdapter`] or `List[T2IAdapter]`):
|
adapter ([`T2IAdapter`] or [`MultiAdapter`] or `List[T2IAdapter]`):
|
||||||
Provides additional conditioning to the unet during the denoising process. If you set multiple Adapter as a
|
Provides additional conditioning to the unet during the denoising process. If you set multiple Adapter as a
|
||||||
|
|||||||
@@ -83,6 +83,11 @@ class TextToVideoSDPipeline(DiffusionPipeline, TextualInversionLoaderMixin, Lora
|
|||||||
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.
|
||||||
|
|||||||
+5
@@ -159,6 +159,11 @@ class VideoToVideoSDPipeline(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 videos to and from latent representations.
|
Variational Auto-Encoder (VAE) Model to encode and decode videos to and from latent representations.
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import torch
|
|||||||
import torch.nn as nn
|
import torch.nn as nn
|
||||||
|
|
||||||
from ...configuration_utils import ConfigMixin, register_to_config
|
from ...configuration_utils import ConfigMixin, register_to_config
|
||||||
|
from ...models.autoencoders.vae import DecoderOutput, VectorQuantizer
|
||||||
from ...models.modeling_utils import ModelMixin
|
from ...models.modeling_utils import ModelMixin
|
||||||
from ...models.vae import DecoderOutput, VectorQuantizer
|
|
||||||
from ...models.vq_model import VQEncoderOutput
|
from ...models.vq_model import VQEncoderOutput
|
||||||
from ...utils.accelerate_utils import apply_forward_hook
|
from ...utils.accelerate_utils import apply_forward_hook
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,10 @@ class WuerstchenPriorPipeline(DiffusionPipeline, LoraLoaderMixin):
|
|||||||
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.LoraLoaderMixin.load_lora_weights`] for loading LoRA weights
|
||||||
|
- [`~loaders.LoraLoaderMixin.save_lora_weights`] for saving LoRA weights
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
prior ([`Prior`]):
|
prior ([`Prior`]):
|
||||||
The canonical unCLIP prior to approximate the image embedding from the text embedding.
|
The canonical unCLIP prior to approximate the image embedding from the text embedding.
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ class CMStochasticIterativeScheduler(SchedulerMixin, ConfigMixin):
|
|||||||
self.custom_timesteps = False
|
self.custom_timesteps = False
|
||||||
self.is_scale_input_called = False
|
self.is_scale_input_called = False
|
||||||
self._step_index = None
|
self._step_index = None
|
||||||
|
self.sigmas.to("cpu") # to avoid too much CPU/GPU communication
|
||||||
|
|
||||||
def index_for_timestep(self, timestep, schedule_timesteps=None):
|
def index_for_timestep(self, timestep, schedule_timesteps=None):
|
||||||
if schedule_timesteps is None:
|
if schedule_timesteps is None:
|
||||||
@@ -230,6 +231,7 @@ class CMStochasticIterativeScheduler(SchedulerMixin, ConfigMixin):
|
|||||||
self.timesteps = torch.from_numpy(timesteps).to(device=device)
|
self.timesteps = torch.from_numpy(timesteps).to(device=device)
|
||||||
|
|
||||||
self._step_index = None
|
self._step_index = None
|
||||||
|
self.sigmas.to("cpu") # to avoid too much CPU/GPU communication
|
||||||
|
|
||||||
# Modified _convert_to_karras implementation that takes in ramp as argument
|
# Modified _convert_to_karras implementation that takes in ramp as argument
|
||||||
def _convert_to_karras(self, ramp):
|
def _convert_to_karras(self, ramp):
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ class DEISMultistepScheduler(SchedulerMixin, ConfigMixin):
|
|||||||
self.model_outputs = [None] * solver_order
|
self.model_outputs = [None] * solver_order
|
||||||
self.lower_order_nums = 0
|
self.lower_order_nums = 0
|
||||||
self._step_index = None
|
self._step_index = None
|
||||||
|
self.sigmas.to("cpu") # to avoid too much CPU/GPU communication
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def step_index(self):
|
def step_index(self):
|
||||||
@@ -254,6 +255,7 @@ class DEISMultistepScheduler(SchedulerMixin, ConfigMixin):
|
|||||||
|
|
||||||
# add an index counter for schedulers that allow duplicated timesteps
|
# add an index counter for schedulers that allow duplicated timesteps
|
||||||
self._step_index = None
|
self._step_index = None
|
||||||
|
self.sigmas.to("cpu") # to avoid too much CPU/GPU communication
|
||||||
|
|
||||||
# Copied from diffusers.schedulers.scheduling_ddpm.DDPMScheduler._threshold_sample
|
# Copied from diffusers.schedulers.scheduling_ddpm.DDPMScheduler._threshold_sample
|
||||||
def _threshold_sample(self, sample: torch.FloatTensor) -> torch.FloatTensor:
|
def _threshold_sample(self, sample: torch.FloatTensor) -> torch.FloatTensor:
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user