|
48 | 48 | _convert_non_diffusers_flux2_lora_to_diffusers, |
49 | 49 | _convert_non_diffusers_hidream_lora_to_diffusers, |
50 | 50 | _convert_non_diffusers_lora_to_diffusers, |
| 51 | + _convert_non_diffusers_ltx2_lora_to_diffusers, |
51 | 52 | _convert_non_diffusers_ltxv_lora_to_diffusers, |
52 | 53 | _convert_non_diffusers_lumina2_lora_to_diffusers, |
53 | 54 | _convert_non_diffusers_qwen_lora_to_diffusers, |
|
74 | 75 | TEXT_ENCODER_NAME = "text_encoder" |
75 | 76 | UNET_NAME = "unet" |
76 | 77 | TRANSFORMER_NAME = "transformer" |
| 78 | +LTX2_CONNECTOR_NAME = "connectors" |
77 | 79 |
|
78 | 80 | _MODULE_NAME_TO_ATTRIBUTE_MAP_FLUX = {"x_embedder": "in_channels"} |
79 | 81 |
|
@@ -3011,6 +3013,233 @@ def unfuse_lora(self, components: List[str] = ["transformer"], **kwargs): |
3011 | 3013 | super().unfuse_lora(components=components, **kwargs) |
3012 | 3014 |
|
3013 | 3015 |
|
| 3016 | +class LTX2LoraLoaderMixin(LoraBaseMixin): |
| 3017 | + r""" |
| 3018 | + Load LoRA layers into [`LTX2VideoTransformer3DModel`]. Specific to [`LTX2Pipeline`]. |
| 3019 | + """ |
| 3020 | + |
| 3021 | + _lora_loadable_modules = ["transformer", "connectors"] |
| 3022 | + transformer_name = TRANSFORMER_NAME |
| 3023 | + connectors_name = LTX2_CONNECTOR_NAME |
| 3024 | + |
| 3025 | + @classmethod |
| 3026 | + @validate_hf_hub_args |
| 3027 | + def lora_state_dict( |
| 3028 | + cls, |
| 3029 | + pretrained_model_name_or_path_or_dict: Union[str, Dict[str, torch.Tensor]], |
| 3030 | + **kwargs, |
| 3031 | + ): |
| 3032 | + r""" |
| 3033 | + See [`~loaders.StableDiffusionLoraLoaderMixin.lora_state_dict`] for more details. |
| 3034 | + """ |
| 3035 | + # Load the main state dict first which has the LoRA layers for either of |
| 3036 | + # transformer and text encoder or both. |
| 3037 | + cache_dir = kwargs.pop("cache_dir", None) |
| 3038 | + force_download = kwargs.pop("force_download", False) |
| 3039 | + proxies = kwargs.pop("proxies", None) |
| 3040 | + local_files_only = kwargs.pop("local_files_only", None) |
| 3041 | + token = kwargs.pop("token", None) |
| 3042 | + revision = kwargs.pop("revision", None) |
| 3043 | + subfolder = kwargs.pop("subfolder", None) |
| 3044 | + weight_name = kwargs.pop("weight_name", None) |
| 3045 | + use_safetensors = kwargs.pop("use_safetensors", None) |
| 3046 | + return_lora_metadata = kwargs.pop("return_lora_metadata", False) |
| 3047 | + |
| 3048 | + allow_pickle = False |
| 3049 | + if use_safetensors is None: |
| 3050 | + use_safetensors = True |
| 3051 | + allow_pickle = True |
| 3052 | + |
| 3053 | + user_agent = {"file_type": "attn_procs_weights", "framework": "pytorch"} |
| 3054 | + |
| 3055 | + state_dict, metadata = _fetch_state_dict( |
| 3056 | + pretrained_model_name_or_path_or_dict=pretrained_model_name_or_path_or_dict, |
| 3057 | + weight_name=weight_name, |
| 3058 | + use_safetensors=use_safetensors, |
| 3059 | + local_files_only=local_files_only, |
| 3060 | + cache_dir=cache_dir, |
| 3061 | + force_download=force_download, |
| 3062 | + proxies=proxies, |
| 3063 | + token=token, |
| 3064 | + revision=revision, |
| 3065 | + subfolder=subfolder, |
| 3066 | + user_agent=user_agent, |
| 3067 | + allow_pickle=allow_pickle, |
| 3068 | + ) |
| 3069 | + |
| 3070 | + is_dora_scale_present = any("dora_scale" in k for k in state_dict) |
| 3071 | + if is_dora_scale_present: |
| 3072 | + warn_msg = "It seems like you are using a DoRA checkpoint that is not compatible in Diffusers at the moment. So, we are going to filter out the keys associated to 'dora_scale` from the state dict. If you think this is a mistake please open an issue https://github.com/huggingface/diffusers/issues/new." |
| 3073 | + logger.warning(warn_msg) |
| 3074 | + state_dict = {k: v for k, v in state_dict.items() if "dora_scale" not in k} |
| 3075 | + |
| 3076 | + final_state_dict = state_dict |
| 3077 | + is_non_diffusers_format = any(k.startswith("diffusion_model.") for k in state_dict) |
| 3078 | + has_connector = any(k.startswith("text_embedding_projection.") for k in state_dict) |
| 3079 | + if is_non_diffusers_format: |
| 3080 | + final_state_dict = _convert_non_diffusers_ltx2_lora_to_diffusers(state_dict) |
| 3081 | + if has_connector: |
| 3082 | + connectors_state_dict = _convert_non_diffusers_ltx2_lora_to_diffusers( |
| 3083 | + state_dict, "text_embedding_projection" |
| 3084 | + ) |
| 3085 | + final_state_dict.update(connectors_state_dict) |
| 3086 | + out = (final_state_dict, metadata) if return_lora_metadata else final_state_dict |
| 3087 | + return out |
| 3088 | + |
| 3089 | + def load_lora_weights( |
| 3090 | + self, |
| 3091 | + pretrained_model_name_or_path_or_dict: Union[str, Dict[str, torch.Tensor]], |
| 3092 | + adapter_name: Optional[str] = None, |
| 3093 | + hotswap: bool = False, |
| 3094 | + **kwargs, |
| 3095 | + ): |
| 3096 | + """ |
| 3097 | + See [`~loaders.StableDiffusionLoraLoaderMixin.load_lora_weights`] for more details. |
| 3098 | + """ |
| 3099 | + if not USE_PEFT_BACKEND: |
| 3100 | + raise ValueError("PEFT backend is required for this method.") |
| 3101 | + |
| 3102 | + low_cpu_mem_usage = kwargs.pop("low_cpu_mem_usage", _LOW_CPU_MEM_USAGE_DEFAULT_LORA) |
| 3103 | + if low_cpu_mem_usage and is_peft_version("<", "0.13.0"): |
| 3104 | + raise ValueError( |
| 3105 | + "`low_cpu_mem_usage=True` is not compatible with this `peft` version. Please update it with `pip install -U peft`." |
| 3106 | + ) |
| 3107 | + |
| 3108 | + # if a dict is passed, copy it instead of modifying it inplace |
| 3109 | + if isinstance(pretrained_model_name_or_path_or_dict, dict): |
| 3110 | + pretrained_model_name_or_path_or_dict = pretrained_model_name_or_path_or_dict.copy() |
| 3111 | + |
| 3112 | + # First, ensure that the checkpoint is a compatible one and can be successfully loaded. |
| 3113 | + kwargs["return_lora_metadata"] = True |
| 3114 | + state_dict, metadata = self.lora_state_dict(pretrained_model_name_or_path_or_dict, **kwargs) |
| 3115 | + |
| 3116 | + is_correct_format = all("lora" in key for key in state_dict.keys()) |
| 3117 | + if not is_correct_format: |
| 3118 | + raise ValueError("Invalid LoRA checkpoint.") |
| 3119 | + |
| 3120 | + transformer_peft_state_dict = { |
| 3121 | + k: v for k, v in state_dict.items() if k.startswith(f"{self.transformer_name}.") |
| 3122 | + } |
| 3123 | + connectors_peft_state_dict = {k: v for k, v in state_dict.items() if k.startswith(f"{self.connectors_name}.")} |
| 3124 | + self.load_lora_into_transformer( |
| 3125 | + transformer_peft_state_dict, |
| 3126 | + transformer=getattr(self, self.transformer_name) if not hasattr(self, "transformer") else self.transformer, |
| 3127 | + adapter_name=adapter_name, |
| 3128 | + metadata=metadata, |
| 3129 | + _pipeline=self, |
| 3130 | + low_cpu_mem_usage=low_cpu_mem_usage, |
| 3131 | + hotswap=hotswap, |
| 3132 | + ) |
| 3133 | + if connectors_peft_state_dict: |
| 3134 | + self.load_lora_into_transformer( |
| 3135 | + connectors_peft_state_dict, |
| 3136 | + transformer=getattr(self, self.connectors_name) |
| 3137 | + if not hasattr(self, "connectors") |
| 3138 | + else self.connectors, |
| 3139 | + adapter_name=adapter_name, |
| 3140 | + metadata=metadata, |
| 3141 | + _pipeline=self, |
| 3142 | + low_cpu_mem_usage=low_cpu_mem_usage, |
| 3143 | + hotswap=hotswap, |
| 3144 | + prefix=self.connectors_name, |
| 3145 | + ) |
| 3146 | + |
| 3147 | + @classmethod |
| 3148 | + def load_lora_into_transformer( |
| 3149 | + cls, |
| 3150 | + state_dict, |
| 3151 | + transformer, |
| 3152 | + adapter_name=None, |
| 3153 | + _pipeline=None, |
| 3154 | + low_cpu_mem_usage=False, |
| 3155 | + hotswap: bool = False, |
| 3156 | + metadata=None, |
| 3157 | + prefix: str = "transformer", |
| 3158 | + ): |
| 3159 | + """ |
| 3160 | + See [`~loaders.StableDiffusionLoraLoaderMixin.load_lora_into_unet`] for more details. |
| 3161 | + """ |
| 3162 | + if low_cpu_mem_usage and is_peft_version("<", "0.13.0"): |
| 3163 | + raise ValueError( |
| 3164 | + "`low_cpu_mem_usage=True` is not compatible with this `peft` version. Please update it with `pip install -U peft`." |
| 3165 | + ) |
| 3166 | + |
| 3167 | + # Load the layers corresponding to transformer. |
| 3168 | + logger.info(f"Loading {prefix}.") |
| 3169 | + transformer.load_lora_adapter( |
| 3170 | + state_dict, |
| 3171 | + network_alphas=None, |
| 3172 | + adapter_name=adapter_name, |
| 3173 | + metadata=metadata, |
| 3174 | + _pipeline=_pipeline, |
| 3175 | + low_cpu_mem_usage=low_cpu_mem_usage, |
| 3176 | + hotswap=hotswap, |
| 3177 | + prefix=prefix, |
| 3178 | + ) |
| 3179 | + |
| 3180 | + @classmethod |
| 3181 | + # Copied from diffusers.loaders.lora_pipeline.CogVideoXLoraLoaderMixin.save_lora_weights |
| 3182 | + def save_lora_weights( |
| 3183 | + cls, |
| 3184 | + save_directory: Union[str, os.PathLike], |
| 3185 | + transformer_lora_layers: Dict[str, Union[torch.nn.Module, torch.Tensor]] = None, |
| 3186 | + is_main_process: bool = True, |
| 3187 | + weight_name: str = None, |
| 3188 | + save_function: Callable = None, |
| 3189 | + safe_serialization: bool = True, |
| 3190 | + transformer_lora_adapter_metadata: Optional[dict] = None, |
| 3191 | + ): |
| 3192 | + r""" |
| 3193 | + See [`~loaders.StableDiffusionLoraLoaderMixin.save_lora_weights`] for more information. |
| 3194 | + """ |
| 3195 | + lora_layers = {} |
| 3196 | + lora_metadata = {} |
| 3197 | + |
| 3198 | + if transformer_lora_layers: |
| 3199 | + lora_layers[cls.transformer_name] = transformer_lora_layers |
| 3200 | + lora_metadata[cls.transformer_name] = transformer_lora_adapter_metadata |
| 3201 | + |
| 3202 | + if not lora_layers: |
| 3203 | + raise ValueError("You must pass at least one of `transformer_lora_layers` or `text_encoder_lora_layers`.") |
| 3204 | + |
| 3205 | + cls._save_lora_weights( |
| 3206 | + save_directory=save_directory, |
| 3207 | + lora_layers=lora_layers, |
| 3208 | + lora_metadata=lora_metadata, |
| 3209 | + is_main_process=is_main_process, |
| 3210 | + weight_name=weight_name, |
| 3211 | + save_function=save_function, |
| 3212 | + safe_serialization=safe_serialization, |
| 3213 | + ) |
| 3214 | + |
| 3215 | + # Copied from diffusers.loaders.lora_pipeline.CogVideoXLoraLoaderMixin.fuse_lora |
| 3216 | + def fuse_lora( |
| 3217 | + self, |
| 3218 | + components: List[str] = ["transformer"], |
| 3219 | + lora_scale: float = 1.0, |
| 3220 | + safe_fusing: bool = False, |
| 3221 | + adapter_names: Optional[List[str]] = None, |
| 3222 | + **kwargs, |
| 3223 | + ): |
| 3224 | + r""" |
| 3225 | + See [`~loaders.StableDiffusionLoraLoaderMixin.fuse_lora`] for more details. |
| 3226 | + """ |
| 3227 | + super().fuse_lora( |
| 3228 | + components=components, |
| 3229 | + lora_scale=lora_scale, |
| 3230 | + safe_fusing=safe_fusing, |
| 3231 | + adapter_names=adapter_names, |
| 3232 | + **kwargs, |
| 3233 | + ) |
| 3234 | + |
| 3235 | + # Copied from diffusers.loaders.lora_pipeline.CogVideoXLoraLoaderMixin.unfuse_lora |
| 3236 | + def unfuse_lora(self, components: List[str] = ["transformer"], **kwargs): |
| 3237 | + r""" |
| 3238 | + See [`~loaders.StableDiffusionLoraLoaderMixin.unfuse_lora`] for more details. |
| 3239 | + """ |
| 3240 | + super().unfuse_lora(components=components, **kwargs) |
| 3241 | + |
| 3242 | + |
3014 | 3243 | class SanaLoraLoaderMixin(LoraBaseMixin): |
3015 | 3244 | r""" |
3016 | 3245 | Load LoRA layers into [`SanaTransformer2DModel`]. Specific to [`SanaPipeline`]. |
|
0 commit comments