comdoleger commited on
Commit
599c120
·
verified ·
1 Parent(s): da53977

Upload extensions_built_in/diffusion_models/hidream/src/schedulers/flash_flow_match.py with huggingface_hub

Browse files
extensions_built_in/diffusion_models/hidream/src/schedulers/flash_flow_match.py ADDED
@@ -0,0 +1,428 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 Stability AI, Katherine Crowson and 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
+ import math
16
+ from dataclasses import dataclass
17
+ from typing import List, Optional, Tuple, Union
18
+
19
+ import numpy as np
20
+ import torch
21
+ from diffusers.configuration_utils import ConfigMixin, register_to_config
22
+ from diffusers.schedulers.scheduling_utils import SchedulerMixin
23
+ from diffusers.utils import BaseOutput, is_scipy_available, logging
24
+ from diffusers.utils.torch_utils import randn_tensor
25
+
26
+ if is_scipy_available():
27
+ import scipy.stats
28
+
29
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
30
+
31
+
32
+ @dataclass
33
+ class FlashFlowMatchEulerDiscreteSchedulerOutput(BaseOutput):
34
+ """
35
+ Output class for the scheduler's `step` function output.
36
+
37
+ Args:
38
+ prev_sample (`torch.FloatTensor` of shape `(batch_size, num_channels, height, width)` for images):
39
+ Computed sample `(x_{t-1})` of previous timestep. `prev_sample` should be used as next model input in the
40
+ denoising loop.
41
+ """
42
+
43
+ prev_sample: torch.FloatTensor
44
+
45
+
46
+ class FlashFlowMatchEulerDiscreteScheduler(SchedulerMixin, ConfigMixin):
47
+ """
48
+ Euler scheduler.
49
+
50
+ This model inherits from [`SchedulerMixin`] and [`ConfigMixin`]. Check the superclass documentation for the generic
51
+ methods the library implements for all schedulers such as loading and saving.
52
+
53
+ Args:
54
+ num_train_timesteps (`int`, defaults to 1000):
55
+ The number of diffusion steps to train the model.
56
+ timestep_spacing (`str`, defaults to `"linspace"`):
57
+ The way the timesteps should be scaled. Refer to Table 2 of the [Common Diffusion Noise Schedules and
58
+ Sample Steps are Flawed](https://huggingface.co/papers/2305.08891) for more information.
59
+ shift (`float`, defaults to 1.0):
60
+ The shift value for the timestep schedule.
61
+ """
62
+
63
+ _compatibles = []
64
+ order = 1
65
+
66
+ @register_to_config
67
+ def __init__(
68
+ self,
69
+ num_train_timesteps: int = 1000,
70
+ shift: float = 1.0,
71
+ use_dynamic_shifting=False,
72
+ base_shift: Optional[float] = 0.5,
73
+ max_shift: Optional[float] = 1.15,
74
+ base_image_seq_len: Optional[int] = 256,
75
+ max_image_seq_len: Optional[int] = 4096,
76
+ invert_sigmas: bool = False,
77
+ use_karras_sigmas: Optional[bool] = False,
78
+ use_exponential_sigmas: Optional[bool] = False,
79
+ use_beta_sigmas: Optional[bool] = False,
80
+ ):
81
+ if self.config.use_beta_sigmas and not is_scipy_available():
82
+ raise ImportError("Make sure to install scipy if you want to use beta sigmas.")
83
+ if sum([self.config.use_beta_sigmas, self.config.use_exponential_sigmas, self.config.use_karras_sigmas]) > 1:
84
+ raise ValueError(
85
+ "Only one of `config.use_beta_sigmas`, `config.use_exponential_sigmas`, `config.use_karras_sigmas` can be used."
86
+ )
87
+ timesteps = np.linspace(1, num_train_timesteps, num_train_timesteps, dtype=np.float32)[::-1].copy()
88
+ timesteps = torch.from_numpy(timesteps).to(dtype=torch.float32)
89
+
90
+ sigmas = timesteps / num_train_timesteps
91
+ if not use_dynamic_shifting:
92
+ # when use_dynamic_shifting is True, we apply the timestep shifting on the fly based on the image resolution
93
+ sigmas = shift * sigmas / (1 + (shift - 1) * sigmas)
94
+
95
+ self.timesteps = sigmas * num_train_timesteps
96
+
97
+ self._step_index = None
98
+ self._begin_index = None
99
+
100
+ self.sigmas = sigmas.to("cpu") # to avoid too much CPU/GPU communication
101
+ self.sigma_min = self.sigmas[-1].item()
102
+ self.sigma_max = self.sigmas[0].item()
103
+
104
+ @property
105
+ def step_index(self):
106
+ """
107
+ The index counter for current timestep. It will increase 1 after each scheduler step.
108
+ """
109
+ return self._step_index
110
+
111
+ @property
112
+ def begin_index(self):
113
+ """
114
+ The index for the first timestep. It should be set from pipeline with `set_begin_index` method.
115
+ """
116
+ return self._begin_index
117
+
118
+ # Copied from diffusers.schedulers.scheduling_dpmsolver_multistep.DPMSolverMultistepScheduler.set_begin_index
119
+ def set_begin_index(self, begin_index: int = 0):
120
+ """
121
+ Sets the begin index for the scheduler. This function should be run from pipeline before the inference.
122
+
123
+ Args:
124
+ begin_index (`int`):
125
+ The begin index for the scheduler.
126
+ """
127
+ self._begin_index = begin_index
128
+
129
+ def scale_noise(
130
+ self,
131
+ sample: torch.FloatTensor,
132
+ timestep: Union[float, torch.FloatTensor],
133
+ noise: Optional[torch.FloatTensor] = None,
134
+ ) -> torch.FloatTensor:
135
+ """
136
+ Forward process in flow-matching
137
+
138
+ Args:
139
+ sample (`torch.FloatTensor`):
140
+ The input sample.
141
+ timestep (`int`, *optional*):
142
+ The current timestep in the diffusion chain.
143
+
144
+ Returns:
145
+ `torch.FloatTensor`:
146
+ A scaled input sample.
147
+ """
148
+ # Make sure sigmas and timesteps have the same device and dtype as original_samples
149
+ sigmas = self.sigmas.to(device=sample.device, dtype=sample.dtype)
150
+
151
+ if sample.device.type == "mps" and torch.is_floating_point(timestep):
152
+ # mps does not support float64
153
+ schedule_timesteps = self.timesteps.to(sample.device, dtype=torch.float32)
154
+ timestep = timestep.to(sample.device, dtype=torch.float32)
155
+ else:
156
+ schedule_timesteps = self.timesteps.to(sample.device)
157
+ timestep = timestep.to(sample.device)
158
+
159
+ # self.begin_index is None when scheduler is used for training, or pipeline does not implement set_begin_index
160
+ if self.begin_index is None:
161
+ step_indices = [self.index_for_timestep(t, schedule_timesteps) for t in timestep]
162
+ elif self.step_index is not None:
163
+ # add_noise is called after first denoising step (for inpainting)
164
+ step_indices = [self.step_index] * timestep.shape[0]
165
+ else:
166
+ # add noise is called before first denoising step to create initial latent(img2img)
167
+ step_indices = [self.begin_index] * timestep.shape[0]
168
+
169
+ sigma = sigmas[step_indices].flatten()
170
+ while len(sigma.shape) < len(sample.shape):
171
+ sigma = sigma.unsqueeze(-1)
172
+
173
+ sample = sigma * noise + (1.0 - sigma) * sample
174
+
175
+ return sample
176
+
177
+ def _sigma_to_t(self, sigma):
178
+ return sigma * self.config.num_train_timesteps
179
+
180
+ def time_shift(self, mu: float, sigma: float, t: torch.Tensor):
181
+ return math.exp(mu) / (math.exp(mu) + (1 / t - 1) ** sigma)
182
+
183
+ def set_timesteps(
184
+ self,
185
+ num_inference_steps: int = None,
186
+ device: Union[str, torch.device] = None,
187
+ sigmas: Optional[List[float]] = None,
188
+ mu: Optional[float] = None,
189
+ ):
190
+ """
191
+ Sets the discrete timesteps used for the diffusion chain (to be run before inference).
192
+
193
+ Args:
194
+ num_inference_steps (`int`):
195
+ The number of diffusion steps used when generating samples with a pre-trained model.
196
+ device (`str` or `torch.device`, *optional*):
197
+ The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
198
+ """
199
+ if self.config.use_dynamic_shifting and mu is None:
200
+ raise ValueError(" you have a pass a value for `mu` when `use_dynamic_shifting` is set to be `True`")
201
+
202
+ if sigmas is None:
203
+ timesteps = np.linspace(
204
+ self._sigma_to_t(self.sigma_max), self._sigma_to_t(self.sigma_min), num_inference_steps
205
+ )
206
+
207
+ sigmas = timesteps / self.config.num_train_timesteps
208
+ else:
209
+ sigmas = np.array(sigmas).astype(np.float32)
210
+ num_inference_steps = len(sigmas)
211
+ self.num_inference_steps = num_inference_steps
212
+
213
+ if self.config.use_dynamic_shifting:
214
+ sigmas = self.time_shift(mu, 1.0, sigmas)
215
+ else:
216
+ sigmas = self.config.shift * sigmas / (1 + (self.config.shift - 1) * sigmas)
217
+
218
+ if self.config.use_karras_sigmas:
219
+ sigmas = self._convert_to_karras(in_sigmas=sigmas, num_inference_steps=num_inference_steps)
220
+
221
+ elif self.config.use_exponential_sigmas:
222
+ sigmas = self._convert_to_exponential(in_sigmas=sigmas, num_inference_steps=num_inference_steps)
223
+
224
+ elif self.config.use_beta_sigmas:
225
+ sigmas = self._convert_to_beta(in_sigmas=sigmas, num_inference_steps=num_inference_steps)
226
+
227
+ sigmas = torch.from_numpy(sigmas).to(dtype=torch.float32, device=device)
228
+ timesteps = sigmas * self.config.num_train_timesteps
229
+
230
+ if self.config.invert_sigmas:
231
+ sigmas = 1.0 - sigmas
232
+ timesteps = sigmas * self.config.num_train_timesteps
233
+ sigmas = torch.cat([sigmas, torch.ones(1, device=sigmas.device)])
234
+ else:
235
+ sigmas = torch.cat([sigmas, torch.zeros(1, device=sigmas.device)])
236
+
237
+ self.timesteps = timesteps.to(device=device)
238
+ self.sigmas = sigmas
239
+ self._step_index = None
240
+ self._begin_index = None
241
+
242
+ def index_for_timestep(self, timestep, schedule_timesteps=None):
243
+ if schedule_timesteps is None:
244
+ schedule_timesteps = self.timesteps
245
+
246
+ indices = (schedule_timesteps == timestep).nonzero()
247
+
248
+ # The sigma index that is taken for the **very** first `step`
249
+ # is always the second index (or the last index if there is only 1)
250
+ # This way we can ensure we don't accidentally skip a sigma in
251
+ # case we start in the middle of the denoising schedule (e.g. for image-to-image)
252
+ pos = 1 if len(indices) > 1 else 0
253
+
254
+ return indices[pos].item()
255
+
256
+ def _init_step_index(self, timestep):
257
+ if self.begin_index is None:
258
+ if isinstance(timestep, torch.Tensor):
259
+ timestep = timestep.to(self.timesteps.device)
260
+ self._step_index = self.index_for_timestep(timestep)
261
+ else:
262
+ self._step_index = self._begin_index
263
+
264
+ def step(
265
+ self,
266
+ model_output: torch.FloatTensor,
267
+ timestep: Union[float, torch.FloatTensor],
268
+ sample: torch.FloatTensor,
269
+ s_churn: float = 0.0,
270
+ s_tmin: float = 0.0,
271
+ s_tmax: float = float("inf"),
272
+ s_noise: float = 1.0,
273
+ generator: Optional[torch.Generator] = None,
274
+ return_dict: bool = True,
275
+ ) -> Union[FlashFlowMatchEulerDiscreteSchedulerOutput, Tuple]:
276
+ """
277
+ Predict the sample from the previous timestep by reversing the SDE. This function propagates the diffusion
278
+ process from the learned model outputs (most often the predicted noise).
279
+
280
+ Args:
281
+ model_output (`torch.FloatTensor`):
282
+ The direct output from learned diffusion model.
283
+ timestep (`float`):
284
+ The current discrete timestep in the diffusion chain.
285
+ sample (`torch.FloatTensor`):
286
+ A current instance of a sample created by the diffusion process.
287
+ s_churn (`float`):
288
+ s_tmin (`float`):
289
+ s_tmax (`float`):
290
+ s_noise (`float`, defaults to 1.0):
291
+ Scaling factor for noise added to the sample.
292
+ generator (`torch.Generator`, *optional*):
293
+ A random number generator.
294
+ return_dict (`bool`):
295
+ Whether or not to return a [`~schedulers.scheduling_euler_discrete.EulerDiscreteSchedulerOutput`] or
296
+ tuple.
297
+
298
+ Returns:
299
+ [`~schedulers.scheduling_euler_discrete.EulerDiscreteSchedulerOutput`] or `tuple`:
300
+ If return_dict is `True`, [`~schedulers.scheduling_euler_discrete.EulerDiscreteSchedulerOutput`] is
301
+ returned, otherwise a tuple is returned where the first element is the sample tensor.
302
+ """
303
+
304
+ if (
305
+ isinstance(timestep, int)
306
+ or isinstance(timestep, torch.IntTensor)
307
+ or isinstance(timestep, torch.LongTensor)
308
+ ):
309
+ raise ValueError(
310
+ (
311
+ "Passing integer indices (e.g. from `enumerate(timesteps)`) as timesteps to"
312
+ " `EulerDiscreteScheduler.step()` is not supported. Make sure to pass"
313
+ " one of the `scheduler.timesteps` as a timestep."
314
+ ),
315
+ )
316
+
317
+ if self.step_index is None:
318
+ self._init_step_index(timestep)
319
+
320
+ # Upcast to avoid precision issues when computing prev_sample
321
+
322
+ sigma = self.sigmas[self.step_index]
323
+
324
+ # Upcast to avoid precision issues when computing prev_sample
325
+ sample = sample.to(torch.float32)
326
+
327
+ denoised = sample - model_output * sigma
328
+
329
+ if self.step_index < self.num_inference_steps - 1:
330
+ sigma_next = self.sigmas[self.step_index + 1]
331
+ noise = randn_tensor(
332
+ model_output.shape,
333
+ generator=generator,
334
+ device=model_output.device,
335
+ dtype=denoised.dtype,
336
+ )
337
+ sample = sigma_next * noise + (1.0 - sigma_next) * denoised
338
+
339
+ self._step_index += 1
340
+ sample = sample.to(model_output.dtype)
341
+
342
+ if not return_dict:
343
+ return (sample,)
344
+
345
+ return FlashFlowMatchEulerDiscreteSchedulerOutput(prev_sample=sample)
346
+
347
+ # Copied from diffusers.schedulers.scheduling_euler_discrete.EulerDiscreteScheduler._convert_to_karras
348
+ def _convert_to_karras(self, in_sigmas: torch.Tensor, num_inference_steps) -> torch.Tensor:
349
+ """Constructs the noise schedule of Karras et al. (2022)."""
350
+
351
+ # Hack to make sure that other schedulers which copy this function don't break
352
+ # TODO: Add this logic to the other schedulers
353
+ if hasattr(self.config, "sigma_min"):
354
+ sigma_min = self.config.sigma_min
355
+ else:
356
+ sigma_min = None
357
+
358
+ if hasattr(self.config, "sigma_max"):
359
+ sigma_max = self.config.sigma_max
360
+ else:
361
+ sigma_max = None
362
+
363
+ sigma_min = sigma_min if sigma_min is not None else in_sigmas[-1].item()
364
+ sigma_max = sigma_max if sigma_max is not None else in_sigmas[0].item()
365
+
366
+ rho = 7.0 # 7.0 is the value used in the paper
367
+ ramp = np.linspace(0, 1, num_inference_steps)
368
+ min_inv_rho = sigma_min ** (1 / rho)
369
+ max_inv_rho = sigma_max ** (1 / rho)
370
+ sigmas = (max_inv_rho + ramp * (min_inv_rho - max_inv_rho)) ** rho
371
+ return sigmas
372
+
373
+ # Copied from diffusers.schedulers.scheduling_euler_discrete.EulerDiscreteScheduler._convert_to_exponential
374
+ def _convert_to_exponential(self, in_sigmas: torch.Tensor, num_inference_steps: int) -> torch.Tensor:
375
+ """Constructs an exponential noise schedule."""
376
+
377
+ # Hack to make sure that other schedulers which copy this function don't break
378
+ # TODO: Add this logic to the other schedulers
379
+ if hasattr(self.config, "sigma_min"):
380
+ sigma_min = self.config.sigma_min
381
+ else:
382
+ sigma_min = None
383
+
384
+ if hasattr(self.config, "sigma_max"):
385
+ sigma_max = self.config.sigma_max
386
+ else:
387
+ sigma_max = None
388
+
389
+ sigma_min = sigma_min if sigma_min is not None else in_sigmas[-1].item()
390
+ sigma_max = sigma_max if sigma_max is not None else in_sigmas[0].item()
391
+
392
+ sigmas = np.exp(np.linspace(math.log(sigma_max), math.log(sigma_min), num_inference_steps))
393
+ return sigmas
394
+
395
+ # Copied from diffusers.schedulers.scheduling_euler_discrete.EulerDiscreteScheduler._convert_to_beta
396
+ def _convert_to_beta(
397
+ self, in_sigmas: torch.Tensor, num_inference_steps: int, alpha: float = 0.6, beta: float = 0.6
398
+ ) -> torch.Tensor:
399
+ """From "Beta Sampling is All You Need" [arXiv:2407.12173] (Lee et. al, 2024)"""
400
+
401
+ # Hack to make sure that other schedulers which copy this function don't break
402
+ # TODO: Add this logic to the other schedulers
403
+ if hasattr(self.config, "sigma_min"):
404
+ sigma_min = self.config.sigma_min
405
+ else:
406
+ sigma_min = None
407
+
408
+ if hasattr(self.config, "sigma_max"):
409
+ sigma_max = self.config.sigma_max
410
+ else:
411
+ sigma_max = None
412
+
413
+ sigma_min = sigma_min if sigma_min is not None else in_sigmas[-1].item()
414
+ sigma_max = sigma_max if sigma_max is not None else in_sigmas[0].item()
415
+
416
+ sigmas = np.array(
417
+ [
418
+ sigma_min + (ppf * (sigma_max - sigma_min))
419
+ for ppf in [
420
+ scipy.stats.beta.ppf(timestep, alpha, beta)
421
+ for timestep in 1 - np.linspace(0, 1, num_inference_steps)
422
+ ]
423
+ ]
424
+ )
425
+ return sigmas
426
+
427
+ def __len__(self):
428
+ return self.config.num_train_timesteps