lint
This commit is contained in:
@@ -20,6 +20,7 @@ exclude: |
|
||||
fastvideo/train\.py|
|
||||
fastvideo/utils/.*|
|
||||
examples/.*|
|
||||
fastvideo/v1/models/schedulers/scheduling_flow_match_euler_discrete.py|
|
||||
.github/workflows/fastvideo-publish.yml|
|
||||
.github/workflows/sta-publish.yml|
|
||||
.github/workflows/build-image-template.yml
|
||||
|
||||
@@ -43,7 +43,10 @@ class ModelConfig:
|
||||
# This should be used only when loading from transformers/diffusers
|
||||
def update_model_arch(self, source_model_dict: Dict[str, Any]) -> None:
|
||||
# Remove all keys that start with "_"
|
||||
keys_to_remove = [key for key in list(source_model_dict.keys()) if str(key).startswith("_")]
|
||||
keys_to_remove = [
|
||||
key for key in list(source_model_dict.keys())
|
||||
if str(key).startswith("_")
|
||||
]
|
||||
for key in keys_to_remove:
|
||||
source_model_dict.pop(key)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from fastvideo.v1.configs.models.dits.flux import FluxImageConfig
|
||||
from fastvideo.v1.configs.models.dits.hunyuanvideo import HunyuanVideoConfig
|
||||
from fastvideo.v1.configs.models.dits.wanvideo import WanVideoConfig
|
||||
from fastvideo.v1.configs.models.dits.flux import FluxImageConfig
|
||||
|
||||
__all__ = ["HunyuanVideoConfig", "WanVideoConfig", "FluxImageConfig"]
|
||||
|
||||
@@ -2,6 +2,7 @@ from dataclasses import dataclass, field
|
||||
from typing import Optional, Tuple
|
||||
|
||||
import torch
|
||||
|
||||
from fastvideo.v1.configs.models.dits.base import DiTArchConfig, DiTConfig
|
||||
|
||||
|
||||
@@ -112,7 +113,7 @@ class FluxImageArchConfig(DiTArchConfig):
|
||||
joint_attention_dim: int = 4096
|
||||
pooled_projection_dim: int = 768
|
||||
guidance_embeds: bool = False
|
||||
axes_dims_rope: Tuple[int] = (16, 56, 56)
|
||||
axes_dims_rope: Tuple[int, ...] = (16, 56, 56)
|
||||
rope_theta: int = 10000
|
||||
dtype: Optional[torch.dtype] = torch.bfloat16
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastvideo.v1.configs.models.vaes.hunyuanvae import HunyuanVAEConfig
|
||||
from fastvideo.v1.configs.models.vaes.wanvae import WanVAEConfig
|
||||
from fastvideo.v1.configs.models.vaes.image_vae import ImageVAEConfig
|
||||
from fastvideo.v1.configs.models.vaes.wanvae import WanVAEConfig
|
||||
|
||||
__all__ = [
|
||||
"HunyuanVAEConfig",
|
||||
|
||||
@@ -1,35 +1,36 @@
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Tuple, Optional
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from fastvideo.v1.configs.models.vaes.base import VAEArchConfig, VAEConfig
|
||||
|
||||
|
||||
@dataclass
|
||||
class ImageVAEArchConfig(VAEArchConfig):
|
||||
in_channels: int = 3,
|
||||
out_channels: int = 3,
|
||||
down_block_types: Tuple[str] = ("DownEncoderBlock2D",),
|
||||
up_block_types: Tuple[str] = ("UpDecoderBlock2D",),
|
||||
block_out_channels: Tuple[int] = (64,),
|
||||
layers_per_block: int = 1,
|
||||
act_fn: str = "silu",
|
||||
latent_channels: int = 4,
|
||||
norm_num_groups: int = 32,
|
||||
sample_size: int = 32,
|
||||
scaling_factor: float = 0.18215,
|
||||
shift_factor: Optional[float] = None,
|
||||
latents_mean: Optional[Tuple[float]] = None,
|
||||
latents_std: Optional[Tuple[float]] = None,
|
||||
force_upcast: float = True,
|
||||
use_quant_conv: bool = True,
|
||||
use_post_quant_conv: bool = True,
|
||||
mid_block_add_attention: bool = True,
|
||||
in_channels: int = 3
|
||||
out_channels: int = 3
|
||||
down_block_types: Tuple[str] = ("DownEncoderBlock2D", )
|
||||
up_block_types: Tuple[str] = ("UpDecoderBlock2D", )
|
||||
block_out_channels: Tuple[int] = (64, )
|
||||
layers_per_block: int = 1
|
||||
act_fn: str = "silu"
|
||||
latent_channels: int = 4
|
||||
norm_num_groups: int = 32
|
||||
sample_size: int = 32
|
||||
scaling_factor: float = 0.18215
|
||||
shift_factor: Optional[float] = None
|
||||
latents_mean: Optional[Tuple[float]] = None
|
||||
latents_std: Optional[Tuple[float]] = None
|
||||
force_upcast: float = True
|
||||
use_quant_conv: bool = True
|
||||
use_post_quant_conv: bool = True
|
||||
mid_block_add_attention: bool = True
|
||||
|
||||
def __post_init__(self):
|
||||
self.spatial_compression_ratio: int = 2**(len(self.block_out_channels) -
|
||||
1)
|
||||
self.temporal_compression_ratio = 1
|
||||
|
||||
|
||||
@dataclass
|
||||
class ImageVAEConfig(VAEConfig):
|
||||
arch_config: VAEArchConfig = field(default_factory=ImageVAEArchConfig)
|
||||
@@ -37,4 +38,4 @@ class ImageVAEConfig(VAEConfig):
|
||||
# overrides VAEConfig
|
||||
use_tiling: bool = False
|
||||
use_temporal_tiling: bool = False
|
||||
use_parallel_tiling: bool = False
|
||||
use_parallel_tiling: bool = False
|
||||
|
||||
@@ -10,24 +10,26 @@ from fastvideo.v1.configs.models.encoders import (BaseEncoderOutput,
|
||||
from fastvideo.v1.configs.models.vaes import ImageVAEConfig
|
||||
from fastvideo.v1.configs.pipelines.base import PipelineConfig
|
||||
|
||||
|
||||
def t5_preprocess_text(prompt: str) -> str:
|
||||
return prompt
|
||||
|
||||
|
||||
def t5_postprocess_text(outputs: BaseEncoderOutput) -> torch.tensor:
|
||||
hidden_state: torch.tensor = outputs.last_hidden_state
|
||||
assert torch.isnan(hidden_state).sum() == 0
|
||||
prompt_embeds_tensor: torch.tensor = torch.stack(
|
||||
[
|
||||
torch.cat([u, u.new_zeros(512 - u.size(0), u.size(1))])
|
||||
for u in hidden_state
|
||||
],
|
||||
dim=0
|
||||
)
|
||||
prompt_embeds_tensor: torch.tensor = torch.stack([
|
||||
torch.cat([u, u.new_zeros(512 - u.size(0), u.size(1))])
|
||||
for u in hidden_state
|
||||
],
|
||||
dim=0)
|
||||
return prompt_embeds_tensor
|
||||
|
||||
|
||||
def clip_preprocess_text(prompt: str) -> str:
|
||||
return prompt
|
||||
|
||||
|
||||
def clip_postprocess_text(outputs: BaseEncoderOutput) -> torch.tensor:
|
||||
pooler_output: torch.tensor = outputs.pooler_output
|
||||
return pooler_output
|
||||
@@ -43,7 +45,7 @@ class FluxConfig(PipelineConfig):
|
||||
# VAE
|
||||
vae_config: VAEConfig = field(default_factory=ImageVAEConfig)
|
||||
# Denoising stage
|
||||
embedded_cfg_scale: int = 3.5
|
||||
embedded_cfg_scale: float = 3.5
|
||||
|
||||
# Text encoding stage
|
||||
text_encoder_configs: Tuple[EncoderConfig, ...] = field(
|
||||
@@ -63,4 +65,4 @@ class FluxConfig(PipelineConfig):
|
||||
|
||||
def __post_init__(self):
|
||||
self.vae_config.load_encoder = False
|
||||
self.vae_config.load_decoder = True
|
||||
self.vae_config.load_decoder = True
|
||||
|
||||
@@ -4,13 +4,13 @@ import os
|
||||
from typing import Callable, Dict, Optional, Type
|
||||
|
||||
from fastvideo.v1.configs.pipelines.base import PipelineConfig
|
||||
from fastvideo.v1.configs.pipelines.flux import FluxConfig
|
||||
from fastvideo.v1.configs.pipelines.hunyuan import (FastHunyuanConfig,
|
||||
HunyuanConfig)
|
||||
from fastvideo.v1.configs.pipelines.wan import (WanI2V480PConfig,
|
||||
WanI2V720PConfig,
|
||||
WanT2V480PConfig,
|
||||
WanT2V720PConfig)
|
||||
from fastvideo.v1.configs.pipelines.flux import FluxConfig
|
||||
from fastvideo.v1.logger import init_logger
|
||||
from fastvideo.v1.utils import (maybe_download_model_index,
|
||||
verify_model_config_and_directory)
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
from typing import Optional, Tuple, List, Union
|
||||
from typing import List, Optional, Tuple, Union
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
|
||||
from diffusers.models.normalization import RMSNorm
|
||||
|
||||
from fastvideo.v1.attention import DistributedAttention
|
||||
from fastvideo.v1.configs.models.dits import FluxImageConfig
|
||||
from fastvideo.v1.models.dits.base import BaseDiT
|
||||
from fastvideo.v1.attention import DistributedAttention, LocalAttention
|
||||
from fastvideo.v1.layers.layernorm import (LayerNormScaleShift, ScaleResidual,
|
||||
ScaleResidualLayerNormScaleShift)
|
||||
from fastvideo.v1.layers.linear import ReplicatedLinear
|
||||
from fastvideo.v1.layers.visual_embedding import (ModulateProjection,
|
||||
PatchEmbed, TimestepEmbedder,
|
||||
unpatchify)
|
||||
from fastvideo.v1.layers.mlp import MLP
|
||||
from fastvideo.v1.layers.rotary_embedding import (_apply_rotary_emb,
|
||||
get_rotary_pos_embed)
|
||||
from fastvideo.v1.layers.layernorm import (LayerNormScaleShift, ScaleResidual,
|
||||
ScaleResidualLayerNormScaleShift)
|
||||
from fastvideo.v1.layers.visual_embedding import (ModulateProjection,
|
||||
TimestepEmbedder)
|
||||
from fastvideo.v1.models.dits.base import BaseDiT
|
||||
from fastvideo.v1.platforms import _Backend
|
||||
from fastvideo.v1.distributed.parallel_state import get_sequence_model_parallel_world_size
|
||||
|
||||
|
||||
class MMDoubleStreamBlock(nn.Module):
|
||||
"""
|
||||
A multimodal DiT block with separate modulation for text and image/video,
|
||||
@@ -225,6 +224,7 @@ class MMDoubleStreamBlock(nn.Module):
|
||||
|
||||
return img, txt
|
||||
|
||||
|
||||
class MMSingleStreamBlock(nn.Module):
|
||||
"""
|
||||
A DiT block with parallel linear layers using distributed attention
|
||||
@@ -354,6 +354,7 @@ class MMSingleStreamBlock(nn.Module):
|
||||
# Apply residual connection with gating using fused operation
|
||||
return self.output_residual(x, output, mod_gate)
|
||||
|
||||
|
||||
class FinalLayer(nn.Module):
|
||||
"""
|
||||
The final layer of DiT that projects features to pixel space.
|
||||
@@ -373,7 +374,7 @@ class FinalLayer(nn.Module):
|
||||
elementwise_affine=False,
|
||||
dtype=dtype)
|
||||
|
||||
output_dim = patch_size ** 3 * out_channels
|
||||
output_dim = patch_size**3 * out_channels
|
||||
|
||||
self.linear = ReplicatedLinear(hidden_size,
|
||||
output_dim,
|
||||
@@ -391,15 +392,18 @@ class FinalLayer(nn.Module):
|
||||
|
||||
def forward(self, img, vec):
|
||||
scale, shift = self.adaLN_modulation(vec).chunk(2, dim=-1)
|
||||
img = self.norm_final(img) * (1.0 + scale.unsqueeze(1)) + shift.unsqueeze(1)
|
||||
img = self.norm_final(img) * (1.0 +
|
||||
scale.unsqueeze(1)) + shift.unsqueeze(1)
|
||||
img, _ = self.linear(img)
|
||||
return img
|
||||
|
||||
|
||||
class FluxTransformer2DModel(BaseDiT):
|
||||
_fsdp_shard_conditions = FluxImageConfig()._fsdp_shard_conditions
|
||||
_supported_attention_backends = FluxImageConfig()._supported_attention_backends
|
||||
_supported_attention_backends = FluxImageConfig(
|
||||
)._supported_attention_backends
|
||||
_param_names_mapping = FluxImageConfig()._param_names_mapping
|
||||
|
||||
|
||||
def __init__(self, config: FluxImageConfig) -> None:
|
||||
super().__init__(config=config)
|
||||
|
||||
@@ -426,21 +430,18 @@ class FluxTransformer2DModel(BaseDiT):
|
||||
dtype=config.dtype,
|
||||
prefix=f"{config.prefix}.time_in")
|
||||
self.txt2_in = MLP(self.text_states_dim_2,
|
||||
self.hidden_size,
|
||||
self.hidden_size,
|
||||
act_type="silu",
|
||||
dtype=config.dtype,
|
||||
prefix=f"{config.prefix}.txt2_in")
|
||||
self.guidance_in = (
|
||||
TimestepEmbedder(
|
||||
self.hidden_size,
|
||||
act_layer="silu",
|
||||
dtype=config.dtype,
|
||||
prefix=f"{config.prefix}.guidance_in"
|
||||
)
|
||||
if config.guidance_embeds else None
|
||||
)
|
||||
|
||||
self.hidden_size,
|
||||
self.hidden_size,
|
||||
act_type="silu",
|
||||
dtype=config.dtype,
|
||||
prefix=f"{config.prefix}.txt2_in")
|
||||
self.guidance_in = (TimestepEmbedder(
|
||||
self.hidden_size,
|
||||
act_layer="silu",
|
||||
dtype=config.dtype,
|
||||
prefix=f"{config.prefix}.guidance_in")
|
||||
if config.guidance_embeds else None)
|
||||
|
||||
# Double blocks
|
||||
self.double_blocks = nn.ModuleList([
|
||||
MMDoubleStreamBlock(
|
||||
@@ -506,10 +507,7 @@ class FluxTransformer2DModel(BaseDiT):
|
||||
|
||||
# Get spatial dimensions
|
||||
# _, _, oh, ow = img.shape
|
||||
th, tw = (
|
||||
h // self.patch_size // 2,
|
||||
w // self.patch_size // 2
|
||||
)
|
||||
th, tw = (h // self.patch_size // 2, w // self.patch_size // 2)
|
||||
|
||||
# Get rotary embeddings
|
||||
freqs_cos_img, freqs_sin_img = get_rotary_pos_embed(
|
||||
@@ -555,7 +553,7 @@ class FluxTransformer2DModel(BaseDiT):
|
||||
|
||||
# Extract image features
|
||||
img = x[:, :img_seq_len, ...]
|
||||
|
||||
|
||||
# Final layer
|
||||
img = self.final_layer(img, vec)
|
||||
|
||||
|
||||
@@ -19,16 +19,16 @@
|
||||
#
|
||||
# ==============================================================================
|
||||
|
||||
from dataclasses import dataclass
|
||||
import math
|
||||
from typing import Optional, Tuple, Union, List
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Optional, Tuple, Union
|
||||
|
||||
import numpy as np
|
||||
import scipy
|
||||
import torch
|
||||
from diffusers.configuration_utils import ConfigMixin, register_to_config
|
||||
from diffusers.schedulers.scheduling_utils import SchedulerMixin
|
||||
from diffusers.utils import BaseOutput, logging, is_scipy_available
|
||||
from diffusers.utils import BaseOutput, is_scipy_available, logging
|
||||
|
||||
from fastvideo.v1.models.schedulers.base import BaseScheduler
|
||||
|
||||
@@ -49,7 +49,8 @@ class FlowMatchEulerDiscreteSchedulerOutput(BaseOutput):
|
||||
prev_sample: torch.FloatTensor
|
||||
|
||||
|
||||
class FlowMatchEulerDiscreteScheduler(SchedulerMixin, ConfigMixin, BaseScheduler):
|
||||
class FlowMatchEulerDiscreteScheduler(SchedulerMixin, ConfigMixin,
|
||||
BaseScheduler):
|
||||
"""
|
||||
Euler scheduler.
|
||||
|
||||
@@ -111,15 +112,23 @@ class FlowMatchEulerDiscreteScheduler(SchedulerMixin, ConfigMixin, BaseScheduler
|
||||
stochastic_sampling: bool = False,
|
||||
):
|
||||
if self.config.use_beta_sigmas and not is_scipy_available():
|
||||
raise ImportError("Make sure to install scipy if you want to use beta sigmas.")
|
||||
if sum([self.config.use_beta_sigmas, self.config.use_exponential_sigmas, self.config.use_karras_sigmas]) > 1:
|
||||
raise ImportError(
|
||||
"Make sure to install scipy if you want to use beta sigmas.")
|
||||
if sum([
|
||||
self.config.use_beta_sigmas, self.config.use_exponential_sigmas,
|
||||
self.config.use_karras_sigmas
|
||||
]) > 1:
|
||||
raise ValueError(
|
||||
"Only one of `config.use_beta_sigmas`, `config.use_exponential_sigmas`, `config.use_karras_sigmas` can be used."
|
||||
)
|
||||
if time_shift_type not in {"exponential", "linear"}:
|
||||
raise ValueError("`time_shift_type` must either be 'exponential' or 'linear'.")
|
||||
raise ValueError(
|
||||
"`time_shift_type` must either be 'exponential' or 'linear'.")
|
||||
|
||||
timesteps = np.linspace(1, num_train_timesteps, num_train_timesteps, dtype=np.float32)[::-1].copy()
|
||||
timesteps = np.linspace(1,
|
||||
num_train_timesteps,
|
||||
num_train_timesteps,
|
||||
dtype=np.float32)[::-1].copy()
|
||||
timesteps = torch.from_numpy(timesteps).to(dtype=torch.float32)
|
||||
|
||||
sigmas = timesteps / num_train_timesteps
|
||||
@@ -134,7 +143,8 @@ class FlowMatchEulerDiscreteScheduler(SchedulerMixin, ConfigMixin, BaseScheduler
|
||||
|
||||
self._shift = shift
|
||||
|
||||
self.sigmas = sigmas.to("cpu") # to avoid too much CPU/GPU communication
|
||||
self.sigmas = sigmas.to(
|
||||
"cpu") # to avoid too much CPU/GPU communication
|
||||
self.sigma_min = self.sigmas[-1].item()
|
||||
self.sigma_max = self.sigmas[0].item()
|
||||
|
||||
@@ -197,7 +207,8 @@ class FlowMatchEulerDiscreteScheduler(SchedulerMixin, ConfigMixin, BaseScheduler
|
||||
|
||||
if sample.device.type == "mps" and torch.is_floating_point(timestep):
|
||||
# mps does not support float64
|
||||
schedule_timesteps = self.timesteps.to(sample.device, dtype=torch.float32)
|
||||
schedule_timesteps = self.timesteps.to(sample.device,
|
||||
dtype=torch.float32)
|
||||
timestep = timestep.to(sample.device, dtype=torch.float32)
|
||||
else:
|
||||
schedule_timesteps = self.timesteps.to(sample.device)
|
||||
@@ -205,7 +216,9 @@ class FlowMatchEulerDiscreteScheduler(SchedulerMixin, ConfigMixin, BaseScheduler
|
||||
|
||||
# self.begin_index is None when scheduler is used for training, or pipeline does not implement set_begin_index
|
||||
if self.begin_index is None:
|
||||
step_indices = [self.index_for_timestep(t, schedule_timesteps) for t in timestep]
|
||||
step_indices = [
|
||||
self.index_for_timestep(t, schedule_timesteps) for t in timestep
|
||||
]
|
||||
elif self.step_index is not None:
|
||||
# add_noise is called after first denoising step (for inpainting)
|
||||
step_indices = [self.step_index] * timestep.shape[0]
|
||||
@@ -279,21 +292,25 @@ class FlowMatchEulerDiscreteScheduler(SchedulerMixin, ConfigMixin, BaseScheduler
|
||||
automatically.
|
||||
"""
|
||||
if self.config.use_dynamic_shifting and mu is None:
|
||||
raise ValueError("`mu` must be passed when `use_dynamic_shifting` is set to be `True`")
|
||||
raise ValueError(
|
||||
"`mu` must be passed when `use_dynamic_shifting` is set to be `True`"
|
||||
)
|
||||
|
||||
if sigmas is not None and timesteps is not None:
|
||||
if len(sigmas) != len(timesteps):
|
||||
raise ValueError("`sigmas` and `timesteps` should have the same length")
|
||||
if sigmas is not None and timesteps is not None and len(sigmas) != len(
|
||||
timesteps):
|
||||
raise ValueError(
|
||||
"`sigmas` and `timesteps` should have the same length")
|
||||
|
||||
if num_inference_steps is not None:
|
||||
if (sigmas is not None and len(sigmas) != num_inference_steps) or (
|
||||
timesteps is not None and len(timesteps) != num_inference_steps
|
||||
):
|
||||
timesteps is not None
|
||||
and len(timesteps) != num_inference_steps):
|
||||
raise ValueError(
|
||||
"`sigmas` and `timesteps` should have the same length as num_inference_steps, if `num_inference_steps` is provided"
|
||||
)
|
||||
else:
|
||||
num_inference_steps = len(sigmas) if sigmas is not None else len(timesteps)
|
||||
num_inference_steps = len(sigmas) if sigmas is not None else len(
|
||||
timesteps)
|
||||
|
||||
self.num_inference_steps = num_inference_steps
|
||||
|
||||
@@ -305,9 +322,9 @@ class FlowMatchEulerDiscreteScheduler(SchedulerMixin, ConfigMixin, BaseScheduler
|
||||
|
||||
if sigmas is None:
|
||||
if timesteps is None:
|
||||
timesteps = np.linspace(
|
||||
self._sigma_to_t(self.sigma_max), self._sigma_to_t(self.sigma_min), num_inference_steps
|
||||
)
|
||||
timesteps = np.linspace(self._sigma_to_t(self.sigma_max),
|
||||
self._sigma_to_t(self.sigma_min),
|
||||
num_inference_steps)
|
||||
sigmas = timesteps / self.config.num_train_timesteps
|
||||
else:
|
||||
sigmas = np.array(sigmas).astype(np.float32)
|
||||
@@ -326,18 +343,22 @@ class FlowMatchEulerDiscreteScheduler(SchedulerMixin, ConfigMixin, BaseScheduler
|
||||
|
||||
# 4. If required, convert sigmas to one of karras, exponential, or beta sigma schedules
|
||||
if self.config.use_karras_sigmas:
|
||||
sigmas = self._convert_to_karras(in_sigmas=sigmas, num_inference_steps=num_inference_steps)
|
||||
sigmas = self._convert_to_karras(
|
||||
in_sigmas=sigmas, num_inference_steps=num_inference_steps)
|
||||
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)
|
||||
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)
|
||||
|
||||
# 5. Convert sigmas and timesteps to tensors and move to specified device
|
||||
sigmas = torch.from_numpy(sigmas).to(dtype=torch.float32, device=device)
|
||||
if not is_timesteps_provided:
|
||||
timesteps = sigmas * self.config.num_train_timesteps
|
||||
else:
|
||||
timesteps = torch.from_numpy(timesteps).to(dtype=torch.float32, device=device)
|
||||
timesteps = torch.from_numpy(timesteps).to(dtype=torch.float32,
|
||||
device=device)
|
||||
|
||||
# 6. Append the terminal sigma value.
|
||||
# If a model requires inverted sigma schedule for denoising but timesteps without inversion, the
|
||||
@@ -354,11 +375,9 @@ class FlowMatchEulerDiscreteScheduler(SchedulerMixin, ConfigMixin, BaseScheduler
|
||||
self._step_index = None
|
||||
self._begin_index = None
|
||||
|
||||
def scale_model_input(
|
||||
self,
|
||||
sample: torch.Tensor,
|
||||
timestep: Optional[int] = None
|
||||
) -> torch.Tensor:
|
||||
def scale_model_input(self,
|
||||
sample: torch.Tensor,
|
||||
timestep: Optional[int] = None) -> torch.Tensor:
|
||||
return sample
|
||||
|
||||
def index_for_timestep(self, timestep, schedule_timesteps=None):
|
||||
@@ -427,18 +446,11 @@ class FlowMatchEulerDiscreteScheduler(SchedulerMixin, ConfigMixin, BaseScheduler
|
||||
otherwise a tuple is returned where the first element is the sample tensor.
|
||||
"""
|
||||
|
||||
if (
|
||||
isinstance(timestep, int)
|
||||
or isinstance(timestep, torch.IntTensor)
|
||||
or isinstance(timestep, torch.LongTensor)
|
||||
):
|
||||
raise ValueError(
|
||||
(
|
||||
"Passing integer indices (e.g. from `enumerate(timesteps)`) as timesteps to"
|
||||
" `FlowMatchEulerDiscreteScheduler.step()` is not supported. Make sure to pass"
|
||||
" one of the `scheduler.timesteps` as a timestep."
|
||||
),
|
||||
)
|
||||
if isinstance(timestep, (int, torch.IntTensor, torch.LongTensor)):
|
||||
raise ValueError((
|
||||
"Passing integer indices (e.g. from `enumerate(timesteps)`) as timesteps to"
|
||||
" `FlowMatchEulerDiscreteScheduler.step()` is not supported. Make sure to pass"
|
||||
" one of the `scheduler.timesteps` as a timestep."), )
|
||||
|
||||
if self.step_index is None:
|
||||
self._init_step_index(timestep)
|
||||
@@ -480,12 +492,13 @@ class FlowMatchEulerDiscreteScheduler(SchedulerMixin, ConfigMixin, BaseScheduler
|
||||
prev_sample = prev_sample.to(model_output.dtype)
|
||||
|
||||
if not return_dict:
|
||||
return (prev_sample,)
|
||||
return (prev_sample, )
|
||||
|
||||
return FlowMatchEulerDiscreteSchedulerOutput(prev_sample=prev_sample)
|
||||
|
||||
# Copied from diffusers.schedulers.scheduling_euler_discrete.EulerDiscreteScheduler._convert_to_karras
|
||||
def _convert_to_karras(self, in_sigmas: torch.Tensor, num_inference_steps) -> torch.Tensor:
|
||||
def _convert_to_karras(self, in_sigmas: torch.Tensor,
|
||||
num_inference_steps) -> torch.Tensor:
|
||||
"""Constructs the noise schedule of Karras et al. (2022)."""
|
||||
|
||||
# Hack to make sure that other schedulers which copy this function don't break
|
||||
@@ -505,13 +518,14 @@ class FlowMatchEulerDiscreteScheduler(SchedulerMixin, ConfigMixin, BaseScheduler
|
||||
|
||||
rho = 7.0 # 7.0 is the value used in the paper
|
||||
ramp = np.linspace(0, 1, num_inference_steps)
|
||||
min_inv_rho = sigma_min ** (1 / rho)
|
||||
max_inv_rho = sigma_max ** (1 / rho)
|
||||
sigmas = (max_inv_rho + ramp * (min_inv_rho - max_inv_rho)) ** rho
|
||||
min_inv_rho = sigma_min**(1 / rho)
|
||||
max_inv_rho = sigma_max**(1 / rho)
|
||||
sigmas = (max_inv_rho + ramp * (min_inv_rho - max_inv_rho))**rho
|
||||
return sigmas
|
||||
|
||||
# Copied from diffusers.schedulers.scheduling_euler_discrete.EulerDiscreteScheduler._convert_to_exponential
|
||||
def _convert_to_exponential(self, in_sigmas: torch.Tensor, num_inference_steps: int) -> torch.Tensor:
|
||||
def _convert_to_exponential(self, in_sigmas: torch.Tensor,
|
||||
num_inference_steps: int) -> torch.Tensor:
|
||||
"""Constructs an exponential noise schedule."""
|
||||
|
||||
# Hack to make sure that other schedulers which copy this function don't break
|
||||
@@ -529,13 +543,17 @@ class FlowMatchEulerDiscreteScheduler(SchedulerMixin, ConfigMixin, BaseScheduler
|
||||
sigma_min = sigma_min if sigma_min is not None else in_sigmas[-1].item()
|
||||
sigma_max = sigma_max if sigma_max is not None else in_sigmas[0].item()
|
||||
|
||||
sigmas = np.exp(np.linspace(math.log(sigma_max), math.log(sigma_min), num_inference_steps))
|
||||
sigmas = np.exp(
|
||||
np.linspace(math.log(sigma_max), math.log(sigma_min),
|
||||
num_inference_steps))
|
||||
return sigmas
|
||||
|
||||
# Copied from diffusers.schedulers.scheduling_euler_discrete.EulerDiscreteScheduler._convert_to_beta
|
||||
def _convert_to_beta(
|
||||
self, in_sigmas: torch.Tensor, num_inference_steps: int, alpha: float = 0.6, beta: float = 0.6
|
||||
) -> torch.Tensor:
|
||||
def _convert_to_beta(self,
|
||||
in_sigmas: torch.Tensor,
|
||||
num_inference_steps: int,
|
||||
alpha: float = 0.6,
|
||||
beta: float = 0.6) -> torch.Tensor:
|
||||
"""From "Beta Sampling is All You Need" [arXiv:2407.12173] (Lee et. al, 2024)"""
|
||||
|
||||
# Hack to make sure that other schedulers which copy this function don't break
|
||||
@@ -553,22 +571,19 @@ class FlowMatchEulerDiscreteScheduler(SchedulerMixin, ConfigMixin, BaseScheduler
|
||||
sigma_min = sigma_min if sigma_min is not None else in_sigmas[-1].item()
|
||||
sigma_max = sigma_max if sigma_max is not None else in_sigmas[0].item()
|
||||
|
||||
sigmas = np.array(
|
||||
[
|
||||
sigma_min + (ppf * (sigma_max - sigma_min))
|
||||
for ppf in [
|
||||
scipy.stats.beta.ppf(timestep, alpha, beta)
|
||||
for timestep in 1 - np.linspace(0, 1, num_inference_steps)
|
||||
]
|
||||
sigmas = np.array([
|
||||
sigma_min + (ppf * (sigma_max - sigma_min)) for ppf in [
|
||||
scipy.stats.beta.ppf(timestep, alpha, beta)
|
||||
for timestep in 1 - np.linspace(0, 1, num_inference_steps)
|
||||
]
|
||||
)
|
||||
])
|
||||
return sigmas
|
||||
|
||||
def _time_shift_exponential(self, mu, sigma, t):
|
||||
return math.exp(mu) / (math.exp(mu) + (1 / t - 1) ** sigma)
|
||||
return math.exp(mu) / (math.exp(mu) + (1 / t - 1)**sigma)
|
||||
|
||||
def _time_shift_linear(self, mu, sigma, t):
|
||||
return mu / (mu + (1 / t - 1) ** sigma)
|
||||
return mu / (mu + (1 / t - 1)**sigma)
|
||||
|
||||
def __len__(self):
|
||||
return self.config.num_train_timesteps
|
||||
return self.config.num_train_timesteps
|
||||
|
||||
@@ -32,16 +32,13 @@ from typing import Dict, Optional, Union
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from diffusers.models.autoencoders.vae import Encoder, Decoder
|
||||
from diffusers.models.attention_processor import (
|
||||
AttentionProcessor,
|
||||
AttnAddedKVProcessor,
|
||||
AttnProcessor,
|
||||
Attention,
|
||||
FusedAttnProcessor2_0,
|
||||
ADDED_KV_ATTENTION_PROCESSORS,
|
||||
CROSS_ATTENTION_PROCESSORS,
|
||||
)
|
||||
from diffusers.models.attention_processor import (ADDED_KV_ATTENTION_PROCESSORS,
|
||||
CROSS_ATTENTION_PROCESSORS,
|
||||
Attention, AttentionProcessor,
|
||||
AttnAddedKVProcessor,
|
||||
AttnProcessor,
|
||||
FusedAttnProcessor2_0)
|
||||
from diffusers.models.autoencoders.vae import Decoder, Encoder
|
||||
|
||||
from fastvideo.v1.configs.models.vaes import ImageVAEConfig
|
||||
from fastvideo.v1.models.vaes.common import DiagonalGaussianDistribution
|
||||
@@ -75,7 +72,7 @@ class AutoencoderKL(nn.Module):
|
||||
Synthesis with Latent Diffusion Models](https://arxiv.org/abs/2112.10752) paper.
|
||||
force_upcast (`bool`, *optional*, default to `True`):
|
||||
If enabled it will force the VAE to run in float32 for high image resolution pipelines, such as SD-XL. VAE
|
||||
can be fine-tuned / trained to a lower range without loosing too much precision in which case
|
||||
can be fine-tuned / trained to a lower range without losing too much precision in which case
|
||||
`force_upcast` can be set to `False` - see: https://huggingface.co/madebyollin/sdxl-vae-fp16-fix
|
||||
mid_block_add_attention (`bool`, *optional*, default to `True`):
|
||||
If enabled, the mid_block of the Encoder and Decoder will have attention blocks. If set to false, the
|
||||
@@ -106,7 +103,9 @@ class AutoencoderKL(nn.Module):
|
||||
double_z=True,
|
||||
mid_block_add_attention=config.mid_block_add_attention,
|
||||
)
|
||||
self.quant_conv = nn.Conv2d(2 * config.latent_channels, 2 * config.latent_channels, 1) if config.use_quant_conv else None
|
||||
self.quant_conv = nn.Conv2d(2 * config.latent_channels, 2 *
|
||||
config.latent_channels,
|
||||
1) if config.use_quant_conv else None
|
||||
|
||||
if config.load_decoder:
|
||||
# pass init params to Decoder
|
||||
@@ -120,19 +119,19 @@ class AutoencoderKL(nn.Module):
|
||||
act_fn=config.act_fn,
|
||||
mid_block_add_attention=config.mid_block_add_attention,
|
||||
)
|
||||
self.post_quant_conv = nn.Conv2d(config.latent_channels, config.latent_channels, 1) if config.use_post_quant_conv else None
|
||||
self.post_quant_conv = nn.Conv2d(
|
||||
config.latent_channels, config.latent_channels,
|
||||
1) if config.use_post_quant_conv else None
|
||||
|
||||
self.use_slicing = False
|
||||
self.use_tiling = False
|
||||
|
||||
# only relevant if vae tiling is enabled
|
||||
self.tile_sample_min_size = config.sample_size
|
||||
sample_size = (
|
||||
config.sample_size[0]
|
||||
if isinstance(config.sample_size, (list, tuple))
|
||||
else config.sample_size
|
||||
)
|
||||
self.tile_latent_min_size = int(sample_size / (2 ** (len(config.block_out_channels) - 1)))
|
||||
sample_size = (config.sample_size[0] if isinstance(
|
||||
config.sample_size, (list, tuple)) else config.sample_size)
|
||||
self.tile_latent_min_size = int(
|
||||
sample_size / (2**(len(config.block_out_channels) - 1)))
|
||||
self.tile_overlap_factor = 0.25
|
||||
|
||||
def enable_tiling(self, use_tiling: bool = True):
|
||||
@@ -173,14 +172,17 @@ class AutoencoderKL(nn.Module):
|
||||
indexed by its weight name.
|
||||
"""
|
||||
# set recursively
|
||||
processors = {}
|
||||
processors: dict[str, AttentionProcessor] = {}
|
||||
|
||||
def fn_recursive_add_processors(name: str, module: torch.nn.Module, processors: Dict[str, AttentionProcessor]):
|
||||
def fn_recursive_add_processors(name: str, module: torch.nn.Module,
|
||||
processors: Dict[str,
|
||||
AttentionProcessor]):
|
||||
if hasattr(module, "get_processor"):
|
||||
processors[f"{name}.processor"] = module.get_processor()
|
||||
|
||||
for sub_name, child in module.named_children():
|
||||
fn_recursive_add_processors(f"{name}.{sub_name}", child, processors)
|
||||
fn_recursive_add_processors(f"{name}.{sub_name}", child,
|
||||
processors)
|
||||
|
||||
return processors
|
||||
|
||||
@@ -190,7 +192,9 @@ class AutoencoderKL(nn.Module):
|
||||
return processors
|
||||
|
||||
# Copied from diffusers.models.unets.unet_2d_condition.UNet2DConditionModel.set_attn_processor
|
||||
def set_attn_processor(self, processor: Union[AttentionProcessor, Dict[str, AttentionProcessor]]):
|
||||
def set_attn_processor(self, processor: Union[AttentionProcessor,
|
||||
Dict[str,
|
||||
AttentionProcessor]]):
|
||||
r"""
|
||||
Sets the attention processor to use to compute attention.
|
||||
|
||||
@@ -211,7 +215,8 @@ class AutoencoderKL(nn.Module):
|
||||
f" number of attention layers: {count}. Please make sure to pass {count} processor classes."
|
||||
)
|
||||
|
||||
def fn_recursive_attn_processor(name: str, module: torch.nn.Module, processor):
|
||||
def fn_recursive_attn_processor(name: str, module: torch.nn.Module,
|
||||
processor):
|
||||
if hasattr(module, "set_processor"):
|
||||
if not isinstance(processor, dict):
|
||||
module.set_processor(processor)
|
||||
@@ -219,7 +224,8 @@ class AutoencoderKL(nn.Module):
|
||||
module.set_processor(processor.pop(f"{name}.processor"))
|
||||
|
||||
for sub_name, child in module.named_children():
|
||||
fn_recursive_attn_processor(f"{name}.{sub_name}", child, processor)
|
||||
fn_recursive_attn_processor(f"{name}.{sub_name}", child,
|
||||
processor)
|
||||
|
||||
for name, module in self.named_children():
|
||||
fn_recursive_attn_processor(name, module, processor)
|
||||
@@ -229,9 +235,11 @@ class AutoencoderKL(nn.Module):
|
||||
"""
|
||||
Disables custom attention processors and sets the default attention implementation.
|
||||
"""
|
||||
if all(proc.__class__ in ADDED_KV_ATTENTION_PROCESSORS for proc in self.attn_processors.values()):
|
||||
if all(proc.__class__ in ADDED_KV_ATTENTION_PROCESSORS
|
||||
for proc in self.attn_processors.values()):
|
||||
processor = AttnAddedKVProcessor()
|
||||
elif all(proc.__class__ in CROSS_ATTENTION_PROCESSORS for proc in self.attn_processors.values()):
|
||||
elif all(proc.__class__ in CROSS_ATTENTION_PROCESSORS
|
||||
for proc in self.attn_processors.values()):
|
||||
processor = AttnProcessor()
|
||||
else:
|
||||
raise ValueError(
|
||||
@@ -243,7 +251,8 @@ class AutoencoderKL(nn.Module):
|
||||
def _encode(self, x: torch.Tensor) -> torch.Tensor:
|
||||
batch_size, num_channels, height, width = x.shape
|
||||
|
||||
if self.use_tiling and (width > self.tile_sample_min_size or height > self.tile_sample_min_size):
|
||||
if self.use_tiling and (width > self.tile_sample_min_size
|
||||
or height > self.tile_sample_min_size):
|
||||
return self._tiled_encode(x)
|
||||
|
||||
enc = self.encoder(x)
|
||||
@@ -276,7 +285,8 @@ class AutoencoderKL(nn.Module):
|
||||
return enc
|
||||
|
||||
def _decode(self, z: torch.Tensor) -> torch.Tensor:
|
||||
if self.use_tiling and (z.shape[-1] > self.tile_latent_min_size or z.shape[-2] > self.tile_latent_min_size):
|
||||
if self.use_tiling and (z.shape[-1] > self.tile_latent_min_size
|
||||
or z.shape[-2] > self.tile_latent_min_size):
|
||||
return self.tiled_decode(z)
|
||||
|
||||
if self.post_quant_conv is not None:
|
||||
@@ -302,23 +312,29 @@ class AutoencoderKL(nn.Module):
|
||||
|
||||
"""
|
||||
if self.use_slicing and z.shape[0] > 1:
|
||||
decoded_slices = [self._decode(z_slice).sample for z_slice in z.split(1)]
|
||||
decoded_slices = [
|
||||
self._decode(z_slice).sample for z_slice in z.split(1)
|
||||
]
|
||||
decoded = torch.cat(decoded_slices)
|
||||
else:
|
||||
decoded = self._decode(z)
|
||||
|
||||
return decoded
|
||||
|
||||
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)
|
||||
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
|
||||
|
||||
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)
|
||||
for x in range(blend_extent):
|
||||
b[:, :, :, x] = a[:, :, :, -blend_extent + x] * (1 - x / blend_extent) + b[:, :, :, x] * (x / blend_extent)
|
||||
b[:, :, :, x] = a[:, :, :, -blend_extent + x] * (
|
||||
1 - x / blend_extent) + b[:, :, :, x] * (x / blend_extent)
|
||||
return b
|
||||
|
||||
def _tiled_encode(self, x: torch.Tensor) -> torch.Tensor:
|
||||
@@ -338,7 +354,8 @@ class AutoencoderKL(nn.Module):
|
||||
The latent representation of the encoded videos.
|
||||
"""
|
||||
|
||||
overlap_size = int(self.tile_sample_min_size * (1 - self.tile_overlap_factor))
|
||||
overlap_size = int(self.tile_sample_min_size *
|
||||
(1 - self.tile_overlap_factor))
|
||||
blend_extent = int(self.tile_latent_min_size * self.tile_overlap_factor)
|
||||
row_limit = self.tile_latent_min_size - blend_extent
|
||||
|
||||
@@ -347,9 +364,10 @@ class AutoencoderKL(nn.Module):
|
||||
for i in range(0, x.shape[2], overlap_size):
|
||||
row = []
|
||||
for j in range(0, x.shape[3], overlap_size):
|
||||
tile = x[:, :, i : i + self.tile_sample_min_size, j : j + self.tile_sample_min_size]
|
||||
tile = x[:, :, i:i + self.tile_sample_min_size,
|
||||
j:j + self.tile_sample_min_size]
|
||||
tile = self.encoder(tile)
|
||||
if self.config.use_quant_conv:
|
||||
if self.quant_conv:
|
||||
tile = self.quant_conv(tile)
|
||||
row.append(tile)
|
||||
rows.append(row)
|
||||
@@ -389,7 +407,8 @@ class AutoencoderKL(nn.Module):
|
||||
`tuple` is returned.
|
||||
"""
|
||||
|
||||
overlap_size = int(self.tile_sample_min_size * (1 - self.tile_overlap_factor))
|
||||
overlap_size = int(self.tile_sample_min_size *
|
||||
(1 - self.tile_overlap_factor))
|
||||
blend_extent = int(self.tile_latent_min_size * self.tile_overlap_factor)
|
||||
row_limit = self.tile_latent_min_size - blend_extent
|
||||
|
||||
@@ -398,9 +417,10 @@ class AutoencoderKL(nn.Module):
|
||||
for i in range(0, x.shape[2], overlap_size):
|
||||
row = []
|
||||
for j in range(0, x.shape[3], overlap_size):
|
||||
tile = x[:, :, i : i + self.tile_sample_min_size, j : j + self.tile_sample_min_size]
|
||||
tile = x[:, :, i:i + self.tile_sample_min_size,
|
||||
j:j + self.tile_sample_min_size]
|
||||
tile = self.encoder(tile)
|
||||
if self.config.use_quant_conv:
|
||||
if self.quant_conv:
|
||||
tile = self.quant_conv(tile)
|
||||
row.append(tile)
|
||||
rows.append(row)
|
||||
@@ -436,7 +456,8 @@ class AutoencoderKL(nn.Module):
|
||||
If return_dict is True, a [`~models.vae.DecoderOutput`] is returned, otherwise a plain `tuple` is
|
||||
returned.
|
||||
"""
|
||||
overlap_size = int(self.tile_latent_min_size * (1 - self.tile_overlap_factor))
|
||||
overlap_size = int(self.tile_latent_min_size *
|
||||
(1 - self.tile_overlap_factor))
|
||||
blend_extent = int(self.tile_sample_min_size * self.tile_overlap_factor)
|
||||
row_limit = self.tile_sample_min_size - blend_extent
|
||||
|
||||
@@ -446,8 +467,9 @@ class AutoencoderKL(nn.Module):
|
||||
for i in range(0, z.shape[2], overlap_size):
|
||||
row = []
|
||||
for j in range(0, z.shape[3], overlap_size):
|
||||
tile = z[:, :, i : i + self.tile_latent_min_size, j : j + self.tile_latent_min_size]
|
||||
if self.config.use_post_quant_conv:
|
||||
tile = z[:, :, i:i + self.tile_latent_min_size,
|
||||
j:j + self.tile_latent_min_size]
|
||||
if self.post_quant_conv:
|
||||
tile = self.post_quant_conv(tile)
|
||||
decoded = self.decoder(tile)
|
||||
row.append(decoded)
|
||||
@@ -507,7 +529,9 @@ class AutoencoderKL(nn.Module):
|
||||
|
||||
for _, attn_processor in self.attn_processors.items():
|
||||
if "Added" in str(attn_processor.__class__.__name__):
|
||||
raise ValueError("`fuse_qkv_projections()` is not supported for models having added KV projections.")
|
||||
raise ValueError(
|
||||
"`fuse_qkv_projections()` is not supported for models having added KV projections."
|
||||
)
|
||||
|
||||
self.original_attn_processors = self.attn_processors
|
||||
|
||||
|
||||
@@ -128,7 +128,10 @@ class ComposedPipelineBase(ABC):
|
||||
modules_config = deepcopy(self.config)
|
||||
|
||||
# remove keys that are not pipeline modules
|
||||
keys_to_remove = [key for key in list(modules_config.keys()) if str(key).startswith("_")]
|
||||
keys_to_remove = [
|
||||
key for key in list(modules_config.keys())
|
||||
if str(key).startswith("_")
|
||||
]
|
||||
for key in keys_to_remove:
|
||||
modules_config.pop(key)
|
||||
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
import importlib
|
||||
from einops import rearrange
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
from fastvideo.v1.fastvideo_args import FastVideoArgs
|
||||
from fastvideo.v1.pipelines.pipeline_batch_info import ForwardBatch
|
||||
from fastvideo.v1.pipelines.stages.base import PipelineStage
|
||||
from fastvideo.v1.pipelines.stages.denoising import DenoisingStage
|
||||
from fastvideo.v1.forward_context import set_forward_context
|
||||
from einops import rearrange
|
||||
|
||||
from fastvideo.v1.attention import get_attn_backend
|
||||
from fastvideo.v1.distributed import (get_sequence_model_parallel_rank,
|
||||
get_sequence_model_parallel_world_size,)
|
||||
get_sequence_model_parallel_world_size)
|
||||
from fastvideo.v1.distributed.communication_op import (
|
||||
sequence_model_parallel_all_gather)
|
||||
from fastvideo.v1.fastvideo_args import FastVideoArgs
|
||||
@@ -20,54 +14,61 @@ from fastvideo.v1.forward_context import set_forward_context
|
||||
from fastvideo.v1.logger import init_logger
|
||||
from fastvideo.v1.pipelines.pipeline_batch_info import ForwardBatch
|
||||
from fastvideo.v1.pipelines.stages.base import PipelineStage
|
||||
from fastvideo.v1.pipelines.stages.denoising import DenoisingStage
|
||||
from fastvideo.v1.platforms import _Backend
|
||||
from fastvideo.v1.utils import PRECISION_TO_TYPE
|
||||
|
||||
|
||||
logger = init_logger(__name__)
|
||||
|
||||
|
||||
class TimestepsPreparationPreStage(PipelineStage):
|
||||
def __init__(self, scheduler):
|
||||
|
||||
def __init__(self, scheduler) -> None:
|
||||
super().__init__()
|
||||
self.scheduler = scheduler
|
||||
|
||||
def calculate_shift(
|
||||
self,
|
||||
image_seq_len,
|
||||
base_seq_len: int = 256,
|
||||
max_seq_len: int = 4096,
|
||||
base_shift: float = 0.5,
|
||||
max_shift: float = 1.15
|
||||
):
|
||||
def calculate_shift(self,
|
||||
image_seq_len,
|
||||
base_seq_len: int = 256,
|
||||
max_seq_len: int = 4096,
|
||||
base_shift: float = 0.5,
|
||||
max_shift: float = 1.15):
|
||||
m = (max_shift - base_shift) / (max_seq_len - base_seq_len)
|
||||
b = base_shift - m * base_seq_len
|
||||
mu = image_seq_len * m + b
|
||||
return mu
|
||||
|
||||
def forward(self, batch: ForwardBatch, fastvideo_args: FastVideoArgs) -> ForwardBatch:
|
||||
def forward(self, batch: ForwardBatch,
|
||||
fastvideo_args: FastVideoArgs) -> ForwardBatch:
|
||||
assert batch.height is not None
|
||||
assert batch.width is not None
|
||||
|
||||
batch.sigmas = np.linspace(
|
||||
1.0, 1 / batch.num_inference_steps, batch.num_inference_steps
|
||||
) if batch.sigmas is None else batch.sigmas
|
||||
1.0, 1 / batch.num_inference_steps,
|
||||
batch.num_inference_steps) if batch.sigmas is None else batch.sigmas
|
||||
spatial_compression_ratio = fastvideo_args.vae_config.arch_config.spatial_compression_ratio
|
||||
batch.extra_set_timesteps_kwargs["mu"] = (
|
||||
batch.extra_set_timesteps_kwargs.get("mu", None) or
|
||||
self.calculate_shift(
|
||||
(batch.height // spatial_compression_ratio // 2) *
|
||||
(batch.width // spatial_compression_ratio // 2),
|
||||
batch.extra_set_timesteps_kwargs.get("mu", None)
|
||||
or self.calculate_shift(
|
||||
(batch.height // spatial_compression_ratio // 2) *
|
||||
(batch.width // spatial_compression_ratio // 2),
|
||||
self.scheduler.config.get("base_image_seq_len", 256),
|
||||
self.scheduler.config.get("max_image_seq_len", 4096),
|
||||
self.scheduler.config.get("base_shift", 0.5),
|
||||
self.scheduler.config.get("max_shift", 1.15),
|
||||
)
|
||||
)
|
||||
))
|
||||
return batch
|
||||
|
||||
|
||||
class DenoisingPreprocessingStage(PipelineStage):
|
||||
def __init__(self):
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
def forward(self, batch: ForwardBatch, fastvideo_args: FastVideoArgs) -> ForwardBatch:
|
||||
def forward(self, batch: ForwardBatch,
|
||||
fastvideo_args: FastVideoArgs) -> ForwardBatch:
|
||||
# [B, in_channels // 4, 1, H, W] -> [B, H // 2, W // 2, in_channels]
|
||||
assert batch.latents is not None
|
||||
b, c, _, h, w = batch.latents.shape
|
||||
latents = batch.latents.view(b, c, h // 2, 2, w // 2, 2)
|
||||
latents = latents.permute(0, 2, 4, 1, 3, 5)
|
||||
@@ -75,11 +76,18 @@ class DenoisingPreprocessingStage(PipelineStage):
|
||||
batch.latents = latents
|
||||
return batch
|
||||
|
||||
|
||||
class DenoisingPostprocessingStage(PipelineStage):
|
||||
def __init__(self):
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
def forward(self, batch: ForwardBatch, fastvideo_args: FastVideoArgs) -> ForwardBatch:
|
||||
def forward(self, batch: ForwardBatch,
|
||||
fastvideo_args: FastVideoArgs) -> ForwardBatch:
|
||||
assert batch.latents is not None
|
||||
assert batch.height_latents is not None
|
||||
assert batch.width_latents is not None
|
||||
|
||||
latents = batch.latents
|
||||
# Skip decoding if output type is latent
|
||||
if fastvideo_args.output_type == "latent":
|
||||
@@ -105,6 +113,7 @@ if spec is not None:
|
||||
from fastvideo.v1.attention.backends.sliding_tile_attn import (
|
||||
SlidingTileAttentionBackend)
|
||||
|
||||
|
||||
class FluxDenoisingStage(DenoisingStage):
|
||||
"""
|
||||
Stage for running the denoising loop in diffusion pipelines.
|
||||
@@ -338,11 +347,13 @@ class FluxDenoisingStage(DenoisingStage):
|
||||
|
||||
|
||||
class ImageOutputStage(PipelineStage):
|
||||
def __init__(self):
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
def forward(self, batch: ForwardBatch, fastvideo_args: FastVideoArgs) -> ForwardBatch:
|
||||
def forward(self, batch: ForwardBatch,
|
||||
fastvideo_args: FastVideoArgs) -> ForwardBatch:
|
||||
output = batch.output
|
||||
output = output.unsqueeze(2)
|
||||
batch.output = output
|
||||
return batch
|
||||
return batch
|
||||
|
||||
@@ -9,17 +9,14 @@ using the modular pipeline architecture.
|
||||
from fastvideo.v1.fastvideo_args import FastVideoArgs
|
||||
from fastvideo.v1.logger import init_logger
|
||||
from fastvideo.v1.pipelines.composed_pipeline_base import ComposedPipelineBase
|
||||
from fastvideo.v1.pipelines.stages import (TextEncodingStage,
|
||||
ConditioningStage,
|
||||
DecodingStage,
|
||||
from fastvideo.v1.pipelines.flux.custom_stages import (
|
||||
DenoisingPostprocessingStage, DenoisingPreprocessingStage,
|
||||
FluxDenoisingStage, ImageOutputStage, TimestepsPreparationPreStage)
|
||||
from fastvideo.v1.pipelines.stages import (ConditioningStage, DecodingStage,
|
||||
InputValidationStage,
|
||||
LatentPreparationStage,
|
||||
TextEncodingStage,
|
||||
TimestepPreparationStage)
|
||||
from fastvideo.v1.pipelines.flux.custom_stages import (DenoisingPreprocessingStage,
|
||||
DenoisingPostprocessingStage,
|
||||
ImageOutputStage,
|
||||
FluxDenoisingStage,
|
||||
TimestepsPreparationPreStage)
|
||||
|
||||
logger = init_logger(__name__)
|
||||
|
||||
@@ -79,7 +76,7 @@ class FluxPipeline(ComposedPipelineBase):
|
||||
self.add_stage(stage_name="decoding_stage",
|
||||
stage=DecodingStage(vae=self.get_module("vae")))
|
||||
|
||||
self.add_stage(stage_name="output_stage",
|
||||
stage=ImageOutputStage())
|
||||
self.add_stage(stage_name="output_stage", stage=ImageOutputStage())
|
||||
|
||||
|
||||
EntryClass = FluxPipeline
|
||||
|
||||
@@ -64,6 +64,7 @@ class DenoisingStage(PipelineStage):
|
||||
Returns:
|
||||
The batch with denoised latents.
|
||||
"""
|
||||
assert batch.latents is not None
|
||||
# Prepare extra step kwargs for scheduler
|
||||
extra_step_kwargs = self.prepare_extra_func_kwargs(
|
||||
self.scheduler.step,
|
||||
@@ -301,8 +302,9 @@ class DenoisingStage(PipelineStage):
|
||||
"""
|
||||
extra_step_kwargs = {}
|
||||
for k, v in kwargs.items():
|
||||
accepts = (k in set(inspect.signature(func).parameters.keys()) or
|
||||
"kwargs" in set(inspect.signature(func).parameters.keys()))
|
||||
accepts = (k in set(inspect.signature(func).parameters.keys())
|
||||
or "kwargs" in set(
|
||||
inspect.signature(func).parameters.keys()))
|
||||
if accepts:
|
||||
extra_step_kwargs[k] = v
|
||||
return extra_step_kwargs
|
||||
|
||||
@@ -7,8 +7,6 @@ This module contains implementations of timestep preparation stages for diffusio
|
||||
|
||||
import inspect
|
||||
|
||||
import numpy as np
|
||||
|
||||
from fastvideo.v1.fastvideo_args import FastVideoArgs
|
||||
from fastvideo.v1.logger import init_logger
|
||||
from fastvideo.v1.pipelines.pipeline_batch_info import ForwardBatch
|
||||
|
||||
Reference in New Issue
Block a user