Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c926fa752 | |||
| f781b8c30c | |||
| 9c0e20de61 | |||
| f35a38725b | |||
| f66bd3261c | |||
| c4c99c3907 | |||
| 862a7d5038 | |||
| 8304adce2a | |||
| b389f339ec | |||
| e222246b4e | |||
| 83709d5a06 | |||
| 8eb73c872a | |||
| 88b015dc9f | |||
| 63cdf9c0ba |
@@ -400,6 +400,8 @@
|
|||||||
title: DiT
|
title: DiT
|
||||||
- local: api/pipelines/flux
|
- local: api/pipelines/flux
|
||||||
title: Flux
|
title: Flux
|
||||||
|
- local: api/pipelines/control_flux_inpaint
|
||||||
|
title: FluxControlInpaint
|
||||||
- local: api/pipelines/hunyuandit
|
- local: api/pipelines/hunyuandit
|
||||||
title: Hunyuan-DiT
|
title: Hunyuan-DiT
|
||||||
- local: api/pipelines/hunyuan_video
|
- local: api/pipelines/hunyuan_video
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<!--Copyright 2024 The HuggingFace Team, The Black Forest 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
# FluxControlInpaint
|
||||||
|
|
||||||
|
FluxControlInpaintPipeline is an implementation of Inpainting for Flux.1 Depth/Canny models. It is a pipeline that allows you to inpaint images using the Flux.1 Depth/Canny models. The pipeline takes an image and a mask as input and returns the inpainted image.
|
||||||
|
|
||||||
|
FLUX.1 Depth and Canny [dev] is a 12 billion parameter rectified flow transformer capable of generating an image based on a text description while following the structure of a given input image. **This is not a ControlNet model**.
|
||||||
|
|
||||||
|
| Control type | Developer | Link |
|
||||||
|
| -------- | ---------- | ---- |
|
||||||
|
| Depth | [Black Forest Labs](https://huggingface.co/black-forest-labs) | [Link](https://huggingface.co/black-forest-labs/FLUX.1-Depth-dev) |
|
||||||
|
| Canny | [Black Forest Labs](https://huggingface.co/black-forest-labs) | [Link](https://huggingface.co/black-forest-labs/FLUX.1-Canny-dev) |
|
||||||
|
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
|
||||||
|
Flux can be quite expensive to run on consumer hardware devices. However, you can perform a suite of optimizations to run it faster and in a more memory-friendly manner. Check out [this section](https://huggingface.co/blog/sd3#memory-optimizations-for-sd3) for more details. Additionally, Flux can benefit from quantization for memory efficiency with a trade-off in inference latency. Refer to [this blog post](https://huggingface.co/blog/quanto-diffusers) to learn more. For an exhaustive list of resources, check out [this gist](https://gist.github.com/sayakpaul/b664605caf0aa3bf8585ab109dd5ac9c).
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
```python
|
||||||
|
import torch
|
||||||
|
from diffusers import FluxControlInpaintPipeline
|
||||||
|
from diffusers.models.transformers import FluxTransformer2DModel
|
||||||
|
from transformers import T5EncoderModel
|
||||||
|
from diffusers.utils import load_image, make_image_grid
|
||||||
|
from image_gen_aux import DepthPreprocessor # https://github.com/huggingface/image_gen_aux
|
||||||
|
from PIL import Image
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
pipe = FluxControlInpaintPipeline.from_pretrained(
|
||||||
|
"black-forest-labs/FLUX.1-Depth-dev",
|
||||||
|
torch_dtype=torch.bfloat16,
|
||||||
|
)
|
||||||
|
# use following lines if you have GPU constraints
|
||||||
|
# ---------------------------------------------------------------
|
||||||
|
transformer = FluxTransformer2DModel.from_pretrained(
|
||||||
|
"sayakpaul/FLUX.1-Depth-dev-nf4", subfolder="transformer", torch_dtype=torch.bfloat16
|
||||||
|
)
|
||||||
|
text_encoder_2 = T5EncoderModel.from_pretrained(
|
||||||
|
"sayakpaul/FLUX.1-Depth-dev-nf4", subfolder="text_encoder_2", torch_dtype=torch.bfloat16
|
||||||
|
)
|
||||||
|
pipe.transformer = transformer
|
||||||
|
pipe.text_encoder_2 = text_encoder_2
|
||||||
|
pipe.enable_model_cpu_offload()
|
||||||
|
# ---------------------------------------------------------------
|
||||||
|
pipe.to("cuda")
|
||||||
|
|
||||||
|
prompt = "a blue robot singing opera with human-like expressions"
|
||||||
|
image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/robot.png")
|
||||||
|
|
||||||
|
head_mask = np.zeros_like(image)
|
||||||
|
head_mask[65:580,300:642] = 255
|
||||||
|
mask_image = Image.fromarray(head_mask)
|
||||||
|
|
||||||
|
processor = DepthPreprocessor.from_pretrained("LiheYoung/depth-anything-large-hf")
|
||||||
|
control_image = processor(image)[0].convert("RGB")
|
||||||
|
|
||||||
|
output = pipe(
|
||||||
|
prompt=prompt,
|
||||||
|
image=image,
|
||||||
|
control_image=control_image,
|
||||||
|
mask_image=mask_image,
|
||||||
|
num_inference_steps=30,
|
||||||
|
strength=0.9,
|
||||||
|
guidance_scale=10.0,
|
||||||
|
generator=torch.Generator().manual_seed(42),
|
||||||
|
).images[0]
|
||||||
|
make_image_grid([image, control_image, mask_image, output.resize(image.size)], rows=1, cols=4).save("output.png")
|
||||||
|
```
|
||||||
|
|
||||||
|
## FluxControlInpaintPipeline
|
||||||
|
[[autodoc]] FluxControlInpaintPipeline
|
||||||
|
- all
|
||||||
|
- __call__
|
||||||
|
|
||||||
|
|
||||||
|
## FluxPipelineOutput
|
||||||
|
[[autodoc]] pipelines.flux.pipeline_output.FluxPipelineOutput
|
||||||
@@ -25,9 +25,9 @@ pip install -U gguf
|
|||||||
|
|
||||||
Since GGUF is a single file format, use [`~FromSingleFileMixin.from_single_file`] to load the model and pass in the [`GGUFQuantizationConfig`].
|
Since GGUF is a single file format, use [`~FromSingleFileMixin.from_single_file`] to load the model and pass in the [`GGUFQuantizationConfig`].
|
||||||
|
|
||||||
When using GGUF checkpoints, the quantized weights remain in a low memory `dtype`(typically `torch.unint8`) and are dynamically dequantized and cast to the configured `compute_dtype` during each module's forward pass through the model. The `GGUFQuantizationConfig` allows you to set the `compute_dtype`.
|
When using GGUF checkpoints, the quantized weights remain in a low memory `dtype`(typically `torch.uint8`) and are dynamically dequantized and cast to the configured `compute_dtype` during each module's forward pass through the model. The `GGUFQuantizationConfig` allows you to set the `compute_dtype`.
|
||||||
|
|
||||||
The functions used for dynamic dequantizatation are based on the great work done by [city96](https://github.com/city96/ComfyUI-GGUF), who created the Pytorch ports of the original (`numpy`)[https://github.com/ggerganov/llama.cpp/blob/master/gguf-py/gguf/quants.py] implementation by [compilade](https://github.com/compilade).
|
The functions used for dynamic dequantizatation are based on the great work done by [city96](https://github.com/city96/ComfyUI-GGUF), who created the Pytorch ports of the original [`numpy`](https://github.com/ggerganov/llama.cpp/blob/master/gguf-py/gguf/quants.py) implementation by [compilade](https://github.com/compilade).
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import torch
|
import torch
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ If you are new to the quantization field, we recommend you to check out these be
|
|||||||
## When to use what?
|
## When to use what?
|
||||||
|
|
||||||
Diffusers currently supports the following quantization methods.
|
Diffusers currently supports the following quantization methods.
|
||||||
- [BitsandBytes]()
|
- [BitsandBytes](./bitsandbytes.md)
|
||||||
- [TorchAO]()
|
- [TorchAO](./torchao.md)
|
||||||
- [GGUF]()
|
- [GGUF](./gguf.md)
|
||||||
|
|
||||||
[This resource](https://huggingface.co/docs/transformers/main/en/quantization/overview#when-to-use-what) provides a good overview of the pros and cons of different quantization techniques.
|
[This resource](https://huggingface.co/docs/transformers/main/en/quantization/overview#when-to-use-what) provides a good overview of the pros and cons of different quantization techniques.
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ The example below only quantizes the weights to int8.
|
|||||||
```python
|
```python
|
||||||
from diffusers import FluxPipeline, FluxTransformer2DModel, TorchAoConfig
|
from diffusers import FluxPipeline, FluxTransformer2DModel, TorchAoConfig
|
||||||
|
|
||||||
model_id = "black-forest-labs/FLUX.1-dev"
|
model_id = "black-forest-labs/Flux.1-Dev"
|
||||||
dtype = torch.bfloat16
|
dtype = torch.bfloat16
|
||||||
|
|
||||||
quantization_config = TorchAoConfig("int8wo")
|
quantization_config = TorchAoConfig("int8wo")
|
||||||
@@ -45,9 +45,7 @@ pipe = FluxPipeline.from_pretrained(
|
|||||||
pipe.to("cuda")
|
pipe.to("cuda")
|
||||||
|
|
||||||
prompt = "A cat holding a sign that says hello world"
|
prompt = "A cat holding a sign that says hello world"
|
||||||
image = pipe(
|
image = pipe(prompt, num_inference_steps=28, guidance_scale=0.0).images[0]
|
||||||
prompt, num_inference_steps=50, guidance_scale=4.5, max_sequence_length=512
|
|
||||||
).images[0]
|
|
||||||
image.save("output.png")
|
image.save("output.png")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ This will also allow us to push the trained LoRA parameters to the Hugging Face
|
|||||||
Now, we can launch training using:
|
Now, we can launch training using:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export MODEL_NAME="Efficient-Large-Model/Sana_1600M_1024px_diffusers"
|
export MODEL_NAME="Efficient-Large-Model/Sana_1600M_1024px_BF16_diffusers"
|
||||||
export INSTANCE_DIR="dog"
|
export INSTANCE_DIR="dog"
|
||||||
export OUTPUT_DIR="trained-sana-lora"
|
export OUTPUT_DIR="trained-sana-lora"
|
||||||
|
|
||||||
@@ -124,4 +124,4 @@ We provide several options for optimizing memory optimization:
|
|||||||
* `cache_latents`: When enabled, we will pre-compute the latents from the input images with the VAE and remove the VAE from memory once done.
|
* `cache_latents`: When enabled, we will pre-compute the latents from the input images with the VAE and remove the VAE from memory once done.
|
||||||
* `--use_8bit_adam`: When enabled, we will use the 8bit version of AdamW provided by the `bitsandbytes` library.
|
* `--use_8bit_adam`: When enabled, we will use the 8bit version of AdamW provided by the `bitsandbytes` library.
|
||||||
|
|
||||||
Refer to the [official documentation](https://huggingface.co/docs/diffusers/main/en/api/pipelines/sana) of the `SanaPipeline` to know more about the models available under the SANA family and their preferred dtypes during inference.
|
Refer to the [official documentation](https://huggingface.co/docs/diffusers/main/en/api/pipelines/sana) of the `SanaPipeline` to know more about the models available under the SANA family and their preferred dtypes during inference.
|
||||||
@@ -277,6 +277,7 @@ else:
|
|||||||
"CogView3PlusPipeline",
|
"CogView3PlusPipeline",
|
||||||
"CycleDiffusionPipeline",
|
"CycleDiffusionPipeline",
|
||||||
"FluxControlImg2ImgPipeline",
|
"FluxControlImg2ImgPipeline",
|
||||||
|
"FluxControlInpaintPipeline",
|
||||||
"FluxControlNetImg2ImgPipeline",
|
"FluxControlNetImg2ImgPipeline",
|
||||||
"FluxControlNetInpaintPipeline",
|
"FluxControlNetInpaintPipeline",
|
||||||
"FluxControlNetPipeline",
|
"FluxControlNetPipeline",
|
||||||
@@ -765,6 +766,7 @@ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
|||||||
CogView3PlusPipeline,
|
CogView3PlusPipeline,
|
||||||
CycleDiffusionPipeline,
|
CycleDiffusionPipeline,
|
||||||
FluxControlImg2ImgPipeline,
|
FluxControlImg2ImgPipeline,
|
||||||
|
FluxControlInpaintPipeline,
|
||||||
FluxControlNetImg2ImgPipeline,
|
FluxControlNetImg2ImgPipeline,
|
||||||
FluxControlNetInpaintPipeline,
|
FluxControlNetInpaintPipeline,
|
||||||
FluxControlNetPipeline,
|
FluxControlNetPipeline,
|
||||||
|
|||||||
@@ -151,6 +151,8 @@ DIFFUSERS_DEFAULT_PIPELINE_PATHS = {
|
|||||||
"animatediff_scribble": {"pretrained_model_name_or_path": "guoyww/animatediff-sparsectrl-scribble"},
|
"animatediff_scribble": {"pretrained_model_name_or_path": "guoyww/animatediff-sparsectrl-scribble"},
|
||||||
"animatediff_rgb": {"pretrained_model_name_or_path": "guoyww/animatediff-sparsectrl-rgb"},
|
"animatediff_rgb": {"pretrained_model_name_or_path": "guoyww/animatediff-sparsectrl-rgb"},
|
||||||
"flux-dev": {"pretrained_model_name_or_path": "black-forest-labs/FLUX.1-dev"},
|
"flux-dev": {"pretrained_model_name_or_path": "black-forest-labs/FLUX.1-dev"},
|
||||||
|
"flux-fill": {"pretrained_model_name_or_path": "black-forest-labs/FLUX.1-Fill-dev"},
|
||||||
|
"flux-depth": {"pretrained_model_name_or_path": "black-forest-labs/FLUX.1-Depth-dev"},
|
||||||
"flux-schnell": {"pretrained_model_name_or_path": "black-forest-labs/FLUX.1-schnell"},
|
"flux-schnell": {"pretrained_model_name_or_path": "black-forest-labs/FLUX.1-schnell"},
|
||||||
"ltx-video": {"pretrained_model_name_or_path": "Lightricks/LTX-Video"},
|
"ltx-video": {"pretrained_model_name_or_path": "Lightricks/LTX-Video"},
|
||||||
"autoencoder-dc-f128c512": {"pretrained_model_name_or_path": "mit-han-lab/dc-ae-f128c512-mix-1.0-diffusers"},
|
"autoencoder-dc-f128c512": {"pretrained_model_name_or_path": "mit-han-lab/dc-ae-f128c512-mix-1.0-diffusers"},
|
||||||
@@ -587,7 +589,13 @@ def infer_diffusers_model_type(checkpoint):
|
|||||||
if any(
|
if any(
|
||||||
g in checkpoint for g in ["guidance_in.in_layer.bias", "model.diffusion_model.guidance_in.in_layer.bias"]
|
g in checkpoint for g in ["guidance_in.in_layer.bias", "model.diffusion_model.guidance_in.in_layer.bias"]
|
||||||
):
|
):
|
||||||
model_type = "flux-dev"
|
if checkpoint["img_in.weight"].shape[1] == 384:
|
||||||
|
model_type = "flux-fill"
|
||||||
|
|
||||||
|
elif checkpoint["img_in.weight"].shape[1] == 128:
|
||||||
|
model_type = "flux-depth"
|
||||||
|
else:
|
||||||
|
model_type = "flux-dev"
|
||||||
else:
|
else:
|
||||||
model_type = "flux-schnell"
|
model_type = "flux-schnell"
|
||||||
|
|
||||||
|
|||||||
@@ -188,8 +188,13 @@ class JointTransformerBlock(nn.Module):
|
|||||||
self._chunk_dim = dim
|
self._chunk_dim = dim
|
||||||
|
|
||||||
def forward(
|
def forward(
|
||||||
self, hidden_states: torch.FloatTensor, encoder_hidden_states: torch.FloatTensor, temb: torch.FloatTensor
|
self,
|
||||||
|
hidden_states: torch.FloatTensor,
|
||||||
|
encoder_hidden_states: torch.FloatTensor,
|
||||||
|
temb: torch.FloatTensor,
|
||||||
|
joint_attention_kwargs: Optional[Dict[str, Any]] = None,
|
||||||
):
|
):
|
||||||
|
joint_attention_kwargs = joint_attention_kwargs or {}
|
||||||
if self.use_dual_attention:
|
if self.use_dual_attention:
|
||||||
norm_hidden_states, gate_msa, shift_mlp, scale_mlp, gate_mlp, norm_hidden_states2, gate_msa2 = self.norm1(
|
norm_hidden_states, gate_msa, shift_mlp, scale_mlp, gate_mlp, norm_hidden_states2, gate_msa2 = self.norm1(
|
||||||
hidden_states, emb=temb
|
hidden_states, emb=temb
|
||||||
@@ -206,7 +211,9 @@ class JointTransformerBlock(nn.Module):
|
|||||||
|
|
||||||
# Attention.
|
# Attention.
|
||||||
attn_output, context_attn_output = self.attn(
|
attn_output, context_attn_output = self.attn(
|
||||||
hidden_states=norm_hidden_states, encoder_hidden_states=norm_encoder_hidden_states
|
hidden_states=norm_hidden_states,
|
||||||
|
encoder_hidden_states=norm_encoder_hidden_states,
|
||||||
|
**joint_attention_kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Process attention outputs for the `hidden_states`.
|
# Process attention outputs for the `hidden_states`.
|
||||||
@@ -214,7 +221,7 @@ class JointTransformerBlock(nn.Module):
|
|||||||
hidden_states = hidden_states + attn_output
|
hidden_states = hidden_states + attn_output
|
||||||
|
|
||||||
if self.use_dual_attention:
|
if self.use_dual_attention:
|
||||||
attn_output2 = self.attn2(hidden_states=norm_hidden_states2)
|
attn_output2 = self.attn2(hidden_states=norm_hidden_states2, **joint_attention_kwargs)
|
||||||
attn_output2 = gate_msa2.unsqueeze(1) * attn_output2
|
attn_output2 = gate_msa2.unsqueeze(1) * attn_output2
|
||||||
hidden_states = hidden_states + attn_output2
|
hidden_states = hidden_states + attn_output2
|
||||||
|
|
||||||
|
|||||||
@@ -792,12 +792,12 @@ class AutoencoderKLHunyuanVideo(ModelMixin, ConfigMixin):
|
|||||||
# The minimal tile height and width for spatial tiling to be used
|
# The minimal tile height and width for spatial tiling to be used
|
||||||
self.tile_sample_min_height = 256
|
self.tile_sample_min_height = 256
|
||||||
self.tile_sample_min_width = 256
|
self.tile_sample_min_width = 256
|
||||||
self.tile_sample_min_num_frames = 64
|
self.tile_sample_min_num_frames = 16
|
||||||
|
|
||||||
# The minimal distance between two spatial tiles
|
# The minimal distance between two spatial tiles
|
||||||
self.tile_sample_stride_height = 192
|
self.tile_sample_stride_height = 192
|
||||||
self.tile_sample_stride_width = 192
|
self.tile_sample_stride_width = 192
|
||||||
self.tile_sample_stride_num_frames = 48
|
self.tile_sample_stride_num_frames = 12
|
||||||
|
|
||||||
def _set_gradient_checkpointing(self, module, value=False):
|
def _set_gradient_checkpointing(self, module, value=False):
|
||||||
if isinstance(module, (HunyuanVideoEncoder3D, HunyuanVideoDecoder3D)):
|
if isinstance(module, (HunyuanVideoEncoder3D, HunyuanVideoDecoder3D)):
|
||||||
@@ -1003,7 +1003,7 @@ class AutoencoderKLHunyuanVideo(ModelMixin, ConfigMixin):
|
|||||||
for i in range(0, height, self.tile_sample_stride_height):
|
for i in range(0, height, self.tile_sample_stride_height):
|
||||||
row = []
|
row = []
|
||||||
for j in range(0, width, self.tile_sample_stride_width):
|
for j in range(0, width, self.tile_sample_stride_width):
|
||||||
tile = x[:, :, :, i : i + self.tile_sample_min_size, j : j + self.tile_sample_min_size]
|
tile = x[:, :, :, i : i + self.tile_sample_min_height, j : j + self.tile_sample_min_width]
|
||||||
tile = self.encoder(tile)
|
tile = self.encoder(tile)
|
||||||
tile = self.quant_conv(tile)
|
tile = self.quant_conv(tile)
|
||||||
row.append(tile)
|
row.append(tile)
|
||||||
@@ -1020,7 +1020,7 @@ class AutoencoderKLHunyuanVideo(ModelMixin, ConfigMixin):
|
|||||||
if j > 0:
|
if j > 0:
|
||||||
tile = self.blend_h(row[j - 1], tile, blend_width)
|
tile = self.blend_h(row[j - 1], tile, blend_width)
|
||||||
result_row.append(tile[:, :, :, :tile_latent_stride_height, :tile_latent_stride_width])
|
result_row.append(tile[:, :, :, :tile_latent_stride_height, :tile_latent_stride_width])
|
||||||
result_rows.append(torch.cat(result_row, dim=-1))
|
result_rows.append(torch.cat(result_row, dim=4))
|
||||||
|
|
||||||
enc = torch.cat(result_rows, dim=3)[:, :, :, :latent_height, :latent_width]
|
enc = torch.cat(result_rows, dim=3)[:, :, :, :latent_height, :latent_width]
|
||||||
return enc
|
return enc
|
||||||
|
|||||||
@@ -691,7 +691,7 @@ class CogVideoXPatchEmbed(nn.Module):
|
|||||||
output_type="pt",
|
output_type="pt",
|
||||||
)
|
)
|
||||||
pos_embedding = pos_embedding.flatten(0, 1)
|
pos_embedding = pos_embedding.flatten(0, 1)
|
||||||
joint_pos_embedding = torch.zeros(
|
joint_pos_embedding = pos_embedding.new_zeros(
|
||||||
1, self.max_text_seq_length + num_patches, self.embed_dim, requires_grad=False
|
1, self.max_text_seq_length + num_patches, self.embed_dim, requires_grad=False
|
||||||
)
|
)
|
||||||
joint_pos_embedding.data[:, self.max_text_seq_length :].copy_(pos_embedding)
|
joint_pos_embedding.data[:, self.max_text_seq_length :].copy_(pos_embedding)
|
||||||
|
|||||||
@@ -497,6 +497,46 @@ class HunyuanVideoTransformerBlock(nn.Module):
|
|||||||
|
|
||||||
|
|
||||||
class HunyuanVideoTransformer3DModel(ModelMixin, ConfigMixin):
|
class HunyuanVideoTransformer3DModel(ModelMixin, ConfigMixin):
|
||||||
|
r"""
|
||||||
|
A Transformer model for video-like data used in [HunyuanVideo](https://huggingface.co/tencent/HunyuanVideo).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
in_channels (`int`, defaults to `16`):
|
||||||
|
The number of channels in the input.
|
||||||
|
out_channels (`int`, defaults to `16`):
|
||||||
|
The number of channels in the output.
|
||||||
|
num_attention_heads (`int`, defaults to `24`):
|
||||||
|
The number of heads to use for multi-head attention.
|
||||||
|
attention_head_dim (`int`, defaults to `128`):
|
||||||
|
The number of channels in each head.
|
||||||
|
num_layers (`int`, defaults to `20`):
|
||||||
|
The number of layers of dual-stream blocks to use.
|
||||||
|
num_single_layers (`int`, defaults to `40`):
|
||||||
|
The number of layers of single-stream blocks to use.
|
||||||
|
num_refiner_layers (`int`, defaults to `2`):
|
||||||
|
The number of layers of refiner blocks to use.
|
||||||
|
mlp_ratio (`float`, defaults to `4.0`):
|
||||||
|
The ratio of the hidden layer size to the input size in the feedforward network.
|
||||||
|
patch_size (`int`, defaults to `2`):
|
||||||
|
The size of the spatial patches to use in the patch embedding layer.
|
||||||
|
patch_size_t (`int`, defaults to `1`):
|
||||||
|
The size of the tmeporal patches to use in the patch embedding layer.
|
||||||
|
qk_norm (`str`, defaults to `rms_norm`):
|
||||||
|
The normalization to use for the query and key projections in the attention layers.
|
||||||
|
guidance_embeds (`bool`, defaults to `True`):
|
||||||
|
Whether to use guidance embeddings in the model.
|
||||||
|
text_embed_dim (`int`, defaults to `4096`):
|
||||||
|
Input dimension of text embeddings from the text encoder.
|
||||||
|
pooled_projection_dim (`int`, defaults to `768`):
|
||||||
|
The dimension of the pooled projection of the text embeddings.
|
||||||
|
rope_theta (`float`, defaults to `256.0`):
|
||||||
|
The value of theta to use in the RoPE layer.
|
||||||
|
rope_axes_dim (`Tuple[int]`, defaults to `(16, 56, 56)`):
|
||||||
|
The dimensions of the axes to use in the RoPE layer.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_supports_gradient_checkpointing = True
|
||||||
|
|
||||||
@register_to_config
|
@register_to_config
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -334,6 +334,7 @@ class MochiTransformer3DModel(ModelMixin, ConfigMixin, PeftAdapterMixin):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
_supports_gradient_checkpointing = True
|
_supports_gradient_checkpointing = True
|
||||||
|
_no_split_modules = ["MochiTransformerBlock"]
|
||||||
|
|
||||||
@register_to_config
|
@register_to_config
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|||||||
@@ -411,11 +411,15 @@ class SD3Transformer2DModel(ModelMixin, ConfigMixin, PeftAdapterMixin, FromOrigi
|
|||||||
hidden_states,
|
hidden_states,
|
||||||
encoder_hidden_states,
|
encoder_hidden_states,
|
||||||
temb,
|
temb,
|
||||||
|
joint_attention_kwargs,
|
||||||
**ckpt_kwargs,
|
**ckpt_kwargs,
|
||||||
)
|
)
|
||||||
elif not is_skip:
|
elif not is_skip:
|
||||||
encoder_hidden_states, hidden_states = block(
|
encoder_hidden_states, hidden_states = block(
|
||||||
hidden_states=hidden_states, encoder_hidden_states=encoder_hidden_states, temb=temb
|
hidden_states=hidden_states,
|
||||||
|
encoder_hidden_states=encoder_hidden_states,
|
||||||
|
temb=temb,
|
||||||
|
joint_attention_kwargs=joint_attention_kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
# controlnet residual
|
# controlnet residual
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ class UNet2DModel(ModelMixin, ConfigMixin):
|
|||||||
out_channels: int = 3,
|
out_channels: int = 3,
|
||||||
center_input_sample: bool = False,
|
center_input_sample: bool = False,
|
||||||
time_embedding_type: str = "positional",
|
time_embedding_type: str = "positional",
|
||||||
|
time_embedding_dim: Optional[int] = None,
|
||||||
freq_shift: int = 0,
|
freq_shift: int = 0,
|
||||||
flip_sin_to_cos: bool = True,
|
flip_sin_to_cos: bool = True,
|
||||||
down_block_types: Tuple[str, ...] = ("DownBlock2D", "AttnDownBlock2D", "AttnDownBlock2D", "AttnDownBlock2D"),
|
down_block_types: Tuple[str, ...] = ("DownBlock2D", "AttnDownBlock2D", "AttnDownBlock2D", "AttnDownBlock2D"),
|
||||||
@@ -122,7 +123,7 @@ class UNet2DModel(ModelMixin, ConfigMixin):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.sample_size = sample_size
|
self.sample_size = sample_size
|
||||||
time_embed_dim = block_out_channels[0] * 4
|
time_embed_dim = time_embedding_dim or block_out_channels[0] * 4
|
||||||
|
|
||||||
# Check inputs
|
# Check inputs
|
||||||
if len(down_block_types) != len(up_block_types):
|
if len(down_block_types) != len(up_block_types):
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ else:
|
|||||||
]
|
]
|
||||||
_import_structure["flux"] = [
|
_import_structure["flux"] = [
|
||||||
"FluxControlPipeline",
|
"FluxControlPipeline",
|
||||||
|
"FluxControlInpaintPipeline",
|
||||||
"FluxControlImg2ImgPipeline",
|
"FluxControlImg2ImgPipeline",
|
||||||
"FluxControlNetPipeline",
|
"FluxControlNetPipeline",
|
||||||
"FluxControlNetImg2ImgPipeline",
|
"FluxControlNetImg2ImgPipeline",
|
||||||
@@ -539,6 +540,7 @@ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
|||||||
)
|
)
|
||||||
from .flux import (
|
from .flux import (
|
||||||
FluxControlImg2ImgPipeline,
|
FluxControlImg2ImgPipeline,
|
||||||
|
FluxControlInpaintPipeline,
|
||||||
FluxControlNetImg2ImgPipeline,
|
FluxControlNetImg2ImgPipeline,
|
||||||
FluxControlNetInpaintPipeline,
|
FluxControlNetInpaintPipeline,
|
||||||
FluxControlNetPipeline,
|
FluxControlNetPipeline,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ else:
|
|||||||
_import_structure["pipeline_flux"] = ["FluxPipeline"]
|
_import_structure["pipeline_flux"] = ["FluxPipeline"]
|
||||||
_import_structure["pipeline_flux_control"] = ["FluxControlPipeline"]
|
_import_structure["pipeline_flux_control"] = ["FluxControlPipeline"]
|
||||||
_import_structure["pipeline_flux_control_img2img"] = ["FluxControlImg2ImgPipeline"]
|
_import_structure["pipeline_flux_control_img2img"] = ["FluxControlImg2ImgPipeline"]
|
||||||
|
_import_structure["pipeline_flux_control_inpaint"] = ["FluxControlInpaintPipeline"]
|
||||||
_import_structure["pipeline_flux_controlnet"] = ["FluxControlNetPipeline"]
|
_import_structure["pipeline_flux_controlnet"] = ["FluxControlNetPipeline"]
|
||||||
_import_structure["pipeline_flux_controlnet_image_to_image"] = ["FluxControlNetImg2ImgPipeline"]
|
_import_structure["pipeline_flux_controlnet_image_to_image"] = ["FluxControlNetImg2ImgPipeline"]
|
||||||
_import_structure["pipeline_flux_controlnet_inpainting"] = ["FluxControlNetInpaintPipeline"]
|
_import_structure["pipeline_flux_controlnet_inpainting"] = ["FluxControlNetInpaintPipeline"]
|
||||||
@@ -44,6 +45,7 @@ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
|||||||
from .pipeline_flux import FluxPipeline
|
from .pipeline_flux import FluxPipeline
|
||||||
from .pipeline_flux_control import FluxControlPipeline
|
from .pipeline_flux_control import FluxControlPipeline
|
||||||
from .pipeline_flux_control_img2img import FluxControlImg2ImgPipeline
|
from .pipeline_flux_control_img2img import FluxControlImg2ImgPipeline
|
||||||
|
from .pipeline_flux_control_inpaint import FluxControlInpaintPipeline
|
||||||
from .pipeline_flux_controlnet import FluxControlNetPipeline
|
from .pipeline_flux_controlnet import FluxControlNetPipeline
|
||||||
from .pipeline_flux_controlnet_image_to_image import FluxControlNetImg2ImgPipeline
|
from .pipeline_flux_controlnet_image_to_image import FluxControlNetImg2ImgPipeline
|
||||||
from .pipeline_flux_controlnet_inpainting import FluxControlNetInpaintPipeline
|
from .pipeline_flux_controlnet_inpainting import FluxControlNetInpaintPipeline
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -188,6 +188,7 @@ class MochiPipeline(DiffusionPipeline, Mochi1LoraLoaderMixin):
|
|||||||
text_encoder: T5EncoderModel,
|
text_encoder: T5EncoderModel,
|
||||||
tokenizer: T5TokenizerFast,
|
tokenizer: T5TokenizerFast,
|
||||||
transformer: MochiTransformer3DModel,
|
transformer: MochiTransformer3DModel,
|
||||||
|
force_zeros_for_empty_prompt: bool = False,
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
@@ -205,10 +206,11 @@ class MochiPipeline(DiffusionPipeline, Mochi1LoraLoaderMixin):
|
|||||||
|
|
||||||
self.video_processor = VideoProcessor(vae_scale_factor=self.vae_spatial_scale_factor)
|
self.video_processor = VideoProcessor(vae_scale_factor=self.vae_spatial_scale_factor)
|
||||||
self.tokenizer_max_length = (
|
self.tokenizer_max_length = (
|
||||||
self.tokenizer.model_max_length if hasattr(self, "tokenizer") and self.tokenizer is not None else 77
|
self.tokenizer.model_max_length if hasattr(self, "tokenizer") and self.tokenizer is not None else 256
|
||||||
)
|
)
|
||||||
self.default_height = 480
|
self.default_height = 480
|
||||||
self.default_width = 848
|
self.default_width = 848
|
||||||
|
self.register_to_config(force_zeros_for_empty_prompt=force_zeros_for_empty_prompt)
|
||||||
|
|
||||||
def _get_t5_prompt_embeds(
|
def _get_t5_prompt_embeds(
|
||||||
self,
|
self,
|
||||||
@@ -236,7 +238,11 @@ class MochiPipeline(DiffusionPipeline, Mochi1LoraLoaderMixin):
|
|||||||
text_input_ids = text_inputs.input_ids
|
text_input_ids = text_inputs.input_ids
|
||||||
prompt_attention_mask = text_inputs.attention_mask
|
prompt_attention_mask = text_inputs.attention_mask
|
||||||
prompt_attention_mask = prompt_attention_mask.bool().to(device)
|
prompt_attention_mask = prompt_attention_mask.bool().to(device)
|
||||||
if prompt == "" or prompt[-1] == "":
|
|
||||||
|
# The original Mochi implementation zeros out empty negative prompts
|
||||||
|
# but this can lead to overflow when placing the entire pipeline under the autocast context
|
||||||
|
# adding this here so that we can enable zeroing prompts if necessary
|
||||||
|
if self.config.force_zeros_for_empty_prompt and (prompt == "" or prompt[-1] == ""):
|
||||||
text_input_ids = torch.zeros_like(text_input_ids, device=device)
|
text_input_ids = torch.zeros_like(text_input_ids, device=device)
|
||||||
prompt_attention_mask = torch.zeros_like(prompt_attention_mask, dtype=torch.bool, device=device)
|
prompt_attention_mask = torch.zeros_like(prompt_attention_mask, dtype=torch.bool, device=device)
|
||||||
|
|
||||||
|
|||||||
@@ -289,6 +289,7 @@ class DEISMultistepScheduler(SchedulerMixin, ConfigMixin):
|
|||||||
sigmas = 1.0 - alphas
|
sigmas = 1.0 - alphas
|
||||||
sigmas = np.flip(self.config.flow_shift * sigmas / (1 + (self.config.flow_shift - 1) * sigmas))[:-1].copy()
|
sigmas = np.flip(self.config.flow_shift * sigmas / (1 + (self.config.flow_shift - 1) * sigmas))[:-1].copy()
|
||||||
timesteps = (sigmas * self.config.num_train_timesteps).copy()
|
timesteps = (sigmas * self.config.num_train_timesteps).copy()
|
||||||
|
sigmas = np.concatenate([sigmas, sigmas[-1:]]).astype(np.float32)
|
||||||
else:
|
else:
|
||||||
sigmas = np.interp(timesteps, np.arange(0, len(sigmas)), sigmas)
|
sigmas = np.interp(timesteps, np.arange(0, len(sigmas)), sigmas)
|
||||||
sigma_last = ((1 - self.alphas_cumprod[0]) / self.alphas_cumprod[0]) ** 0.5
|
sigma_last = ((1 - self.alphas_cumprod[0]) / self.alphas_cumprod[0]) ** 0.5
|
||||||
|
|||||||
@@ -291,14 +291,17 @@ class DPMSolverMultistepInverseScheduler(SchedulerMixin, ConfigMixin):
|
|||||||
elif self.config.use_exponential_sigmas:
|
elif self.config.use_exponential_sigmas:
|
||||||
sigmas = self._convert_to_exponential(in_sigmas=sigmas, num_inference_steps=num_inference_steps)
|
sigmas = self._convert_to_exponential(in_sigmas=sigmas, num_inference_steps=num_inference_steps)
|
||||||
timesteps = np.array([self._sigma_to_t(sigma, log_sigmas) for sigma in sigmas])
|
timesteps = np.array([self._sigma_to_t(sigma, log_sigmas) for sigma in sigmas])
|
||||||
|
sigmas = np.concatenate([sigmas, sigmas[-1:]]).astype(np.float32)
|
||||||
elif self.config.use_beta_sigmas:
|
elif self.config.use_beta_sigmas:
|
||||||
sigmas = self._convert_to_beta(in_sigmas=sigmas, num_inference_steps=num_inference_steps)
|
sigmas = self._convert_to_beta(in_sigmas=sigmas, num_inference_steps=num_inference_steps)
|
||||||
timesteps = np.array([self._sigma_to_t(sigma, log_sigmas) for sigma in sigmas])
|
timesteps = np.array([self._sigma_to_t(sigma, log_sigmas) for sigma in sigmas])
|
||||||
|
sigmas = np.concatenate([sigmas, sigmas[-1:]]).astype(np.float32)
|
||||||
elif self.config.use_flow_sigmas:
|
elif self.config.use_flow_sigmas:
|
||||||
alphas = np.linspace(1, 1 / self.config.num_train_timesteps, num_inference_steps + 1)
|
alphas = np.linspace(1, 1 / self.config.num_train_timesteps, num_inference_steps + 1)
|
||||||
sigmas = 1.0 - alphas
|
sigmas = 1.0 - alphas
|
||||||
sigmas = np.flip(self.config.flow_shift * sigmas / (1 + (self.config.flow_shift - 1) * sigmas))[:-1].copy()
|
sigmas = np.flip(self.config.flow_shift * sigmas / (1 + (self.config.flow_shift - 1) * sigmas))[:-1].copy()
|
||||||
timesteps = (sigmas * self.config.num_train_timesteps).copy()
|
timesteps = (sigmas * self.config.num_train_timesteps).copy()
|
||||||
|
sigmas = np.concatenate([sigmas, sigmas[-1:]]).astype(np.float32)
|
||||||
else:
|
else:
|
||||||
sigmas = np.interp(timesteps, np.arange(0, len(sigmas)), sigmas)
|
sigmas = np.interp(timesteps, np.arange(0, len(sigmas)), sigmas)
|
||||||
sigma_max = (
|
sigma_max = (
|
||||||
|
|||||||
@@ -318,6 +318,7 @@ class SASolverScheduler(SchedulerMixin, ConfigMixin):
|
|||||||
sigmas = 1.0 - alphas
|
sigmas = 1.0 - alphas
|
||||||
sigmas = np.flip(self.config.flow_shift * sigmas / (1 + (self.config.flow_shift - 1) * sigmas))[:-1].copy()
|
sigmas = np.flip(self.config.flow_shift * sigmas / (1 + (self.config.flow_shift - 1) * sigmas))[:-1].copy()
|
||||||
timesteps = (sigmas * self.config.num_train_timesteps).copy()
|
timesteps = (sigmas * self.config.num_train_timesteps).copy()
|
||||||
|
sigmas = np.concatenate([sigmas, sigmas[-1:]]).astype(np.float32)
|
||||||
else:
|
else:
|
||||||
sigmas = np.interp(timesteps, np.arange(0, len(sigmas)), sigmas)
|
sigmas = np.interp(timesteps, np.arange(0, len(sigmas)), sigmas)
|
||||||
sigma_last = ((1 - self.alphas_cumprod[0]) / self.alphas_cumprod[0]) ** 0.5
|
sigma_last = ((1 - self.alphas_cumprod[0]) / self.alphas_cumprod[0]) ** 0.5
|
||||||
|
|||||||
@@ -381,6 +381,15 @@ class UniPCMultistepScheduler(SchedulerMixin, ConfigMixin):
|
|||||||
sigmas = 1.0 - alphas
|
sigmas = 1.0 - alphas
|
||||||
sigmas = np.flip(self.config.flow_shift * sigmas / (1 + (self.config.flow_shift - 1) * sigmas))[:-1].copy()
|
sigmas = np.flip(self.config.flow_shift * sigmas / (1 + (self.config.flow_shift - 1) * sigmas))[:-1].copy()
|
||||||
timesteps = (sigmas * self.config.num_train_timesteps).copy()
|
timesteps = (sigmas * self.config.num_train_timesteps).copy()
|
||||||
|
if self.config.final_sigmas_type == "sigma_min":
|
||||||
|
sigma_last = sigmas[-1]
|
||||||
|
elif self.config.final_sigmas_type == "zero":
|
||||||
|
sigma_last = 0
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"`final_sigmas_type` must be one of 'zero', or 'sigma_min', but got {self.config.final_sigmas_type}"
|
||||||
|
)
|
||||||
|
sigmas = np.concatenate([sigmas, [sigma_last]]).astype(np.float32)
|
||||||
else:
|
else:
|
||||||
sigmas = np.interp(timesteps, np.arange(0, len(sigmas)), sigmas)
|
sigmas = np.interp(timesteps, np.arange(0, len(sigmas)), sigmas)
|
||||||
if self.config.final_sigmas_type == "sigma_min":
|
if self.config.final_sigmas_type == "sigma_min":
|
||||||
|
|||||||
@@ -392,6 +392,21 @@ class FluxControlImg2ImgPipeline(metaclass=DummyObject):
|
|||||||
requires_backends(cls, ["torch", "transformers"])
|
requires_backends(cls, ["torch", "transformers"])
|
||||||
|
|
||||||
|
|
||||||
|
class FluxControlInpaintPipeline(metaclass=DummyObject):
|
||||||
|
_backends = ["torch", "transformers"]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
requires_backends(self, ["torch", "transformers"])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_config(cls, *args, **kwargs):
|
||||||
|
requires_backends(cls, ["torch", "transformers"])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_pretrained(cls, *args, **kwargs):
|
||||||
|
requires_backends(cls, ["torch", "transformers"])
|
||||||
|
|
||||||
|
|
||||||
class FluxControlNetImg2ImgPipeline(metaclass=DummyObject):
|
class FluxControlNetImg2ImgPipeline(metaclass=DummyObject):
|
||||||
_backends = ["torch", "transformers"]
|
_backends = ["torch", "transformers"]
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ from diffusers import (
|
|||||||
)
|
)
|
||||||
from diffusers.utils.testing_utils import (
|
from diffusers.utils.testing_utils import (
|
||||||
floats_tensor,
|
floats_tensor,
|
||||||
is_peft_available,
|
|
||||||
is_torch_version,
|
is_torch_version,
|
||||||
require_peft_backend,
|
require_peft_backend,
|
||||||
skip_mps,
|
skip_mps,
|
||||||
@@ -37,9 +36,6 @@ from diffusers.utils.testing_utils import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
if is_peft_available():
|
|
||||||
pass
|
|
||||||
|
|
||||||
sys.path.append(".")
|
sys.path.append(".")
|
||||||
|
|
||||||
from utils import PeftLoraLoaderMixinTests, check_if_lora_correctly_set # noqa: E402
|
from utils import PeftLoraLoaderMixinTests, check_if_lora_correctly_set # noqa: E402
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ from transformers import AutoTokenizer, T5EncoderModel
|
|||||||
from diffusers import AutoencoderKLMochi, FlowMatchEulerDiscreteScheduler, MochiPipeline, MochiTransformer3DModel
|
from diffusers import AutoencoderKLMochi, FlowMatchEulerDiscreteScheduler, MochiPipeline, MochiTransformer3DModel
|
||||||
from diffusers.utils.testing_utils import (
|
from diffusers.utils.testing_utils import (
|
||||||
floats_tensor,
|
floats_tensor,
|
||||||
is_peft_available,
|
|
||||||
is_torch_version,
|
is_torch_version,
|
||||||
require_peft_backend,
|
require_peft_backend,
|
||||||
skip_mps,
|
skip_mps,
|
||||||
@@ -31,9 +30,6 @@ from diffusers.utils.testing_utils import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
if is_peft_available():
|
|
||||||
pass
|
|
||||||
|
|
||||||
sys.path.append(".")
|
sys.path.append(".")
|
||||||
|
|
||||||
from utils import PeftLoraLoaderMixinTests, check_if_lora_correctly_set # noqa: E402
|
from utils import PeftLoraLoaderMixinTests, check_if_lora_correctly_set # noqa: E402
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ from diffusers import (
|
|||||||
from diffusers.utils import load_image
|
from diffusers.utils import load_image
|
||||||
from diffusers.utils.import_utils import is_accelerate_available
|
from diffusers.utils.import_utils import is_accelerate_available
|
||||||
from diffusers.utils.testing_utils import (
|
from diffusers.utils.testing_utils import (
|
||||||
is_peft_available,
|
|
||||||
numpy_cosine_similarity_distance,
|
numpy_cosine_similarity_distance,
|
||||||
require_peft_backend,
|
require_peft_backend,
|
||||||
require_torch_gpu,
|
require_torch_gpu,
|
||||||
@@ -37,9 +36,6 @@ from diffusers.utils.testing_utils import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
if is_peft_available():
|
|
||||||
pass
|
|
||||||
|
|
||||||
sys.path.append(".")
|
sys.path.append(".")
|
||||||
|
|
||||||
from utils import PeftLoraLoaderMixinTests # noqa: E402
|
from utils import PeftLoraLoaderMixinTests # noqa: E402
|
||||||
|
|||||||
@@ -43,10 +43,14 @@ class AutoencoderKLHunyuanVideoTests(ModelTesterMixin, UNetTesterMixin, unittest
|
|||||||
"down_block_types": (
|
"down_block_types": (
|
||||||
"HunyuanVideoDownBlock3D",
|
"HunyuanVideoDownBlock3D",
|
||||||
"HunyuanVideoDownBlock3D",
|
"HunyuanVideoDownBlock3D",
|
||||||
|
"HunyuanVideoDownBlock3D",
|
||||||
|
"HunyuanVideoDownBlock3D",
|
||||||
),
|
),
|
||||||
"up_block_types": (
|
"up_block_types": (
|
||||||
"HunyuanVideoUpBlock3D",
|
"HunyuanVideoUpBlock3D",
|
||||||
"HunyuanVideoUpBlock3D",
|
"HunyuanVideoUpBlock3D",
|
||||||
|
"HunyuanVideoUpBlock3D",
|
||||||
|
"HunyuanVideoUpBlock3D",
|
||||||
),
|
),
|
||||||
"block_out_channels": (8, 8, 8, 8),
|
"block_out_channels": (8, 8, 8, 8),
|
||||||
"layers_per_block": 1,
|
"layers_per_block": 1,
|
||||||
@@ -154,6 +158,27 @@ class AutoencoderKLHunyuanVideoTests(ModelTesterMixin, UNetTesterMixin, unittest
|
|||||||
}
|
}
|
||||||
super().test_gradient_checkpointing_is_applied(expected_set=expected_set)
|
super().test_gradient_checkpointing_is_applied(expected_set=expected_set)
|
||||||
|
|
||||||
|
# We need to overwrite this test because the base test does not account length of down_block_types
|
||||||
|
def test_forward_with_norm_groups(self):
|
||||||
|
init_dict, inputs_dict = self.prepare_init_args_and_inputs_for_common()
|
||||||
|
|
||||||
|
init_dict["norm_num_groups"] = 16
|
||||||
|
init_dict["block_out_channels"] = (16, 16, 16, 16)
|
||||||
|
|
||||||
|
model = self.model_class(**init_dict)
|
||||||
|
model.to(torch_device)
|
||||||
|
model.eval()
|
||||||
|
|
||||||
|
with torch.no_grad():
|
||||||
|
output = model(**inputs_dict)
|
||||||
|
|
||||||
|
if isinstance(output, dict):
|
||||||
|
output = output.to_tuple()[0]
|
||||||
|
|
||||||
|
self.assertIsNotNone(output)
|
||||||
|
expected_shape = inputs_dict["sample"].shape
|
||||||
|
self.assertEqual(output.shape, expected_shape, "Input and output shapes do not match")
|
||||||
|
|
||||||
@unittest.skip("Unsupported test.")
|
@unittest.skip("Unsupported test.")
|
||||||
def test_outputs_equivalence(self):
|
def test_outputs_equivalence(self):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -0,0 +1,215 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
from PIL import Image
|
||||||
|
from transformers import AutoTokenizer, CLIPTextConfig, CLIPTextModel, CLIPTokenizer, T5EncoderModel
|
||||||
|
|
||||||
|
from diffusers import (
|
||||||
|
AutoencoderKL,
|
||||||
|
FlowMatchEulerDiscreteScheduler,
|
||||||
|
FluxControlInpaintPipeline,
|
||||||
|
FluxTransformer2DModel,
|
||||||
|
)
|
||||||
|
from diffusers.utils.testing_utils import (
|
||||||
|
torch_device,
|
||||||
|
)
|
||||||
|
|
||||||
|
from ..test_pipelines_common import (
|
||||||
|
PipelineTesterMixin,
|
||||||
|
check_qkv_fusion_matches_attn_procs_length,
|
||||||
|
check_qkv_fusion_processors_exist,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class FluxControlInpaintPipelineFastTests(unittest.TestCase, PipelineTesterMixin):
|
||||||
|
pipeline_class = FluxControlInpaintPipeline
|
||||||
|
params = frozenset(["prompt", "height", "width", "guidance_scale", "prompt_embeds", "pooled_prompt_embeds"])
|
||||||
|
batch_params = frozenset(["prompt"])
|
||||||
|
|
||||||
|
# there is no xformers processor for Flux
|
||||||
|
test_xformers_attention = False
|
||||||
|
|
||||||
|
def get_dummy_components(self):
|
||||||
|
torch.manual_seed(0)
|
||||||
|
transformer = FluxTransformer2DModel(
|
||||||
|
patch_size=1,
|
||||||
|
in_channels=8,
|
||||||
|
out_channels=4,
|
||||||
|
num_layers=1,
|
||||||
|
num_single_layers=1,
|
||||||
|
attention_head_dim=16,
|
||||||
|
num_attention_heads=2,
|
||||||
|
joint_attention_dim=32,
|
||||||
|
pooled_projection_dim=32,
|
||||||
|
axes_dims_rope=[4, 4, 8],
|
||||||
|
)
|
||||||
|
clip_text_encoder_config = CLIPTextConfig(
|
||||||
|
bos_token_id=0,
|
||||||
|
eos_token_id=2,
|
||||||
|
hidden_size=32,
|
||||||
|
intermediate_size=37,
|
||||||
|
layer_norm_eps=1e-05,
|
||||||
|
num_attention_heads=4,
|
||||||
|
num_hidden_layers=5,
|
||||||
|
pad_token_id=1,
|
||||||
|
vocab_size=1000,
|
||||||
|
hidden_act="gelu",
|
||||||
|
projection_dim=32,
|
||||||
|
)
|
||||||
|
|
||||||
|
torch.manual_seed(0)
|
||||||
|
text_encoder = CLIPTextModel(clip_text_encoder_config)
|
||||||
|
|
||||||
|
torch.manual_seed(0)
|
||||||
|
text_encoder_2 = T5EncoderModel.from_pretrained("hf-internal-testing/tiny-random-t5")
|
||||||
|
|
||||||
|
tokenizer = CLIPTokenizer.from_pretrained("hf-internal-testing/tiny-random-clip")
|
||||||
|
tokenizer_2 = AutoTokenizer.from_pretrained("hf-internal-testing/tiny-random-t5")
|
||||||
|
|
||||||
|
torch.manual_seed(0)
|
||||||
|
vae = AutoencoderKL(
|
||||||
|
sample_size=32,
|
||||||
|
in_channels=3,
|
||||||
|
out_channels=3,
|
||||||
|
block_out_channels=(4,),
|
||||||
|
layers_per_block=1,
|
||||||
|
latent_channels=1,
|
||||||
|
norm_num_groups=1,
|
||||||
|
use_quant_conv=False,
|
||||||
|
use_post_quant_conv=False,
|
||||||
|
shift_factor=0.0609,
|
||||||
|
scaling_factor=1.5035,
|
||||||
|
)
|
||||||
|
|
||||||
|
scheduler = FlowMatchEulerDiscreteScheduler()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"scheduler": scheduler,
|
||||||
|
"text_encoder": text_encoder,
|
||||||
|
"text_encoder_2": text_encoder_2,
|
||||||
|
"tokenizer": tokenizer,
|
||||||
|
"tokenizer_2": tokenizer_2,
|
||||||
|
"transformer": transformer,
|
||||||
|
"vae": vae,
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_dummy_inputs(self, device, seed=0):
|
||||||
|
if str(device).startswith("mps"):
|
||||||
|
generator = torch.manual_seed(seed)
|
||||||
|
else:
|
||||||
|
generator = torch.Generator(device="cpu").manual_seed(seed)
|
||||||
|
|
||||||
|
image = Image.new("RGB", (8, 8), 0)
|
||||||
|
control_image = Image.new("RGB", (8, 8), 0)
|
||||||
|
mask_image = Image.new("RGB", (8, 8), 255)
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
"prompt": "A painting of a squirrel eating a burger",
|
||||||
|
"control_image": control_image,
|
||||||
|
"generator": generator,
|
||||||
|
"image": image,
|
||||||
|
"mask_image": mask_image,
|
||||||
|
"strength": 0.8,
|
||||||
|
"num_inference_steps": 2,
|
||||||
|
"guidance_scale": 30.0,
|
||||||
|
"height": 8,
|
||||||
|
"width": 8,
|
||||||
|
"max_sequence_length": 48,
|
||||||
|
"output_type": "np",
|
||||||
|
}
|
||||||
|
return inputs
|
||||||
|
|
||||||
|
# def test_flux_different_prompts(self):
|
||||||
|
# pipe = self.pipeline_class(**self.get_dummy_components()).to(torch_device)
|
||||||
|
|
||||||
|
# inputs = self.get_dummy_inputs(torch_device)
|
||||||
|
# output_same_prompt = pipe(**inputs).images[0]
|
||||||
|
|
||||||
|
# inputs = self.get_dummy_inputs(torch_device)
|
||||||
|
# inputs["prompt_2"] = "a different prompt"
|
||||||
|
# output_different_prompts = pipe(**inputs).images[0]
|
||||||
|
|
||||||
|
# max_diff = np.abs(output_same_prompt - output_different_prompts).max()
|
||||||
|
|
||||||
|
# # Outputs should be different here
|
||||||
|
# # For some reasons, they don't show large differences
|
||||||
|
# assert max_diff > 1e-6
|
||||||
|
|
||||||
|
def test_flux_prompt_embeds(self):
|
||||||
|
pipe = self.pipeline_class(**self.get_dummy_components()).to(torch_device)
|
||||||
|
inputs = self.get_dummy_inputs(torch_device)
|
||||||
|
|
||||||
|
output_with_prompt = pipe(**inputs).images[0]
|
||||||
|
|
||||||
|
inputs = self.get_dummy_inputs(torch_device)
|
||||||
|
prompt = inputs.pop("prompt")
|
||||||
|
|
||||||
|
(prompt_embeds, pooled_prompt_embeds, text_ids) = pipe.encode_prompt(
|
||||||
|
prompt,
|
||||||
|
prompt_2=None,
|
||||||
|
device=torch_device,
|
||||||
|
max_sequence_length=inputs["max_sequence_length"],
|
||||||
|
)
|
||||||
|
output_with_embeds = pipe(
|
||||||
|
prompt_embeds=prompt_embeds,
|
||||||
|
pooled_prompt_embeds=pooled_prompt_embeds,
|
||||||
|
**inputs,
|
||||||
|
).images[0]
|
||||||
|
|
||||||
|
max_diff = np.abs(output_with_prompt - output_with_embeds).max()
|
||||||
|
assert max_diff < 1e-4
|
||||||
|
|
||||||
|
def test_fused_qkv_projections(self):
|
||||||
|
device = "cpu" # ensure determinism for the device-dependent torch.Generator
|
||||||
|
components = self.get_dummy_components()
|
||||||
|
pipe = self.pipeline_class(**components)
|
||||||
|
pipe = pipe.to(device)
|
||||||
|
pipe.set_progress_bar_config(disable=None)
|
||||||
|
|
||||||
|
inputs = self.get_dummy_inputs(device)
|
||||||
|
image = pipe(**inputs).images
|
||||||
|
original_image_slice = image[0, -3:, -3:, -1]
|
||||||
|
|
||||||
|
# TODO (sayakpaul): will refactor this once `fuse_qkv_projections()` has been added
|
||||||
|
# to the pipeline level.
|
||||||
|
pipe.transformer.fuse_qkv_projections()
|
||||||
|
assert check_qkv_fusion_processors_exist(
|
||||||
|
pipe.transformer
|
||||||
|
), "Something wrong with the fused attention processors. Expected all the attention processors to be fused."
|
||||||
|
assert check_qkv_fusion_matches_attn_procs_length(
|
||||||
|
pipe.transformer, pipe.transformer.original_attn_processors
|
||||||
|
), "Something wrong with the attention processors concerning the fused QKV projections."
|
||||||
|
|
||||||
|
inputs = self.get_dummy_inputs(device)
|
||||||
|
image = pipe(**inputs).images
|
||||||
|
image_slice_fused = image[0, -3:, -3:, -1]
|
||||||
|
|
||||||
|
pipe.transformer.unfuse_qkv_projections()
|
||||||
|
inputs = self.get_dummy_inputs(device)
|
||||||
|
image = pipe(**inputs).images
|
||||||
|
image_slice_disabled = image[0, -3:, -3:, -1]
|
||||||
|
|
||||||
|
assert np.allclose(
|
||||||
|
original_image_slice, image_slice_fused, atol=1e-3, rtol=1e-3
|
||||||
|
), "Fusion of QKV projections shouldn't affect the outputs."
|
||||||
|
assert np.allclose(
|
||||||
|
image_slice_fused, image_slice_disabled, atol=1e-3, rtol=1e-3
|
||||||
|
), "Outputs, with QKV projection fusion enabled, shouldn't change when fused QKV projections are disabled."
|
||||||
|
assert np.allclose(
|
||||||
|
original_image_slice, image_slice_disabled, atol=1e-2, rtol=1e-2
|
||||||
|
), "Original outputs should match when fused QKV projections are disabled."
|
||||||
|
|
||||||
|
def test_flux_image_output_shape(self):
|
||||||
|
pipe = self.pipeline_class(**self.get_dummy_components()).to(torch_device)
|
||||||
|
inputs = self.get_dummy_inputs(torch_device)
|
||||||
|
|
||||||
|
height_width_pairs = [(32, 32), (72, 57)]
|
||||||
|
for height, width in height_width_pairs:
|
||||||
|
expected_height = height - height % (pipe.vae_scale_factor * 2)
|
||||||
|
expected_width = width - width % (pipe.vae_scale_factor * 2)
|
||||||
|
|
||||||
|
inputs.update({"height": height, "width": width})
|
||||||
|
image = pipe(**inputs).images[0]
|
||||||
|
output_height, output_width, _ = image.shape
|
||||||
|
assert (output_height, output_width) == (expected_height, expected_width)
|
||||||
@@ -275,7 +275,7 @@ class MochiPipelineIntegrationTests(unittest.TestCase):
|
|||||||
gc.collect()
|
gc.collect()
|
||||||
torch.cuda.empty_cache()
|
torch.cuda.empty_cache()
|
||||||
|
|
||||||
def test_cogvideox(self):
|
def test_mochi(self):
|
||||||
generator = torch.Generator("cpu").manual_seed(0)
|
generator = torch.Generator("cpu").manual_seed(0)
|
||||||
|
|
||||||
pipe = MochiPipeline.from_pretrained("genmo/mochi-1-preview", torch_dtype=torch.float16)
|
pipe = MochiPipeline.from_pretrained("genmo/mochi-1-preview", torch_dtype=torch.float16)
|
||||||
|
|||||||
Reference in New Issue
Block a user