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
Update JiT-B-16-SIM/pipeline.py
Browse files- JiT-B-16-SIM/pipeline.py +280 -0
JiT-B-16-SIM/pipeline.py
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Hub custom pipeline: JiTPipeline for FD-Loss post-trained JiT checkpoints.
|
| 2 |
+
|
| 3 |
+
Uses FD-Loss sampling (legacy time convention, velocity Euler/Heun, t: 1→0).
|
| 4 |
+
See libs/FD-Loss-diffusers and scripts/evaluate_released_ckpt.sh (JiT_B preset).
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
from __future__ import annotations
|
| 8 |
+
|
| 9 |
+
import json
|
| 10 |
+
from pathlib import Path
|
| 11 |
+
from typing import Dict, List, Optional, Tuple, Union
|
| 12 |
+
|
| 13 |
+
import torch
|
| 14 |
+
from diffusers.pipelines.pipeline_utils import DiffusionPipeline, ImagePipelineOutput
|
| 15 |
+
from diffusers.utils.torch_utils import randn_tensor
|
| 16 |
+
|
| 17 |
+
from scheduling_flow_match_fd import FDLossFlowMatchScheduler
|
| 18 |
+
|
| 19 |
+
RECOMMENDED_NOISE_BY_SIZE = {
|
| 20 |
+
256: 1.0,
|
| 21 |
+
512: 2.0,
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
RECOMMENDED_CFG_BY_VARIANT = {
|
| 25 |
+
"JiT-B": 3.0,
|
| 26 |
+
"JiT-L": 2.4,
|
| 27 |
+
"JiT-H": 2.2,
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class JiTPipeline(DiffusionPipeline):
|
| 32 |
+
r"""
|
| 33 |
+
Pipeline for FD-Loss post-trained JiT (flow matching, legacy time convention).
|
| 34 |
+
|
| 35 |
+
Parameters:
|
| 36 |
+
transformer ([`JiTTransformer2DModel`]):
|
| 37 |
+
Class-conditioned JiT backbone.
|
| 38 |
+
scheduler ([`FDLossFlowMatchScheduler`]):
|
| 39 |
+
Flow timesteps from 1 (noise) to 0 (data).
|
| 40 |
+
legacy_time_convention (`bool`, *optional*, defaults to `True`):
|
| 41 |
+
Flip flow time when passing to the backbone (`t_bb = 1 - t`), as in FD-Loss training.
|
| 42 |
+
id2label (`dict[int, str]`, *optional*):
|
| 43 |
+
ImageNet class id to English label mapping.
|
| 44 |
+
"""
|
| 45 |
+
|
| 46 |
+
model_cpu_offload_seq = "transformer"
|
| 47 |
+
|
| 48 |
+
def __init__(
|
| 49 |
+
self,
|
| 50 |
+
transformer,
|
| 51 |
+
scheduler=None,
|
| 52 |
+
id2label: Optional[Dict[Union[int, str], str]] = None,
|
| 53 |
+
legacy_time_convention: bool = True,
|
| 54 |
+
):
|
| 55 |
+
super().__init__()
|
| 56 |
+
if scheduler is None:
|
| 57 |
+
scheduler = FDLossFlowMatchScheduler()
|
| 58 |
+
self.register_modules(transformer=transformer, scheduler=scheduler)
|
| 59 |
+
self.legacy_time_convention = legacy_time_convention
|
| 60 |
+
self._id2label = self._normalize_id2label(id2label)
|
| 61 |
+
self.labels = self._build_label2id(self._id2label)
|
| 62 |
+
self._labels_loaded_from_model_index = bool(self._id2label)
|
| 63 |
+
|
| 64 |
+
def _backbone_t(self, t: torch.Tensor) -> torch.Tensor:
|
| 65 |
+
if self.legacy_time_convention:
|
| 66 |
+
return 1.0 - t
|
| 67 |
+
return t
|
| 68 |
+
|
| 69 |
+
def _normalize_class_labels(
|
| 70 |
+
self,
|
| 71 |
+
class_labels: Union[int, str, List[Union[int, str]]],
|
| 72 |
+
) -> List[int]:
|
| 73 |
+
if isinstance(class_labels, int):
|
| 74 |
+
return [class_labels]
|
| 75 |
+
if isinstance(class_labels, str):
|
| 76 |
+
return self.get_label_ids(class_labels)
|
| 77 |
+
if class_labels and isinstance(class_labels[0], str):
|
| 78 |
+
return self.get_label_ids(class_labels)
|
| 79 |
+
return list(class_labels)
|
| 80 |
+
|
| 81 |
+
def _forward_velocity(
|
| 82 |
+
self,
|
| 83 |
+
z: torch.Tensor,
|
| 84 |
+
t: torch.Tensor,
|
| 85 |
+
labels: torch.Tensor,
|
| 86 |
+
guidance_scale: float,
|
| 87 |
+
cfg_interval: Optional[Tuple[float, float]],
|
| 88 |
+
t_eps: float,
|
| 89 |
+
) -> torch.Tensor:
|
| 90 |
+
t_view = t.reshape(-1, *([1] * (z.ndim - 1)))
|
| 91 |
+
t_bb = self._backbone_t(t).flatten().expand(z.shape[0])
|
| 92 |
+
|
| 93 |
+
x_cond = self.transformer(
|
| 94 |
+
z,
|
| 95 |
+
timestep=t_bb,
|
| 96 |
+
class_labels=labels,
|
| 97 |
+
interpolate_pos_encoding=interpolate_pos_encoding,
|
| 98 |
+
).sample
|
| 99 |
+
v_cond = (z - x_cond) / t_view.clamp_min(t_eps)
|
| 100 |
+
|
| 101 |
+
if guidance_scale == 1.0:
|
| 102 |
+
return v_cond
|
| 103 |
+
|
| 104 |
+
null_class = int(
|
| 105 |
+
getattr(self.transformer.config, "num_classes", getattr(self.transformer.config, "num_class_embeds", 1000))
|
| 106 |
+
)
|
| 107 |
+
class_null = torch.full_like(labels, null_class)
|
| 108 |
+
x_uncond = self.transformer(
|
| 109 |
+
z,
|
| 110 |
+
timestep=t_bb,
|
| 111 |
+
class_labels=class_null,
|
| 112 |
+
interpolate_pos_encoding=interpolate_pos_encoding,
|
| 113 |
+
).sample
|
| 114 |
+
v_uncond = (z - x_uncond) / t_view.clamp_min(t_eps)
|
| 115 |
+
|
| 116 |
+
if cfg_interval is None:
|
| 117 |
+
return v_uncond + guidance_scale * (v_cond - v_uncond)
|
| 118 |
+
|
| 119 |
+
low, high = cfg_interval
|
| 120 |
+
mask = (t < high) & ((low == 0) | (t > low))
|
| 121 |
+
scale = torch.where(
|
| 122 |
+
mask,
|
| 123 |
+
torch.tensor(guidance_scale, device=z.device, dtype=z.dtype),
|
| 124 |
+
torch.tensor(1.0, device=z.device, dtype=z.dtype),
|
| 125 |
+
)
|
| 126 |
+
while scale.ndim < v_cond.ndim:
|
| 127 |
+
scale = scale.unsqueeze(-1)
|
| 128 |
+
return v_uncond + scale * (v_cond - v_uncond)
|
| 129 |
+
|
| 130 |
+
@staticmethod
|
| 131 |
+
def _normalize_id2label(id2label: Optional[Dict[Union[int, str], str]]) -> Dict[int, str]:
|
| 132 |
+
if not id2label:
|
| 133 |
+
return {}
|
| 134 |
+
return {int(key): value for key, value in id2label.items()}
|
| 135 |
+
|
| 136 |
+
@staticmethod
|
| 137 |
+
def _read_id2label_from_model_index(variant_path: Optional[str]) -> Dict[int, str]:
|
| 138 |
+
if not variant_path:
|
| 139 |
+
return {}
|
| 140 |
+
variant_dir = Path(variant_path).resolve()
|
| 141 |
+
model_index_path = variant_dir / "model_index.json"
|
| 142 |
+
if not model_index_path.exists():
|
| 143 |
+
return {}
|
| 144 |
+
raw = json.loads(model_index_path.read_text(encoding="utf-8"))
|
| 145 |
+
id2label = raw.get("id2label")
|
| 146 |
+
if not isinstance(id2label, dict):
|
| 147 |
+
return {}
|
| 148 |
+
return {int(key): value for key, value in id2label.items()}
|
| 149 |
+
|
| 150 |
+
@staticmethod
|
| 151 |
+
def _build_label2id(id2label: Dict[int, str]) -> Dict[str, int]:
|
| 152 |
+
label2id: Dict[str, int] = {}
|
| 153 |
+
for class_id, value in id2label.items():
|
| 154 |
+
for synonym in value.split(","):
|
| 155 |
+
synonym = synonym.strip()
|
| 156 |
+
if synonym:
|
| 157 |
+
label2id[synonym] = int(class_id)
|
| 158 |
+
return dict(sorted(label2id.items()))
|
| 159 |
+
|
| 160 |
+
def _ensure_labels_loaded(self) -> None:
|
| 161 |
+
if self._labels_loaded_from_model_index:
|
| 162 |
+
return
|
| 163 |
+
loaded = self._read_id2label_from_model_index(getattr(self.config, "_name_or_path", None))
|
| 164 |
+
if loaded:
|
| 165 |
+
self._id2label = loaded
|
| 166 |
+
self.labels = self._build_label2id(self._id2label)
|
| 167 |
+
self._labels_loaded_from_model_index = True
|
| 168 |
+
|
| 169 |
+
@property
|
| 170 |
+
def id2label(self) -> Dict[int, str]:
|
| 171 |
+
self._ensure_labels_loaded()
|
| 172 |
+
return self._id2label
|
| 173 |
+
|
| 174 |
+
def get_label_ids(self, label: Union[str, List[str]]) -> List[int]:
|
| 175 |
+
self._ensure_labels_loaded()
|
| 176 |
+
label2id = self.labels
|
| 177 |
+
if not label2id:
|
| 178 |
+
raise ValueError("No English labels loaded. Ensure `id2label` exists in model_index.json.")
|
| 179 |
+
if isinstance(label, str):
|
| 180 |
+
label = [label]
|
| 181 |
+
missing = [item for item in label if item not in label2id]
|
| 182 |
+
if missing:
|
| 183 |
+
preview = ", ".join(list(label2id.keys())[:8])
|
| 184 |
+
raise ValueError(f"Unknown English label(s): {missing}. Example valid labels: {preview}, ...")
|
| 185 |
+
return [label2id[item] for item in label]
|
| 186 |
+
|
| 187 |
+
@torch.inference_mode()
|
| 188 |
+
def __call__(
|
| 189 |
+
self,
|
| 190 |
+
class_labels: Union[int, str, List[Union[int, str]]],
|
| 191 |
+
num_inference_steps: int = 1,
|
| 192 |
+
guidance_scale: float = 3.0,
|
| 193 |
+
guidance_interval_min: float = 0.1,
|
| 194 |
+
guidance_interval_max: float = 1.0,
|
| 195 |
+
sampling_method: str = "euler",
|
| 196 |
+
noise_scale: Optional[float] = None,
|
| 197 |
+
t_eps: float = 5e-2,
|
| 198 |
+
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
|
| 199 |
+
height: Optional[int] = None,
|
| 200 |
+
width: Optional[int] = None,
|
| 201 |
+
interpolate_pos_encoding: bool = True,
|
| 202 |
+
output_type: Optional[str] = "pil",
|
| 203 |
+
return_dict: bool = True,
|
| 204 |
+
) -> Union[ImagePipelineOutput, Tuple]:
|
| 205 |
+
if num_inference_steps < 1:
|
| 206 |
+
raise ValueError("num_inference_steps must be >= 1.")
|
| 207 |
+
if output_type not in {"pil", "np", "pt"}:
|
| 208 |
+
raise ValueError("output_type must be one of: 'pil', 'np', 'pt'.")
|
| 209 |
+
if sampling_method not in {"euler", "heun"}:
|
| 210 |
+
raise ValueError("sampling_method must be 'euler' or 'heun'.")
|
| 211 |
+
|
| 212 |
+
class_label_ids = self._normalize_class_labels(class_labels)
|
| 213 |
+
batch_size = len(class_label_ids)
|
| 214 |
+
image_size = int(self.transformer.config.sample_size)
|
| 215 |
+
patch_size = int(self.transformer.config.patch_size)
|
| 216 |
+
height = int(height or image_size)
|
| 217 |
+
width = int(width or image_size)
|
| 218 |
+
if height % patch_size != 0 or width % patch_size != 0:
|
| 219 |
+
raise ValueError(
|
| 220 |
+
f"height and width must be divisible by patch_size={patch_size}. Got {(height, width)}."
|
| 221 |
+
)
|
| 222 |
+
channels = int(self.transformer.config.in_channels)
|
| 223 |
+
|
| 224 |
+
if noise_scale is None:
|
| 225 |
+
noise_scale = RECOMMENDED_NOISE_BY_SIZE.get(max(height, width), 1.0)
|
| 226 |
+
|
| 227 |
+
z = randn_tensor(
|
| 228 |
+
shape=(batch_size, channels, height, width),
|
| 229 |
+
generator=generator,
|
| 230 |
+
device=self._execution_device,
|
| 231 |
+
dtype=self.transformer.dtype,
|
| 232 |
+
) * noise_scale
|
| 233 |
+
|
| 234 |
+
labels = torch.tensor(class_label_ids, device=self._execution_device, dtype=torch.long).reshape(-1)
|
| 235 |
+
null_class_val = int(
|
| 236 |
+
getattr(self.transformer.config, "num_classes", getattr(self.transformer.config, "num_class_embeds", 1000))
|
| 237 |
+
)
|
| 238 |
+
labels = labels.clamp(0, null_class_val - 1)
|
| 239 |
+
|
| 240 |
+
cfg_interval = [
|
| 241 |
+
float(self._backbone_t(torch.tensor(guidance_interval_min, device=z.device))),
|
| 242 |
+
float(self._backbone_t(torch.tensor(guidance_interval_max, device=z.device))),
|
| 243 |
+
]
|
| 244 |
+
cfg_interval = (min(cfg_interval), max(cfg_interval))
|
| 245 |
+
|
| 246 |
+
timesteps = self.scheduler.set_timesteps(num_inference_steps, device=self._execution_device)
|
| 247 |
+
ts = timesteps.view(-1, *([1] * z.ndim)).expand(-1, batch_size, -1, -1, -1)
|
| 248 |
+
|
| 249 |
+
for i in self.progress_bar(range(num_inference_steps - 1)):
|
| 250 |
+
t_cur = ts[i]
|
| 251 |
+
t_next = ts[i + 1]
|
| 252 |
+
if sampling_method == "heun":
|
| 253 |
+
dt = t_next - t_cur
|
| 254 |
+
v1 = self._forward_velocity(z, t_cur, labels, guidance_scale, cfg_interval, t_eps)
|
| 255 |
+
z_mid = z + dt * v1
|
| 256 |
+
v2 = self._forward_velocity(z_mid, t_next, labels, guidance_scale, cfg_interval, t_eps)
|
| 257 |
+
z = z + dt * 0.5 * (v1 + v2)
|
| 258 |
+
else:
|
| 259 |
+
v = self._forward_velocity(z, t_cur, labels, guidance_scale, cfg_interval, t_eps)
|
| 260 |
+
z = z + (t_next - t_cur) * v
|
| 261 |
+
|
| 262 |
+
if num_inference_steps >= 1:
|
| 263 |
+
t_cur = ts[-2]
|
| 264 |
+
t_next = ts[-1]
|
| 265 |
+
v = self._forward_velocity(z, t_cur, labels, guidance_scale, cfg_interval, t_eps)
|
| 266 |
+
z = z + (t_next - t_cur) * v
|
| 267 |
+
|
| 268 |
+
images_pt = ((z.float().clamp(-1, 1) + 1.0) / 2.0).cpu()
|
| 269 |
+
if output_type == "pt":
|
| 270 |
+
images = images_pt
|
| 271 |
+
elif output_type == "np":
|
| 272 |
+
images = images_pt.permute(0, 2, 3, 1).numpy()
|
| 273 |
+
else:
|
| 274 |
+
images = self.numpy_to_pil(images_pt.permute(0, 2, 3, 1).numpy())
|
| 275 |
+
|
| 276 |
+
self.maybe_free_model_hooks()
|
| 277 |
+
|
| 278 |
+
if not return_dict:
|
| 279 |
+
return (images,)
|
| 280 |
+
return ImagePipelineOutput(images=images)
|