nara-indra-ocr-r2

A fine-tune of the line recognizer in Datalab's Indra OCR for 18th- and 19th-century English handwriting in American archival records β€” trained on US Revolutionary War pension files held by the National Archives (NARA): declarations, affidavits, certificates and printed forms completed by hand.

Not affiliated with or endorsed by Datalab. This is an independent research derivative of their model. Indra OCR and its weights are Β© Datalab; this repo is distributed under their license, below.

Use it with detector threshold 0.8

Indra's line detector turns its line-probability map into boxes at a threshold of 0.5. On this handwriting, closely spaced lines touch at 0.5 and come out as single boxes the recognizer cannot read; at 0.8 they separate. This model's training crops were cut at 0.8 and it is measured at 0.8, so set it:

# pip install indra-ocr==0.3.1
import functools
import indra_ocr
from huggingface_hub import snapshot_download

# Detector threshold 0.8 (Datalab's default is 0.5). Patches indra-ocr 0.3.1's
# module-level decode(); set it before converting.
indra_ocr.pipeline.decode = functools.partial(indra_ocr.pipeline.decode, thresh=0.8)

conv = indra_ocr.load(path=snapshot_download("wjbmattingly/nara-indra-ocr-r2"), furniture=True)
for _, page in conv.convert("page.jpg"):
    print(page.text)

furniture=True keeps page headers and footers, which Indra drops by default. Runs on CPU (~0.1 s/page). Without the patch the model still works, at the 0.5 figures below.

Results

End to end with Datalab's unchanged detector, reading-order and layout models, on 762 held-out pages from 39 documents, none of which contributed any page to training. Micro CER and WER (NFD normalisation is a no-op on this English text).

model CER macro CER WER
Indra OCR as shipped, threshold 0.5 0.7195 0.7945 0.8851
Indra OCR as shipped, threshold 0.8 0.6792 0.7609 0.8674
this model, threshold 0.5 0.4301 0.5350 0.5949
this model, threshold 0.8 (recommended) 0.3604 0.4899 0.5423
LightOnOCR-3 4B, full fine-tune 0.2312
LightOnOCR-3 0.8B, full fine-tune 0.2550
Qwen3.5-2B, LoRA 0.2688
Qwen3.5-0.8B, LoRA 0.2868

The VLM rows are fine-tuned on the same training split and scored on the same holdout with the same metric; they remain clearly ahead, at 650–3,300Γ— the parameters and a GPU. The references are page transcriptions written for reading, not for line-by-line OCR β€” whole paragraphs on one line, editorial notes such as [printed form completed in handwriting] that are not on the page β€” so no line-based reader can reach zero, and line recall is not reported.

The main remaining limit is the line detector, which this fine-tune does not change: on faint or crowded handwriting it still misses some lines and splits others into word-sized fragments. This model outputs ~84% as many characters as the references contain.

What was changed, and what was not

Indra is a pipeline of small ONNX models. Only the line recognizer was retrained.

file status
rec.onnx MODIFIED β€” all 1,236,110 recognizer weights fine-tuned; classifier head widened from 1,678 to 1698 classes
meta.json MODIFIED β€” vocab extended by 20 characters; all other fields unchanged
det.onnx, order.onnx, orient.onnx, script.onnx, rec_cjk.onnx unmodified copies of Datalab's files
LICENSE Datalab's license, verbatim

The weights were written back into Datalab's original graph (never re-exported), so its masking, length computation and decoding are theirs. The detector threshold is an inference setting, not part of these files.

Training

The source transcriptions are per page with no line geometry, so line labels were recovered by alignment, in 2 rounds. Indra's own detector (threshold 0.8) finds the lines and a recognizer reads them; the page's readings are joined and aligned to the whole transcription character by character, and each detected line takes the slice of the transcription its characters map to, snapped to word boundaries. A line is kept only if its reading and its slice are at least 40% similar and within 1.5Γ— in length; editorial notes in brackets are removed first, and pages Indra rotated are skipped. Each round's labels come from the previous round's model; every round trains from Datalab's shipped recognizer.

CTC fine-tune on 685,666 line crops from 35,904 pages (8 epochs, AdamW, one-cycle learning rate peaking at 3e-4, batch 64, BatchNorm frozen, one NVIDIA RTX PRO 6000, 31 minutes). Transcriptions: wjbmattingly/si-test.

License and use restrictions

Distributed under Datalab's modified OpenRAIL-M license β€” see LICENSE, included verbatim. It is share-alike: this model, any derivative of it, and its output are governed by that same license.

By using this model you agree to the use-based restrictions in Attachment A of the License, which apply to it as an enforceable provision. In summary β€” the License governs, not this summary β€” you may not use it, its derivatives or its output: unlawfully or to infringe intellectual-property rights; for any purpose if you or your employer have over US$20M in annual gross revenue or over US$20M in funding, except where your use is limited to personal use or research purposes; or if you or your employer offer a product or service that competes with Datalab's. Broader licensing is available from Datalab at https://www.datalab.to/.

Limits

A research artifact for cultural-heritage and archival work, trained on one collection's hands and conventions. Expect it to degrade on other scripts, periods and languages, and verify any reading before relying on it.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Model tree for wjbmattingly/nara-indra-ocr-r2

Quantized
(6)
this model