Text-to-Image
Diffusers
Safetensors
English
fd-loss
jit
imf
pmf
image-generation
class-conditional
imagenet
Instructions to use BiliSakura/FD-Loss-diffusers with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use BiliSakura/FD-Loss-diffusers with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("BiliSakura/FD-Loss-diffusers", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- Draw Things
- DiffusionBee
| """Hub custom pipeline: IMFPipeline. | |
| Load with native Hugging Face diffusers and trust_remote_code=True. | |
| """ | |
| from __future__ import annotations | |
| import importlib.util | |
| import inspect | |
| import json | |
| from pathlib import Path | |
| from typing import Dict, List, Optional, Tuple, Union, Any | |
| import torch | |
| from diffusers.image_processor import VaeImageProcessor | |
| from diffusers.pipelines.pipeline_utils import DiffusionPipeline, ImagePipelineOutput | |
| from diffusers.utils.torch_utils import randn_tensor | |
| # imeanflow / FD-Loss sd-vae latent statistics (stabilityai/sd-vae-ft-mse) | |
| LATENT_CHANNEL_MEAN = (0.86488, -0.27787343, 0.21616915, 0.3738409) | |
| LATENT_CHANNEL_STD = (4.85503674, 5.31922414, 3.93725398, 3.9870003) | |
| def _load_bundled_vae(transformer) -> Optional[Any]: | |
| transformer_path = getattr(transformer.config, "_name_or_path", None) | |
| if not transformer_path: | |
| return None | |
| vae_dir = Path(transformer_path).resolve().parent / "vae" | |
| if not vae_dir.is_dir() or not (vae_dir / "config.json").is_file(): | |
| return None | |
| from diffusers import AutoencoderKL | |
| vae_dtype = getattr(transformer, "dtype", torch.float32) | |
| return AutoencoderKL.from_pretrained(str(vae_dir), torch_dtype=vae_dtype) | |
| class IMFPipeline(DiffusionPipeline): | |
| def prepare_extra_step_kwargs( | |
| scheduler, | |
| generator=None, | |
| eta: float | None = None, | |
| ): | |
| kwargs = {} | |
| step_params = set(inspect.signature(scheduler.step).parameters.keys()) | |
| if "generator" in step_params: | |
| kwargs["generator"] = generator | |
| if eta is not None and "eta" in step_params: | |
| kwargs["eta"] = eta | |
| return kwargs | |
| def _prepare_generator( | |
| generator: Optional[Union[torch.Generator, List[torch.Generator]]], | |
| ) -> Optional[Union[torch.Generator, List[torch.Generator]]]: | |
| if generator is None: | |
| return None | |
| if isinstance(generator, list): | |
| for gen in generator: | |
| if gen is not None: | |
| gen.manual_seed(int(gen.initial_seed())) | |
| return generator | |
| generator.manual_seed(int(generator.initial_seed())) | |
| return generator | |
| def _coerce_scheduler(scheduler, transformer) -> Any: | |
| if scheduler is not None and not isinstance(scheduler, (list, tuple)): | |
| return scheduler | |
| variant_path = getattr(transformer.config, "_name_or_path", None) | |
| if variant_path: | |
| scheduler_dir = Path(variant_path).resolve().parent / "scheduler" | |
| module_path = scheduler_dir / "scheduling_imf.py" | |
| config_path = scheduler_dir / "scheduler_config.json" | |
| if module_path.is_file() and config_path.is_file(): | |
| spec = importlib.util.spec_from_file_location("scheduling_imf", module_path) | |
| if spec is not None and spec.loader is not None: | |
| module = importlib.util.module_from_spec(spec) | |
| spec.loader.exec_module(module) | |
| return module.IMFScheduler.from_pretrained(str(scheduler_dir)) | |
| raise ValueError( | |
| "IMFPipeline could not load IMFScheduler. Ensure the variant includes scheduler/scheduling_imf.py." | |
| ) | |
| def _resolve_inference_generator( | |
| device: Union[str, torch.device], | |
| generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None, | |
| ) -> Optional[Union[torch.Generator, List[torch.Generator]]]: | |
| if generator is None: | |
| return None | |
| if isinstance(device, str): | |
| device = torch.device(device) | |
| device_type = device.type | |
| def _relocate(gen: torch.Generator) -> torch.Generator: | |
| if gen.device.type == device_type: | |
| return gen | |
| return torch.Generator(device=device_type).manual_seed(gen.initial_seed()) | |
| if isinstance(generator, list): | |
| return [_relocate(g) for g in generator] | |
| return _relocate(generator) | |
| model_cpu_offload_seq = "transformer->vae" | |
| def __init__( | |
| self, | |
| transformer, | |
| scheduler, | |
| vae=None, | |
| id2label: Optional[Dict[Union[int, str], str]] = None, | |
| ): | |
| super().__init__() | |
| scheduler = self._coerce_scheduler(scheduler, transformer) | |
| if scheduler is None: | |
| raise ValueError("IMFPipeline requires a scheduler loaded from the checkpoint.") | |
| if isinstance(vae, (list, tuple)): | |
| vae = None | |
| if vae is None: | |
| vae = _load_bundled_vae(transformer) | |
| if vae is not None: | |
| vae = vae.to(device=transformer.device, dtype=getattr(transformer, "dtype", torch.float32)) | |
| self.register_modules(transformer=transformer, scheduler=scheduler, vae=vae) | |
| self._id2label = self._normalize_id2label(id2label) | |
| self.labels = self._build_label2id(self._id2label) | |
| self._labels_loaded_from_model_index = bool(self._id2label) | |
| self.latent_channel_mean = torch.tensor(LATENT_CHANNEL_MEAN, dtype=torch.float32).view(1, 4, 1, 1) | |
| self.latent_channel_std = torch.tensor(LATENT_CHANNEL_STD, dtype=torch.float32).view(1, 4, 1, 1) | |
| vae_scale_factor = 8 | |
| if vae is not None: | |
| vae_scale_factor = 2 ** (len(vae.config.block_out_channels) - 1) | |
| self.vae_scale_factor = vae_scale_factor | |
| self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor) | |
| def _ensure_labels_loaded(self) -> None: | |
| if self._labels_loaded_from_model_index: | |
| return | |
| loaded = self._read_id2label_from_model_index(getattr(self.config, "_name_or_path", None)) | |
| if loaded: | |
| self._id2label = loaded | |
| self.labels = self._build_label2id(self._id2label) | |
| self._labels_loaded_from_model_index = True | |
| def _normalize_id2label(id2label: Optional[Dict[Union[int, str], str]]) -> Dict[int, str]: | |
| if not id2label: | |
| return {} | |
| return {int(key): value for key, value in id2label.items()} | |
| def _read_id2label_from_model_index(variant_path: Optional[str]) -> Dict[int, str]: | |
| if not variant_path: | |
| return {} | |
| model_index_path = Path(variant_path).resolve() / "model_index.json" | |
| if not model_index_path.exists(): | |
| return {} | |
| raw = json.loads(model_index_path.read_text(encoding="utf-8")) | |
| id2label = raw.get("id2label") | |
| if not isinstance(id2label, dict): | |
| return {} | |
| return {int(key): value for key, value in id2label.items()} | |
| def _build_label2id(id2label: Dict[int, str]) -> Dict[str, int]: | |
| label2id: Dict[str, int] = {} | |
| for class_id, value in id2label.items(): | |
| for synonym in value.split(","): | |
| synonym = synonym.strip() | |
| if synonym: | |
| label2id[synonym] = int(class_id) | |
| return dict(sorted(label2id.items())) | |
| def id2label(self) -> Dict[int, str]: | |
| self._ensure_labels_loaded() | |
| return self._id2label | |
| def get_label_ids(self, label: Union[str, List[str]]) -> List[int]: | |
| self._ensure_labels_loaded() | |
| if not self.labels: | |
| raise ValueError("No labels loaded. Ensure `id2label` exists in model_index.json.") | |
| if isinstance(label, str): | |
| label = [label] | |
| missing = [item for item in label if item not in self.labels] | |
| if missing: | |
| preview = ", ".join(list(self.labels.keys())[:8]) | |
| raise ValueError(f"Unknown label(s): {missing}. Example valid labels: {preview}, ...") | |
| return [self.labels[item] for item in label] | |
| def _normalize_class_labels(self, class_labels: Union[int, str, List[Union[int, str]]]) -> List[int]: | |
| if isinstance(class_labels, int): | |
| return [class_labels] | |
| if isinstance(class_labels, str): | |
| return self.get_label_ids(class_labels) | |
| if class_labels and isinstance(class_labels[0], str): | |
| return self.get_label_ids(class_labels) | |
| return list(class_labels) | |
| def _denormalize_latents(self, latents: torch.Tensor) -> torch.Tensor: | |
| mean = self.latent_channel_mean.to(device=latents.device, dtype=latents.dtype) | |
| std = self.latent_channel_std.to(device=latents.device, dtype=latents.dtype) | |
| return latents * std + mean | |
| def decode_latents(self, latents: torch.Tensor, output_type: str = "pil"): | |
| if output_type == "latent": | |
| return latents | |
| if self.vae is None: | |
| raise ValueError( | |
| "Cannot decode latents without a VAE. Ensure model_index.json lists vae and the variant includes vae/." | |
| ) | |
| vae_dtype = next(self.vae.parameters()).dtype | |
| if next(self.vae.parameters()).device != latents.device: | |
| self.vae.to(device=latents.device, dtype=vae_dtype) | |
| latents = self._denormalize_latents(latents).to(dtype=vae_dtype) | |
| image = self.vae.decode(latents).sample | |
| return self.image_processor.postprocess(image, output_type=output_type) | |
| def _predict_velocity_u( | |
| self, | |
| latents: torch.Tensor, | |
| timestep: torch.Tensor, | |
| time_gap: torch.Tensor, | |
| class_labels: torch.Tensor, | |
| class_null: torch.Tensor, | |
| guidance_scale: float, | |
| guidance_interval_start: float, | |
| guidance_interval_end: float, | |
| do_classifier_free_guidance: bool, | |
| ) -> torch.Tensor: | |
| if do_classifier_free_guidance: | |
| latents_in = torch.cat([latents, latents], dim=0) | |
| labels = torch.cat([class_labels, class_null], dim=0) | |
| omega = torch.tensor([guidance_scale, 1.0], device=latents.device, dtype=latents.dtype) | |
| t_min = torch.tensor([guidance_interval_start, 0.0], device=latents.device, dtype=latents.dtype) | |
| t_max = torch.tensor([guidance_interval_end, 1.0], device=latents.device, dtype=latents.dtype) | |
| batch = latents.shape[0] | |
| timestep_in = timestep.reshape(1).repeat(2 * batch).to(dtype=latents.dtype) | |
| time_gap_in = time_gap.reshape(1).repeat(2 * batch).to(dtype=latents.dtype) | |
| omega = omega.repeat(batch) | |
| t_min = t_min.repeat(batch) | |
| t_max = t_max.repeat(batch) | |
| else: | |
| latents_in = latents | |
| labels = class_labels | |
| batch = latents.shape[0] | |
| timestep_in = timestep.reshape(1).repeat(batch).to(dtype=latents.dtype) | |
| time_gap_in = time_gap.reshape(1).repeat(batch).to(dtype=latents.dtype) | |
| omega = torch.full((batch,), guidance_scale, device=latents.device, dtype=latents.dtype) | |
| t_min = torch.full((batch,), guidance_interval_start, device=latents.device, dtype=latents.dtype) | |
| t_max = torch.full((batch,), guidance_interval_end, device=latents.device, dtype=latents.dtype) | |
| outputs = self.transformer( | |
| sample=latents_in, | |
| timestep=timestep_in, | |
| class_labels=labels, | |
| time_gap=time_gap_in, | |
| guidance_scale=omega, | |
| guidance_interval_start=t_min, | |
| guidance_interval_end=t_max, | |
| return_dict=True, | |
| ) | |
| velocity_u = outputs.velocity_u | |
| if not do_classifier_free_guidance: | |
| return velocity_u | |
| u_cond, u_uncond = velocity_u.chunk(2, dim=0) | |
| return u_uncond + guidance_scale * (u_cond - u_uncond) | |
| def __call__( | |
| self, | |
| class_labels: Union[int, str, List[Union[int, str]]], | |
| num_inference_steps: int = 1, | |
| guidance_scale: float = 2.7, | |
| guidance_interval_start: float = 0.1, | |
| guidance_interval_end: float = 0.9, | |
| generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None, | |
| latents: Optional[torch.Tensor] = None, | |
| output_type: Optional[str] = "pil", | |
| return_dict: bool = True, | |
| ) -> Union[ImagePipelineOutput, Tuple]: | |
| if output_type not in {"pil", "np", "pt", "latent"}: | |
| raise ValueError("output_type must be one of: 'pil', 'np', 'pt', 'latent'.") | |
| class_label_ids = self._normalize_class_labels(class_labels) | |
| do_classifier_free_guidance = guidance_scale > 1.0 | |
| batch_size = len(class_label_ids) | |
| generator = self._resolve_inference_generator(self._execution_device, generator) | |
| image_size = int(self.transformer.config.sample_size) | |
| channels = int(self.transformer.config.in_channels) | |
| null_class_val = int(getattr(self.transformer.config, "num_classes", 1000)) | |
| generator = self._prepare_generator(generator) | |
| if latents is None: | |
| latents = randn_tensor( | |
| shape=(batch_size, channels, image_size, image_size), | |
| generator=generator, | |
| device=self._execution_device, | |
| dtype=self.transformer.dtype, | |
| ) | |
| class_labels_t = torch.tensor(class_label_ids, device=latents.device, dtype=torch.long).reshape(-1) | |
| class_labels_t = class_labels_t.clamp(0, null_class_val - 1) | |
| class_null = torch.full_like(class_labels_t, null_class_val) | |
| self.scheduler.set_timesteps(num_inference_steps, device=latents.device) | |
| timesteps = self.scheduler.timesteps | |
| extra_step_kwargs = self.prepare_extra_step_kwargs(self.scheduler, generator=generator) | |
| for i in self.progress_bar(range(num_inference_steps)): | |
| t = timesteps[i] | |
| t_next = timesteps[i + 1] | |
| time_gap = t - t_next | |
| velocity_u = self._predict_velocity_u( | |
| latents, | |
| t, | |
| time_gap, | |
| class_labels_t, | |
| class_null, | |
| guidance_scale, | |
| guidance_interval_start, | |
| guidance_interval_end, | |
| do_classifier_free_guidance, | |
| ) | |
| latents = self.scheduler.step(velocity_u, t, latents, **extra_step_kwargs).prev_sample | |
| images = self.decode_latents(latents, output_type=output_type) | |
| self.maybe_free_model_hooks() | |
| if not return_dict: | |
| return (images,) | |
| return ImagePipelineOutput(images=images) | |
| IMFPipelineOutput = ImagePipelineOutput | |