# Copyright 2024 NVIDIA CORPORATION & AFFILIATES # # 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. # # SPDX-License-Identifier: Apache-2.0 import json from dataclasses import asdict, dataclass, field from typing import Any, Dict, List, Optional, Tuple, Union @dataclass class BaseConfig: def get(self, attribute_name, default=None): return getattr(self, attribute_name, default) def pop(self, attribute_name, default=None): if hasattr(self, attribute_name): value = getattr(self, attribute_name) delattr(self, attribute_name) return value else: return default def __str__(self): return json.dumps(asdict(self), indent=4) @dataclass class DataConfig(BaseConfig): data_dir: List[str] = field(default_factory=list) caption_proportion: Dict[str, int] = field(default_factory=lambda: {"prompt": 1}) external_caption_suffixes: List[str] = field(default_factory=list) external_clipscore_suffixes: List[str] = field(default_factory=list) caption_selection_type: str = ( "clipscore" # clipscore: use $external_clipscore_suffixes, proportion: use $caption_proportion ) clip_thr_temperature: float = 1.0 clip_thr: float = 0.0 del_img_clip_thr: float = 0.0 sort_dataset: bool = False load_text_feat: bool = False load_vae_feat: bool = False aspect_ratio_type: str = "ASPECT_RATIO_1024" transform: str = "default_train" type: str = "SanaWebDatasetMS" image_size: int = 512 hq_only: bool = False valid_num: int = 0 data: Any = None num_frames: int = 81 extra: Any = None @dataclass class VideoDataConfig(DataConfig): data_dir: Dict[str, str] = field(default_factory=lambda: {"video_toy_data: data/video_toy_data"}) aspect_ratio_type: str = "ASPECT_RATIO_VIDEO_256_MS" external_data_filter: Dict[str, Dict[str, Dict[str, float]]] = field(default_factory=lambda: {}) motion_score_file_thres: Dict[str, Optional[float]] = field(default_factory=dict) motion_score_cal_type: str = "average" # average, max target_fps: int = 16 resample_fps: bool = True shuffle_dataset: bool = False vae_cache_dir: Optional[str] = None json_cache_dir: Optional[str] = None load_first_frame: bool = False @dataclass class ModelConfig(BaseConfig): model: str = "SanaMS_600M_P1_D28" teacher: Optional[str] = None image_size: int = 512 mixed_precision: str = "fp16" # ['fp16', 'fp32', 'bf16'] fp32_attention: bool = True load_from: Optional[str] = None discriminator_model: Optional[str] = None teacher_model: Optional[str] = None teacher_model_weight_dtype: Optional[str] = None resume_from: Optional[Union[Dict[str, Any], str]] = field( default_factory=lambda: { "checkpoint": None, "load_ema": False, "resume_lr_scheduler": True, "resume_optimizer": True, } ) aspect_ratio_type: Optional[str] = None multi_scale: bool = True pe_interpolation: float = 1.0 micro_condition: bool = False attn_type: str = "linear" autocast_linear_attn: bool = False ffn_type: str = "glumbconv" mlp_acts: List[Optional[str]] = field(default_factory=lambda: ["silu", "silu", None]) mlp_ratio: float = 2.5 use_pe: bool = False pos_embed_type: str = "sincos" # "sincos", "flux_rope", "wan_rope" qk_norm: bool = False class_dropout_prob: float = 0.0 linear_head_dim: int = 32 cross_norm: bool = False cross_attn_type: str = "flash" logvar: bool = False cfg_scale: int = 4 cfg_embed: bool = False cfg_embed_scale: float = 1.0 guidance_type: str = "classifier-free" pag_applied_layers: List[int] = field(default_factory=lambda: [8]) # for ladd ladd_multi_scale: bool = True head_block_ids: Optional[List[int]] = None extra: Any = None @dataclass class ModelVideoConfig(ModelConfig): # stage1 remove_state_dict_keys: Optional[List[str]] = None # stage2 rope_fhw_dim: Optional[Tuple[int, int, int]] = None t_kernel_size: int = 3 flash_attn_window_count: Optional[List[int]] = None pack_latents: bool = False encode_image_prompt_embeds: bool = False # stage3 cross_attn_image_embeds: bool = False image_latent_mode: str = "video_zero" # chunkcasual chunk_index: Optional[List[int]] = None @dataclass class AEConfig(BaseConfig): vae_type: str = "AutoencoderDC" vae_pretrained: str = "mit-han-lab/dc-ae-f32c32-sana-1.1-diffusers" weight_dtype: str = "float32" scale_factor: float = 0.41407 scaling_factor: Optional[Union[float, List[float]]] = None # for st-dc-ae vae_latent_dim: int = 32 vae_downsample_rate: int = 32 sample_posterior: bool = True vae_stride: Optional[List[int]] = None if_cache: bool = False cache_dir: Optional[str] = None # Framewise / tiling fields used by LTX2VAE_diffusers for long-video decode. use_framewise_encoding: bool = False use_framewise_decoding: bool = False tile_sample_stride_num_frames: int = 64 tile_sample_min_num_frames: int = 96 extra: Any = None @dataclass class TextEncoderConfig(BaseConfig): text_encoder_name: str = "gemma-2-2b-it" caption_channels: int = 2304 y_norm: bool = True y_norm_scale_factor: float = 1.0 model_max_length: int = 300 chi_prompt: List[Optional[str]] = field(default_factory=lambda: []) extra: Any = None @dataclass class ImageEncoderConfig(BaseConfig): image_encoder_name: Optional[str] = None image_encoder_path: Optional[str] = None weight_dtype: Optional[str] = "bf16" @dataclass class SchedulerConfig(BaseConfig): train_sampling_steps: int = 1000 predict_flow_v: bool = True noise_schedule: str = "linear_flow" pred_sigma: bool = False learn_sigma: bool = True vis_sampler: str = "flow_dpm-solver" flow_shift: float = 1.0 inference_flow_shift: Optional[float] = None # logit-normal timestep weighting_scheme: Optional[str] = "logit_normal" weighting_scheme_discriminator: Optional[str] = "logit_normal_trigflow" add_noise_timesteps: List[float] = field(default_factory=lambda: [1.57080]) logit_mean: float = 0.0 logit_std: float = 1.0 logit_mean_discriminator: float = 0.0 logit_std_discriminator: float = 1.0 mode_scale: float = 1.29 sigma_data: float = 1.0 p_low: Optional[float] = None p_high: Optional[float] = None timestep_norm_scale_factor: float = 1.0 pretrain_timestep_norm_scale_factor: float = 1.0 discrete_norm_timestep: bool = False extra: Any = None @dataclass class TrainingConfig(BaseConfig): num_workers: int = 4 seed: int = 42 train_batch_size: int = 32 train_batch_size_image: int = 32 early_stop_hours: float = 100 num_epochs: int = 100 gradient_accumulation_steps: int = 1 grad_checkpointing: bool = False gradient_clip: float = 1.0 gc_step: int = 1 optimizer: Dict[str, Any] = field( default_factory=lambda: {"eps": 1.0e-10, "lr": 0.0001, "type": "AdamW", "weight_decay": 0.03} ) optimizer_D: Dict[str, Any] = field( default_factory=lambda: {"eps": 1.0e-10, "lr": 0.0001, "type": "AdamW", "weight_decay": 0.03} ) load_from_optimizer: bool = False load_from_lr_scheduler: bool = False resume_lr_scheduler: bool = True lr_schedule: str = "constant" lr_schedule_args: Dict[str, int] = field(default_factory=lambda: {"num_warmup_steps": 500}) auto_lr: Optional[Dict[str, str]] = field(default_factory=lambda: {"rule": "sqrt"}) ema_rate: float = 0.9999 eval_batch_size: int = 16 use_fsdp: bool = False fsdp_version: int = 1 cp_size: int = 0 use_flash_attn: bool = False eval_sampling_steps: int = 250 lora_rank: int = 4 log_interval: int = 50 mask_type: str = "null" mask_loss_coef: float = 0.0 load_mask_index: bool = False snr_loss: bool = False real_prompt_ratio: float = 1.0 save_image_epochs: int = 1 save_model_epochs: int = 1 save_model_steps: int = 1000000 visualize: bool = False null_embed_root: str = "output/pretrained_models/" valid_prompt_embed_root: str = "output/tmp_embed/" validation_prompts: List[str] = field( default_factory=lambda: [ "dog", "portrait photo of a girl, photograph, highly detailed face, depth of field", "Self-portrait oil painting, a beautiful cyborg with golden hair, 8k", "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k", "A photo of beautiful mountain with realistic sunset and blue lake, highly detailed, masterpiece", ] ) local_save_vis: bool = False deterministic_validation: bool = True online_metric: bool = False eval_metric_step: int = 5000 online_metric_dir: str = "metric_helper" work_dir: str = "/cache/exps/" skip_step: int = 0 loss_type: str = "huber" huber_c: float = 0.001 num_ddim_timesteps: int = 50 w_max: float = 15.0 w_min: float = 3.0 ema_decay: float = 0.95 debug_nan: bool = False ema_update: bool = False ema_rate: float = 0.9999 weight_loss: bool = True tangent_warmup_steps: int = 10000 scm_cfg_scale: Union[float, List[float]] = field(default_factory=lambda: [1.0]) cfg_interval: Optional[List[float]] = None scm_logvar_loss: bool = True norm_invariant_to_spatial_dim: bool = True norm_same_as_512_scale: bool = False g_norm_constant: float = 0.1 g_norm_r: float = 1.0 show_gradient: bool = False lr_scale: Optional[Dict[str, List[str]]] = None # for ladd adv_lambda: float = 1.0 scm_loss: bool = True scm_lambda: float = 1.0 loss_scale: float = 1.0 r1_penalty: bool = False r1_penalty_weight: float = 1.0e-5 diff_timesteps_D: bool = True # for adversarial loss suffix_checkpoints: Optional[str] = "disc" misaligned_pairs_D: bool = False discriminator_loss: str = "cross entropy" largest_timestep: float = 1.57080 train_largest_timestep: bool = False largest_timestep_prob: float = 0.5 extra: Any = None @dataclass class TrainVideoConfig(TrainingConfig): validation_images: Optional[List[str]] = None image_prior_type: Optional[str] = None # [flux-siglip2 joint_training_interval: int = 50 timestep_weight: bool = False noise_multiplier: Optional[float] = 0.0 ltx_image_condition_prob: float = 0.0 # for ltx, the image condition is used for the first frame chunk_sampling_strategy: str = "uniform" # uniform, incremental same_timestep_prob: float = ( 0.0 # for incremental sampling, the probability of using the same timestep for all chunks ) # temporal coherence loss for video training temporal_coherence_loss: bool = False temporal_coherence_weight: float = 0.0 @dataclass class ControlNetConfig(BaseConfig): control_signal_type: str = "scribble" validation_scribble_maps: List[str] = field( default_factory=lambda: [ "output/tmp_embed/controlnet/dog_scribble_thickness_3.jpg", "output/tmp_embed/controlnet/girl_scribble_thickness_3.jpg", "output/tmp_embed/controlnet/cyborg_scribble_thickness_3.jpg", "output/tmp_embed/controlnet/Astronaut_scribble_thickness_3.jpg", "output/tmp_embed/controlnet/mountain_scribble_thickness_3.jpg", ] ) @dataclass class ModelGrowthConfig(BaseConfig): """Model growth configuration for initializing larger models from smaller ones""" pretrained_ckpt_path: str = "" init_strategy: str = "constant" # ['cyclic', 'block_expand', 'progressive', 'interpolation', 'random', 'constant'] init_params: Dict[str, Any] = field( default_factory=lambda: { "expand_ratio": 3, "noise_scale": 0.01, } ) source_num_layers: int = 20 target_num_layers: int = 30 extra: Any = None @dataclass class SanaConfig(BaseConfig): data: DataConfig model: ModelConfig vae: AEConfig text_encoder: TextEncoderConfig scheduler: SchedulerConfig train: TrainingConfig controlnet: Optional[ControlNetConfig] = None model_growth: Optional[ModelGrowthConfig] = None work_dir: str = "output/" resume_from: Optional[str] = None load_from: Optional[str] = None debug: bool = False caching: bool = False report_to: str = "wandb" tracker_project_name: str = "sana-video-baseline" name: str = "baseline" loss_report_name: str = "loss" @dataclass class WanTextEncoderConfig(BaseConfig): t5_model: str = "umt5_xxl" t5_dtype: str = "bfloat16" text_len: int = 512 t5_checkpoint: str = "checkpoints/Wan2.1-T2V-1.3B/models_t5_umt5-xxl-enc-bf16.pth" t5_tokenizer: str = "google/umt5-xxl" extra: Any = None caption_channels: int = 4096 @dataclass class DistillConfig(BaseConfig): pass @dataclass class SanaVideoConfig(BaseConfig): data: VideoDataConfig model: ModelVideoConfig vae: AEConfig text_encoder: TextEncoderConfig scheduler: SchedulerConfig train: TrainVideoConfig image_data: Optional[DataConfig] = None image_encoder: Optional[ImageEncoderConfig] = field(default_factory=lambda: {}) model_growth: Optional[ModelGrowthConfig] = None text_encoder_wan: Optional[WanTextEncoderConfig] = None work_dir: str = "output/" resume_from: Optional[str] = None load_from: Optional[str] = None debug: bool = False caching: bool = False report_to: str = "wandb" tracker_project_name: str = "sana-video" name: str = "baseline" loss_report_name: str = "loss" task: str = "t2v" # t2v or ti2v distill: Optional[DistillConfig] = None @dataclass class SanaVideoStage1Config(BaseConfig): data: DataConfig model: ModelVideoConfig vae: AEConfig text_encoder: TextEncoderConfig scheduler: SchedulerConfig train: TrainVideoConfig model_growth: Optional[ModelGrowthConfig] = None work_dir: str = "output/" resume_from: Optional[str] = None load_from: Optional[str] = None debug: bool = False caching: bool = False report_to: str = "wandb" tracker_project_name: str = "sana-video" name: str = "baseline" loss_report_name: str = "loss" task: str = "t2v" # t2v or ti2v or df def model_init_config(config: SanaConfig, latent_size: int = 32): pred_sigma = getattr(config.scheduler, "pred_sigma", True) learn_sigma = getattr(config.scheduler, "learn_sigma", True) and pred_sigma return { "input_size": latent_size, "pe_interpolation": config.model.pe_interpolation, "config": config, "model_max_length": config.text_encoder.model_max_length, "qk_norm": config.model.qk_norm, "micro_condition": config.model.micro_condition, "caption_channels": config.text_encoder.caption_channels, "class_dropout_prob": config.model.class_dropout_prob, "y_norm": config.text_encoder.y_norm, "attn_type": config.model.attn_type, "ffn_type": config.model.ffn_type, "mlp_ratio": config.model.mlp_ratio, "mlp_acts": list(config.model.mlp_acts), "in_channels": config.vae.vae_latent_dim, "y_norm_scale_factor": config.text_encoder.y_norm_scale_factor, "use_pe": config.model.use_pe, "pos_embed_type": config.model.pos_embed_type, "linear_head_dim": config.model.linear_head_dim, "pred_sigma": pred_sigma, "learn_sigma": learn_sigma, "cross_norm": config.model.cross_norm, "cross_attn_type": config.model.cross_attn_type, "timestep_norm_scale_factor": config.scheduler.timestep_norm_scale_factor, "discrete_norm_timestep": config.scheduler.discrete_norm_timestep, } def model_video_init_config(config: SanaVideoConfig, latent_size: int = 32): pred_sigma = getattr(config.scheduler, "pred_sigma", True) learn_sigma = getattr(config.scheduler, "learn_sigma", True) and pred_sigma return { "input_size": latent_size, "pe_interpolation": config.model.pe_interpolation, "config": config, "model_max_length": config.text_encoder.model_max_length, "qk_norm": config.model.qk_norm, "micro_condition": config.model.micro_condition, "caption_channels": config.text_encoder.caption_channels, "class_dropout_prob": config.model.class_dropout_prob, "y_norm": config.text_encoder.y_norm, "attn_type": config.model.attn_type, "ffn_type": config.model.ffn_type, "mlp_ratio": config.model.mlp_ratio, "mlp_acts": list(config.model.mlp_acts), "in_channels": config.vae.vae_latent_dim, "use_pe": config.model.use_pe, "pos_embed_type": config.model.pos_embed_type, "rope_fhw_dim": config.model.rope_fhw_dim, "linear_head_dim": config.model.linear_head_dim, "pred_sigma": pred_sigma, "learn_sigma": learn_sigma, "cross_norm": config.model.cross_norm, "cross_attn_type": config.model.cross_attn_type, "cross_attn_image_embeds": config.model.cross_attn_image_embeds, "t_kernel_size": config.model.t_kernel_size, "flash_attn_window_count": config.model.flash_attn_window_count, "pack_latents": config.model.pack_latents, }