Work with LibreYOLO checkpoint metadata
Metadata is the model-loading source of truth (REVIEW.md axiom): a .pt is
a torch.save() dict whose top-level keys identify family, size, task,
classes, and input size, so loading never depends on filename parsing or
state-dict sniffing. The contract is docs/checkpoint_schema.md (v1.0
required keys, task-specific extras, training-checkpoint fields, export
runtime metadata). Read that doc for the schema; do not trust a from-memory
key list, including this skill's examples.
Inspect (first move for any load problem)
libreyolo metadata path=weights/LibreYOLO9t.pt # raw embedded metadata
libreyolo info model=LibreYOLO9t.pt # resolved family/size/task/nc/device
Python, when you need it programmatic:
from libreyolo.utils.serialization import load_untrusted_torch_file, validate_checkpoint_metadata
ckpt = load_untrusted_torch_file("file.pt") # weights_only-safe load for unknown files
validate_checkpoint_metadata(ckpt) # raises CheckpointMetadataError with specifics
Produce (converters, trainers, tools)
The helpers in libreyolo/utils/serialization.py are the only sanctioned
writer/reader; hand-assembled metadata dicts drift from the schema:
wrap_libreyolo_checkpoint(...): builds the v1.0 wrapper around a
state dict. Every weights/convert_*_weights.py script ends in this.
validate_checkpoint_metadata(...): strict check; call it in the
converter after writing and in tests.
normalize_checkpoint_names(...) / build_class_names(...): names-dict
hygiene (dict[int, str], keys 0..nc-1).
load_untrusted_torch_file vs load_trusted_torch_file: use the
untrusted loader for anything a user hands you.
Two checkpoint shapes, one schema core:
- Lean/published: state dict + required metadata only. What HF repos
ship. No optimizer, epoch, config, or EMA-resume keys.
- Training: adds
epoch, optimizer, config, EMA fields
(is_ema_weights declares whether model is the EMA weights). best.pt
and last.pt are these. Strip to lean before publishing
(libreyolo-upload-hf-model validates this).
Debug a load failure (the decision tree)
- "Foreign/unrecognized checkpoint": it is an upstream file, not a
LibreYOLO one. Route: the family's
weights/convert_*_weights.py, or the
runtime auto-converter (libreyolo/models/autoconvert.py + per-family
recognizers) which handles known upstream layouts transparently. If a
known upstream format is rejected, the recognizer is the bug.
- Cross-family or cross-task rejection ("checkpoint is family X, model
is Y"): working as designed; these must fail loudly. Never "fix" by
loosening the check; load with the right class/task instead.
- Metadata invalid (missing keys, bad
names range, wrong
schema_version): the producer is at fault; fix the converter/trainer
and regenerate, do not hand-patch the file (a hand-patched checkpoint in
the wild is a provenance mystery later).
- Legacy LibreYOLO checkpoint (pre-v1.0, loads with a warning): the
compatibility path works but is legacy; recommend re-converting. State-
dict sniffing exists only on this path and must not spread to new code.
- Class-count surprises (nc=90 vs 80 RF-DETR-style): read the
"Legacy And Foreign Weights" section of the schema doc before touching
anything; the COCO-normalization rules there are subtle, deliberate, and
test-covered.
Change the contract (rarely, and never quietly)
Adding/removing/reinterpreting a metadata key is a contract change.
CONTRIBUTING.md hard-requires: update docs/checkpoint_schema.md and
the helpers in libreyolo/utils/serialization.py in the same PR. Also:
- New required keys break every existing published weight; prefer optional
keys with reader defaults (the
crop_pct/interpolation classify keys
are the pattern).
- Exported-runtime metadata (ONNX props etc.) is a parallel surface with
its own section in the schema doc; exporter-writes and backend-reads must
change together (
libreyolo-export-formats).
- Task-specific extras (pose keypoint fields, restore
degradation) belong
in the task's schema section, defined when the task lands
(libreyolo-add-task step 9).
- The unit tests around serialization and per-family load/reject
(
tests/unit/test_serialization*.py, test_*_can_load*-style) are the
gate; extend them with the change.
Related
docs/checkpoint_schema.md: the contract (always read it, it moves).
skills/libreyolo-port-model/: converter templates that emit v1.0.
skills/libreyolo-upload-hf-model/: pre-upload validation of the file.
skills/libreyolo-export-formats/: the runtime-metadata twin surface.
1---2name: libreyolo-checkpoint-metadata3description: Inspect, validate, produce, and debug LibreYOLO .pt checkpoint metadata. Use when a checkpoint won't load ("not a LibreYOLO checkpoint", wrong family/task/class-count errors), when writing or reviewing a conversion script, when deciding what a converter must emit, when preparing weights for HF upload, or when a schema change is proposed. Covers the inspection commands, the serialization helpers that are the only sanctioned writer/reader, the lean-vs-training checkpoint distinction, the legacy/foreign-weights paths and auto-conversion, and the contract-change protocol. The schema itself lives in docs/checkpoint_schema.md; this skill is how to work with it.4---56# Work with LibreYOLO checkpoint metadata78Metadata is the model-loading source of truth (REVIEW.md axiom): a `.pt` is9a `torch.save()` dict whose top-level keys identify family, size, task,10classes, and input size, so loading never depends on filename parsing or11state-dict sniffing. The contract is `docs/checkpoint_schema.md` (v1.012required keys, task-specific extras, training-checkpoint fields, export13runtime metadata). **Read that doc for the schema; do not trust a from-memory14key list, including this skill's examples.**1516## Inspect (first move for any load problem)1718```bash19libreyolo metadata path=weights/LibreYOLO9t.pt # raw embedded metadata20libreyolo info model=LibreYOLO9t.pt # resolved family/size/task/nc/device21```2223Python, when you need it programmatic:2425```python26from libreyolo.utils.serialization import load_untrusted_torch_file, validate_checkpoint_metadata27ckpt = load_untrusted_torch_file("file.pt") # weights_only-safe load for unknown files28validate_checkpoint_metadata(ckpt) # raises CheckpointMetadataError with specifics29```3031## Produce (converters, trainers, tools)3233The helpers in `libreyolo/utils/serialization.py` are the only sanctioned34writer/reader; hand-assembled metadata dicts drift from the schema:3536- `wrap_libreyolo_checkpoint(...)`: builds the v1.0 wrapper around a37 state dict. Every `weights/convert_*_weights.py` script ends in this.38- `validate_checkpoint_metadata(...)`: strict check; call it in the39 converter *after* writing and in tests.40- `normalize_checkpoint_names(...)` / `build_class_names(...)`: names-dict41 hygiene (`dict[int, str]`, keys `0..nc-1`).42- `load_untrusted_torch_file` vs `load_trusted_torch_file`: use the43 untrusted loader for anything a user hands you.4445Two checkpoint shapes, one schema core:4647- **Lean/published**: state dict + required metadata only. What HF repos48 ship. No optimizer, epoch, config, or EMA-resume keys.49- **Training**: adds `epoch`, `optimizer`, `config`, EMA fields50 (`is_ema_weights` declares whether `model` is the EMA weights). `best.pt`51 and `last.pt` are these. Strip to lean before publishing52 (`libreyolo-upload-hf-model` validates this).5354## Debug a load failure (the decision tree)55561. **"Foreign/unrecognized checkpoint"**: it is an upstream file, not a57 LibreYOLO one. Route: the family's `weights/convert_*_weights.py`, or the58 runtime auto-converter (`libreyolo/models/autoconvert.py` + per-family59 recognizers) which handles known upstream layouts transparently. If a60 known upstream format is rejected, the recognizer is the bug.612. **Cross-family or cross-task rejection** ("checkpoint is family X, model62 is Y"): working as designed; these must fail loudly. Never "fix" by63 loosening the check; load with the right class/task instead.643. **Metadata invalid** (missing keys, bad `names` range, wrong65 `schema_version`): the producer is at fault; fix the converter/trainer66 and regenerate, do not hand-patch the file (a hand-patched checkpoint in67 the wild is a provenance mystery later).684. **Legacy LibreYOLO checkpoint** (pre-v1.0, loads with a warning): the69 compatibility path works but is legacy; recommend re-converting. State-70 dict sniffing exists only on this path and must not spread to new code.715. **Class-count surprises** (nc=90 vs 80 RF-DETR-style): read the72 "Legacy And Foreign Weights" section of the schema doc before touching73 anything; the COCO-normalization rules there are subtle, deliberate, and74 test-covered.7576## Change the contract (rarely, and never quietly)7778Adding/removing/reinterpreting a metadata key is a contract change.79CONTRIBUTING.md hard-requires: update `docs/checkpoint_schema.md` **and**80the helpers in `libreyolo/utils/serialization.py` in the same PR. Also:8182- New required keys break every existing published weight; prefer optional83 keys with reader defaults (the `crop_pct`/`interpolation` classify keys84 are the pattern).85- Exported-runtime metadata (ONNX props etc.) is a parallel surface with86 its own section in the schema doc; exporter-writes and backend-reads must87 change together (`libreyolo-export-formats`).88- Task-specific extras (pose keypoint fields, restore `degradation`) belong89 in the task's schema section, defined when the task lands90 (`libreyolo-add-task` step 9).91- The unit tests around serialization and per-family load/reject92 (`tests/unit/test_serialization*.py`, `test_*_can_load*`-style) are the93 gate; extend them with the change.9495## Related9697- `docs/checkpoint_schema.md`: the contract (always read it, it moves).98- `skills/libreyolo-port-model/`: converter templates that emit v1.0.99- `skills/libreyolo-upload-hf-model/`: pre-upload validation of the file.100- `skills/libreyolo-export-formats/`: the runtime-metadata twin surface.