BiliSakura commited on
Commit
bbb2e16
·
verified ·
1 Parent(s): e0defb6

Update pMF-B-32/pipeline.py

Browse files
Files changed (1) hide show
  1. pMF-B-32/pipeline.py +54 -87
pMF-B-32/pipeline.py CHANGED
@@ -1,34 +1,16 @@
1
- # Copyright 2026 The HuggingFace Team. All rights reserved.
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- """Hub custom pipeline: PMFPipeline.
16
-
17
- Load with native Hugging Face diffusers and trust_remote_code=True.
18
- """
19
 
20
  from __future__ import annotations
21
 
 
22
  import json
23
  from pathlib import Path
24
- from typing import Dict, List, Optional, Tuple, Union
25
 
26
  import torch
27
  from diffusers.pipelines.pipeline_utils import DiffusionPipeline, ImagePipelineOutput
28
- from diffusers.schedulers import FlowMatchEulerDiscreteScheduler
29
  from diffusers.utils.torch_utils import randn_tensor
30
 
31
-
32
  DEFAULT_CFG_BY_MODEL: Dict[str, Dict[str, float]] = {
33
  "pMF-B/16": {"guidance_scale": 7.5, "guidance_interval_min": 0.1, "guidance_interval_max": 0.8},
34
  "pMF-B/32": {"guidance_scale": 6.5, "guidance_interval_min": 0.1, "guidance_interval_max": 0.7},
@@ -48,50 +30,37 @@ RECOMMENDED_NOISE_BY_MODEL: Dict[str, float] = {
48
  }
49
 
50
 
51
- def _set_pmf_timesteps(
52
- scheduler: FlowMatchEulerDiscreteScheduler,
53
- num_inference_steps: int,
54
- device: torch.device,
55
- ) -> torch.Tensor:
56
- r"""Set linear flow sigmas from 1.0 to 0.0 for pMF sampling."""
57
- flow_sigmas = torch.linspace(1.0, 0.0, num_inference_steps + 1, device=device, dtype=torch.float32)
58
- scheduler.set_timesteps(sigmas=flow_sigmas.tolist(), device=device)
59
- return flow_sigmas
60
-
61
-
62
  class PMFPipeline(DiffusionPipeline):
63
- r"""
64
- Pipeline for ImageNet class-conditional generation with Pixel Mean Flows (pMF).
65
-
66
- Parameters:
67
- transformer ([`PMFTransformer2DModel`]):
68
- Class-conditioned pMF transformer that predicts mean-flow velocity.
69
- scheduler ([`FlowMatchEulerDiscreteScheduler`]):
70
- Built-in flow-matching Euler scheduler.
71
- id2label (`dict[int, str]`, *optional*):
72
- ImageNet class id to English label mapping.
73
- """
74
-
75
  model_cpu_offload_seq = "transformer"
76
 
77
  def __init__(
78
  self,
79
- transformer,
80
- scheduler,
81
  id2label: Optional[Dict[Union[int, str], str]] = None,
82
- ):
83
  super().__init__()
84
  if scheduler is None:
85
- scheduler = FlowMatchEulerDiscreteScheduler(
86
- num_train_timesteps=1000,
87
- shift=1.0,
88
- stochastic_sampling=False,
89
- )
90
  self.register_modules(transformer=transformer, scheduler=scheduler)
91
  self._id2label = self._normalize_id2label(id2label)
92
  self.labels = self._build_label2id(self._id2label)
93
  self._labels_loaded_from_model_index = bool(self._id2label)
94
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  def _ensure_labels_loaded(self) -> None:
96
  if self._labels_loaded_from_model_index:
97
  return
@@ -152,8 +121,8 @@ class PMFPipeline(DiffusionPipeline):
152
  if isinstance(class_labels, str):
153
  return self.get_label_ids(class_labels)
154
  if class_labels and isinstance(class_labels[0], str):
155
- return self.get_label_ids(class_labels)
156
- return list(class_labels)
157
 
158
  def _recommended_noise_scale(self) -> float:
159
  model_type = getattr(self.transformer.config, "model_type", None)
@@ -168,6 +137,28 @@ class PMFPipeline(DiffusionPipeline):
168
  return dict(DEFAULT_CFG_BY_MODEL[model_type])
169
  return {"guidance_scale": 7.5, "guidance_interval_min": 0.1, "guidance_interval_max": 0.8}
170
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  @torch.inference_mode()
172
  def __call__(
173
  self,
@@ -181,33 +172,6 @@ class PMFPipeline(DiffusionPipeline):
181
  output_type: Optional[str] = "pil",
182
  return_dict: bool = True,
183
  ) -> Union[ImagePipelineOutput, Tuple]:
184
- r"""
185
- Generate class-conditional images with pMF.
186
-
187
- Args:
188
- class_labels (`int`, `str`, or `list`):
189
- ImageNet class id(s) or label name(s).
190
- num_inference_steps (`int`, *optional*, defaults to 1):
191
- Number of flow steps. pMF is typically used with 1 step.
192
- guidance_scale (`float`, *optional*):
193
- Classifier-free guidance scale. Defaults to model-specific preset.
194
- guidance_interval_min (`float`, *optional*):
195
- Lower bound of the CFG interval in normalized time.
196
- guidance_interval_max (`float`, *optional*):
197
- Upper bound of the CFG interval in normalized time.
198
- noise_scale (`float`, *optional*):
199
- Initial Gaussian noise scale. Defaults to model-specific preset.
200
- generator (`torch.Generator`, *optional*):
201
- Random generator for reproducibility.
202
- output_type (`str`, *optional*, defaults to `"pil"`):
203
- Output format: `"pil"`, `"np"`, or `"pt"`.
204
- return_dict (`bool`, *optional*, defaults to `True`):
205
- Whether to return an [`~pipelines.ImagePipelineOutput`].
206
-
207
- Returns:
208
- [`~pipelines.ImagePipelineOutput`] or `tuple`:
209
- Generated images.
210
- """
211
  if num_inference_steps < 1:
212
  raise ValueError("num_inference_steps must be >= 1.")
213
  if output_type not in {"pil", "np", "pt"}:
@@ -247,15 +211,17 @@ class PMFPipeline(DiffusionPipeline):
247
  t_min = torch.full((batch_size,), guidance_interval_min, device=device, dtype=dtype)
248
  t_max = torch.full((batch_size,), guidance_interval_max, device=device, dtype=dtype)
249
 
250
- flow_sigmas = _set_pmf_timesteps(self.scheduler, num_inference_steps, device)
 
 
251
 
252
  for step_index in self.progress_bar(range(num_inference_steps)):
253
- t = flow_sigmas[step_index]
254
- t_next = flow_sigmas[step_index + 1]
255
  h = (t - t_next).expand(batch_size).to(device=device, dtype=dtype)
256
  t_batch = t.expand(batch_size).to(device=device, dtype=dtype)
257
 
258
- output = self.transformer(
259
  sample=latents,
260
  timestep=t_batch,
261
  class_labels=class_labels_t,
@@ -263,9 +229,8 @@ class PMFPipeline(DiffusionPipeline):
263
  omega=omega,
264
  guidance_interval_min=t_min,
265
  guidance_interval_max=t_max,
266
- return_dict=True,
267
  )
268
- latents = self.scheduler.step(output.u, self.scheduler.timesteps[step_index], latents).prev_sample
269
 
270
  images_pt = ((latents.float().clamp(-1, 1) + 1.0) / 2.0).cpu()
271
  if output_type == "pt":
@@ -283,3 +248,5 @@ class PMFPipeline(DiffusionPipeline):
283
 
284
 
285
  PMFPipelineOutput = ImagePipelineOutput
 
 
 
1
+ """Hub custom pipeline: PMFPipeline."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  from __future__ import annotations
4
 
5
+ import inspect
6
  import json
7
  from pathlib import Path
8
+ from typing import Any, Dict, List, Optional, Tuple, Union
9
 
10
  import torch
11
  from diffusers.pipelines.pipeline_utils import DiffusionPipeline, ImagePipelineOutput
 
12
  from diffusers.utils.torch_utils import randn_tensor
13
 
 
14
  DEFAULT_CFG_BY_MODEL: Dict[str, Dict[str, float]] = {
15
  "pMF-B/16": {"guidance_scale": 7.5, "guidance_interval_min": 0.1, "guidance_interval_max": 0.8},
16
  "pMF-B/32": {"guidance_scale": 6.5, "guidance_interval_min": 0.1, "guidance_interval_max": 0.7},
 
30
  }
31
 
32
 
 
 
 
 
 
 
 
 
 
 
 
33
  class PMFPipeline(DiffusionPipeline):
 
 
 
 
 
 
 
 
 
 
 
 
34
  model_cpu_offload_seq = "transformer"
35
 
36
  def __init__(
37
  self,
38
+ transformer: Any,
39
+ scheduler: Any,
40
  id2label: Optional[Dict[Union[int, str], str]] = None,
41
+ ) -> None:
42
  super().__init__()
43
  if scheduler is None:
44
+ raise ValueError("PMFPipeline requires a scheduler loaded from the checkpoint.")
 
 
 
 
45
  self.register_modules(transformer=transformer, scheduler=scheduler)
46
  self._id2label = self._normalize_id2label(id2label)
47
  self.labels = self._build_label2id(self._id2label)
48
  self._labels_loaded_from_model_index = bool(self._id2label)
49
 
50
+ @staticmethod
51
+ def prepare_extra_step_kwargs(
52
+ scheduler: Any,
53
+ generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
54
+ eta: float | None = None,
55
+ ) -> Dict[str, Any]:
56
+ kwargs: Dict[str, Any] = {}
57
+ step_params = set(inspect.signature(scheduler.step).parameters.keys())
58
+ if "generator" in step_params:
59
+ kwargs["generator"] = generator
60
+ if eta is not None and "eta" in step_params:
61
+ kwargs["eta"] = eta
62
+ return kwargs
63
+
64
  def _ensure_labels_loaded(self) -> None:
65
  if self._labels_loaded_from_model_index:
66
  return
 
121
  if isinstance(class_labels, str):
122
  return self.get_label_ids(class_labels)
123
  if class_labels and isinstance(class_labels[0], str):
124
+ return self.get_label_ids(class_labels) # type: ignore[arg-type]
125
+ return [int(class_id) for class_id in class_labels]
126
 
127
  def _recommended_noise_scale(self) -> float:
128
  model_type = getattr(self.transformer.config, "model_type", None)
 
137
  return dict(DEFAULT_CFG_BY_MODEL[model_type])
138
  return {"guidance_scale": 7.5, "guidance_interval_min": 0.1, "guidance_interval_max": 0.8}
139
 
140
+ def predict_u(
141
+ self,
142
+ sample: torch.Tensor,
143
+ timestep: torch.Tensor,
144
+ class_labels: torch.Tensor,
145
+ h: torch.Tensor,
146
+ omega: torch.Tensor,
147
+ guidance_interval_min: torch.Tensor,
148
+ guidance_interval_max: torch.Tensor,
149
+ ) -> torch.Tensor:
150
+ output = self.transformer(
151
+ sample=sample,
152
+ timestep=timestep,
153
+ class_labels=class_labels,
154
+ h=h,
155
+ omega=omega,
156
+ guidance_interval_min=guidance_interval_min,
157
+ guidance_interval_max=guidance_interval_max,
158
+ return_dict=True,
159
+ )
160
+ return output.u
161
+
162
  @torch.inference_mode()
163
  def __call__(
164
  self,
 
172
  output_type: Optional[str] = "pil",
173
  return_dict: bool = True,
174
  ) -> Union[ImagePipelineOutput, Tuple]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  if num_inference_steps < 1:
176
  raise ValueError("num_inference_steps must be >= 1.")
177
  if output_type not in {"pil", "np", "pt"}:
 
211
  t_min = torch.full((batch_size,), guidance_interval_min, device=device, dtype=dtype)
212
  t_max = torch.full((batch_size,), guidance_interval_max, device=device, dtype=dtype)
213
 
214
+ self.scheduler.set_timesteps(num_inference_steps, device=device)
215
+ extra_step_kwargs = self.prepare_extra_step_kwargs(self.scheduler, generator=generator)
216
+ timesteps = self.scheduler.timesteps
217
 
218
  for step_index in self.progress_bar(range(num_inference_steps)):
219
+ t = timesteps[step_index]
220
+ t_next = timesteps[step_index + 1]
221
  h = (t - t_next).expand(batch_size).to(device=device, dtype=dtype)
222
  t_batch = t.expand(batch_size).to(device=device, dtype=dtype)
223
 
224
+ u = self.predict_u(
225
  sample=latents,
226
  timestep=t_batch,
227
  class_labels=class_labels_t,
 
229
  omega=omega,
230
  guidance_interval_min=t_min,
231
  guidance_interval_max=t_max,
 
232
  )
233
+ latents = self.scheduler.step(u, t, latents, **extra_step_kwargs).prev_sample
234
 
235
  images_pt = ((latents.float().clamp(-1, 1) + 1.0) / 2.0).cpu()
236
  if output_type == "pt":
 
248
 
249
 
250
  PMFPipelineOutput = ImagePipelineOutput
251
+
252
+ __all__ = ["PMFPipeline", "PMFPipelineOutput"]