Skip to content

feat(benchmarks): score OCR on olmOCR-Bench with its own tests - #457

Open
burntcookiedough wants to merge 15 commits into
StarTrail-org:mainfrom
burntcookiedough:feat/ocr-olmocr-benchmark
Open

burntcookiedough wants to merge 15 commits into
StarTrail-org:mainfrom
burntcookiedough:feat/ocr-olmocr-benchmark

Conversation

@burntcookiedough

@burntcookiedough burntcookiedough commented Nov 1, 2026 •

Copy link
Copy Markdown

What does this PR do?

Adds benchmarks/ocr_benchmark/, which scores LEANN's OCR path on olmOCR-Bench. Part of #158.

The benchmark was removed from #190 because it could not produce a valid score. This version follows the approach @ASuresh0524 outlined in that review:

  • It downloads the tests and PDFs with huggingface_hub, because the datasets-server API still returns HTTP 500 for this dataset.
  • It scores with olmOCR's own pass/fail tests rather than CER or WER, because the dataset has no full-page transcriptions.
  • It reports the score the way olmOCR's benchmark.py does: every PDF also gets olmOCR's baseline test, and the score is the average pass rate of the splits and the baseline group.
  • If OCR returns no text for a PDF, all of its tests count as failed, because empty output would otherwise pass every "absent" test. This is the one rule that is stricter than olmOCR's.

This PR builds on #456, which adds the tier argument it needs. Please review #456 first. Only the last five commits are new here.

Results

10 PDFs per split (--max-pdfs 10 --seed 0) on MinerU 4.0.12, Windows 11, and an RTX 3060 Laptop GPU (6 GB). This is a small sample, so it is a rough guide and not a published score. The math splits were not run, because they need a Playwright browser.

Group flash basic standard advanced
old_scans 16/49 (32.7%) 16/49 (32.7%) 21/49 (42.9%) 22/49 (44.9%)
multi_column 29/36 (80.6%) 30/36 (83.3%) 32/36 (88.9%) 30/36 (83.3%)
long_tiny_text 54/61 (88.5%) 53/61 (86.9%) 54/61 (88.5%) 57/61 (93.4%)
headers_footers 21/23 (91.3%) 21/23 (91.3%) 23/23 (100%) 23/23 (100%)
table_tests 1/47 (2.1%) 24/47 (51.1%) 36/47 (76.6%) 44/47 (93.6%)
baseline 46/49 (93.9%) 46/49 (93.9%) 48/49 (98.0%) 48/49 (98.0%)
Score 64.8% 73.2% 82.5% 85.5%
OCR time for 50 PDFs 340s 580s 645s 669s

I checked the scoring by running olmOCR's benchmark.py on the same OCR output. It gives the same score for standard and advanced. For flash and basic it gives about 1.5 points more (66.3% and 74.6%), because each returned no text for one headers_footers PDF, and olmOCR lets that empty output pass the PDF's "absent" tests.

advanced scored highest, mostly on tables, and took 4% longer than standard. standard stays the default, because it is MinerU's own default for parse(). flash writes tables as plain text instead of Markdown tables, so it fails almost every table test even though the cell text is there.

GPU memory use peaked at 3.5 GB with standard and advanced and at 1.6 GB with flash, against 1.2 GB when idle.

Related Issues

Part of #158.

Checklist

  • Tests pass (uv run pytest): the new tests pass, with and without MinerU installed. The full suite has the same failures as main on my laptop, as described in feat(apps): OCR scanned pages and images in document RAG #456.
  • Code formatted (ruff format and ruff check)
  • Pre-commit hooks pass (pre-commit run --all-files)

Without MinerU installed, `--use-ocr` returned empty text, the reader
dropped every PDF, and the run ended with "No documents found" and exit
code 0, so a missing dependency looked like an empty folder.

Raise ImportError with the install command instead. It has to be
ImportError: SimpleDirectoryReader logs and skips a file on any other
exception type.
Auto-detect only looked at the whole PDF, so a PDF with a normal text
layer and a few scanned pages lost those pages. It now finds image-only
pages and OCRs just those.

Also:
- add --ocr-tier, since basic is much faster than standard on CPU
- OCR .png, .jpg, and .jpeg files when --use-ocr is set
- say that the first OCR run downloads models
- skip files with no text and name them, instead of indexing empty chunks
@ASuresh0524

Copy link
Copy Markdown
Collaborator

This answers the three objections that got the benchmark stripped from #190, and — the part that matters — it actually produces a measurement this time, which the previous version could not.

Confirmed against the earlier review:

  • huggingface_hub instead of the datasets-server API, which still 500s for this dataset.
  • olmOCR's own pass/fail tests instead of CER/WER. This was the real blocker before: scoring a correct extraction against an assertion snippet gave CER 11.544, a meaningless number, because the dataset has no full-page transcriptions.
  • Empty OCR output counts all of a PDF's tests as failed, so a reader that returns "" cannot pass every "absent" test. That is the right default and worth the explicit note in the README.

18/18 tests pass locally (5 benchmark + 13 from the OCR change).

Merge ordering: this branch strictly contains #456 — I confirmed with git merge-base --is-ancestor — and adds exactly 2 commits on top. So #456 first, then this, and this PR collapses to just the benchmark. Merging this one alone would land #456's app changes under this PR's title.

Thanks for reporting the results as a rough guide rather than a published score, and for stating the hardware and sample size. 10 PDFs per split is small, and saying so is the right call.

LGTM, after #456.

An image has no text layer, so auto-detect now sends .png, .jpg and
.jpeg files to OCR as well. Before, they loaded as empty documents and
were skipped unless --use-ocr was set.

Print the MinerU install hint only when a skipped file is a PDF or an
image, since OCR cannot help an empty text file.
MinerU's Markdown embeds each figure as a base64 data URI. On the
olmOCR-Bench sample, these made up 92% of all OCR output, which would
fill the index with noise. Remove them after rendering. Captions and
recognized text are separate blocks, so no text is lost.
The OCR reader returned no document for a file without text, so the
file vanished without a message. Return the empty document instead, so
drop_empty_documents names it with the other skipped files.
@burntcookiedough
burntcookiedough force-pushed the feat/ocr-olmocr-benchmark branch from 30c99b4 to 51f9ad6 Compare November 3, 2026 22:18
@burntcookiedough

Copy link
Copy Markdown
Author

Heads-up, since the numbers changed after your review: the script now scores the way olmOCR's benchmark.py does (51f9ad6). It adds olmOCR's baseline test to every PDF and averages the pass rates of the splits and the baseline group, instead of pooling all tests. That raised every tier by 6 to 9 points.

To check it, I ran olmOCR's benchmark.py on the same OCR output. It gives the same score for standard (82.5%) and advanced (85.5%). flash and basic differ by about 1.5 points, only because of the empty-output rule you agreed with.

The table in the description now covers all four tiers. #456 also gained a fix that removes the base64 images MinerU embeds in its Markdown (22f68d6). They were 92% of the OCR text, and no score changed.

Mixed PDFs put each OCR page back at its own position instead of
appending all OCR text after the text layer. Scanned-page detection
rotates image boxes to match the page, so rotated scans are found.
Skipped files are reported by path, so two files with the same name
in different folders are told apart.
@burntcookiedough
burntcookiedough force-pushed the feat/ocr-olmocr-benchmark branch from 51f9ad6 to fafe980 Compare November 4, 2026 08:58
olmOCR adds a baseline test to every PDF that lacks one and reports the
average pass rate of each test file and the baseline group. The script
pooled all tests instead and skipped the baseline, so it understated
every tier by 6 to 9 points.

Add the baseline group, report olmOCR's score, and add the advanced tier
to the results. Running olmOCR's benchmark.py on the same OCR output
gives the same score for standard and advanced.
@burntcookiedough
burntcookiedough force-pushed the feat/ocr-olmocr-benchmark branch from fafe980 to 9d9b302 Compare November 4, 2026 09:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants