SpiceNet / code /make_failure_cases.py
Noushad999's picture
Upload folder using huggingface_hub
1ea7ba6 verified
Raw
History Blame Contribute Delete
3.35 kB
#!/usr/bin/env python
"""Qualitative failure cases: real in-the-wild images that the studio-trained
SpiceFusionNet misclassifies, captioned with the (wrong) predicted class. The
emotional core of a shortcut-learning result. Saves outputs/failure_cases.{png,pdf}.
"""
import sys, os
from pathlib import Path
import numpy as np
import torch
from torch.utils.data import DataLoader
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
_base = "/mnt/d/SpiceNet" if os.path.exists("/mnt/d/SpiceNet") else "D:/SpiceNet"
sys.path.insert(0, _base)
from src.model import SpiceFusionNet
from src.dataset import load_manifest_splits, SpiceDataset, get_val_transform
ROOT = Path(_base)
CKPT = ROOT / "outputs/checkpoints/overlap_indian_s42/p1_best.pth" # studio-trained
SS_M = str(ROOT / "outputs/manifest_overlap_ss.json") # wild test
PRIORITY = ["coriander", "black_pepper", "green_cardamom", "cumin", "cloves", "nutmeg"]
def nice(c): return c.replace("_", " ")
def main():
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
splits, classes = load_manifest_splits(SS_M)
paths, labels = splits["test"]
labels = np.array(labels)
model = SpiceFusionNet(num_classes=len(classes)).to(device)
ck = torch.load(CKPT, map_location=device, weights_only=False)
model.load_state_dict(ck["model_state"]); model.eval()
ds = SpiceDataset(paths, labels.tolist(), get_val_transform(), multimodal=False)
dl = DataLoader(ds, batch_size=64, num_workers=2)
preds, conf = [], []
with torch.no_grad():
for imgs, tex, col, y in dl:
p = torch.softmax(model.forward_image(imgs.to(device)), 1)
c, k = p.max(1)
preds.extend(k.cpu().tolist()); conf.extend(c.cpu().tolist())
preds, conf = np.array(preds), np.array(conf)
cls_idx = {c: i for i, c in enumerate(classes)}
# one dramatic misclassification per priority class (highest-confidence wrong)
picks = []
for c in PRIORITY:
if c not in cls_idx:
continue
ti = cls_idx[c]
cand = np.where((labels == ti) & (preds != ti))[0]
if len(cand):
picks.append(cand[np.argmax(conf[cand])])
if len(picks) == 6:
break
fig, axes = plt.subplots(2, 3, figsize=(9, 6.4))
for ax, i in zip(axes.ravel(), picks):
try:
ax.imshow(mpimg.imread(paths[i]))
except Exception:
ax.set_facecolor("#eee")
ax.set_xticks([]); ax.set_yticks([])
ax.set_title(f"true: {nice(classes[labels[i]])}\npredicted: {nice(classes[preds[i]])}"
f" ({conf[i]*100:.0f}%)", fontsize=9)
for s in ax.spines.values():
s.set_edgecolor("#c0504d"); s.set_linewidth(1.6)
for ax in axes.ravel()[len(picks):]:
ax.axis("off")
fig.suptitle("In-the-wild images the studio-trained model gets wrong",
fontsize=11, y=0.99)
fig.tight_layout(rect=[0, 0, 1, 0.97])
out = ROOT / "outputs" / "failure_cases"
fig.savefig(str(out) + ".png", dpi=300, bbox_inches="tight")
fig.savefig(str(out) + ".pdf", bbox_inches="tight")
print("saved", out, "| picks:", [f"{nice(classes[labels[i]])}->{nice(classes[preds[i]])}" for i in picks])
if __name__ == "__main__":
main()