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
mem
Browse files- ambient_diffusion.py +17 -2
- conditional_ddpm_pipeline.py +12 -1
- handler.py +109 -34
- labels.npy +0 -0
ambient_diffusion.py
CHANGED
|
@@ -110,6 +110,7 @@ class AmbientDDPMPipeline(DDPMPipeline):
|
|
| 110 |
guidance_scale: float = 1.5,
|
| 111 |
output_type: str = "pt",
|
| 112 |
return_dict: bool = True,
|
|
|
|
| 113 |
):
|
| 114 |
device = self.device
|
| 115 |
h = w = self.unet.config.sample_size
|
|
@@ -126,6 +127,9 @@ class AmbientDDPMPipeline(DDPMPipeline):
|
|
| 126 |
|
| 127 |
self.scheduler.set_timesteps(num_inference_steps, device=device)
|
| 128 |
|
|
|
|
|
|
|
|
|
|
| 129 |
# Set up conditioning labels
|
| 130 |
if class_labels is None:
|
| 131 |
cond_lbls = torch.zeros(
|
|
@@ -138,7 +142,7 @@ class AmbientDDPMPipeline(DDPMPipeline):
|
|
| 138 |
|
| 139 |
uncond_lbls = torch.zeros_like(cond_lbls)
|
| 140 |
|
| 141 |
-
for t in self.scheduler.timesteps:
|
| 142 |
# Always include mask channel to match training, but mask is all ones
|
| 143 |
x_with_mask = torch.cat([x, dummy_mask], dim=1)
|
| 144 |
|
|
@@ -156,6 +160,10 @@ class AmbientDDPMPipeline(DDPMPipeline):
|
|
| 156 |
)
|
| 157 |
eps = eps_uncond + guidance_scale * (eps_cond - eps_uncond)
|
| 158 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
# Standard update (same as before)
|
| 160 |
x0_hat = eps
|
| 161 |
sigma = self.scheduler._get_variance(t).sqrt()
|
|
@@ -167,4 +175,11 @@ class AmbientDDPMPipeline(DDPMPipeline):
|
|
| 167 |
img = x.cpu().permute(0, 2, 3, 1).numpy()
|
| 168 |
if output_type == "pil":
|
| 169 |
img = self.numpy_to_pil(img)
|
| 170 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
guidance_scale: float = 1.5,
|
| 111 |
output_type: str = "pt",
|
| 112 |
return_dict: bool = True,
|
| 113 |
+
mem = False
|
| 114 |
):
|
| 115 |
device = self.device
|
| 116 |
h = w = self.unet.config.sample_size
|
|
|
|
| 127 |
|
| 128 |
self.scheduler.set_timesteps(num_inference_steps, device=device)
|
| 129 |
|
| 130 |
+
if mem:
|
| 131 |
+
TCNP = torch.empty((batch_size, num_inference_steps), device=self.device)
|
| 132 |
+
|
| 133 |
# Set up conditioning labels
|
| 134 |
if class_labels is None:
|
| 135 |
cond_lbls = torch.zeros(
|
|
|
|
| 142 |
|
| 143 |
uncond_lbls = torch.zeros_like(cond_lbls)
|
| 144 |
|
| 145 |
+
for i, t in enumerate(self.scheduler.timesteps):
|
| 146 |
# Always include mask channel to match training, but mask is all ones
|
| 147 |
x_with_mask = torch.cat([x, dummy_mask], dim=1)
|
| 148 |
|
|
|
|
| 160 |
)
|
| 161 |
eps = eps_uncond + guidance_scale * (eps_cond - eps_uncond)
|
| 162 |
|
| 163 |
+
if mem:
|
| 164 |
+
# print(cond_output.squeeze().shape, uncond_output.shape)
|
| 165 |
+
TCNP[:, i] = torch.linalg.norm(eps_cond.squeeze() - eps_uncond.squeeze(), dim=[0,1])
|
| 166 |
+
|
| 167 |
# Standard update (same as before)
|
| 168 |
x0_hat = eps
|
| 169 |
sigma = self.scheduler._get_variance(t).sqrt()
|
|
|
|
| 175 |
img = x.cpu().permute(0, 2, 3, 1).numpy()
|
| 176 |
if output_type == "pil":
|
| 177 |
img = self.numpy_to_pil(img)
|
| 178 |
+
|
| 179 |
+
if not return_dict:
|
| 180 |
+
return (img,)
|
| 181 |
+
|
| 182 |
+
if mem:
|
| 183 |
+
return dict(images=img, TCNP=TCNP)
|
| 184 |
+
|
| 185 |
+
return dict(images=img)
|
conditional_ddpm_pipeline.py
CHANGED
|
@@ -15,6 +15,7 @@ class ConditionalDDPMPipeline(DDPMPipeline):
|
|
| 15 |
class_labels=None,
|
| 16 |
guidance_scale=1.5,
|
| 17 |
return_dict=True,
|
|
|
|
| 18 |
):
|
| 19 |
# Initialize with random noise (exactly like original)
|
| 20 |
image = torch.randn(
|
|
@@ -31,8 +32,11 @@ class ConditionalDDPMPipeline(DDPMPipeline):
|
|
| 31 |
# Setup the scheduler (exactly like original)
|
| 32 |
self.scheduler.set_timesteps(num_inference_steps)
|
| 33 |
|
|
|
|
|
|
|
|
|
|
| 34 |
# Denoising process
|
| 35 |
-
for t in self.scheduler.timesteps:
|
| 36 |
# Only difference is we pass class_labels to the model
|
| 37 |
with torch.no_grad():
|
| 38 |
if guidance_scale > 1.0 and class_labels is not None:
|
|
@@ -51,6 +55,10 @@ class ConditionalDDPMPipeline(DDPMPipeline):
|
|
| 51 |
model_output = uncond_output + guidance_scale * (
|
| 52 |
cond_output - uncond_output
|
| 53 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
else:
|
| 55 |
# Standard pass with conditioning
|
| 56 |
model_output = self.unet(
|
|
@@ -71,5 +79,8 @@ class ConditionalDDPMPipeline(DDPMPipeline):
|
|
| 71 |
|
| 72 |
if not return_dict:
|
| 73 |
return (image,)
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
return dict(images=image)
|
|
|
|
| 15 |
class_labels=None,
|
| 16 |
guidance_scale=1.5,
|
| 17 |
return_dict=True,
|
| 18 |
+
mem=False,
|
| 19 |
):
|
| 20 |
# Initialize with random noise (exactly like original)
|
| 21 |
image = torch.randn(
|
|
|
|
| 32 |
# Setup the scheduler (exactly like original)
|
| 33 |
self.scheduler.set_timesteps(num_inference_steps)
|
| 34 |
|
| 35 |
+
if mem:
|
| 36 |
+
TCNP = torch.empty((batch_size, num_inference_steps), device=self.device)
|
| 37 |
+
|
| 38 |
# Denoising process
|
| 39 |
+
for i, t in enumerate(self.scheduler.timesteps):
|
| 40 |
# Only difference is we pass class_labels to the model
|
| 41 |
with torch.no_grad():
|
| 42 |
if guidance_scale > 1.0 and class_labels is not None:
|
|
|
|
| 55 |
model_output = uncond_output + guidance_scale * (
|
| 56 |
cond_output - uncond_output
|
| 57 |
)
|
| 58 |
+
|
| 59 |
+
if mem:
|
| 60 |
+
# print(cond_output.squeeze().shape, uncond_output.shape)
|
| 61 |
+
TCNP[:, i] = torch.linalg.norm(cond_output.squeeze() - uncond_output.squeeze(), dim=[0,1])
|
| 62 |
else:
|
| 63 |
# Standard pass with conditioning
|
| 64 |
model_output = self.unet(
|
|
|
|
| 79 |
|
| 80 |
if not return_dict:
|
| 81 |
return (image,)
|
| 82 |
+
|
| 83 |
+
if mem:
|
| 84 |
+
return dict(images=image, TCNP=TCNP)
|
| 85 |
|
| 86 |
return dict(images=image)
|
handler.py
CHANGED
|
@@ -86,7 +86,7 @@ class EndpointHandler:
|
|
| 86 |
scheduler = DDPMScheduler(num_train_timesteps=1000, beta_schedule="linear")
|
| 87 |
print("Created new default DDPMScheduler instance.")
|
| 88 |
|
| 89 |
-
if
|
| 90 |
scheduler.config.prediction_type = "sample"
|
| 91 |
else:
|
| 92 |
scheduler.config.prediction_type = "epsilon"
|
|
@@ -97,13 +97,15 @@ class EndpointHandler:
|
|
| 97 |
|
| 98 |
def __call__(self, data: dict) -> dict:
|
| 99 |
inputs = data.pop("inputs", data)
|
|
|
|
|
|
|
| 100 |
model_variant = inputs.get("model_variant", "conditional").lower()
|
| 101 |
pipeline_type = inputs.get("pipeline_type", model_variant).lower()
|
| 102 |
conditions_input = inputs.get("conditions", ["No Finding"])
|
| 103 |
guidance_scale = float(inputs.get("guidance_scale", 3.0))
|
| 104 |
seed = int(inputs.get("seed", 42))
|
| 105 |
num_inference_steps = int(inputs.get("num_inference_steps", 50))
|
| 106 |
-
p_mask = float(inputs.get("p_mask", 0.
|
| 107 |
|
| 108 |
print(f"Request: model_variant='{model_variant}', pipeline_type='{pipeline_type}', conditions='{conditions_input}', guidance={guidance_scale}, seed={seed}, steps={num_inference_steps}")
|
| 109 |
|
|
@@ -145,36 +147,109 @@ class EndpointHandler:
|
|
| 145 |
error_msg = f"Invalid pipeline_type: '{pipeline_type}'. Choose 'conditional' or 'ambient'."
|
| 146 |
print(error_msg)
|
| 147 |
return {"error": error_msg}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
| 86 |
scheduler = DDPMScheduler(num_train_timesteps=1000, beta_schedule="linear")
|
| 87 |
print("Created new default DDPMScheduler instance.")
|
| 88 |
|
| 89 |
+
if model_variant_name == "ambient":
|
| 90 |
scheduler.config.prediction_type = "sample"
|
| 91 |
else:
|
| 92 |
scheduler.config.prediction_type = "epsilon"
|
|
|
|
| 97 |
|
| 98 |
def __call__(self, data: dict) -> dict:
|
| 99 |
inputs = data.pop("inputs", data)
|
| 100 |
+
task = inputs.get("task", "generate_image").lower()
|
| 101 |
+
|
| 102 |
model_variant = inputs.get("model_variant", "conditional").lower()
|
| 103 |
pipeline_type = inputs.get("pipeline_type", model_variant).lower()
|
| 104 |
conditions_input = inputs.get("conditions", ["No Finding"])
|
| 105 |
guidance_scale = float(inputs.get("guidance_scale", 3.0))
|
| 106 |
seed = int(inputs.get("seed", 42))
|
| 107 |
num_inference_steps = int(inputs.get("num_inference_steps", 50))
|
| 108 |
+
p_mask = float(inputs.get("p_mask", 0.75))
|
| 109 |
|
| 110 |
print(f"Request: model_variant='{model_variant}', pipeline_type='{pipeline_type}', conditions='{conditions_input}', guidance={guidance_scale}, seed={seed}, steps={num_inference_steps}")
|
| 111 |
|
|
|
|
| 147 |
error_msg = f"Invalid pipeline_type: '{pipeline_type}'. Choose 'conditional' or 'ambient'."
|
| 148 |
print(error_msg)
|
| 149 |
return {"error": error_msg}
|
| 150 |
+
|
| 151 |
+
if task == "tcnp_batch":
|
| 152 |
+
print("Processing task: tcnp_batch")
|
| 153 |
+
# Parameters for TCNP batch processing
|
| 154 |
+
start_index = int(inputs.get("start_index", 0))
|
| 155 |
+
num_to_process = int(inputs.get("num_conditions_to_process", 10)) # Chunk size
|
| 156 |
+
guidance_scale = float(inputs.get("guidance_scale", 1.5)) # Default from TCNP.py
|
| 157 |
+
num_inference_steps = int(inputs.get("num_inference_steps", 1000)) # Default from TCNP.py
|
| 158 |
+
base_seed = int(inputs.get("seed", 42)) # Default from TCNP.py
|
| 159 |
+
|
| 160 |
+
# --- Load labels.npy ---
|
| 161 |
+
try:
|
| 162 |
+
labels_file_path = os.path.join(self.model_dir, "labels.npy")
|
| 163 |
+
if not os.path.exists(labels_file_path):
|
| 164 |
+
return {"error": "labels.npy not found in model directory.", "task_processed": task}
|
| 165 |
+
all_labels_np = np.load(labels_file_path)
|
| 166 |
+
except Exception as e:
|
| 167 |
+
return {"error": f"Failed to load labels.npy: {str(e)}", "task_processed": task}
|
| 168 |
+
|
| 169 |
+
# --- Determine the slice of labels to process for the current chunk ---
|
| 170 |
+
actual_end_index = min(start_index + num_to_process, all_labels_np.shape[0])
|
| 171 |
+
if start_index >= all_labels_np.shape[0]:
|
| 172 |
+
return {"error": f"start_index ({start_index}) is out of bounds for labels.npy (size: {all_labels_np.shape[0]}).", "task_processed": task}
|
| 173 |
+
|
| 174 |
+
target_labels_np_slice = all_labels_np[start_index:actual_end_index]
|
| 175 |
+
actual_conditions_processed_count = target_labels_np_slice.shape[0]
|
| 176 |
+
|
| 177 |
+
if actual_conditions_processed_count == 0:
|
| 178 |
+
return {"error": "No conditions selected for processing with the given indices for this chunk.", "task_processed": task}
|
| 179 |
+
|
| 180 |
+
collected_tcnp_metrics_for_batch = []
|
| 181 |
+
for i in range(actual_conditions_processed_count):
|
| 182 |
+
current_condition_np = target_labels_np_slice[i]
|
| 183 |
+
# Convert numpy multi-hot vector to torch tensor
|
| 184 |
+
condition_vector = torch.from_numpy(current_condition_np).float().to(self.device)
|
| 185 |
+
class_labels_for_pipeline = condition_vector.unsqueeze(0) # Add batch dimension
|
| 186 |
+
|
| 187 |
+
current_iteration_seed = base_seed + (start_index + i) # Seed varies based on original index
|
| 188 |
+
generator = torch.Generator(device=self.device).manual_seed(current_iteration_seed)
|
| 189 |
+
|
| 190 |
+
print(f"TCNP Batch: Processing condition (original index {start_index + i}) with seed {current_iteration_seed}...")
|
| 191 |
+
|
| 192 |
+
with torch.no_grad():
|
| 193 |
+
result = active_pipeline(
|
| 194 |
+
batch_size=1, # Process one condition at a time from the batch
|
| 195 |
+
generator=generator,
|
| 196 |
+
num_inference_steps=num_inference_steps,
|
| 197 |
+
output_type="pt", # We only need the TCNP tensor
|
| 198 |
+
class_labels=class_labels_for_pipeline,
|
| 199 |
+
guidance_scale=guidance_scale,
|
| 200 |
+
mem=True # CRUCIAL: Enable TCNP calculation
|
| 201 |
+
)
|
| 202 |
+
|
| 203 |
+
if "TCNP" in result:
|
| 204 |
+
# result["TCNP"] should be shape (1, num_inference_steps)
|
| 205 |
+
collected_tcnp_metrics_for_batch.append(result["TCNP"].squeeze().cpu().numpy()) # Squeeze to (steps,)
|
| 206 |
+
else:
|
| 207 |
+
print(f"Warning: TCNP not found in result for condition original index {start_index + i}")
|
| 208 |
+
collected_tcnp_metrics_for_batch.append(np.zeros(num_inference_steps)) # Error placeholder
|
| 209 |
+
|
| 210 |
+
final_metrics_array_for_batch = np.array(collected_tcnp_metrics_for_batch)
|
| 211 |
+
|
| 212 |
+
return {
|
| 213 |
+
"task_processed": task,
|
| 214 |
+
"processed_batch_info": {
|
| 215 |
+
"requested_start_index": start_index,
|
| 216 |
+
"requested_num_to_process": num_to_process,
|
| 217 |
+
"actual_conditions_processed_count": actual_conditions_processed_count,
|
| 218 |
+
"original_indices_processed": list(range(start_index, start_index + actual_conditions_processed_count))
|
| 219 |
+
},
|
| 220 |
+
"aggregated_tcnp_metrics": final_metrics_array_for_batch.tolist()
|
| 221 |
+
}
|
| 222 |
|
| 223 |
+
else:
|
| 224 |
+
condition_vector = torch.zeros(len(_CHEXPERT_CLASSES), device=self.device)
|
| 225 |
+
for cond_name in conditions_input:
|
| 226 |
+
if cond_name in _CHEXPERT_CLASSES:
|
| 227 |
+
condition_index = _CHEXPERT_CLASSES.index(cond_name)
|
| 228 |
+
condition_vector[condition_index] = 1.0
|
| 229 |
+
class_labels = condition_vector.unsqueeze(0).float()
|
| 230 |
+
|
| 231 |
+
generator = torch.Generator(device=self.device).manual_seed(seed)
|
| 232 |
+
|
| 233 |
+
print(f"Starting inference with {pipeline_type} pipeline using UNet '{model_variant}'...")
|
| 234 |
+
with torch.no_grad():
|
| 235 |
+
result = active_pipeline(
|
| 236 |
+
batch_size=1, generator=generator, num_inference_steps=num_inference_steps,
|
| 237 |
+
output_type="np", class_labels=class_labels, guidance_scale=guidance_scale
|
| 238 |
+
)
|
| 239 |
+
image_np = result["images"][0]
|
| 240 |
+
print("Inference complete.")
|
| 241 |
+
|
| 242 |
+
if image_np.ndim == 3 and image_np.shape[2] == 1: # Ensure it's grayscale C=1
|
| 243 |
+
image_np = np.repeat(image_np, 3, axis=2) # Convert to C=3 for PNG
|
| 244 |
+
elif image_np.ndim == 2: # Grayscale H,W
|
| 245 |
+
image_np = np.stack((image_np,)*3, axis=-1) # Convert to H,W,3
|
| 246 |
+
|
| 247 |
+
image_np = (image_np * 255).round().astype("uint8")
|
| 248 |
+
pil_image = Image.fromarray(image_np)
|
| 249 |
+
|
| 250 |
+
buffered = BytesIO()
|
| 251 |
+
pil_image.save(buffered, format="PNG")
|
| 252 |
+
image_base64 = base64.b64encode(buffered.getvalue()).decode("utf-8")
|
| 253 |
+
print("Image processed and encoded to base64.")
|
| 254 |
+
|
| 255 |
+
return {"generated_image_base64": image_base64, "model_variant_used": model_variant, "pipeline_used": pipeline_type}
|
labels.npy
ADDED
|
Binary file (42.5 kB). View file
|
|
|