Skip to content

Running a County

Jakub Ziolkowski edited this page Oct 14, 2026 · 13 revisions

Running a County

The standing order for producing Year Built predictions for one county. Scope is one county — all of its polygon parts — per run, which is what the runner and the tray task are built for.

This page is the operator entry point. Using-the-ConsoleApp is the reference for every option and exit code; this page says what to run, in what order, and how to know it worked.

Important

The detections and scoring passes are now a single run. They used to be two, and the second one rebuilt its building list from the results file on disk rather than from the detection columns already stored — so a scratch directory cleaned between them skipped the county in silence. See §2. The split passes remain available for manual recovery (§6).

The tray application finds the runner in extensions\DiGi.GIS.YOLO.UI.ConsoleApp beside its own executable, an optional part of the deployment: a machine without it simply does not offer Predict year built. DiGi.GIS.YOLO.UI#7 still has to exercise that task on a deployed host for the first time.


1. Why there is an order at all

Dating is detector-only. The stored year is no longer predicted by a model: a building is dated by the first confident detection — the first year the detector saw it at confidence ≥ 0.5, falling back to the first year it was seen at all — and every prediction is stamped first-confident-detection@0.5 instead of a model file's SHA-256. The rule reads the detection group of building_data — the 90 columns this pipeline writes — and never the detections the run has just parsed.

Different runs fill different parts of that table, and a group that is wholly absent or wholly default is a run that has not happened — which nothing can tell apart from a sparse county. The retired regressor proved the point the expensive way: Słupski was scored for 44 492 buildings with 108 of 172 features at zero, and exited 0.

Query.UnpopulatedColumnNames is the guard. A county whose required group — the detection group, since the dating reads it alone — is wholly empty is refused (exit 4), with the run that fills it named in the message; a partially empty group is a warning. The other feature groups are still checked and warned about but no longer gate a run: the regressor that needed them is retired. It is structural rather than a percentage on purpose — roughly 40 of the 90 detection columns are legitimately near-constant per county, and which 40 varies, so a percentage bound would have to be loosened until it stopped detecting anything.

Feature group Columns Filled by
base 31 Update building data — General, Database, Occupancy
grid cell coverage 25 Update building data — General
radial ratio 8 Update building data — RadialRatios
population 18 Update building data — Statistical
detection 90 this pipeline, with UpdateDetections
total 172

2. The order

# Where What Cost
0 PowerShell CheckBuildingDataColumns.ps1 -CountyId <id> -Group seconds
1 Tray app → Update building data General, Database, Occupancy, RadialRatios, Statistical minutes to hours
2 Console app, or tray app → Predict year built one run: export imagery, run the detector, write the 90 detection columns, score, write year_built_data and the column hours — dominated by inference
3 Tray app → Update building data YearBuilt only — skip when step 2 ran with UpdatePredictedYearBuilt minutes

Steps 2 and 3 used to be two separate runs, to make the ordering explicit rather than incidental. They are now one, because the split carried a gap that the explicitness did not pay for: the scoring pass rebuilds its list of buildings from results.bbrf on disk, never from the detection columns it had just written to building_data. A scratch directory cleaned between the two runs therefore skipped the county reporting exactly what a county with no detections at all reports — and only one of those is recoverable. A single run has no between, and CleanScratchDirectory (default true) removes the scratch folder of each county that completes, so nothing can grow the same dependency again.

The hazard that motivated the split is handled directly instead: when the detection write half-fails, the run now skips scoring that county rather than scoring it against columns that are partly this run's and partly whatever preceded them. The county is named in FailedStepNames and re-run.

The split passes are still available for manual recovery — §6 — and their templates set CleanScratchDirectory: false, which is what makes them work at all.


3. Step 0 — is the county ready?

PowerShell -ExecutionPolicy Bypass -File "DiGi.Maintenance/Scripts/CheckBuildingDataColumns.ps1" -CountyId 104106 -Group

A prepared county (104106, m. Świnoujście) against one that has never been swept (73485, part of słupski):

  base                    31 /  31   fully filled:  29        base                    31 /  31   fully filled:  29
  grid cell coverage      25 /  25   fully filled:  25        grid cell coverage      25 /  25   fully filled:  25
  detection               90 /  90   fully filled:   0        detection               90 /  90   fully filled:   0
  population              18 /  18   fully filled:  16        population              18 /  18   fully filled:   0
  radial ratio             8 /   8   fully filled:   8        radial ratio             8 /   8   fully filled:   8
              104106 — ready                                              73485 — not ready

Warning

detection … fully filled: 0 is normal, including on a fully prepared county. The left column above is 104106 — 5 538 buildings scored and written — and it still reads 0. Detection columns are sparse by nature: a building is not detected in every year, so no per-year column is full for every row. Do not read that line as "no detections".

The readiness signal is the population row (16/18 versus 0/18) plus the pipeline's own refusal. The x / y figure on the left is existence in the catalogue, which is global — it is the same for every county and says nothing about this one.

Call this sequentially, never against several counties at once: concurrent coverage calls exhaust the connection pool and the host answers 500 on every terrain endpoint until the service is restarted.


4. Step 1 — Update building data

Tray application (DiGi.GIS.PostgreSQL.UI) → Update building data, scoped to the county, with these update types selected:

General · Database · Occupancy · RadialRatios · Statistical

This is 82 of the 172 features, and it is the step słupski never got. Statistical needs a populated unit and statistical_data_collection hierarchy; without one it writes nothing, and the run now reports that as an unfulfilled update type rather than as success.

Doing this for every county is DiGi.GIS.PostgreSQL#61.


5. Step 2 — the run

Tray application first. Predict year built asks for the run's scope and nothing else: the counties, the scratch directory, the interpreter, Max concurrent requests, and Clean scratch directory. Five controls.

Caution

A tray run has one shape, and it writes. OK means the full six-step flow — export, detector, write detections, score, write history, write column — over the deployed data of every county selected. There is no read-only tray run any more; the dialog says so above the county list, and the task names the counties back in a confirmation before it launches anything. For a dry run, or for either half of the split recovery passes, use the console app and a template below.

That is #8's decision applied: the six flags stay on the options class and the console app for hand-driven diagnostics, and "only the tray-driven default flow collapses them into one combined run." Eight checkboxes offered 256 combinations of which three were real — the three templates below — and the rest failed late, after the export and the inference had already been paid for.

Fourteen members are deliberately not in the dialog. A console run settles them in the options file below; a tray run has no options file to read, so it takes them from the defaults on YearBuiltPredictionPipelineOptions and writes the result into the scratch directory. Those defaults are therefore part of the tray contract rather than a convenience — see the note under ModelPath.

Not in the dialog Why
ExportImages, RunPrediction, Score, UpdateDetections, UpdateYearBuiltData, UpdatePredictedYearBuilt A tray run is all six. Written on by the prompt, so a run that stored nothing while reporting success is not reachable from the tray.
Resume Fixed on. With the cleanup on it only helps inside one run and on the retry of a county that failed — which keeps its scratch folder for exactly that reason — so off is never the better answer.
Years, Radiuses They decide which columns the feature projection asks for.
Confidence Passed to the detector as --conf; every detection it lets through is written into the ninety Prediction Confidence <year> columns the model reads back.
ModelPath Different weights produce different detections, through the same columns. A path typed in the tray application would also be resolved in the runner's directory, so the dialog could not say whether the file exists. It defaults to user files/YOLO/models/model.pt, which is where the deployment puts the weights — a tray run has no other way to name them, and the default is what makes one work at all.
WorkingDirectory Not a choice: the runner writes predict.py and utils.py into it, and left null it is the county's own scratch folder.
BatchSize Sizes the write requests only — it never reaches the detector, so it does nothing at all until a write step is on.
ReferenceBatchSize One sensible value: above the endpoint's 10 000 cap is refused server side, below it is the same work in more requests.

The first four are one hazard: each of them changes what the dating is handed, and a value that disagrees with the predictor's contract drops columns the rule reads — a Years window that cuts off a building's first detection dates it late, by an amount nothing measures. Two of the four are now caught before any county is read: the run checks Years and Radiuses against the range and radiuses the predictor states it reads — the first-detection rule states exactly what the options' defaults name (2008..2025, the default radiuses) — so a projection that omits a feature it reads is refused (exit code 1) and one that adds features is reported as surplus and ignored (DiGi.GIS.ML#6). Confidence and ModelPath are not — they change the values the detector writes, not the columns the rule reads, and the guard sees only column names, so a wrong threshold or wrong weights still scores without failing. With UpdateDetections on, those are stored into building_data and read back by every later dating run, so the mistake outlives the run that made it. Leave Years and Radiuses null, and leave Confidence and ModelPath at the values the deployed weights were trained against.

A tray run — tray application → Predict year built — is the same run started for you: the task forces the six step and write flags on (ExportImages, RunPrediction, Score, UpdateDetections, UpdateYearBuiltData, UpdatePredictedYearBuilt), fixes Resume: true, supplies the county identifiers and an absolute ScratchDirectory, and leaves Years, Radiuses, Confidence and ModelPath at their defaults; CleanScratchDirectory is the one flag it offers as a choice.

One invocation does all six steps: export the imagery, run the detector, write the detection columns, score, write the history and write the column. The committed default already has its final name — put your county identifiers in, then run from the runner's own folder, so relative paths resolve against it:

.\DiGi.GIS.YOLO.UI.ConsoleApp.exe "YearBuiltPredictionPipelineOptions.json"

The runner's own folder is not the same place on a deployed machine. In a workspace checkout it is DiGi.GIS.YOLO.UI\bin; on a deployed host the runner ships as an optional extension of the tray application and lives in <DiGi.GIS.PostgreSQL.UI>\extensions\DiGi.GIS.YOLO.UI.ConsoleApp. The weights, the .mlnet model and the runner's own GIS_WebAPI_Client.conf sit beside the executable in both.

Edits in the runner folder do not survive the next build — CopyFiles re-copies the committed default — so a permanent local configuration belongs in user files/YearBuiltPredictionPipelineOptions.json, which CopyUserFiles overwrites onto it. That overwrite reaches the deployed folder too, so the YearBuiltPredictionPipelineOptions.json sitting next to a deployed runner is whatever was in the build machine's user files, not the committed default — check its CountyIds before running it by name.

YearBuiltPredictionPipelineOptions.json — the combined template, and only that one — ships with all three write flags off, so copying it cannot write deployed data by accident. The two split templates in §6 do not: …Score.json ships UpdateYearBuiltData and UpdatePredictedYearBuilt both true, and …Detections.json ships UpdateDetections: true. Read the flags of whichever file you copied rather than trusting the folder.

The off-by-default combined template is not the configuration that populates a county — for a real run:

{
  "CountyIds": [ 104106 ],
  "ScratchDirectory": "scratch",
  "ModelPath": "user files/YOLO/models/model.pt",
  "PythonPath": null,
  "WorkingDirectory": null,
  "Confidence": 0.1,
  "Years": null,
  "Radiuses": null,
  "MaxConcurrentRequests": 8,
  "BatchSize": 5000,
  "ReferenceBatchSize": 10000,
  "Resume": true,
  "CleanScratchDirectory": true,
  "ExportImages": true,
  "RunPrediction": true,
  "Score": true,
  "UpdateDetections": true,
  "UpdateYearBuiltData": true,
  "UpdatePredictedYearBuilt": true
}

UpdateDetections writes the 90 detection columns before the dating step reads them back, inside the same loop — which is why one run works where two used to need an order between them.

Caution

A read-only pass is not the first pass over a new county. Dating reads the detection columns from building_data, so with UpdateDetections: false on a county that has never had them written, Score is refused with exit code 4 and the missing detection group named with its remedy. That is the guard working, not a misconfiguration. The first run over a county writes the detections; a dry run — every write flag false — is only meaningful afterwards.

What lands on disk while the run is working, per county identifier:

<ScratchDirectory>/<countyId>/images/<reference>_<year>.jpeg
<ScratchDirectory>/<countyId>/results.bbrf

With CleanScratchDirectory at its default this is gone once the county finishes — provided the county recorded no failed step. A county that failed keeps its folder, so correcting the cause and re-running costs seconds instead of repeating the export and the inference. A cancelled county is cleaned: stopping a run is a deliberate act, not a failure.

The scratch root is never removed: the tray application writes the options a run was started with into it, and that is the only record of what the run was asked to do.

Cost. Measured over słupski, both polygon parts: 417 034 images, 372 347 detections over 44 492 buildings, with the export taking 30.5 minutes. Inference dominates the rest at 11.9–12.8 ms/image on CUDA — roughly 85 minutes for that county. On CPU it is about fifteen times slower and not worth starting.

A successful run prints its tallies. This is 104106, the whole six-step flow in one run:

 Buildings: 5538      Images: 55366      Detections: 52675
 Predictions: 5538
 Building Data updated: 11076      Year Built Data updated: 5538

Note

Building Data updated is not a row count, and on a combined run it is twice the building count. It accumulates at two places — once when the detection columns are written and once when the predicted year built column is — so a run with both steps on counts every building twice. 2 × 5538 = 11 076. Only a run with one of the two on prints a figure equal to the buildings, which is what the split templates in §6 do. Compare it against the buildings, doubled, rather than against the buildings.

Warning

Resume no longer helps across runs for a county that succeeded. Its folder is gone, which is the point of §2. A county that failed keeps everything, so Resume still makes the retry cheap — this is what saves the read-only pass over a fresh county, which is refused by the feature coverage guard only after the export and the inference have already been paid for. A cancelled county is cleaned and does restart from the export, so set CleanScratchDirectory: false before a run you expect to interrupt — the tray prompt's Clean scratch directory checkbox is where.


6. Advanced — the split passes, for manual recovery

The two-pass templates are still committed and still work. Use them for a detections-only diagnostic pass, to score imagery a previous run left on disk, or to recover a run that failed after the expensive steps. Both set CleanScratchDirectory: false, which is what makes the hand-off between them possible at all.

.\DiGi.GIS.YOLO.UI.ConsoleApp.exe "YearBuiltPredictionPipelineOptions.Detections.json"
.\DiGi.GIS.YOLO.UI.ConsoleApp.exe "YearBuiltPredictionPipelineOptions.Score.json"

The detections pass runs ExportImages/RunPrediction/UpdateDetections with Score off, so it never reaches the dating step and needs no model of any kind. The scoring pass runs with ExportImages/RunPrediction off, reading results.bbrf straight off disk — minutes rather than hours.

Important

The scoring pass depends on the detections pass's scratch directory still being there. If it is not, the county is refused with exit code 4 naming the path that was looked for. It is no longer reported as a county with no detections, which is what made this gap invisible in the first place.

Re-running is cheap. With Resume: true and CleanScratchDirectory: false, a second detections pass skipped the whole export in 4.5 seconds, and the detector reproduced results.bbrf byte for byte — so the frozen detector is demonstrably deterministic over the same imagery.

Want a read-only dry run? Take the combined file and set UpdateDetections, UpdateYearBuiltData and UpdatePredictedYearBuilt to false. It scores and reports without storing anything — on a county whose detections are already stored, per the caution in §5.


7. Step 3 — re-project the column (usually skip)

Tray application → Update building data with YearBuilt only. This rebuilds the three year-built columns from the stored year_built_data history: Predicted year built (the most frequent prediction when a building carries several), User year built (the most frequent entry when several users gave one) and Calculated year built (the user entry when one exists, otherwise the predicted year).

It is redundant for the predicted column when step 2 ran with UpdatePredictedYearBuilt: true, which writes that column from the same merged data. It is the path that (re)projects all three — use it after a history correction, after a user entry, or for a county predicted before the columns existed.


8. Verifying the run

Through api.digiproject.uk, sequentially, never against a local .conf — a .conf reaches a partial development database.

Check How
The columns exist GET gis/buildingdata/columns — Predicted year built, User year built and Calculated year built are all in the catalogue
Features filled CheckBuildingDataColumns.ps1 -CountyId <id> -Group again; the population group full and the detection group no longer wholly default
History written GET gis/yearbuiltdata/itemsbyreferences — each reference carries a PredictedYearBuilt stamped with the run timestamp
Predictor identity the run log carries Model SHA-256: 2dd833e4… for the weights and Year built predictor: first-confident-detection@0.5 for the dating rule
No stranded rows reference count in equals stored object count out, none duplicated
Labels preserved references that carried a UserYearBuilt still carry it — the run adds to the history, it does not replace it
Calculated follows the rule where a building has both, Calculated year built equals its User year built; otherwise its Predicted year built
Idempotent run step 2 again: same object count, two run stamps, labels intact, column overwritten rather than duplicated

One stamp per run: PredictedYearBuilt.Source is Prediction_{ticks} and the stored datum keys on it, so a re-run adds one history entry per county rather than one per building.


9. Exit codes

Code Meaning First thing to check
0 Succeeded —
1 Configuration Options file found? CountyIds positive? ScratchDirectory non-empty? Identifiers, not codes? Or a Years/Radiuses projection narrower than the predictor's contract (feature-contract refusal)?
2 Environment preflight Python 3.9+ with ultralytics and torch, or the detector weights ModelPath resolves to. Both are checked before a single image is exported; the dating rule needs no model file
3 Authorization GIS_WebAPI_Client.conf beside the runner — see §10
4 Pipeline failure Read the [NOTE] lines: an unresolvable county, or a refused feature group naming the run that fills it
5 Cancelled Ctrl+C. Written rows stay committed. Cancelling is not a failure, so the county in progress is still cleaned unless CleanScratchDirectory: false

Anything other than 0 means no run finished. The tallies of a cancelled or killed run are not a record of what was stored.


10. Traps

  1. County identifiers, never four-character codes. 2212 is a code; słupski is the two rows 73482 and 73485. Eighteen counties have disconnected territory and BDOT10k stores each part as its own row, so a multi-part county needs every part listed in CountyIds. Query.UnknownCountyIds refuses a code up front and prints the identifiers it maps to — before this guard existed, a run scoped to 2212 exported nothing, wrote nothing and exited 0.
  2. Leave Years and Radiuses null. They decide which columns the projection asks for. The run now checks them against the range and radiuses the predictor states it reads — the first-detection rule states the same defaults (2008..2025, the default radiuses) — and refuses a narrower projection before it reads a county (exit code 1, naming the missing columns); a wider one runs with the surplus reported — DiGi.GIS.ML#6. Years is a range object ({ "Min": 2008, "Max": 2025 }), not a list.
  3. There are two GIS_WebAPI_Client.conf files. The tray application authorizes with its own; the runner authorizes with the one beside its own executable. An exit 3 from a tray-started run is almost always the runner's file, not the one the tray app uses.
  4. Host flags gate every write. AllowUpdateBuildingData and AllowUpdateYearBuiltData are read with no default, so an absent flag is false and every write returns 401 however valid the key is.
  5. Misspelt options are dropped silently. Deserialization ignores unknown properties, so a typo means the default, not an error. Check casing against YearBuiltPredictionPipelineOptions.json.
  6. Only detected buildings are dated. A building the detector never fired on has no detection series — the rule's only input — so it is skipped rather than dated on a blank vector, and its stored Predicted year built is cleared rather than left stale (#28): a building this run exported and the detector never fired on loses the year an earlier run stored for it, while its user entry is never touched. A building with no stored imagery was never exported, so it is neither dated nor cleared. The pipeline therefore predicts for fewer buildings than the deprecated file-based workflow, which scored every row of its table.
  7. Started from the tray app, paths behave differently — and a relative scratch path lands inside the deployment. The two processes do not share a working directory, so the tray task makes ScratchDirectory and WorkingDirectory absolute before handing them over, and writes the options it used into the scratch directory as YearBuiltPredictionPipelineOptions.json. It resolves them against the tray application's own folder, which on a deployed host is the deployment directory itself — so a bare scratch exports tens of thousands of images into the installed application, and into whatever backs it up or syncs it. Type an absolute path on a local disk. ModelPath is deliberately left relative and is resolved against the runner instead, so the weights are found beside it in both a checkout and a deployment.
  8. CleanScratchDirectory defaults to true, and it fires on cancellation but not on failure. A county that completes has its scratch folder removed — that is what closes the pass 1 / pass 2 gap. A county that failed keeps everything, so the retry is cheap. A cancelled county is cleaned, because stopping a run is a deliberate act rather than a failure. Set it to false for anything you intend to resume or to score in a second pass — the split templates already do.
  9. Stopping the tray task kills the run. The whole process tree goes, detector included, so a batch being written may be half written. Every step is idempotent and a stopped run is re-runnable, but its tallies are not a record of what was stored.
  10. On a machine that also holds a checkout, two tray applications carry the same icon. DiGi.GIS.PostgreSQL.UI.Application.exe runs from the deployment and from DiGi.GIS.PostgreSQL.UI\bin, both find a runner beside themselves under extensions\, and neither says which one you opened. Confirm it before concluding anything about a deployment: the log the run writes is under the folder the application was started from, and Get-Process DiGi.GIS.PostgreSQL.UI.Application | Select-Object Path settles it. A run started from bin also holds its assemblies open against the next build.

11. What a good result does and does not mean

The deployed identity, shipped on 2026-10-08:

Identity
Detector model.pt: train9_fresh, YOLO26x, ultralytics 8.4.165 SHA-256 2dd833e438ffcb1352ae7675063c9f0c97ca1c39ce6410a0bfab96e8b3cbdc9a
Dating rule, stamped on every stored prediction first-confident-detection@0.5

The detector still ships with OrtoBuildingDetectionModel.mlnet (2e120f495e830f0917cace33ad21c5c4c7bb0c4e8f1711a363682932b7c0b7fa) beside the runner, but the regressor is retired from production: scored on counties it was not trained on, every retrained regressor came out years too late, while the first-detection rule matched those counties' labels with MAE 0.15 (DiGi.GIS.ML#15). It stays as the evaluation baseline every new model must beat.

  • The parked regressor's training data: 25 008 labelled buildings over 217 counties, on train9_fresh detections.
  • Its held-out accuracy (through the deployed path): 0.338 MAE / 1.271 RMSE / R² 0.896 on the random 20 % holdout, against the first-detection rule's 0.358 / 1.797 / 0.791 on the same detections. On the 179 buildings no model has seen, the regressor scores 1.251 / 2.609 / 0.601.

The detections are still what the dating reads. The rule works off a building's stored detection columns, so before a county is dated its detections must have been written by train9_fresh. A run with RunPrediction, UpdateDetections and Score all on does that in one pass. A score-only run over a county whose stored detections are still train8's dates buildings from an old detector's series, and nothing refuses it: the coverage guard sees populated columns. The every-county rewrite is #23. The detection write replaces each scored building's detection columns (#21), so no train8 value survives on a building the new detector looked at. A building this run exported that the detector never fires on has its stored Predicted year built cleared — never left stale — and its user entry is untouched (#28); a building with no stored imagery was never exported and is never cleared.

Scoring one of the labelled counties measures fit and the write path, not generalisation. Most labelled buildings sit in five counties; the 179 clean buildings over 112 others are the only out-of-sample check, and they are few (DiGi.GIS.ML#4).

Narrowing a run to a list of buildings. ReferencesFilePath names a dataset_references.tsv manifest. Every step is then limited to its buildings within the named CountyIds, as for the detection rewrite before a regressor retrain (#22). A manifest that does not read refuses the run (exit 1). For such a training-side run, set an absolute ScratchDirectory in the training directory: a relative one lands in the runner's bin.


12. Training dataset, label check and detector evaluation

These are used when the detector is retrained (#12), not when a county is scored. All three modes read one options file, YOLOTrainingDatasetOptions.json, and only --dataset talks to the Web API. The full rules (labels, holdout, duplicates, Legacy, the manifest) are in the README.

Step Command Stop if
0 Run the Year Built building-data update for the counties (§4). The labels are the User year built column it writes. —
1 DiGi.GIS.YOLO.UI.ConsoleApp.exe --dataset options.json with "CountOnly": true (the template's default). Post the table on #12. the counts, the Legacy agreement table or the estimate look wrong
2 The same command with "CountOnly": false and an absolute, new or empty OutputDirectory. exit ≠ 0: read the [NOTE] lines. Re-running continues where it stopped.
3 --check-labels options.json with ModelPath set to the current detector. Look at the overlays in ReportsDirectory/label_check. the mean IoU is low: the boxes are shifted or scaled, so raise it on #12 before training
4 Train on OutputDirectory/conf.yaml, normally with --train or the tray task, which also run steps 2, 3 and 5. See Training-the-Detector for a fresh model or for continuing an existing one. —
5 --evaluate-detector options.json with WeightsPaths listing model.pt and every candidate. — the rows go to the gate on #12, with the subset sizes

Traps:

  1. OutputDirectory must be absolute. It is written into conf.yaml as the dataset path. A non-empty folder that is not a dataset this builder started is refused, and so is an existing dataset with Resume off.
  2. The legacy list is required. Copy DiGi.GIS.ML/Data/Data_2025.05.27.tsv into user files/. If the list is missing, the build is refused rather than reporting every held-out building as clean.
  3. LegacySource = unknown is not clean. When a county part's year_built_data cannot be read in full, its Test buildings are marked unknown and left out of the clean subset, and the run exits 4.
  4. The manifest is the journal. Do not edit dataset_references.tsv by hand. When a run is resumed, it deletes the image and label files of any reference the manifest does not name.
  5. The weights identity is logged. Every prediction run now logs the SHA-256 of its weights on its start line and in its [NOTE] output. Use it to tell which detector wrote a county's detections.

Related