Skip to content

Commit e583d7b

Browse files
artizclaude
andcommitted
fix(ocr): make recognition deterministic (single-threaded ORT)
ORT's multi-threaded float-reduction order varies across runs, flipping the CTC argmax on low-confidence characters. This was latent until the sharper supersampled render pushed a noisy fax (sample_with_rotation_mismatch) to borderline confidence, making its snapshot non-deterministic (pdf_conformance 75/76 drift). Recognition inputs are tiny per-line crops, so single-threaded ORT costs negligible throughput and restores determinism — the snapshot baseline is unchanged (the committed output already matched the deterministic one). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 5521e75 commit e583d7b

1 file changed

Lines changed: 6 additions & 1 deletion

File tree

  • crates/fleischwolf-pdf/src

crates/fleischwolf-pdf/src/ocr.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,14 @@ impl OcrModel {
4747
std::env::var("DOCLING_OCR_REC_ONNX").unwrap_or_else(|_| "models/ocr_rec.onnx".into());
4848
let dict_path =
4949
std::env::var("DOCLING_OCR_DICT").unwrap_or_else(|_| "models/ppocr_keys_v1.txt".into());
50+
// Single-threaded: ORT's multi-threaded float-reduction order varies
51+
// across runs, which flips the CTC argmax on low-confidence characters
52+
// (e.g. noisy faxes) and makes the snapshot output non-deterministic. The
53+
// recognition inputs are tiny per-line crops, so the throughput cost is
54+
// negligible.
5055
let rec = Session::builder()
5156
.map_err(|e| format!("ocr: builder: {e}"))?
52-
.with_intra_threads(crate::intra_threads())
57+
.with_intra_threads(1)
5358
.map_err(|e| format!("ocr: intra_threads: {e}"))?
5459
.commit_from_file(&rec_path)
5560
.map_err(|e| format!("ocr: load {rec_path}: {e}"))?;

0 commit comments

Comments
 (0)