dedelste commited on
Commit
4a95232
·
1 Parent(s): b9255c1
Files changed (4) hide show
  1. ambient_diffusion.py +17 -2
  2. conditional_ddpm_pipeline.py +12 -1
  3. handler.py +109 -34
  4. 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
- return dict(images=img) if return_dict else (img,)
 
 
 
 
 
 
 
 
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 pipeline_type == "ambient":
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.9))
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
- condition_vector = torch.zeros(len(_CHEXPERT_CLASSES), device=self.device)
150
- for cond_name in conditions_input:
151
- if cond_name in _CHEXPERT_CLASSES:
152
- condition_index = _CHEXPERT_CLASSES.index(cond_name)
153
- condition_vector[condition_index] = 1.0
154
- class_labels = condition_vector.unsqueeze(0).float()
155
-
156
- generator = torch.Generator(device=self.device).manual_seed(seed)
157
-
158
- print(f"Starting inference with {pipeline_type} pipeline using UNet '{model_variant}'...")
159
- with torch.no_grad():
160
- result = active_pipeline(
161
- batch_size=1, generator=generator, num_inference_steps=num_inference_steps,
162
- output_type="np", class_labels=class_labels, guidance_scale=guidance_scale
163
- )
164
- image_np = result["images"][0]
165
- print("Inference complete.")
166
-
167
- if image_np.ndim == 3 and image_np.shape[2] == 1: # Ensure it's grayscale C=1
168
- image_np = np.repeat(image_np, 3, axis=2) # Convert to C=3 for PNG
169
- elif image_np.ndim == 2: # Grayscale H,W
170
- image_np = np.stack((image_np,)*3, axis=-1) # Convert to H,W,3
171
-
172
- image_np = (image_np * 255).round().astype("uint8")
173
- pil_image = Image.fromarray(image_np)
174
-
175
- buffered = BytesIO()
176
- pil_image.save(buffered, format="PNG")
177
- image_base64 = base64.b64encode(buffered.getvalue()).decode("utf-8")
178
- print("Image processed and encoded to base64.")
179
-
180
- return {"generated_image_base64": image_base64, "model_variant_used": model_variant, "pipeline_used": pipeline_type}
 
 
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