justinkay
commited on
Commit
·
bdc0687
1
Parent(s):
c72fcf7
Add new models
Browse files- compute_accuracy.py +142 -0
- iwildcam_demo.pt +2 -2
- iwildcam_demo_labels.pt +1 -1
- models.txt +3 -1
- process_iwildcam_data.py +133 -0
compute_accuracy.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Compute top-1 accuracy for each model by comparing predictions with ground truth.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import json
|
| 7 |
+
import os
|
| 8 |
+
from collections import OrderedDict
|
| 9 |
+
|
| 10 |
+
# Species mapping from demo/app.py
|
| 11 |
+
SPECIES_MAP = OrderedDict([
|
| 12 |
+
(24, "Jaguar"), # panthera onca
|
| 13 |
+
(10, "Ocelot"), # leopardus pardalis
|
| 14 |
+
(6, "Mountain Lion"), # puma concolor
|
| 15 |
+
(101, "Common Eland"), # tragelaphus oryx
|
| 16 |
+
(102, "Waterbuck"), # kobus ellipsiprymnus
|
| 17 |
+
(163, "African Wild Dog") # lycaon pictus
|
| 18 |
+
])
|
| 19 |
+
|
| 20 |
+
def load_ground_truth():
|
| 21 |
+
"""Load ground truth labels from annotations."""
|
| 22 |
+
with open('iwildcam_demo_annotations.json', 'r') as f:
|
| 23 |
+
data = json.load(f)
|
| 24 |
+
|
| 25 |
+
# Create mapping from filename to true label
|
| 26 |
+
ground_truth = {}
|
| 27 |
+
for annotation in data['annotations']:
|
| 28 |
+
image_id = annotation['image_id']
|
| 29 |
+
category_id = annotation['category_id']
|
| 30 |
+
image_info = next((img for img in data['images'] if img['id'] == image_id), None)
|
| 31 |
+
if image_info:
|
| 32 |
+
filename = image_info['file_name']
|
| 33 |
+
true_label = SPECIES_MAP.get(category_id, "Unknown")
|
| 34 |
+
if true_label != "Unknown":
|
| 35 |
+
ground_truth[filename] = true_label
|
| 36 |
+
|
| 37 |
+
return ground_truth
|
| 38 |
+
|
| 39 |
+
def compute_accuracy(results_file, ground_truth):
|
| 40 |
+
"""Compute top-1 accuracy for a model's results."""
|
| 41 |
+
with open(results_file, 'r') as f:
|
| 42 |
+
data = json.load(f)
|
| 43 |
+
|
| 44 |
+
model_name = data['model']
|
| 45 |
+
results = data['results']
|
| 46 |
+
|
| 47 |
+
correct = 0
|
| 48 |
+
total = 0
|
| 49 |
+
|
| 50 |
+
for filename, scores in results.items():
|
| 51 |
+
if filename in ground_truth:
|
| 52 |
+
# Get predicted class (highest score)
|
| 53 |
+
predicted_class = max(scores, key=scores.get)
|
| 54 |
+
true_class = ground_truth[filename]
|
| 55 |
+
|
| 56 |
+
if predicted_class == true_class:
|
| 57 |
+
correct += 1
|
| 58 |
+
total += 1
|
| 59 |
+
|
| 60 |
+
accuracy = correct / total if total > 0 else 0.0
|
| 61 |
+
return accuracy, correct, total
|
| 62 |
+
|
| 63 |
+
def main():
|
| 64 |
+
"""Compute accuracy for all models."""
|
| 65 |
+
print("Computing top-1 accuracy for each model...\n")
|
| 66 |
+
|
| 67 |
+
# Load ground truth
|
| 68 |
+
ground_truth = load_ground_truth()
|
| 69 |
+
print(f"Loaded ground truth for {len(ground_truth)} images")
|
| 70 |
+
|
| 71 |
+
# Find all results files
|
| 72 |
+
results_files = [f for f in os.listdir('.') if f.startswith('zeroshot_results_') and f.endswith('.json')]
|
| 73 |
+
|
| 74 |
+
if not results_files:
|
| 75 |
+
print("No results files found!")
|
| 76 |
+
return
|
| 77 |
+
|
| 78 |
+
print(f"Found {len(results_files)} results files\n")
|
| 79 |
+
|
| 80 |
+
# Compute accuracy for each model
|
| 81 |
+
accuracies = {}
|
| 82 |
+
for results_file in sorted(results_files):
|
| 83 |
+
try:
|
| 84 |
+
accuracy, correct, total = compute_accuracy(results_file, ground_truth)
|
| 85 |
+
|
| 86 |
+
# Extract model name from filename
|
| 87 |
+
model_name = results_file.replace('zeroshot_results_', '').replace('.json', '').replace('_', '/')
|
| 88 |
+
|
| 89 |
+
accuracies[model_name] = {
|
| 90 |
+
'accuracy': accuracy,
|
| 91 |
+
'correct': correct,
|
| 92 |
+
'total': total
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
print(f"{model_name}:")
|
| 96 |
+
print(f" Accuracy: {accuracy:.4f} ({correct}/{total})")
|
| 97 |
+
print()
|
| 98 |
+
|
| 99 |
+
except Exception as e:
|
| 100 |
+
print(f"Error processing {results_file}: {e}")
|
| 101 |
+
|
| 102 |
+
# Summary
|
| 103 |
+
print("="*60)
|
| 104 |
+
print("SUMMARY")
|
| 105 |
+
print("="*60)
|
| 106 |
+
|
| 107 |
+
# Sort by accuracy
|
| 108 |
+
sorted_models = sorted(accuracies.items(), key=lambda x: x[1]['accuracy'], reverse=True)
|
| 109 |
+
|
| 110 |
+
for i, (model_name, stats) in enumerate(sorted_models, 1):
|
| 111 |
+
print(f"{i}. {model_name}: {stats['accuracy']:.4f}")
|
| 112 |
+
|
| 113 |
+
# Show some example predictions vs ground truth
|
| 114 |
+
print("\n" + "="*60)
|
| 115 |
+
print("SAMPLE PREDICTIONS (first 10 images)")
|
| 116 |
+
print("="*60)
|
| 117 |
+
|
| 118 |
+
if results_files:
|
| 119 |
+
# Use the first model's results to show examples
|
| 120 |
+
with open(results_files[0], 'r') as f:
|
| 121 |
+
data = json.load(f)
|
| 122 |
+
|
| 123 |
+
results = data['results']
|
| 124 |
+
count = 0
|
| 125 |
+
|
| 126 |
+
for filename, scores in results.items():
|
| 127 |
+
if filename in ground_truth and count < 10:
|
| 128 |
+
predicted_class = max(scores, key=scores.get)
|
| 129 |
+
true_class = ground_truth[filename]
|
| 130 |
+
confidence = scores[predicted_class]
|
| 131 |
+
|
| 132 |
+
status = "✓" if predicted_class == true_class else "✗"
|
| 133 |
+
|
| 134 |
+
print(f"{filename}:")
|
| 135 |
+
print(f" True: {true_class}")
|
| 136 |
+
print(f" Pred: {predicted_class} ({confidence:.4f}) {status}")
|
| 137 |
+
print()
|
| 138 |
+
|
| 139 |
+
count += 1
|
| 140 |
+
|
| 141 |
+
if __name__ == "__main__":
|
| 142 |
+
main()
|
iwildcam_demo.pt
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:22671a83d662556a98fe14daaa122da9e9a22f9f08e24c7c2a4467c58fc3d206
|
| 3 |
+
size 128467
|
iwildcam_demo_labels.pt
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
size 11780
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:67935d5cd3e5616d16f1105afdfdb936b5fc3bacd6fba733584efc2c2a951b90
|
| 3 |
size 11780
|
models.txt
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
|
|
| 1 |
google/siglip2-so400m-patch16-naflex
|
| 2 |
openai/clip-vit-large-patch14
|
| 3 |
-
imageomics/bioclip
|
|
|
|
|
|
| 1 |
+
facebook/PE-Core-L14-336
|
| 2 |
google/siglip2-so400m-patch16-naflex
|
| 3 |
openai/clip-vit-large-patch14
|
| 4 |
+
imageomics/bioclip-2
|
| 5 |
+
laion/CLIP-ViT-L-14-laion2B-s32B-b82K
|
process_iwildcam_data.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import torch
|
| 3 |
+
import numpy as np
|
| 4 |
+
|
| 5 |
+
def load_annotations(annotation_file):
|
| 6 |
+
with open(annotation_file, 'r') as f:
|
| 7 |
+
data = json.load(f)
|
| 8 |
+
|
| 9 |
+
# Create mapping from image_id to category_id
|
| 10 |
+
image_to_category = {}
|
| 11 |
+
for annotation in data['annotations']:
|
| 12 |
+
image_to_category[annotation['image_id']] = annotation['category_id']
|
| 13 |
+
|
| 14 |
+
# Get image order and their corresponding labels
|
| 15 |
+
images = []
|
| 16 |
+
labels = []
|
| 17 |
+
for image in data['images']:
|
| 18 |
+
image_id = image['id']
|
| 19 |
+
file_name = image['file_name']
|
| 20 |
+
if image_id in image_to_category:
|
| 21 |
+
images.append(file_name)
|
| 22 |
+
labels.append(image_to_category[image_id])
|
| 23 |
+
|
| 24 |
+
return images, labels
|
| 25 |
+
|
| 26 |
+
def load_model_predictions(model_files):
|
| 27 |
+
models = []
|
| 28 |
+
all_predictions = {}
|
| 29 |
+
class_names = None
|
| 30 |
+
|
| 31 |
+
for model_file in model_files:
|
| 32 |
+
with open(model_file, 'r') as f:
|
| 33 |
+
data = json.load(f)
|
| 34 |
+
|
| 35 |
+
model_name = data['model']
|
| 36 |
+
models.append(model_name)
|
| 37 |
+
|
| 38 |
+
if class_names is None:
|
| 39 |
+
class_names = data['class_names']
|
| 40 |
+
|
| 41 |
+
# Store predictions for each image
|
| 42 |
+
for image_name, predictions in data['results'].items():
|
| 43 |
+
if image_name not in all_predictions:
|
| 44 |
+
all_predictions[image_name] = {}
|
| 45 |
+
all_predictions[image_name][model_name] = predictions
|
| 46 |
+
|
| 47 |
+
return models, all_predictions, class_names
|
| 48 |
+
|
| 49 |
+
def create_tensors():
|
| 50 |
+
# Load annotations
|
| 51 |
+
images, labels = load_annotations('iwildcam_demo_annotations.json')
|
| 52 |
+
|
| 53 |
+
# Load model predictions
|
| 54 |
+
model_files = [
|
| 55 |
+
'zeroshot_results_facebook_PE_Core_L14_336.json',
|
| 56 |
+
'zeroshot_results_google_siglip2_so400m_patch16_naflex.json',
|
| 57 |
+
'zeroshot_results_openai_clip_vit_large_patch14.json',
|
| 58 |
+
'zeroshot_results_imageomics_bioclip_2.json',
|
| 59 |
+
'zeroshot_results_laion_CLIP_ViT_L_14_laion2B_s32B_b82K.json',
|
| 60 |
+
]
|
| 61 |
+
models, all_predictions, class_names = load_model_predictions(model_files)
|
| 62 |
+
|
| 63 |
+
# Create class to index mapping (0-indexed)
|
| 64 |
+
class_to_idx = {class_name: idx for idx, class_name in enumerate(class_names)}
|
| 65 |
+
|
| 66 |
+
# Filter images that have predictions from all models
|
| 67 |
+
valid_images = []
|
| 68 |
+
valid_labels = []
|
| 69 |
+
for i, image_name in enumerate(images):
|
| 70 |
+
if image_name in all_predictions and len(all_predictions[image_name]) == len(models):
|
| 71 |
+
valid_images.append(image_name)
|
| 72 |
+
valid_labels.append(labels[i])
|
| 73 |
+
|
| 74 |
+
print(f"Found {len(valid_images)} images with predictions from all {len(models)} models")
|
| 75 |
+
|
| 76 |
+
# Create prediction tensor: H x N x C
|
| 77 |
+
H = len(models) # number of models
|
| 78 |
+
N = len(valid_images) # number of images
|
| 79 |
+
C = len(class_names) # number of classes
|
| 80 |
+
|
| 81 |
+
prediction_tensor = torch.zeros(H, N, C)
|
| 82 |
+
|
| 83 |
+
for h, model_name in enumerate(models):
|
| 84 |
+
for n, image_name in enumerate(valid_images):
|
| 85 |
+
predictions = all_predictions[image_name][model_name]
|
| 86 |
+
for class_name, score in predictions.items():
|
| 87 |
+
c = class_to_idx[class_name]
|
| 88 |
+
prediction_tensor[h, n, c] = score
|
| 89 |
+
|
| 90 |
+
# Convert category_ids to 0-indexed labels based on class_names order
|
| 91 |
+
# Need to map from species names back to category IDs
|
| 92 |
+
from collections import OrderedDict
|
| 93 |
+
SPECIES_MAP = OrderedDict([
|
| 94 |
+
(24, "Jaguar"),
|
| 95 |
+
(10, "Ocelot"),
|
| 96 |
+
(6, "Mountain Lion"),
|
| 97 |
+
(101, "Common Eland"),
|
| 98 |
+
(102, "Waterbuck")
|
| 99 |
+
])
|
| 100 |
+
reverse_species_map = {v: k for k, v in SPECIES_MAP.items()}
|
| 101 |
+
|
| 102 |
+
# Create category_to_label mapping based on class_names order (not sorted category IDs)
|
| 103 |
+
category_order = [reverse_species_map[name] for name in class_names]
|
| 104 |
+
category_to_label = {cat: idx for idx, cat in enumerate(category_order)}
|
| 105 |
+
label_tensor = torch.tensor([category_to_label[cat] for cat in valid_labels])
|
| 106 |
+
|
| 107 |
+
# Save tensors
|
| 108 |
+
torch.save(prediction_tensor, 'iwildcam_demo.pt')
|
| 109 |
+
torch.save(label_tensor, 'iwildcam_demo_labels.pt')
|
| 110 |
+
|
| 111 |
+
# Save text files
|
| 112 |
+
with open('models.txt', 'w') as f:
|
| 113 |
+
for model in models:
|
| 114 |
+
f.write(f"{model}\n")
|
| 115 |
+
|
| 116 |
+
with open('images.txt', 'w') as f:
|
| 117 |
+
for image in valid_images:
|
| 118 |
+
f.write(f"{image}\n")
|
| 119 |
+
|
| 120 |
+
with open('classes.txt', 'w') as f:
|
| 121 |
+
for class_name in class_names:
|
| 122 |
+
f.write(f"{class_name}\n")
|
| 123 |
+
|
| 124 |
+
print(f"Saved prediction tensor of shape {prediction_tensor.shape} to iwildcam_demo.pt")
|
| 125 |
+
print(f"Saved label tensor of shape {label_tensor.shape} to iwildcam_demo_labels.pt")
|
| 126 |
+
print(f"Saved {len(models)} models to models.txt")
|
| 127 |
+
print(f"Saved {len(valid_images)} images to images.txt")
|
| 128 |
+
print(f"Saved {len(class_names)} classes to classes.txt")
|
| 129 |
+
|
| 130 |
+
return prediction_tensor, label_tensor, models, valid_images, class_names
|
| 131 |
+
|
| 132 |
+
if __name__ == "__main__":
|
| 133 |
+
create_tensors()
|