Spaces:
Running on Zero
Running on Zero
Update pk_workflow.py
Browse files- pk_workflow.py +59 -2
pk_workflow.py
CHANGED
|
@@ -175,6 +175,47 @@ SCHEDULE_SIGMA_FUNCS = {
|
|
| 175 |
"normal": normal_sigmas,
|
| 176 |
}
|
| 177 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
|
| 179 |
class use_schedule:
|
| 180 |
"""Set each scheduler's shift for one request, and — for anything but `native` — force its sigma grid onto
|
|
@@ -187,11 +228,13 @@ class use_schedule:
|
|
| 187 |
are shared, request-spanning objects that must not carry one request's shift into the next.
|
| 188 |
"""
|
| 189 |
|
| 190 |
-
def __init__(self, pipe, steps: int, schedule_name: str, video_shift: float, audio_shift: float, threshold_noise: float = 0.025):
|
| 191 |
self.pipe = pipe
|
| 192 |
self.attr_names = ["scheduler", "audio_scheduler"]
|
| 193 |
self.shifts = [float(video_shift), float(audio_shift)]
|
| 194 |
self.schedule_name = schedule_name
|
|
|
|
|
|
|
| 195 |
self.steps = int(steps)
|
| 196 |
self.threshold_noise = float(threshold_noise)
|
| 197 |
self._originals: dict = {}
|
|
@@ -215,11 +258,25 @@ class use_schedule:
|
|
| 215 |
return _f(_s, None, device, _grid)
|
| 216 |
|
| 217 |
scheduler.set_timesteps = forced
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 218 |
return self
|
| 219 |
|
| 220 |
def __exit__(self, *_):
|
| 221 |
for attr_name, original in self._originals.items():
|
| 222 |
-
getattr(self.pipe, attr_name)
|
|
|
|
|
|
|
| 223 |
setattr(self.pipe, attr_name, original)
|
| 224 |
return False
|
| 225 |
|
|
|
|
| 175 |
"normal": normal_sigmas,
|
| 176 |
}
|
| 177 |
|
| 178 |
+
def _euler_ancestral_step(scheduler, generator, model_output, timestep, sample, eta: float = 1.0, s_noise: float = 1.0):
|
| 179 |
+
"""Ports k-diffusion's `sample_euler_ancestral_RF` — the flow-matching branch `sample_euler_ancestral`
|
| 180 |
+
dispatches to for `CONST`-style model sampling, which is what MiniMax-H3's `[0, 1]` sigma space is — onto one
|
| 181 |
+
`MiniMaxH3Scheduler.step()` call. Single model evaluation, same shape as `step()` itself, with fresh
|
| 182 |
+
ancestral noise injected each step instead of a plain Euler blend. Mirrors `step()`'s own care around
|
| 183 |
+
recomputing `sigma_from_timestep` from `timestep` rather than reading `self.sigmas` at the current index, for
|
| 184 |
+
the same numerical-consistency reason documented there.
|
| 185 |
+
"""
|
| 186 |
+
if scheduler._step_index is None:
|
| 187 |
+
scheduler._step_index = scheduler.index_for_timestep(timestep) if scheduler._begin_index is None else scheduler._begin_index
|
| 188 |
+
|
| 189 |
+
if not isinstance(timestep, torch.Tensor):
|
| 190 |
+
timestep = torch.tensor(timestep, dtype=sample.dtype)
|
| 191 |
+
sigma_from_timestep = 1 - timestep.to(device=sample.device, dtype=sample.dtype)
|
| 192 |
+
while sigma_from_timestep.ndim < sample.ndim:
|
| 193 |
+
sigma_from_timestep = sigma_from_timestep.unsqueeze(-1)
|
| 194 |
+
denoised = sample + sigma_from_timestep * model_output
|
| 195 |
+
|
| 196 |
+
compute_dtype = torch.float32 if sample.dtype in (torch.float16, torch.bfloat16) else sample.dtype
|
| 197 |
+
sigma = scheduler.sigmas[scheduler._step_index].to(device=sample.device, dtype=compute_dtype)
|
| 198 |
+
sigma_next = scheduler.sigmas[scheduler._step_index + 1].to(device=sample.device, dtype=compute_dtype)
|
| 199 |
+
x = sample.to(dtype=compute_dtype)
|
| 200 |
+
denoised = denoised.to(dtype=compute_dtype)
|
| 201 |
+
|
| 202 |
+
if sigma_next == 0:
|
| 203 |
+
prev_sample = denoised
|
| 204 |
+
else:
|
| 205 |
+
downstep_ratio = 1 + (sigma_next / sigma - 1) * eta
|
| 206 |
+
sigma_down = sigma_next * downstep_ratio
|
| 207 |
+
alpha_next = 1 - sigma_next
|
| 208 |
+
alpha_down = 1 - sigma_down
|
| 209 |
+
renoise_coeff = (sigma_next**2 - sigma_down**2 * alpha_next**2 / alpha_down**2).clamp_min(0).sqrt()
|
| 210 |
+
ratio = sigma_down / sigma
|
| 211 |
+
prev_sample = ratio * x + (1 - ratio) * denoised
|
| 212 |
+
if eta > 0:
|
| 213 |
+
noise = torch.randn(x.shape, dtype=x.dtype, device=x.device, generator=generator)
|
| 214 |
+
prev_sample = (alpha_next / alpha_down) * prev_sample + noise * s_noise * renoise_coeff
|
| 215 |
+
|
| 216 |
+
prev_sample = prev_sample.to(dtype=sample.dtype)
|
| 217 |
+
scheduler._step_index += 1
|
| 218 |
+
return prev_sample
|
| 219 |
|
| 220 |
class use_schedule:
|
| 221 |
"""Set each scheduler's shift for one request, and — for anything but `native` — force its sigma grid onto
|
|
|
|
| 228 |
are shared, request-spanning objects that must not carry one request's shift into the next.
|
| 229 |
"""
|
| 230 |
|
| 231 |
+
def __init__(self, pipe, steps: int, schedule_name: str, video_shift: float, audio_shift: float, sampler_name: str = "euler", seed: int = 0, threshold_noise: float = 0.025):
|
| 232 |
self.pipe = pipe
|
| 233 |
self.attr_names = ["scheduler", "audio_scheduler"]
|
| 234 |
self.shifts = [float(video_shift), float(audio_shift)]
|
| 235 |
self.schedule_name = schedule_name
|
| 236 |
+
self.sampler_name = sampler_name
|
| 237 |
+
self.seed = int(seed)
|
| 238 |
self.steps = int(steps)
|
| 239 |
self.threshold_noise = float(threshold_noise)
|
| 240 |
self._originals: dict = {}
|
|
|
|
| 258 |
return _f(_s, None, device, _grid)
|
| 259 |
|
| 260 |
scheduler.set_timesteps = forced
|
| 261 |
+
|
| 262 |
+
if self.sampler_name == "euler_ancestral":
|
| 263 |
+
# Separate `torch.Generator` per scheduler (offset seeds) so video and audio ancestral noise don't
|
| 264 |
+
# correlate — each generator advances across every step call to *that* scheduler over the request.
|
| 265 |
+
for offset, attr_name in enumerate(self.attr_names):
|
| 266 |
+
scheduler = getattr(self.pipe, attr_name)
|
| 267 |
+
generator = torch.Generator(device="cpu").manual_seed(self.seed + offset)
|
| 268 |
+
|
| 269 |
+
def stepped(model_output, timestep, sample, return_dict=True, _s=scheduler, _g=generator, **_kwargs):
|
| 270 |
+
return (_euler_ancestral_step(_s, _g, model_output, timestep, sample),)
|
| 271 |
+
|
| 272 |
+
scheduler.step = stepped
|
| 273 |
return self
|
| 274 |
|
| 275 |
def __exit__(self, *_):
|
| 276 |
for attr_name, original in self._originals.items():
|
| 277 |
+
current = getattr(self.pipe, attr_name)
|
| 278 |
+
current.__dict__.pop("set_timesteps", None)
|
| 279 |
+
current.__dict__.pop("step", None)
|
| 280 |
setattr(self.pipe, attr_name, original)
|
| 281 |
return False
|
| 282 |
|