| split | images | independent groups | infected | healthy | groups (infected) | groups (healthy) |
|---|---|---|---|---|---|---|
| dev | 160.000 | 133.000 | 71.000 | 89.000 | 61.000 | 72.000 |
| test | 40.000 | 22.000 | 18.000 | 22.000 | 9.000 | 13.000 |
Grouping strategy: metadata_column (column: tree_id, trust: strong, 155 groups)
Test-set construction: group_stratified_random_holdout. Test groups were drawn at random from the same collection. Unseen trees, yes; unseen farm/camera/season, no. Performance on a genuinely new site will typically be lower.
| fold | n_images | n_groups | n_infected |
|---|---|---|---|
| 0 | 32.000 | 26.000 | 14.000 |
| 1 | 32.000 | 27.000 | 14.000 |
| 2 | 32.000 | 27.000 | 15.000 |
| 3 | 32.000 | 26.000 | 14.000 |
| 4 | 32.000 | 27.000 | 14.000 |
| exact duplicate groups | near-duplicate clusters | cross-class near duplicates | conflicting labels on identical files |
|---|---|---|---|
| 20.000 | 20.000 | 0.000 | 0.000 |
| field | single-class values | message |
|---|---|---|
| camera_id | 2.000 | 2 value(s) of 'camera_id' map to (almost) one class only. The model can learn the acquisition condition instead of the disease. |
| site_id | 2.000 | 2 value(s) of 'site_id' map to (almost) one class only. The model can learn the acquisition condition instead of the disease. |
| capture_date | 3.000 | 3 value(s) of 'capture_date' map to (almost) one class only. The model can learn the acquisition condition instead of the disease. |
| session_id | 8.000 | 8 value(s) of 'session_id' map to (almost) one class only. The model can learn the acquisition condition instead of the disease. |
| image_resolution | 3.000 | Some exact resolutions occur in one class only, which usually means the two classes came from different devices or sources. |
Contact sheets were written to reports/audit/20260909-172221: montage_healthy.png, montage_infected.png. Open them and check the labels by eye: no metric in this report can detect a systematically mislabelled class.
| fold | n_val | n_val_groups | best_epoch | roc_auc | pr_auc | sensitivity@0.5 | specificity@0.5 | device | seconds |
|---|---|---|---|---|---|---|---|---|---|
| 0.000 | 32.000 | 26.000 | 9.000 | 0.992 | 0.990 | 0.857 | 0.944 | mps | 213.700 |
| 1.000 | 32.000 | 27.000 | 6.000 | 1.000 | 1.000 | 0.857 | 1.000 | mps | 174.300 |
| 2.000 | 32.000 | 27.000 | 0.000 | 1.000 | 1.000 | 0.600 | 1.000 | mps | 162.600 |
| 3.000 | 32.000 | 26.000 | 1.000 | 1.000 | 1.000 | 0.929 | 1.000 | mps | 161.500 |
| 4.000 | 32.000 | 27.000 | 6.000 | 0.996 | 0.995 | 0.929 | 0.944 | mps | 174.500 |
| metric | mean | std | min | max |
|---|---|---|---|---|
| roc_auc | 0.998 | 0.004 | 0.992 | 1.000 |
| pr_auc | 0.997 | 0.004 | 0.990 | 1.000 |
| balanced_accuracy | 0.906 | 0.063 | 0.800 | 0.964 |
| sensitivity_infected | 0.834 | 0.136 | 0.600 | 0.929 |
| specificity_healthy | 0.978 | 0.030 | 0.944 | 1.000 |
| calibration method | fitted on | n calibration points | threshold | policy | target recall | selected on | review band |
|---|---|---|---|---|---|---|---|
| None | None | None | 0.588 | max_specificity_at_recall | 0.900 | dev_out_of_fold | 0.100 |
Evaluated once on 40 images from 22 independent groups at threshold 0.588 (fixed before this evaluation). Class counts: {'healthy': 22, 'infected': 18}.
| predicted healthy | predicted infected | |
|---|---|---|
| actual healthy | 22.000 | 0.000 |
| actual infected | 0.000 | 18.000 |
0 infected palm(s) were missed and 0 healthy palm(s) were flagged. 0 prediction(s) fell inside the review band and would be routed to a human.
Sensitivity and specificity do not change with prevalence; precision and NPV do. This dataset is roughly balanced, a real grove is not.
| field prevalence | precision (PPV) | NPV | false alarms per 100 healthy | missed per 100 infected |
|---|---|---|---|---|
| 1.0% | 1.000 | 1.000 | 0.000 | 0.000 |
| 5.0% | 1.000 | 1.000 | 0.000 | 0.000 |
| 10.0% | 1.000 | 1.000 | 0.000 | 0.000 |
| 25.0% | 1.000 | 1.000 | 0.000 | 0.000 |
| 50.0% | 1.000 | 1.000 | 0.000 | 0.000 |
| seed | split manifest | config fingerprint | model version | weights sha256 | device | platform | git commit |
|---|---|---|---|---|---|---|---|
| 1337.000 | 20260909-181039_seed1337_fb02ca6c3c098c5b | fb02ca6c3c098c5b | 0.1.0+efficientnet_b0.20260909185105 | 96a80aa9ddd7252a | mps | macOS-27.0-arm64-arm-64bit-Mach-O | not a git checkout |
| package | version |
|---|---|
| python | 3.13.0 |
| torch | 2.14.0 |
| torchvision | 0.29.0 |
| numpy | 2.5.3 |
| sklearn | 1.9.0 |
| PIL | 12.3.0 |
| scipy | 1.18.1 |
| pandas | 3.0.5 |
{
"project_name": "palm-screen",
"model_version": "0.1.0",
"data": {
"raw_dir": "data/raw",
"metadata_csv": "data/metadata.csv",
"classes": [
"healthy",
"infected"
],
"positive_class": "infected",
"group_key_priority": [
"tree_id",
"session_id",
"site_id"
],
"phash_pseudo_groups": true,
"phash_group_distance": 6,
"allow_image_level_split": true,
"holdout_column": null,
"holdout_values": []
},
"split": {
"test_fraction": 0.2,
"n_folds": 5,
"seed": 1337,
"manifest_dir": "artifacts/splits"
},
"train": {
"arch": "efficientnet_b0",
"image_size": 224,
"batch_size": 16,
"num_workers": 2,
"head_epochs": 6,
"finetune_epochs": 25,
"lr_head": 0.001,
"lr_backbone": 0.0001,
"weight_decay": 0.0001,
"label_smoothing": 0.0,
"unfreeze_last_n_blocks": 3,
"freeze_batchnorm": true,
"early_stopping_metric": "val_pr_auc",
"early_stopping_patience": 6,
"early_stopping_min_delta": 0.0001,
"grad_clip_norm": 1.0,
"use_class_weights": false,
"amp": false,
"seed": 1337,
"checkpoint_dir": "artifacts/checkpoints",
"augmentation": {
"random_resized_crop": true,
"crop_scale_min": 0.8,
"crop_scale_max": 1.0,
"rotation_degrees": 12,
"horizontal_flip": true,
"vertical_flip": false,
"brightness": 0.15,
"contrast": 0.15,
"saturation": 0.1,
"hue": 0.02,
"mixup": false,
"cutmix": false
}
},
"threshold": {
"policy": "max_specificity_at_recall",
"target_recall": 0.9,
"fixed_threshold": 0.5,
"review_band": 0.1
},
"calibration": {
"method": "platt"
},
"report": {
"output_dir": "reports",
"bootstrap_iterations": 2000,
"ece_bins": 10,
"prevalence_grid": [
0.01,
0.05,
0.1,
0.25,
0.5
]
}
}{
"manifest_version": "20260909-181039_seed1337_fb02ca6c3c098c5b",
"created_at": "20260909-181039",
"seed": 1337,
"config_fingerprint": "fb02ca6c3c098c5b",
"grouping": {
"strategy": "metadata_column",
"column": "tree_id",
"n_groups": 155,
"trust": "strong",
"warnings": []
},
"split_info": {
"strategy": "group_stratified_random_holdout",
"stats": {
"target_n": 40,
"actual_n": 40,
"n_infected": 18,
"n_healthy": 22,
"forced_groups_for_class_balance": [],
"actual_pos": 18,
"actual_pos_rate": 0.45,
"overall_pos_rate": 0.445
},
"note": "Test groups were drawn at random from the same collection. Unseen trees, yes; unseen farm/camera/season, no. Performance on a genuinely new site will typically be lower.",
"n_dev": 160,
"n_dev_infected": 71,
"n_dev_groups": 133,
"n_test": 40,
"n_test_infected": 18,
"n_test_groups": 22
},
"leakage_audit": {
"clean": true,
"group_overlap": [],
"sha256_overlap": [],
"near_duplicate_pairs_across_splits": [],
"fold_group_overlap": []
},
"counts": {
"total_images": 200,
"dev": {
"n_images": 160,
"n_groups": 133,
"by_class": {
"healthy": 89,
"infected": 71
},
"groups_by_class": {
"healthy": 72,
"infected": 61
},
"folds": {
"0": {
"n_images": 32,
"n_groups": 26,
"n_infected": 14
},
"1": {
"n_images": 32,
"n_groups": 27,
"n_infected": 14
},
"2": {
"n_images": 32,
"n_groups": 27,
"n_infected": 15
},
"3": {
"n_images": 32,
"n_groups": 26,
"n_infected": 14
},
"4": {
"n_images": 32,
"n_groups": 27,
"n_infected": 14
}
}
},
"test": {
"n_images": 40,
"n_groups": 22,
"by_class": {
"healthy": 22,
"infected": 18
},
"groups_by_class": {
"healthy": 13,
"infected": 9
}
}
},
"csv": "split_20260909-181039_seed1337_fb02ca6c3c098c5b.csv",
"provenance": {
"platform": "macOS-27.0-arm64-arm-64bit-Mach-O",
"machine": "arm64",
"processor": "arm",
"git_commit": null,
"packages": {
"python": "3.13.0",
"torch": "2.14.0",
"torchvision": "0.29.0",
"numpy": "2.5.3",
"sklearn": "1.9.0",
"PIL": "12.3.0",
"scipy": "1.18.1",
"pandas": "3.0.5"
},
"device": {
"device": "mps",
"reason": "Apple Silicon GPU (MPS) available",
"mps_available": true,
"mps_built": true
}
}
}