Five state crash files to an on-device risk score.
A clustering algorithm tuned against a real percolation failure. A label leak caught and fixed with spatial cross-validation. A fusion rule that guarantees bad weather never makes the app less cautious.
What the scripts and Swift source in this repo actually do.
Clustering tuned against its own failure mode
The textbook DBSCAN eps (the "k-distance knee") was implemented, measured, and rejected. On Iowa's 85,366-point dataset it picks eps ≈ 2,593 m. At that radius DBSCAN percolates: crash points chain along the road network into one county-spanning mega-cluster, not usable corridors.
The percolation sweep — Iowa
v3 sweeps a density-adaptive eps grid and takes the largest value that doesn't percolate: no cluster over 5% of the state's points.
The result
| Textbook knee | — |
|---|---|
| Percolation-bounded | — |
At the knee's ~2,593 m, this curve sits between 34% and 65% of Iowa's points in a single cluster, a county rather than a corridor. 899 m keeps every cluster under the 5% ceiling.
The leak, and the fix
Two features (distance and severity of the nearest corridor) come from these clusters. The earlier pipeline built clusters from every positive row, including the ones later used to test the model: a crash helped create the corridor whose distance then predicted that same crash. Reported ROC AUC: 0.867.
Spatial block cross-validation
~22 km geographic blocks held out as whole units. A held-out block's corridor features rebuild from training data only.
The result
The exported model fits on out-of-fold corridor features, so the leak stays out of the model's learned weights, not just the metric.
| Leaky, reported | 0.867 |
|---|---|
| Leaky, reproduced | 0.853–0.860 |
| Honest — spatial CV | 0.8205 |
A lower honest number is the correct outcome of fixing a leak. Per-state breakdown on the Data page.
Export: the model card travels with the model
The trained classifier converts directly to CoreML with the same 15-feature schema the Swift side expects. No field mapping to keep in sync by hand. Honest metrics, the leaky comparison, and known limitations are written into the .mlpackage itself.
Staged, not live
Exports to WildlifeRiskModel_v3.mlpackage, never overwriting the shipping model. Swapping the live model is a separate, human-performed step.
Self-documenting
Version 3.0-real-multistate-leakage-fixed-ia-il-va-ma-tn. Honest metrics and states used are embedded in the model file.
Fully on-device
No network call at inference time: WildlifeRiskModel.prediction(input:) runs locally.
Weather, applied last
The model predicts lower collision probability for fog, rain, and snow than for clear weather. Not because bad weather is safer, but because most driving happens in clear conditions, so training data holds far more clear-weather crashes than bad-weather ones. Feeding it live weather made the score go down in fog.
One weather multiplier, applied last to the combined blend, not fed into the model directly.
Watch it run against real hotspots.
The simulation runs the same real hotspot data this pipeline produced.
Open the simulation →