Unverified Commit 7956c36a authored by YiYi Xu's avatar YiYi Xu Committed by GitHub
Browse files

add a `from_pipe` method to `DiffusionPipeline` (#7241)



* add from_pipe



---------
Co-authored-by: default avataryiyixuxu <yixu310@gmail,com>
Co-authored-by: default avatarSayak Paul <spsayakpaul@gmail.com>
Co-authored-by: default avatarDhruv Nair <dhruv.nair@gmail.com>
parent 5266ab79
...@@ -26,10 +26,12 @@ from diffusers import ( ...@@ -26,10 +26,12 @@ from diffusers import (
DDIMScheduler, DDIMScheduler,
DiffusionPipeline, DiffusionPipeline,
StableDiffusionPipeline, StableDiffusionPipeline,
StableDiffusionXLPipeline,
UNet2DConditionModel, UNet2DConditionModel,
) )
from diffusers.image_processor import VaeImageProcessor from diffusers.image_processor import VaeImageProcessor
from diffusers.loaders import IPAdapterMixin from diffusers.loaders import IPAdapterMixin
from diffusers.models.attention_processor import AttnProcessor
from diffusers.models.unets.unet_3d_condition import UNet3DConditionModel from diffusers.models.unets.unet_3d_condition import UNet3DConditionModel
from diffusers.models.unets.unet_i2vgen_xl import I2VGenXLUNet from diffusers.models.unets.unet_i2vgen_xl import I2VGenXLUNet
from diffusers.models.unets.unet_motion_model import UNetMotionModel from diffusers.models.unets.unet_motion_model import UNetMotionModel
...@@ -511,6 +513,186 @@ class PipelineLatentTesterMixin: ...@@ -511,6 +513,186 @@ class PipelineLatentTesterMixin:
assert out_vae_np.shape == out_np.shape assert out_vae_np.shape == out_np.shape
@require_torch
class PipelineFromPipeTesterMixin:
@property
def original_pipeline_class(self):
if "xl" in self.pipeline_class.__name__.lower():
original_pipeline_class = StableDiffusionXLPipeline
else:
original_pipeline_class = StableDiffusionPipeline
return original_pipeline_class
def get_dummy_inputs_pipe(self, device, seed=0):
inputs = self.get_dummy_inputs(device, seed=seed)
inputs["output_type"] = "np"
inputs["return_dict"] = False
return inputs
def get_dummy_inputs_for_pipe_original(self, device, seed=0):
inputs = {}
for k, v in self.get_dummy_inputs_pipe(device, seed=seed).items():
if k in set(inspect.signature(self.original_pipeline_class.__call__).parameters.keys()):
inputs[k] = v
return inputs
def test_from_pipe_consistent_config(self):
if self.original_pipeline_class == StableDiffusionPipeline:
original_repo = "hf-internal-testing/tiny-stable-diffusion-pipe"
original_kwargs = {"requires_safety_checker": False}
elif self.original_pipeline_class == StableDiffusionXLPipeline:
original_repo = "hf-internal-testing/tiny-stable-diffusion-xl-pipe"
original_kwargs = {"requires_aesthetics_score": True, "force_zeros_for_empty_prompt": False}
else:
raise ValueError(
"original_pipeline_class must be either StableDiffusionPipeline or StableDiffusionXLPipeline"
)
# create original_pipeline_class(sd/sdxl)
pipe_original = self.original_pipeline_class.from_pretrained(original_repo, **original_kwargs)
# original_pipeline_class(sd/sdxl) -> pipeline_class
pipe_components = self.get_dummy_components()
pipe_additional_components = {}
for name, component in pipe_components.items():
if name not in pipe_original.components:
pipe_additional_components[name] = component
pipe = self.pipeline_class.from_pipe(pipe_original, **pipe_additional_components)
# pipeline_class -> original_pipeline_class(sd/sdxl)
original_pipe_additional_components = {}
for name, component in pipe_original.components.items():
if name not in pipe.components or not isinstance(component, pipe.components[name].__class__):
original_pipe_additional_components[name] = component
pipe_original_2 = self.original_pipeline_class.from_pipe(pipe, **original_pipe_additional_components)
# compare the config
original_config = {k: v for k, v in pipe_original.config.items() if not k.startswith("_")}
original_config_2 = {k: v for k, v in pipe_original_2.config.items() if not k.startswith("_")}
assert original_config_2 == original_config
def test_from_pipe_consistent_forward_pass(self, expected_max_diff=1e-3):
components = self.get_dummy_components()
original_expected_modules, _ = self.original_pipeline_class._get_signature_keys(self.original_pipeline_class)
# pipeline components that are also expected to be in the original pipeline
original_pipe_components = {}
# additional components that are not in the pipeline, but expected in the original pipeline
original_pipe_additional_components = {}
# additional components that are in the pipeline, but not expected in the original pipeline
current_pipe_additional_components = {}
for name, component in components.items():
if name in original_expected_modules:
original_pipe_components[name] = component
else:
current_pipe_additional_components[name] = component
for name in original_expected_modules:
if name not in original_pipe_components:
if name in self.original_pipeline_class._optional_components:
original_pipe_additional_components[name] = None
else:
raise ValueError(f"missing required module for {self.original_pipeline_class.__class__}: {name}")
pipe_original = self.original_pipeline_class(**original_pipe_components, **original_pipe_additional_components)
for component in pipe_original.components.values():
if hasattr(component, "set_default_attn_processor"):
component.set_default_attn_processor()
pipe_original.to(torch_device)
pipe_original.set_progress_bar_config(disable=None)
inputs = self.get_dummy_inputs_for_pipe_original(torch_device)
output_original = pipe_original(**inputs)[0]
pipe = self.pipeline_class(**components)
for component in pipe.components.values():
if hasattr(component, "set_default_attn_processor"):
component.set_default_attn_processor()
pipe.to(torch_device)
pipe.set_progress_bar_config(disable=None)
inputs = self.get_dummy_inputs_pipe(torch_device)
output = pipe(**inputs)[0]
pipe_from_original = self.pipeline_class.from_pipe(pipe_original, **current_pipe_additional_components)
pipe_from_original.to(torch_device)
pipe_from_original.set_progress_bar_config(disable=None)
inputs = self.get_dummy_inputs_pipe(torch_device)
output_from_original = pipe_from_original(**inputs)[0]
max_diff = np.abs(to_np(output) - to_np(output_from_original)).max()
self.assertLess(
max_diff,
expected_max_diff,
"The outputs of the pipelines created with `from_pipe` and `__init__` are different.",
)
inputs = self.get_dummy_inputs_for_pipe_original(torch_device)
output_original_2 = pipe_original(**inputs)[0]
max_diff = np.abs(to_np(output_original) - to_np(output_original_2)).max()
self.assertLess(max_diff, expected_max_diff, "`from_pipe` should not change the output of original pipeline.")
for component in pipe_original.components.values():
if hasattr(component, "attn_processors"):
assert all(
type(proc) == AttnProcessor for proc in component.attn_processors.values()
), "`from_pipe` changed the attention processor in original pipeline."
@unittest.skipIf(
torch_device != "cuda" or not is_accelerate_available() or is_accelerate_version("<", "0.14.0"),
reason="CPU offload is only available with CUDA and `accelerate v0.14.0` or higher",
)
def test_from_pipe_consistent_forward_pass_cpu_offload(self, expected_max_diff=1e-3):
components = self.get_dummy_components()
pipe = self.pipeline_class(**components)
for component in pipe.components.values():
if hasattr(component, "set_default_attn_processor"):
component.set_default_attn_processor()
pipe.enable_model_cpu_offload()
pipe.set_progress_bar_config(disable=None)
inputs = self.get_dummy_inputs_pipe(torch_device)
output = pipe(**inputs)[0]
original_expected_modules, _ = self.original_pipeline_class._get_signature_keys(self.original_pipeline_class)
# pipeline components that are also expected to be in the original pipeline
original_pipe_components = {}
# additional components that are not in the pipeline, but expected in the original pipeline
original_pipe_additional_components = {}
# additional components that are in the pipeline, but not expected in the original pipeline
current_pipe_additional_components = {}
for name, component in components.items():
if name in original_expected_modules:
original_pipe_components[name] = component
else:
current_pipe_additional_components[name] = component
for name in original_expected_modules:
if name not in original_pipe_components:
if name in self.original_pipeline_class._optional_components:
original_pipe_additional_components[name] = None
else:
raise ValueError(f"missing required module for {self.original_pipeline_class.__class__}: {name}")
pipe_original = self.original_pipeline_class(**original_pipe_components, **original_pipe_additional_components)
for component in pipe_original.components.values():
if hasattr(component, "set_default_attn_processor"):
component.set_default_attn_processor()
pipe_original.set_progress_bar_config(disable=None)
pipe_from_original = self.pipeline_class.from_pipe(pipe_original, **current_pipe_additional_components)
pipe_from_original.enable_model_cpu_offload()
pipe_from_original.set_progress_bar_config(disable=None)
inputs = self.get_dummy_inputs_pipe(torch_device)
output_from_original = pipe_from_original(**inputs)[0]
max_diff = np.abs(to_np(output) - to_np(output_from_original)).max()
self.assertLess(
max_diff,
expected_max_diff,
"The outputs of the pipelines created with `from_pipe` and `__init__` are different.",
)
@require_torch @require_torch
class PipelineKarrasSchedulerTesterMixin: class PipelineKarrasSchedulerTesterMixin:
""" """
......
...@@ -30,7 +30,7 @@ from diffusers.utils.import_utils import is_accelerate_available, is_accelerate_ ...@@ -30,7 +30,7 @@ from diffusers.utils.import_utils import is_accelerate_available, is_accelerate_
from diffusers.utils.testing_utils import enable_full_determinism, nightly, require_torch_gpu, torch_device from diffusers.utils.testing_utils import enable_full_determinism, nightly, require_torch_gpu, torch_device
from ..pipeline_params import TEXT_TO_IMAGE_BATCH_PARAMS, TEXT_TO_IMAGE_IMAGE_PARAMS, TEXT_TO_IMAGE_PARAMS from ..pipeline_params import TEXT_TO_IMAGE_BATCH_PARAMS, TEXT_TO_IMAGE_IMAGE_PARAMS, TEXT_TO_IMAGE_PARAMS
from ..test_pipelines_common import PipelineTesterMixin from ..test_pipelines_common import PipelineFromPipeTesterMixin, PipelineTesterMixin
enable_full_determinism() enable_full_determinism()
...@@ -43,7 +43,7 @@ def to_np(tensor): ...@@ -43,7 +43,7 @@ def to_np(tensor):
return tensor return tensor
class TextToVideoZeroSDXLPipelineFastTests(PipelineTesterMixin, unittest.TestCase): class TextToVideoZeroSDXLPipelineFastTests(PipelineTesterMixin, PipelineFromPipeTesterMixin, unittest.TestCase):
pipeline_class = TextToVideoZeroSDXLPipeline pipeline_class = TextToVideoZeroSDXLPipeline
params = TEXT_TO_IMAGE_PARAMS params = TEXT_TO_IMAGE_PARAMS
batch_params = TEXT_TO_IMAGE_BATCH_PARAMS batch_params = TEXT_TO_IMAGE_BATCH_PARAMS
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment