Pixal3D_Modular / guidance.py
OzzyGT's picture
OzzyGT HF Staff
Add Pixal3D modular blocks
92011e4
Raw History Blame Contribute Delete
3.4 kB
from diffusers.configuration_utils import register_to_config
from diffusers.guiders.guider_utils import BaseGuidance, GuiderOutput
class Pixal3DGuidance(BaseGuidance):
"""Classifier-free guidance with Pixal3D's sigma interval and clean-sample (x0) rescaling.
Guidance is active while the current sigma lies inside `guidance_interval`; outside it, or with
`guidance_scale=1`, only the conditional prediction is computed. The rescale matches the standard
deviation of the guided x0 estimate to the conditional one, which needs the current `latents`.
"""
_input_predictions = ["pred_cond", "pred_uncond"]
@register_to_config
def __init__(
self,
guidance_scale=7.5,
guidance_rescale=0.7,
guidance_interval=(0.6, 1.0),
sigma_min=1e-5,
enabled=True,
):
super().__init__(enabled=enabled)
@property
def _scale(self):
sigma = self._timestep
low, high = self.config.guidance_interval
if self._enabled and sigma is not None and low <= sigma <= high:
return self.config.guidance_scale
return 1.0
@property
def is_conditional(self):
return self._count_prepared == 1
@property
def num_conditions(self):
return 1 if self._scale in (0, 1) else 2
def _predictions(self):
# guidance_scale=0 is purely unconditional; 1 (or outside the interval) is purely conditional.
if self._scale == 0:
return ["pred_uncond"]
return self._input_predictions[: self.num_conditions]
def prepare_inputs(self, data):
return [self._prepare_batch(data, i, name) for i, name in self._identified_predictions()]
def prepare_inputs_from_block_state(self, data, input_fields):
return [
self._prepare_batch_from_block_state(input_fields, data, i, name)
for i, name in self._identified_predictions()
]
def _identified_predictions(self):
return [(self._input_predictions.index(name), name) for name in self._predictions()]
def __call__(self, data):
predictions = {getattr(d, self._identifier_key): d.noise_pred for d in data}
return self.forward(**predictions, latents=data[0].latents)
def forward(self, pred_cond=None, pred_uncond=None, latents=None):
if pred_cond is None:
return GuiderOutput(pred=pred_uncond)
if pred_uncond is None:
return GuiderOutput(pred=pred_cond)
latents = latents.to(pred_cond.device)
pred_uncond = pred_uncond.to(pred_cond.device)
scale = self._scale
result = scale * pred_cond + (1 - scale) * pred_uncond
if self.config.guidance_rescale > 0:
sigma_min = self.config.sigma_min
sigma = sigma_min + (1 - sigma_min) * self._timestep
clean_pos = (1 - sigma_min) * latents - sigma * pred_cond
clean_cfg = (1 - sigma_min) * latents - sigma * result
dims = list(range(1, latents.ndim))
rescaled = clean_cfg * (clean_pos.std(dim=dims, keepdim=True) / clean_cfg.std(dim=dims, keepdim=True))
clean = self.config.guidance_rescale * rescaled + (1 - self.config.guidance_rescale) * clean_cfg
result = ((1 - sigma_min) * latents - clean) / sigma
return GuiderOutput(pred=result, pred_cond=pred_cond, pred_uncond=pred_uncond)