Instructions to use DROPTABLE/chxprt with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use DROPTABLE/chxprt with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("DROPTABLE/chxprt", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
File size: 6,538 Bytes
1156de8 b9255c1 1156de8 4a95232 1156de8 b9255c1 1156de8 b9255c1 1156de8 4a95232 b9255c1 1156de8 b9255c1 1156de8 4a95232 b9255c1 1156de8 b9255c1 1156de8 b9255c1 1156de8 4a95232 b9255c1 1156de8 4a95232 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | """
All-in-one utilities for Ambient Diffusion (random-inpainting variant).
Implements
----------
sample_inpainting_mask - A (Eq. 3.1)
further_corrupt - ~A (Eq. 3.2)
make_ambient_batch - build (~A x_t , A) for training
ambient_loss - J_corr (Eq. 3.2)
AmbientDDPMPipeline - fixed-mask sampler (Eq. 3.3)
"""
from __future__ import annotations
import torch
from torch import Tensor
from typing import Tuple, Optional
from diffusers.schedulers.scheduling_ddpm import DDPMScheduler
from diffusers import DDPMPipeline
# ------------------------------------------------------------------
# Corruptions
# ------------------------------------------------------------------
def sample_inpainting_mask(
shape: Tuple[int, int, int, int],
p: float = 0.9,
device: torch.device | str | None = None,
) -> Tensor:
"""Diagonal Bernoulli mask A: 1 = keep, 0 = erase."""
B, C, H, W = shape
return torch.bernoulli(torch.full((B, 1, H, W), p, device=device)).expand(-1, C, -1, -1)
def further_corrupt(A: Tensor, delta: float = 0.05) -> Tensor:
"""Sample ~A = B A by turning surviving pixels off with prob delta."""
B_mat = torch.bernoulli(torch.full_like(A, 1.0 - delta))
return B_mat * A
# ------------------------------------------------------------------
# Training helpers
# ------------------------------------------------------------------
def make_ambient_batch(
clean: Tensor,
noise_scheduler: DDPMScheduler,
timesteps: Tensor,
p: float = 0.9,
delta: float = 0.05,
) -> tuple[Tensor, Tensor]:
"""
Returns:
y_t_tilde : ~A x_t (to feed the network)
A_mask : A (needed only for the loss)
"""
B, C, H, W = clean.shape
device = clean.device
# 1. A x₀
A = sample_inpainting_mask((B, C, H, W), p=p, device=device)
# 2. add diffusion noise → x_t , then A x_t
noise = torch.randn_like(clean)
if timesteps.ndim == 0:
timesteps = torch.full((B,), timesteps, device=device, dtype=torch.long)
elif timesteps.shape[0] != B:
raise ValueError(f"Timesteps batch size {timesteps.shape[0]} does not match input batch size {B}")
x_t = noise_scheduler.add_noise(clean, noise, timesteps)
# 3. ~A x_t
A_tilde = further_corrupt(A, delta=delta)
mask_ch = A_tilde[:, :1] # (B,1,H,W)
net_input = torch.cat([A_tilde * x_t, mask_ch], dim=1)
return net_input, A
def ambient_loss(pred, clean, A_mask, snr_weights=None):
"""
Masked L2 loss with optional SNR weights (Eq. 2 in the paper).
"""
diff = A_mask * (pred - clean)
loss = 0.5 * diff.pow(2)
if snr_weights is not None:
loss = snr_weights * loss
return loss.mean()
# ------------------------------------------------------------------
# Sampler
# ------------------------------------------------------------------
class AmbientDDPMPipeline(DDPMPipeline):
"""
Fixed-mask sampler (Eq. 3.3). Drop-in replacement for DDPMPipeline.
"""
def __init__(self, *, unet, scheduler, p_mask: float = 0.9):
# 1) Let base class register the trainable modules
super().__init__(unet=unet, scheduler=scheduler)
# 2) Store hyper‑parameter in the *config* **and** as an attribute
self.register_to_config(p_mask=p_mask) # guarantees serialisation
self.p_mask = p_mask # convenient runtime access
@torch.no_grad()
def __call__(
self,
batch_size: int = 1,
generator: Optional[torch.Generator] = None,
num_inference_steps: int = 250,
class_labels: Optional[torch.Tensor] = None,
guidance_scale: float = 1.5,
output_type: str = "pt",
return_dict: bool = True,
mem = False
):
device = self.device
h = w = self.unet.config.sample_size
# Create a dummy mask that's all ones (no masking)
dummy_mask = torch.ones((batch_size, 1, h, w), device=device)
# Start with random noise
x = torch.randn(
(batch_size, 1, h, w),
generator=generator,
device=device,
)
self.scheduler.set_timesteps(num_inference_steps, device=device)
if mem:
TCNP = torch.empty((batch_size, num_inference_steps), device=self.device)
# Set up conditioning labels
if class_labels is None:
cond_lbls = torch.zeros(
(batch_size, getattr(self.unet.config, "multihot_dim", 1)),
dtype=torch.long if not hasattr(self.unet.config, "multihot_dim") else torch.float,
device=device,
)
else:
cond_lbls = class_labels.to(device)
uncond_lbls = torch.zeros_like(cond_lbls)
for i, t in enumerate(self.scheduler.timesteps):
# Always include mask channel to match training, but mask is all ones
x_with_mask = torch.cat([x, dummy_mask], dim=1)
if guidance_scale == 1.0 or class_labels is None:
# Single conditional/unconditional pass
eps = self.unet(x_with_mask, t, class_labels=cond_lbls).sample
else:
# Classifier-free guidance with duplicate batch
inp = torch.cat([x_with_mask, x_with_mask], dim=0)
lbls = torch.cat([cond_lbls, uncond_lbls], dim=0)
tids = t.expand(2 * batch_size)
eps_cond, eps_uncond = (
self.unet(inp, tids, class_labels=lbls).sample.chunk(2)
)
eps = eps_uncond + guidance_scale * (eps_cond - eps_uncond)
if mem:
# print(cond_output.squeeze().shape, uncond_output.shape)
TCNP[:, i] = torch.linalg.norm(eps_cond.squeeze() - eps_uncond.squeeze(), dim=[0,1])
# Standard update (same as before)
x0_hat = eps
sigma = self.scheduler._get_variance(t).sqrt()
gamma = sigma ** 2 / (sigma ** 2 + 1)
x = gamma * x + (1 - gamma) * x0_hat
x = self.scheduler.step(eps, t, x).prev_sample
x = (x / 2 + 0.5).clamp(0, 1)
img = x.cpu().permute(0, 2, 3, 1).numpy()
if output_type == "pil":
img = self.numpy_to_pil(img)
if not return_dict:
return (img,)
if mem:
return dict(images=img, TCNP=TCNP)
return dict(images=img) |