SawitGuard-GNN — Model Card
Trained weights for the SawitGuard-GNN oil palm risk-ranking pipeline (Datathon 2026, RISTEK Fasilkom UI, University Track Semifinal). Code: SawitGuard-GNN. Companion data: oil-palm-detection-data.
Licence note. The MIT tag above covers these weights. The datasets they were trained on carry their own upstream licences, one of which (Eg9PP, CC BY-SA 4.0) is share-alike — see the companion dataset card before redistributing anything derived from them.
Model description
SawitGuard-GNN is two independently-evaluated layers, deliberately not merged into one model:
- Layer 1 (UAV imagery → per-tree crown inventory)
layer1/yolo12_runs/*/weights/best.pt— YOLOv12n crown detector (ds_B, Roboflow UAV tiles)layer1/lightgbm_health/model.txt— LightGBM crown-health classifier (RGB colour/texture features)- ExG+Otsu crown-area segmentation is a parameter-free per-image algorithm (no trained weights)
layer1/anom_peru/— the same YOLO architecture trained on an independent Peru dataset, a separate cross-site evidence line (never merged with the ds_B weights)
- Layer 2 (per-tree contact graph → disease-spread forecast)
layer2/stgnn_final.pt— full model, 24 input features (self-history + genotype + neighbour state), trained on the Eg9PP 25-year field panellayer2/stgnn_v3_photo.pt— single-photo variant, 1 input feature (neighbour symptomatic status only) — this is the checkpoint the demo actually runs, since a single UAV photo cannot supply the other 23 columns (18 of 24 are simply unfillable from one flight: 4 need a time axis, 14 are genotype, invisible from imagery)
Intended use
A ground-inspection prioritization demonstrator: given one UAV photo (or a sample tile), rank detected trees by estimated near-term BSR risk so a plantation can direct manual inspection instead of sweeping a whole block. It is not a certified BSR/Ganoderma diagnostic, and its Layer 1 "Unhealthy" label is generic crown-health from crowdsourced UAV annotation, not field-verified disease status.
Out of scope: any automated treatment/removal decision, any claim about trees outside the training sites/sensors, any performance number quoted from a live demo session (the demo checkpoint was never itself evaluated on held-out data — see Evaluation below).
Training data
See the companion dataset card (datasets/dataset_card.md in the sibling oil-palm-detection-data
repo) for full provenance, licenses, and the tile-overlap deduplication procedure. In short:
Layer 1 detector + health classifier train on layer1_uav_crowns (Roboflow ds_B, CC BY 4.0);
Layer 1 Peru weights train on peru_palm_anomaly (CC BY 4.0); Layer 2 trains on
layer2_eg9pp_panel (Tisné et al. 2017, CC BY-SA 4.0).
Evaluation
All numbers below are leave-one-orthomosaic-out (Layer 1, 3 folds) or leave-one-parcel-out
(Layer 2, 2 folds) block cross-validation — never random splits, which leak on this data (tiles
overlap ~30×). Significance is judged by the project's paired() rule everywhere: report
mean±std across paired (fold, seed) units plus a sign-count, and call anything with |mean| < 1 std
INCONCLUSIVE.
Layer 1 — crown detection & health (ds_B, primary)
| Metric | Value | Note |
|---|---|---|
| Crown-centre F1 (primary metric) | 0.960 ± 0.024 | conf threshold 0.75, cross-fold selected |
| Precision / Recall | 0.950 ± 0.019 / 0.971 ± 0.030 | |
| Centre RMSE | 0.071 ± 0.011 × planting spacing | |
| mAP50 | 0.687 ± 0.071 | label-ceilinged — GT boxes are fixed-size stamps, not tight boxes; not directly comparable to detection literature |
| mAP50-95 | 0.425 ± 0.078 | |
| Crown-health PR-AUC | 0.182 ± 0.059 | LightGBM, random baseline ≈ 0.013 |
| Crown-health ROC-AUC | 0.861 | |
| Bridge degree, predicted vs. Layer 2 | 5.54 ± 0.12 vs. 5.74 | 3.5% apart — interface compatibility check, not a joined dataset |
Layer 1 — Peru transfer (anom_peru/, supporting, ⚠ 1 fold / 1 seed only)
mAP50 0.9495, mAP50-95 0.7032. Not comparable to the ds_B numbers above — no mean±std exists for this run, so the paired-significance rule doesn't apply. Reported as qualitative cross-site support only. Two caveats travel with it: the "full" control (crown+context) scores worse than crown alone (ROC-AUC 0.880 vs. 0.952 — surrounding pixels dilute signal, not add it), and the detector over-predicts by 36% (1.72 boxes/image vs. 1.267 ground-truth), so the high mAP50 does not mean tree counting is accurate.
Layer 2 — Eg9PP full model (stgnn_final.pt, 24 features, W=3, pooled AP)
| h (census horizon) | STRUCTURE (true − random contact map) | sign-count | verdict |
|---|---|---|---|
| 1 | +0.0044 ± 0.0046 | 30/40 | INCONCLUSIVE |
| 2 | +0.0098 ± 0.0060 | 37/40 | POS |
| 3 | +0.0151 ± 0.0081 | 39/40 | POS (paper-primary) |
| 4 | +0.0165 ± 0.0097 | 39/40 | POS |
Layer 2 — photo variant (stgnn_v3_photo.pt, 1 feature, W=1, within-census AP — the fair metric
for ranking inside one snapshot, and what the demo evaluates against)
| Metric | Value | Note |
|---|---|---|
| Within-census AP, correct graph | 0.1015 ± 0.0079 (6-column ablation) / 0.0916 ± 0.0081 (1-column, demo config) | |
| vs. full model (24-col) within-census AP | 0.0973 ± 0.0107 | photo variant is not weaker: +0.0042 ± 0.0035, 36/40 POS |
| STRUCTURE (true − random), within-census | +0.0296 ± 0.0057, 40/40 POS | 77% of the photo variant's skill comes specifically from the correct contact map |
| Lift over no-skill, clean input | 1.45× (1-column) | this is the demo's own configuration |
| Lift over no-skill, detector-noise input | 1.27× (0.0800 AP) | 59% of the signal survives substituting field status with detector output (recall 0.446, fpr 0.0094, measured on ds_B) |
| Within-family+parcel permutation null | 0/200 permutations reach the observed value | z=+6.04; 64% of the graph effect is spatial, 36% is family-relatedness contamination |
Rejected/negative results, kept deliberately (do not silently re-claim these as positive):
- SI(D) mechanistic head vs. plain STGNN: NEG at all 4 horizons (h=3: −0.029 ± 0.019, 4/40 positive)
- Block-level (parcel) aggregation vs. per-tree ranking: lift 1.24× < per-tree 1.61× — rejected, per-tree list is the correct operational output
- 2-hop diffusion: NEG
- Per-class (Unhealthy) confidence threshold tuning vs. reusing the localisation threshold 0.75: F1 0.370 vs. 0.406 — rejected, keep 0.75
⚠ Never cite from a live demo run. The demo checkpoint (stgnn_v3_photo.pt) is the trained
production model — it was fit on the full panel and was never itself scored on held-out data in
that exact single-photo serving configuration. All performance numbers above come from the
dedicated cross-validation scripts, not from the demo.
Known limitations
- fold1 of the Layer 1 3-fold run (
yolo12_runs/yolo12n_base_fold1_s42/) has noargs.yaml(confirmed missing) — the exact Ultralytics training config for that fold is not recorded, only its weights andresults.csv. Folds 0, 2, and the 1-fold verification run do have it. - Layer 1 "Unhealthy" is generic crowdsourced crown health, not field-verified BSR/Ganoderma — only the Layer 2 Eg9PP panel has field-verified disease labels.
- Layer 1's F1 0.960±0.024 is measured leave-one-orthomosaic-out within one plantation. Imagery from a different estate, sensor, or flight altitude is an untested fourth domain.
- Peru transfer numbers are 1 fold / 1 seed — do not compare to ds_B's 3-fold mean±std.
- The photo-variant graph effect is 36% contaminated by family relatedness (measured, not assumed, via permutation null) — the remaining 64% is genuine spatial signal.
- Detector-substitution cost is measured on ds_B's own detection error rates, not on Eg9PP's estate — the true end-to-end cost on a genuinely different site is not measured.
- Layer 1 and Layer 2 are never joined at the data level (different estates, no georeferencing, no join key); only graph-structure compatibility is verified (5.54±0.12 vs. 5.74 mean degree).
License
Code: MIT (see the submission repository's LICENSE). Weights inherit the license of the data they
were trained on for any redistribution of derived outputs — see the dataset card for per-dataset
terms (CC BY 4.0 / CC BY-SA 4.0).
Citation
If you build on the Layer 2 weights (trained on the Eg9PP panel), please also cite:
Tisné, S. et al. (2017). Identification of Ganoderma disease resistance loci using natural field infection of an oil palm multiparental population. G3: Genes, Genomes, Genetics, 7(6), 1683–1692. https://doi.org/10.1534/g3.117.041764