skills/odl-pdf/references/eval-metrics.md
How to tell whether an extraction is good, which measure is weak, and what to do
about it — expressed as durable methodology. The metric definitions are stable;
the options you would reach for to improve a weak metric are named only as
capabilities here (find the current option in the installed --help — SKILL.md
"Source-of-truth rule"). No benchmark runner is bundled with this skill.
NID — Normalized Indel similarity (1 − normalized Indel distance).
Normalized text-sequence similarity between the extracted linear text and the
ground truth. ODL's benchmark uses it as its reading-order proxy — a two-column
page read left-to-right across both columns diverges from the correct sequence and
scores lower. But NID also drops on any text divergence (OCR errors, missing or
extra text), so a low score is not necessarily a reading-order problem: to
isolate reading order, inspect the ordered output directly. Range 0–1, higher is
better. Weakest signal on multi-column layouts, merged-cell tables, inline
footnotes, sidebars.
TEDS — Tree-Edit Distance Similarity. Table structure accuracy: structural similarity between the extracted table tree and ground truth via tree edit distance, normalized by tree size. Range 0–1, higher is better. Weak on borderless tables, merged/spanning cells, nested tables, and tables that are actually images.
MHS — Markdown Heading Similarity. Heading structure accuracy: how well the extracted heading hierarchy matches ground truth, penalizing both missing headings and wrong level assignments. Range 0–1, higher is better. Weak when headings are simulated with bold/large text (no semantic markup) or embedded in images.
Table Detection F1. Table region detection: harmonic mean of precision (detected regions that are real tables) and recall (real tables that were detected). Unlike TEDS it does not judge internal structure — only whether the region was found. Range 0–1, higher is better. Weak on dense text blocks that resemble tables, tables spanning page boundaries, very small tables.
Speed. Throughput in seconds per page; lower is better, not normalized to 0–1. Relative shape: local (no backend) fastest; per-page triage close to local for most pages, paying the backend round-trip only on triaged pages; whole-document backend routing slowest. Absolute figures depend on hardware and document set — measure on your own corpus.
Reporting note: an "Overall" is often the average of NID / TEDS / MHS; Table Detection F1 is reported per-document and not folded into that average. Hardcoded snapshot scores drift whenever extraction code or the document set changes, so they are intentionally not reproduced here.
Escalate least-invasive first, verify after each single change (SKILL.md "DIAGNOSE by symptom").
Low NID (reading order) — columns/sections interleaved wrong, paragraphs out of sequence, footnotes misplaced.
option-interactions.md §B.6).hybrid-guide.md).Low TEDS (table structure) — tables as plain text, merged cells wrong, borderless tables missed.
option-interactions.md §B.9).Low MHS (heading structure) — headings unrecognized or at the wrong level.
Low Table Detection F1 (table regions) — tables missed (low recall) or non-tables flagged (low precision).
format-guide.md).scripts/quick-eval.py gives a rough text-similarity score against a reference
file — it does not measure table structure, cell spans, reading order, or
detection precision/recall. Use it as a smoke check, not a substitute for the
metrics above.
# scripts/ resolves against this skill's directory, not your CWD
python scripts/quick-eval.py extracted.md ground-truth.md
python scripts/quick-eval.py extracted.md ground-truth.md --verbose # diff snippets
It compares text similarity (difflib, or rapidfuzz if available) and reports pass/fail against a configurable threshold. For full multi-metric benchmarking across a corpus, apply the definitions above to whatever benchmark harness your project maintains — none is bundled here.
Cross-references: SKILL.md "VERIFY", "DIAGNOSE by symptom";
option-interactions.md (§B.6 struct-tree no-op, §B.9 borderless cost);
hybrid-guide.md; format-guide.md; scripts/quick-eval.py.