Files
geointel/docs/accuracy-program/03-baseline-and-gaps.md
T

230 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# GeoIntel Accuracy Improvement Program — 03 Baseline en gaps
## 1. Phase-1-oordeel
De technische foundation draait: frontend, API, PostGIS, jobs, datasetopslag, een echte Ultralytics/PyTorch-adapter en NVIDIA CUDA-inference zijn aantoonbaar operationeel. De accuracy-/releasebaseline is echter rood. Er is geen bewijs voor nationale gebouwdetectiekwaliteit, geen complete menselijke corpusreview, geen onafhankelijke protected-testcyclus en geen sluitende modelhash-per-run-lineage. Zeven kritieke/hoge correctheidsproblemen zijn deterministisch gereproduceerd en de canonieke backend-releasegate is niet groen.
Daarom gelden op 1 augustus 2026 de volgende harde uitspraken:
- “GeoIntel kan één bestaande tile met het actieve model op de RTX 4080 SUPER verwerken” is bewezen.
- “GeoIntel is 100% getraind”, “nationaal gevalideerd”, “productie-accurate” of “release ready” is niet bewezen en mag niet worden geclaimd.
- De bestaande v56/v58/v62/v66-artefacten zijn diagnostische/trainingsevidence; geen daarvan vormt een geldige nationale promotiebundel.
- Phase 2 mag remediëren en nieuwe evidence opbouwen, maar mag de protected test pas opnieuw bevriezen nadat de leakage-, corpus- en lineageproblemen zijn opgelost.
## 2. Reproduceerbare softwarebaseline
Alle hieronder genoemde logs zijn retained onder `C:\Projects\geointel\artifacts\evidence\accuracy\P1`.
| Check | Exact commando | Uitkomst | Evidence |
|---|---|---|---|
| volledige backendtestset vanuit repo-root | `python -m pytest backend/tests -q -p no:cacheprovider -W error::DeprecationWarning --junitxml=artifacts/evidence/accuracy/P1/backend-full-suite.junit.xml` | **fail**: 1.180 passed, 17 failed, 70,63 s | `backend-full-suite.txt`, `backend-full-suite.junit.xml` |
| canonieke CI/backend-entrypoint | vanuit `backend`: `python -m pytest -W error::DeprecationWarning` | **collection fail**: 1.194 items verzameld plus importerror `scripts.render_operator_polygon_label_qa` | `backend-ci-entrypoint.txt` |
| Phase-1 collectortests | `python -m pytest tests/test_accuracy_phase1_baseline.py -q -p no:cacheprovider` | **pass**: 4/4 | `phase1-tooling-tests.txt` |
| frontend unit | vanuit `frontend`: `npm run test:unit` | **pass**: 16 files, 51 tests | `frontend-vitest-unit.txt` |
| generiek frontendtestcommando | `npm test -- --run` | **fail**: script `test` ontbreekt | `frontend-vitest.txt` |
| frontend typecheck | `npm run typecheck` | **pass** | `frontend-typecheck.txt` |
| frontend build | `npm run build` | **pass**: 1.896 modules; Vite-build voltooid | `frontend-build.txt` |
| frontend lint | `npm run lint` | **fail**: script `lint` ontbreekt | `frontend-lint.txt` |
| Python lintbaseline | `python -m ruff check backend scripts tests --output-format json` | **fail**: 112 findings | `repository-ruff-baseline.json`, `.txt` |
| Alembic head | vanuit `backend`: `python -m alembic heads` | **pass**: één head `202607260001` | `alembic-heads.txt` |
| volledige offline migratieketen | `python -m alembic upgrade head --sql` | **pass**: alle 11 migraties renderen tot commit | `alembic-offline-upgrade.sql` |
| API-contractaudit | `python scripts/audit_api_contracts.py` | **pass**: 147 routes; 10 expliciete non-envelope endpoints | `openapi-contract-audit.txt` |
De 112 Ruff-bevindingen zijn: E402 13, E701 2, E702 69, F401 23, F403 1, F811 2 en F841 2. De nieuw toegevoegde Phase-1-audittools waren in de afzonderlijke check Ruff-clean; de telling is repositorybreed.
De 17 backendtestfailures zijn bron-/contractasserties tegen frontend-, README- en deployteksten/implementatiedetails. Dat maakt ze niet automatisch onbelangrijk of “alleen stale”: de verwachte automatische model/theme-selectie is bijvoorbeeld doelbewust gewijzigd naar gebruikersselectie, terwijl tests nog het oude contract eisen. Test en actueel productcontract moeten expliciet worden gereconcilieerd. Tot dat gebeurt is de releasegate rood.
De CI-entrypointfout heeft een afzonderlijke oorzaak: vanuit `backend` resolveert `scripts` naar `backend/scripts`, waardoor de rootmodule `scripts/render_operator_polygon_label_qa.py` niet importeerbaar is. Een root-run met expliciet importpad kan de tests wel verzamelen, maar repareert de feitelijke CI-opdracht niet.
## 3. Wat de huidige readinessgate niet uitvoert
`scripts/run_readiness_check.sh` compileert veel Python en draait backendtests, Alembic head, frontend unit/typecheck/build. De volgende checks zijn daar slechts syntaxcontroles:
- `node --check` voor de twee frontend-E2E-scripts;
- `bash -n` voor live migration, deploy, upgrade/fresh-install, browserruntime, demo, real-data detection/QA, calibratie, training en cleanupflows.
De gate voert dus geen volledige browserjourney, live PostGIS-migratie, externe provideracquisitie, echte CUDA-modelinference, protected-test-evaluatie of AI-imagebuild uit. De GitHub-/Gitea-build installeert standaard geen AI-dependencies. Een groene toekomstige unit/readinessgate blijft daarom onvoldoende zonder afzonderlijke live-, data- en modelgates.
## 4. Golden QA/QC-baseline
`python scripts/run_golden_qa_benchmark.py --json` slaagde twee keer semantisch met vier fixture-scenario's:
| Scenario | Precision | Recall | F1 | Mean IoU | FP | FN |
|---|---:|---:|---:|---:|---:|---:|
| partial match | 0,5 | 0,5 | 0,5 | 0,833976834 | 1 | 1 |
| perfect match | 1 | 1 | 1 | 1 | 0 | 0 |
| no overlap | 0 | 0 | `null` | `null` | 1 | 1 |
| exact MultiPolygon | 1 | 1 | 1 | 1 | 0 | 0 |
De twee retained JSON-runs zijn niet byte-identiek: SHA-256 `ec96862b…` tegenover `4f900711…`. De semantische resultaten zijn gelijk; UUID4-gegenereerde project/dataset/quality-check-id's maken de output nondeterministisch. Voor een reproduceerbare benchmarkbundel moeten ids deterministisch zijn of vóór hashing worden genormaliseerd.
Het no-overlapscenario legt daarnaast een metriccontractgap bloot: precision en recall zijn 0, maar F1 is `null`. Dit kan wiskundig als undefined worden verdedigd, maar aggregators en releasegates moeten één expliciete semantiek hanteren. De Tower-database bevestigt bredere nullvariatie: 58/697 F1, 58/697 precision, 1/697 recall en 90/697 mean IoU zijn null.
De golden benchmark gebruikt kleine checked-in fixtures. Hij bewijst rekenkundige regressiestabiliteit, niet de nauwkeurigheid van het actieve model op Belgische luchtbeelden.
## 5. Runtime-, database- en GPU-baseline
### 5.1 GPU-smoke
De retained smoke gebruikte het actieve model:
- model `/app/models/geointel-building-yolov8s-smallbld-minpx3-img640-ft30.pt`;
- SHA-256 `a9088b8491dfae36694b53e9e9406cb4e3511d334a5712fa34f75078a47759c1`;
- één 512×512 RGB-tile in EPSG:31370, tile-SHA `134a9e86850c92c577c73bc6ee57a9df7d4c1c513ae6450263e800b6dd47b6ee`;
- manifest-SHA `6ab8a96bf2a1405e224932afb90255311a09bbeaed4e9a2fdcdf8b1bc2230abd`;
- PyTorch `2.11.0+cu128`, Ultralytics `8.4.99`, NVIDIA GeForce RTX 4080 SUPER;
- seed `20260801`, deterministische algoritmen, `imgsz=640`, confidence 0,5, `max_det=1000`;
- 17 raw `building`-detecties; inference 0,8837 s, totale model-load plus inference 1,3689 s.
Deze smoke is read-only en passeert. Hij toetst één tile en schrijft geen AnalysisRun/Detection/QA/Export. Hij levert daarom geen accuracy-, calibratie-, georeference-persistence-, schaal- of generalisatieclaim.
### 5.2 Databaseintegriteit
Positieve baseline:
- runtime Alembic-head `202607260001`;
- 3.377 datasets en 1.671 versies hebben checksums en source/provenance-metadata;
- 5.816 directe dataset/version/export-storagepaden gecontroleerd, 0 ontbrekend;
- 387 area- en 6.689.447 vectorfeaturegeometrieën: 0 leeg, 0 ongeldig, 0 wrong-SRID, 0 buiten EPSG:4326-domein in de gebonden query;
- 299.233 detection-confidences: 0 buiten `[0,1]`.
Negatieve baseline:
- 2.377/3.377 datasets zonder `observed_at` en 1.761/3.377 zonder `source_version`; eerst per bronfamilie classificeren, niet blind invullen;
- vier detectiegeometrieën hebben SRID 4326 maar numerieke Lambertcoördinaten rond X 193k/Y 206k;
- alle 1.146 modelruns hebben een lege modelversie; 1.143 bewaren een modelassethash, drie niet; alle 1.146 missen tile-manifest-SHA, runtime/hardware en seed;
- 0 segmentaties en 0 detection reviews;
- 135/4.460 jobs en 2/1.146 analysis runs hebben status failed;
- PostGIS 3.6.4 meldt dat core/topology procedures uit 3.4.3 een upgrade nodig hebben.
De storagecheck dekt alleen directe DB-referenties. De volledige recursieve storageaudit eindigde in een time-out en is geen pass; daarmee is niet bewezen dat alle niet-gerefereerde caches, trainingoutputs of orphan artifacts bekend zijn.
## 6. Deterministisch gereproduceerde productfouten
`forensic-reproductions.json` bevat zeven read-only, deterministische reproducties; alle zeven zijn opnieuw waargenomen.
| ID | Ernst | Geobserveerd | Waarom blokkerend |
|---|---|---|---|
| P1-COV-001 | critical | een kleine buildings-partitie wordt `fully_covered=true` doordat een roads-bbox in dezelfde bounded union zit | coverage- en beschikbaarheidsclaims kunnen inhoudelijk fout zijn |
| P1-CRS-001 | critical | 100 “meter” buffer levert bounds `[-95,-49,105,151]`, 200 graden lengtespan | units/CRS worden verwisseld; derived geometrie is onbruikbaar |
| P1-CRS-002 | critical | EPSG:31370-coördinaten `(150000,210000)` worden ongewijzigd met SRID 4326 opgebouwd | valide SRID-label maskeert verkeerde werkelijkheid |
| P1-AUTH-001 | critical | caller-controlled upload `source_name=grb` wordt `operational`/`authoritative` | gebruikersmetadata kan officiële bronautoriteit spoofen |
| P1-AI-001 | critical | area `Mol validation bypass` met geometrie in Noord-Amerika passeert modelscope | model draait buiten de gevalideerde geografie |
| P1-COV-002 | high | dezelfde Vlaamse geometrie werkt als naam `Flanders`, maar wordt `outside=true` na rename naar `Vlaanderen` | wettelijke scope hangt van een muteerbare displaynaam af |
| P1-API-001 | high | Area PATCH accepteert payload met geometry maar negeert die stil | contract en opgeslagen AOI lopen uiteen |
Aanvullende statisch bewezen risico's staan nog buiten deze zeven reproductions:
- clip/buffer/intersect roepen `_persist_derived_dataset` standaard aan met `persist_vector_features=False`; een `ready` derived artifact hoeft dus niet PostGIS-querybaar te zijn;
- meerdere services slikken secundaire fouten of vallen stil terug, onder meer projectdetailbootstrap en cached rasterpreview; fallback moet expliciete status/provenance krijgen;
- het tracked mirror `geointel/` bevat 1.153 bestanden, waarvan 68 van de rootversie verschillen; Docker sluit de mirror uit, lokale tools niet noodzakelijk.
## 7. Corpus-, split- en trainingbaseline
### 7.1 Inventaris is geen kwaliteit
Tower bevat 26 modelassets, 229 trainingscheckpoints (28.512.052.142 bytes), 424 JSON-trainingsrapporten en 36 operator-manifests. De aantallen tonen veel experimenten, niet dat de beste kandidaat is gevonden of geldig vrijgegeven.
### 7.2 V56 is de breedste aangetroffen corpusbasis, maar niet vrijgegeven
De retained v56-evidence toont:
- 180 AOI's over Vlaanderen, Wallonië en Brussel;
- immutable manifest, 0 exacte cross-split rasterhashduplicates en 0 cross-split raster-dataset-id-duplicates;
- dHash-screen over 4.833 cross-split paren: minimumafstand 17, geen paren op/onder 4;
- bbox-afstandscreen: minimum 95,7203 m en 24 cross-split paren onder 2 km;
- 31.452 inputfeatures; 30.662 geaccepteerd, 326 onder pixel-resolutie, 464 na de imageryperiode;
- automated corpusstatus `needs_human_review`; 0/180 gereviewd, alle 180 pending;
- 48 background candidates, maar slechts drie pure-empty background-test-AOI's: Vlaanderen 2, Wallonië 1, Brussel 0;
- de drie overige background-test-AOI's bevatten 2, 107 en 141 referentiefeatures en zijn moeilijke negatieven.
De exact-hash- en dHashscreen zijn positief maar begrensd. Ze bewijzen geen gebouw-instance-, gemeente-, vluchtstrook-, seizoen- of bronopname-onafhankelijkheid. De 95,7-meter train/testnabijheid en 24 paren onder 2 km vereisen expliciete imagery-/instance-audits voordat een protected split wordt geaccepteerd.
De automated tile-qualityaudit meldt 2.496 tiles, 60.229 geldige labels, 0 invalid/missing labels, 2.072 positieve en 424 negatieve tiles. Dat valideert syntax en enkele pixelregels, niet of daken correct, volledig, tijdsconform of contextueel representatief zijn. Contact sheets bestaan, maar menselijke acceptatiebeslissingen ontbreken.
### 7.3 V58/V62 halen geen regionale gate
V58 en v62 hebben alleen 144-tile `val`/calibratiebewijs met IoU 0,5 en 13 confidence-sweeps. Bij threshold 0,15 rapporteren beide aggregate F1 0,512905, precision 0,545647 en recall 0,483871, terwijl Vlaanderen 0 true positives, recall 0 en F1 0 heeft. Bij threshold 0,02 blijft Vlaanderen zwak: v58 F1 0,080402/recall 0,103380; v62 F1 0,067111/recall 0,275348. Dit is geen protected test en geen nationale releaseprestatie.
V62 `best.pt` bestaat, maar de hash `889ee5…` is niet de actieve productiemodelhash `a9088b…`. Er is geen geldige promotie aangetoond.
### 7.4 V66 is geen nationale opvolger
Het v66-manifest heeft slechts drie positieve Vlaamse `train`-AOI's in twee low-rise-contexten, zonder val/calibration/test/background en zonder Wallonië/Brussel. Het is een gerichte trial.
### 7.5 Protected-testleakage
De trainingloop opent na een geslaagde calibration zowel test als background. Als de kandidaat vervolgens faalt, geeft de loop de volledige assessment aan de failure-driven sampler. Die kiest `assessment.get("test") or assessment.get("calibration")`, gebruikt regionale testmetrics en background-failures en verandert daarmee repeats van trainingtiles voor de volgende iteratie. Protected tiles worden niet letterlijk opgenomen, maar hun uitkomsten sturen training. De huidige testset is dus voor die loop niet langer onafhankelijk en moet na de correctie worden vervangen of aantoonbaar nooit eerder ingezien zijn.
## 8. Claim-matrix
| Claim | Status | Maximaal verdedigbare formulering |
|---|---|---|
| platform draait technisch | ondersteund | gezonde Tower-container, echte PostGIS-data en uitvoerbare frontend/API |
| NVIDIA/PyTorch wordt gebruikt | ondersteund | één actieve YOLO-adaptercall draaide op RTX 4080 SUPER/CUDA 12.8 |
| volledige inferenceketen is correct | niet ondersteund | smoke sloeg persistence/georeference/QA/export over; vier historische geometrieën zijn corrupt |
| actief model is reproduceerbaar | gedeeltelijk | actief bestand heeft SHA; historische runs missen volledige manifest/runtime-lineage en drie runs missen ook de modelassethash |
| labels zijn correct | niet ondersteund | automated syntax ok, maar v56 menselijke review 0/180 |
| splits zijn onafhankelijk | niet ondersteund | exact/dHashscreen positief, maar nabijheid onbeslist en testuitkomsten sturen retraining |
| building accuracy voor Mol/Kempen | niet vastgesteld in Phase 1 | er is geen retained protected Mol/Kempen-releasebenchmark voor actieve hash |
| building accuracy voor heel België | weerlegd als actuele releaseclaim | v58/v62 calibration heeft bij bruikbare aggregate threshold Vlaanderen recall/F1 0 |
| pure-backgroundrobustheid per regio | niet ondersteund | slechts 3 pure-empty AOI's, geen in Brussel |
| productie-segmentatie | niet ondersteund | abstraction/fixture aanwezig, 0 runtime segmentaties |
| officiële bronnen zijn authentiek | niet ondersteund | source identity kan via uploadmetadata worden gespooft |
| databasegeometrieën zijn integraal | gedeeltelijk | areas/vectorfeatures schoon in gebonden query; 4 detecties buiten domein |
| release ready | nee | backend/CI/lint rood plus kritieke data-/GIS-/ML-blockers |
## 9. Geprioriteerde gaps
### P0 — vóór nieuwe modeltraining of productclaim
1. Fix en regressietest de zeven gereproduceerde contract-/CRS-/scope-/authorityfouten; maak coverage unions themaspecifiek, source authority server-attested en modelscope geometrisch/checksumgebonden.
2. Quarantaineer de vier buiten-domeindetecties, identificeer hun producerende code/image/model, herbereken of verwijder ze via een gecontroleerde migratie en laat detailtellingen de gate falen.
3. Maak vector- en detectie-CRS-transformaties expliciet; verbied SRID relabeling; voer metric buffers in een geschikte lokale CRS uit; persisteer derived features en runlineage atomisch.
4. Verwijder test/background uit failure-driven sampling en checkpointpromotie. Bevries daarna een nieuwe, ongeziene protected test met hashes en éénmalige-openingspolicy.
5. Voer menselijke review uit op alle 180 v56-AOI's met beslissingen, reviewer, timestamp, label-/beeldversie en reden; herbouw contact sheets wanneer bron of label verandert.
6. Voeg onafhankelijke AOI's en pure-empty backgrounds toe per regio/context, in het bijzonder Brussel; audit de 24 cross-split paren onder 2 km op imagery-, vluchtstrook- en instance-overlap.
7. Herzie image/label time deltas, vooral de 464 uitgesloten features en dichte PICC/UrbIS-zones; definieer regels voor onzekere/occluded/nieuwe/verdwenen gebouwen.
8. Koppel iedere training, inference-run, detection, QA en export aan model-SHA, datasetmanifest-SHA, codecommit, containerdigest, seed, parameters en dependency lock.
### P1 — vóór releasecandidate
1. Maak de canonieke backend-CI-entrypoint verzamelbaar en reconcilieer de 17 contracttests met de actuele, handmatige modelselectie-UX.
2. Voeg Ruff en frontend lint toe aan de readinessgate; werk de 112 bestaande bevindingen gecontroleerd weg.
3. Bouw en test de echte AI/CUDA-image in CI; pin PyTorch/torchvision/Ultralytics en leg image digest/SBOM vast; hef cu128/cu130-drift op.
4. Draai echte browser-E2E, live PostGIS upgrade/fresh-install, externe-provider- en full inference/persistence/QA/exportjourneys.
5. Definieer null-/zero-/undefinedsemantiek voor precision, recall, F1 en IoU; maak golden outputs byte-reproduceerbaar of canonicaliseer ids.
6. Classificeer de 2.377 missende observatietijden en 1.761 missende bronversies per broncontract en maak onverklaarde gevallen fail-closed.
7. Verwijder bronambiguïteit door de tracked `geointel/`-mirror gecontroleerd te migreren; niet in Phase 1 destructief opruimen.
## 10. Wanneer Phase 2 veilig kan trainen
Nieuwe GPU-training is pas zinvol nadat de P0-datacontracten, human review en splitpolicy zijn opgelost. Anders optimaliseert een nieuwe run opnieuw tegen mogelijk foutieve labels, onvoldoende negatives en een gecontamineerde testlus. De veilige volgorde is:
`fix contracten -> nieuwe regressietests -> corpusbeslissingen -> onafhankelijke splits -> frozen manifests/hashes -> train/val/calibration -> kandidaatselectie -> éénmalige protected test -> onafhankelijke menselijke foutreview -> promotiebundel -> shadow deploy`.
Een trainingsloop mag itereren op train/validation/calibration. Hij mag de protected test niet opnieuw in de loop voeren. “100%” wordt niet als numerieke gate gebruikt; release vereist vooraf vastgelegde, context- en regiogebonden thresholds met confidence intervals, failure budgets en expliciete abstention/unsupported-statussen.
## 11. Evidence-integriteit
| Retained bestand | Bytes | SHA-256 |
|---|---:|---|
| `backend-full-suite.txt` | 23.972 | `8521ed48b17b382752418750b3ea374831958fb063e83ef87048212e0fd5ea69` |
| `backend-ci-entrypoint.txt` | 1.601 | `0ac774ec19b4ff0a15142aab5f1db68c2592a230401d794ae7d040320e3ac0c0` |
| `frontend-vitest-unit.txt` | 2.774 | `6341bfa51ca3f4fe5ec7d4af7239c3c5e1a29e6bfe8bdfae85e824a2a6482ad0` |
| `frontend-typecheck.txt` | 163 | `3891c85c77b5ff50a1eb6d27a2a65d40c2c05423768734efd9d980f3784d68fa` |
| `frontend-build.txt` | 2.356 | `c3b8e10ef177ac1c2dc045bd710df1caeb46f8922a721291be431b304abbc079` |
| `frontend-lint.txt` | 418 | `1115b013515c753e2dfb73abdad9024aec7b4c2337d117c7e1181341fef15c7f` |
| `repository-ruff-baseline.json` | 59.359 | `de6617f030e49550e714c49b6e14bf291bf85016fd58086e9ca38b33a52252e9` |
| `alembic-heads.txt` | 116 | `da4521233c6718fc7a5865c53904e73685fbdce65a1449b19cd0dc2e40d037ed` |
| `alembic-offline-upgrade.sql` | 20.199 | `e8905b881890cf95885a7515e3d9dcf2a7a0a24c4edbc57e98a363edcb22dd0e` |
| `openapi-contract-audit.txt` | 212 | `5af3d8f00be3f57fc309bc198fa3995d1eae7270a5f210b3e94d1aeeb653119d` |
| `forensic-reproductions.json` | 3.533 | `f6349199a15ae789092d3d65c17a39a9b32ea3ed557571c0c228c4be3cf7235e` |
| `golden-qa-reproducibility.json` | 505 | `576e5667a989c34086db3a2bc57003115a8a61b14e9f49f5407ee380baf0829d` |
| `phase1-tooling-tests.txt` | 250 | `a9bf261e1a811ad3e8bfa8edc439a11f00b46bc157f9ab6fc970033a87f45748` |
| `tower-gpu-inference-smoke.json` | 5.675 | `692a9fa193d589123b042110d7e80755f0c6634854f3134c291fc6083adb7b77` |
| `tower-runtime-database-snapshot-detailed.json` | 14.946 | `744389b6c384a9fb3a9e16e56f1477f3b752e11a5b98e1fad7df47b4703a9ca1` |
| `tower-ml-data-lineage-snapshot.json` | 71.659 | `d80275e8198ce63366d2a2d44eb8fba1f27c29d85aaaaedb94991d8c56febfb6` |
Deze hashes zijn van de retained Phase-1-bestanden op het moment van documentgeneratie. Als evidence opnieuw wordt gegenereerd, moet een nieuwe evidence-manifestversie de nieuwe hash, producerende commandoregel, timestamp en reden bewaren; oude evidence wordt niet overschreven of als identiek voorgesteld.