mirror of
https://github.com/huggingface/diffusers.git
synced 2026-10-27 21:33:02 +03:00
* refactor the pipeline-level attention processor tests * simplify stuff. * remove utils.py
178 lines
5.9 KiB
Python
178 lines
5.9 KiB
Python
# coding=utf-8
|
|
# Copyright 2026 HuggingFace Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import torch
|
|
|
|
from diffusers import ChromaTransformer2DModel
|
|
from diffusers.utils.torch_utils import randn_tensor
|
|
|
|
from ...testing_utils import enable_full_determinism, torch_device
|
|
from ..testing_utils import (
|
|
AttentionTesterMixin,
|
|
BaseModelTesterConfig,
|
|
LoraHotSwappingForModelTesterMixin,
|
|
LoraTesterMixin,
|
|
ModelTesterMixin,
|
|
SingleFileTesterMixin,
|
|
TrainingTesterMixin,
|
|
)
|
|
|
|
|
|
enable_full_determinism()
|
|
|
|
|
|
class ChromaTransformerTesterConfig(BaseModelTesterConfig):
|
|
@property
|
|
def model_class(self):
|
|
return ChromaTransformer2DModel
|
|
|
|
@property
|
|
def main_input_name(self) -> str:
|
|
return "hidden_states"
|
|
|
|
@property
|
|
def model_split_percents(self) -> list:
|
|
return [0.8, 0.7, 0.7]
|
|
|
|
@property
|
|
def output_shape(self) -> tuple:
|
|
return (16, 4)
|
|
|
|
@property
|
|
def input_shape(self) -> tuple:
|
|
return (16, 4)
|
|
|
|
@property
|
|
def generator(self):
|
|
return torch.Generator("cpu").manual_seed(0)
|
|
|
|
def get_init_dict(self) -> dict:
|
|
return {
|
|
"patch_size": 1,
|
|
"in_channels": 4,
|
|
"num_layers": 1,
|
|
"num_single_layers": 1,
|
|
"attention_head_dim": 16,
|
|
"num_attention_heads": 2,
|
|
"joint_attention_dim": 32,
|
|
"axes_dims_rope": [4, 4, 8],
|
|
"approximator_num_channels": 8,
|
|
"approximator_hidden_dim": 16,
|
|
"approximator_layers": 1,
|
|
}
|
|
|
|
def get_dummy_inputs(self, batch_size: int = 1) -> dict[str, torch.Tensor]:
|
|
num_latent_channels = 4
|
|
num_image_channels = 3
|
|
height = width = 4
|
|
sequence_length = 48
|
|
embedding_dim = 32
|
|
|
|
return {
|
|
"hidden_states": randn_tensor(
|
|
(batch_size, height * width, num_latent_channels), generator=self.generator, device=torch_device
|
|
),
|
|
"encoder_hidden_states": randn_tensor(
|
|
(batch_size, sequence_length, embedding_dim), generator=self.generator, device=torch_device
|
|
),
|
|
"img_ids": randn_tensor(
|
|
(height * width, num_image_channels), generator=self.generator, device=torch_device
|
|
),
|
|
"txt_ids": randn_tensor(
|
|
(sequence_length, num_image_channels), generator=self.generator, device=torch_device
|
|
),
|
|
"timestep": torch.tensor([1.0]).to(torch_device).expand(batch_size),
|
|
}
|
|
|
|
|
|
class TestChromaTransformer(ChromaTransformerTesterConfig, ModelTesterMixin):
|
|
def test_deprecated_inputs_img_txt_ids_3d(self):
|
|
init_dict = self.get_init_dict()
|
|
inputs_dict = self.get_dummy_inputs()
|
|
|
|
model = self.model_class(**init_dict)
|
|
model.to(torch_device)
|
|
model.eval()
|
|
|
|
with torch.no_grad():
|
|
output_1 = model(**inputs_dict).to_tuple()[0]
|
|
|
|
text_ids_3d = inputs_dict["txt_ids"].unsqueeze(0)
|
|
image_ids_3d = inputs_dict["img_ids"].unsqueeze(0)
|
|
|
|
assert text_ids_3d.ndim == 3
|
|
assert image_ids_3d.ndim == 3
|
|
|
|
inputs_dict["txt_ids"] = text_ids_3d
|
|
inputs_dict["img_ids"] = image_ids_3d
|
|
|
|
with torch.no_grad():
|
|
output_2 = model(**inputs_dict).to_tuple()[0]
|
|
|
|
assert output_1.shape == output_2.shape
|
|
assert torch.allclose(output_1, output_2, atol=1e-5), (
|
|
"output with deprecated inputs (img_ids and txt_ids as 3d torch tensors) "
|
|
"are not equal as them as 2d inputs"
|
|
)
|
|
|
|
|
|
class TestChromaTransformerAttention(ChromaTransformerTesterConfig, AttentionTesterMixin):
|
|
"""Attention processor tests for Chroma Transformer."""
|
|
|
|
|
|
class TestChromaTransformerTraining(ChromaTransformerTesterConfig, TrainingTesterMixin):
|
|
def test_gradient_checkpointing_is_applied(self):
|
|
expected_set = {"ChromaTransformer2DModel"}
|
|
super().test_gradient_checkpointing_is_applied(expected_set=expected_set)
|
|
|
|
|
|
class TestChromaTransformerLoRA(ChromaTransformerTesterConfig, LoraTesterMixin):
|
|
pass
|
|
|
|
|
|
class TestChromaTransformerLoRAHotSwap(ChromaTransformerTesterConfig, LoraHotSwappingForModelTesterMixin):
|
|
@property
|
|
def different_shapes_for_compilation(self):
|
|
return [(4, 4), (4, 8), (8, 8)]
|
|
|
|
def get_dummy_inputs(self, height: int = 4, width: int = 4) -> dict[str, torch.Tensor]:
|
|
batch_size = 1
|
|
num_latent_channels = 4
|
|
num_image_channels = 3
|
|
sequence_length = 24
|
|
embedding_dim = 32
|
|
|
|
return {
|
|
"hidden_states": randn_tensor((batch_size, height * width, num_latent_channels), device=torch_device),
|
|
"encoder_hidden_states": randn_tensor((batch_size, sequence_length, embedding_dim), device=torch_device),
|
|
"img_ids": randn_tensor((height * width, num_image_channels), device=torch_device),
|
|
"txt_ids": randn_tensor((sequence_length, num_image_channels), device=torch_device),
|
|
"timestep": torch.tensor([1.0]).to(torch_device).expand(batch_size),
|
|
}
|
|
|
|
|
|
class TestChromaTransformer2DSingleFile(ChromaTransformerTesterConfig, SingleFileTesterMixin):
|
|
@property
|
|
def ckpt_path(self):
|
|
return "https://huggingface.co/lodestones/Chroma1-HD/blob/main/Chroma1-HD.safetensors"
|
|
|
|
@property
|
|
def pretrained_model_name_or_path(self):
|
|
return "lodestones/Chroma1-HD"
|
|
|
|
@property
|
|
def pretrained_model_kwargs(self):
|
|
return {"subfolder": "transformer"}
|