diffu_test / diffu /flow.py
Gabriel's picture
Diffu Studio ZeroGPU Space: app.py + vendored packages + aokit AoT + redesigned panel
333ff0e verified
Raw
History Blame Contribute Delete
2.63 kB
"""Rectified flow / flow matching objective (SD3 formulation), native PyTorch.
Convention: predict velocity v = eps - x0 along the linear interpolant
x_t = (1 - t) * x0 + t * eps , t in [0, 1] (t=0 data, t=1 noise).
"""
from __future__ import annotations
import torch
import torch.nn.functional as F
from diffusers.training_utils import compute_density_for_timestep_sampling
def repa_loss(proj_hidden: torch.Tensor, target_feats: torch.Tensor) -> torch.Tensor:
"""REPA (ICLR'25): cosine-align projected DiT hidden states to frozen-encoder features.
Args:
proj_hidden: Projected DiT hidden states, ``[B, D]`` or ``[B, N, D]``.
target_feats: Frozen-encoder (DINOv3) features, same shape as ``proj_hidden``.
Returns:
Scalar alignment loss (lower is better fine structure).
"""
p = F.normalize(proj_hidden, dim=-1)
t = F.normalize(target_feats, dim=-1)
return (1.0 - (p * t).sum(dim=-1)).mean()
def sample_timesteps(batch: int, device: torch.device, mean: float = 0.0, std: float = 1.0) -> torch.Tensor:
"""Logit-normal flow-matching timesteps ``[B]`` in ``[0, 1]`` (emphasizes the hard mid-range).
Delegates to diffusers' own SD3 sampler instead of re-deriving the formula: with
``weighting_scheme="logit_normal"`` it returns ``sigmoid(N(mean, std))`` — identical to the
hand-rolled ``sigmoid(randn*std + mean)`` it replaces.
"""
return compute_density_for_timestep_sampling(
weighting_scheme="logit_normal", batch_size=batch, logit_mean=mean, logit_std=std, device=device
)
def interpolate(x0: torch.Tensor, eps: torch.Tensor, t: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
"""Linear interpolant ``x_t`` and its velocity target ``v = eps - x0``.
Args:
x0: Clean data latents ``[B, ...]`` (t=0 endpoint).
eps: Gaussian noise of the same shape (t=1 endpoint).
t: Per-sample timesteps ``[B]`` in ``[0, 1]``.
Returns:
``(x_t, target_v)``, both shaped like ``x0``.
"""
t_ = t.view(-1, *([1] * (x0.dim() - 1)))
x_t = (1 - t_) * x0 + t_ * eps
target_v = eps - x0
return x_t, target_v
def flow_loss(
v_pred: torch.Tensor, target_v: torch.Tensor, weight: torch.Tensor | None = None
) -> torch.Tensor:
"""MSE on velocity. ``weight`` (broadcastable, e.g. ink-focal map) is applied as a weighted mean,
normalized by the mean weight so the loss magnitude stays comparable to the unweighted case."""
se = (v_pred - target_v) ** 2
if weight is not None:
return (se * weight).mean() / weight.mean().clamp_min(1e-6)
return se.mean()