Files
Hyun-Puer-ComfyUI-Contextua…/repulsion.py
T
2026-04-01 15:52:50 +00:00

65 lines
2.4 KiB
Python

import torch
import torch.nn.functional as F
def compute_repulsion_gradient(embeddings, eps_noise=1e-4):
"""Compute the normalized gradient of the Vendi Score diversity loss.
Based on "On-the-fly Repulsion in the Contextual Space for Rich Diversity
in Diffusion Transformers" (arXiv:2603.28762).
The gradient is normalized per-sample so that repulsion_scale has a
consistent effect regardless of embedding dimensionality.
Args:
embeddings: Tensor of shape [B, N, D] — batch of token sequences.
eps_noise: Small noise added to break symmetry when embeddings are identical.
Returns:
Normalized gradient tensor of shape [B, N, D]. Subtract (scale * grad)
from embeddings to increase diversity.
"""
B, N, D = embeddings.shape
if B < 2:
return torch.zeros_like(embeddings)
with torch.inference_mode(False):
with torch.enable_grad():
# Clone into float32 with grad tracking
emb = embeddings.detach().float().clone().requires_grad_(True)
# Break symmetry for initially identical embeddings
noise = torch.randn_like(emb) * eps_noise
emb_noisy = emb + noise
# Flatten: [B, N, D] -> [B, N*D]
flat = emb_noisy.reshape(B, -1)
# Cosine similarity kernel: [B, B]
flat_norm = F.normalize(flat, dim=-1)
K = flat_norm @ flat_norm.T
# Normalized kernel
K_tilde = K / B
# Eigenvalues (K_tilde is real symmetric positive semi-definite)
eigenvalues = torch.linalg.eigvalsh(K_tilde)
# Von Neumann entropy loss (negative entropy — minimize to maximize diversity)
eigenvalues_clamped = eigenvalues.clamp(min=1e-8)
loss = -(eigenvalues_clamped * eigenvalues_clamped.log()).sum()
loss.backward()
grad = emb.grad # [B, N, D]
# Normalize: scale each sample's gradient to have unit norm relative
# to the embedding norm, so repulsion_scale is interpretable as a
# fraction of embedding magnitude.
emb_norms = embeddings.float().reshape(B, -1).norm(dim=-1, keepdim=True).clamp(min=1e-8) # [B, 1]
grad_norms = grad.reshape(B, -1).norm(dim=-1, keepdim=True).clamp(min=1e-8) # [B, 1]
grad = grad.reshape(B, -1) * (emb_norms / grad_norms)
grad = grad.reshape(B, N, D)
return grad.to(embeddings.dtype)