thyroid-training-scripts / gradcam_and_push.py
Johnyquest7's picture
Upload gradcam_and_push.py
532037a verified
"""
Generate Grad-CAM visualizations and push them to Hugging Face Hub.
"""
import os, sys, math, json, random, warnings, traceback
warnings.filterwarnings("ignore")
import numpy as np
from PIL import Image
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import torch
import torch.nn.functional as F
from datasets import load_dataset
from transformers import AutoImageProcessor, AutoModelForImageClassification
from huggingface_hub import HfApi, login
HF_USERNAME = "Johnyquest7"
DATASET_NAME = "BTX24/thyroid-cancer-classification-ultrasound-dataset"
MODEL_NAME = f"{HF_USERNAME}/ML-Inter_thyroid"
OUTPUT_DIR = "./gradcam_outputs"
SEED = 42
BATCH_SIZE = 16
random.seed(SEED)
np.random.seed(SEED)
torch.manual_seed(SEED)
def main():
print("=" * 60)
print("Thyroid Grad-CAM Generation + Hub Upload")
print("=" * 60)
os.makedirs(OUTPUT_DIR, exist_ok=True)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"\nDevice: {device}")
processor = AutoImageProcessor.from_pretrained(MODEL_NAME)
model = AutoModelForImageClassification.from_pretrained(MODEL_NAME).to(device).eval()
id2label = model.config.id2label
print(f"Model loaded: {sum(p.numel() for p in model.parameters())/1e6:.1f}M params")
ds = load_dataset(DATASET_NAME, split="train")
ds = ds.shuffle(seed=SEED)
train_test = ds.train_test_split(test_size=0.2, stratify_by_column="label", seed=SEED)
test_ds = train_test["test"]
print(f"Test samples: {len(test_ds)}")
# Get predictions
all_logits, all_labels = [], []
for i in range(0, len(test_ds), BATCH_SIZE):
batch_items = [test_ds[j] for j in range(i, min(i+BATCH_SIZE, len(test_ds)))]
images = [item["image"].convert("RGB") for item in batch_items]
inputs = processor(images, return_tensors="pt")
with torch.no_grad():
outputs = model(pixel_values=inputs["pixel_values"].to(device))
all_logits.extend(outputs.logits.cpu().numpy())
all_labels.extend([item["label"] for item in batch_items])
y_true = np.array(all_labels)
y_pred = np.argmax(np.array(all_logits), axis=1)
correct_idx = [i for i in range(len(y_true)) if y_true[i] == y_pred[i]]
incorrect_idx = [i for i in range(len(y_true)) if y_true[i] != y_pred[i]]
random.shuffle(correct_idx)
random.shuffle(incorrect_idx)
selected = correct_idx[:5] + incorrect_idx[:5]
print(f"\nGenerating Grad-CAM for {len(selected)} samples ({len(correct_idx[:5])} correct, {len(incorrect_idx[:5])} incorrect)...")
# Hooks
gradcam_data = {}
def fwd_hook(module, input, output):
gradcam_data["feat"] = output.detach()
def bwd_hook(module, grad_input, grad_output):
gradcam_data["grad"] = grad_output[0].detach()
target_layer = model.swinv2.encoder.layers[-1].blocks[-1].layernorm_after
fwd_handle = target_layer.register_forward_hook(fwd_hook)
bwd_handle = target_layer.register_full_backward_hook(bwd_hook)
uploaded_files = []
for idx in selected:
try:
item = test_ds[idx]
img = item["image"].convert("RGB")
label = item["label"]
inputs = processor(img, return_tensors="pt")
img_tensor = inputs["pixel_values"].to(device).requires_grad_(True)
model.zero_grad()
outputs = model(pixel_values=img_tensor)
target_class = int(y_pred[idx])
score = outputs.logits[0, target_class]
score.backward()
feat = gradcam_data["feat"][0]
grads = gradcam_data["grad"][0]
weights = grads.mean(dim=0, keepdim=True)
cam = torch.matmul(feat, weights.t()).squeeze()
H = W = int(math.sqrt(cam.shape[0]))
cam = cam.reshape(H, W)
cam = F.relu(cam)
cam = cam - cam.min()
cam = cam / (cam.max() + 1e-8)
cam = F.interpolate(cam.unsqueeze(0).unsqueeze(0), size=(256, 256), mode="bilinear", align_corners=False)
cam = cam.squeeze().cpu().numpy()
img_np = img_tensor.squeeze().detach().cpu().permute(1,2,0).numpy()
img_np = (img_np - img_np.min()) / (img_np.max() - img_np.min() + 1e-8)
plt.figure(figsize=(6,6))
plt.imshow(img_np)
plt.imshow(cam, cmap="jet", alpha=0.5)
pred_name = id2label.get(target_class, str(target_class))
true_name = id2label.get(label, str(label))
status = "CORRECT" if y_true[idx] == y_pred[idx] else "WRONG"
plt.title(f"{status}: Pred={pred_name} | True={true_name}")
plt.axis("off")
fname = f"gradcam_{status}_sample{idx}_{pred_name}_vs_{true_name}.png"
fpath = os.path.join(OUTPUT_DIR, fname)
plt.savefig(fpath, bbox_inches="tight", dpi=150)
plt.close()
print(f" Saved {fpath}")
# Upload to Hub
api = HfApi()
try:
api.upload_file(
path_or_fileobj=fpath,
path_in_file=f"gradcam/{fname}",
repo_id=f"{HF_USERNAME}/thyroid-training-scripts",
repo_type="model"
)
uploaded_files.append(f"gradcam/{fname}")
print(f" Uploaded to gradcam/{fname}")
except Exception as e:
print(f" Upload failed for {fname}: {e}")
except Exception as e:
print(f" Skipped sample {idx}: {e}")
traceback.print_exc()
fwd_handle.remove()
bwd_handle.remove()
print(f"\n{'='*60}")
print(f"Done. Uploaded {len(uploaded_files)} images to:")
for f in uploaded_files:
print(f" https://huggingface.co/{HF_USERNAME}/thyroid-training-scripts/tree/main/{f}")
if __name__ == "__main__":
main()