Skip to content

Commit 6eeff10

Browse files
committed
docs: add VietOCR recognizer benchmark
Document the KV-cache/early-exit decoder rationale, the architecture guard, the correctness verification (0 mismatch vs stock predict_batch on 2000 crops), and the measured ~17x isolated speedup / ~3x in-pipeline speedup.
1 parent 9312f8f commit 6eeff10

1 file changed

Lines changed: 61 additions & 0 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# VietOCR recognizer — benchmark
2+
3+
`lang='vi'` routes the recognition step to VietOCR (`vgg_transformer`) instead
4+
of PaddleOCR's shared recognizer (see `mineru/model/ocr/vietocr_fast_batch.py`).
5+
This page documents the decode optimization and its measured effect.
6+
7+
## Why a custom decoder
8+
9+
`vietocr.tool.predictor.Predictor.predict_batch()` has two problems on
10+
full-page crop lists:
11+
12+
1. It groups crops by resized width and concatenates each group into a single
13+
unbounded `torch.cat()` — a full page can request a multi-GB contiguous
14+
allocation and OOM.
15+
2. `translate()` decodes the whole batch autoregressively **without a
16+
KV-cache**, recomputing self-attention over the entire growing prefix every
17+
step (per-step cost grows with prefix length), and only stops when the
18+
*slowest* sequence in the batch emits EOS.
19+
20+
`predict_batch_grouped()` replaces it with:
21+
22+
- **Sub-batching** by a fixed size (bounds peak memory).
23+
- **Length-aware grouping** (sort each width bucket by estimated text length so
24+
a sub-batch isn't a mix of very short and very long lines).
25+
- **KV-cache** decode: self-attention K/V are cached per step; cross-attention
26+
K/V over the fixed encoder memory are projected once. Per-step cost stops
27+
growing with prefix length.
28+
- **Early-exit**: sequences drop out of the active batch as soon as they emit
29+
EOS instead of being stepped to the longest sequence's length.
30+
31+
It is **architecture-guarded** — the hand-rolled KV-cache decode runs only on a
32+
post-norm `nn.TransformerDecoderLayer` stack; any other architecture falls back
33+
to a decoder that drives the model's own `forward_decoder` (correct for any
34+
`norm_first`), so a differently-configured model cannot produce silently-wrong
35+
output.
36+
37+
## Correctness
38+
39+
Verified byte-identical to per-image `Predictor.predict()` — 0 text mismatches
40+
across thousands of real crops, for both the KV-cache path and the fallback
41+
path. The architecture guard was checked to select the fast path for the
42+
reference model and reject pre-norm / subclassed-layer / missing-norm variants.
43+
44+
## Speed
45+
46+
Isolated microbenchmark, 2000 real Vietnamese line crops, single RTX 5080,
47+
0/2000 text mismatches vs stock:
48+
49+
| Decoder | ms/crop | total |
50+
|---|---|---|
51+
| `predict_batch()` (stock vietocr) | 155.4 | 310.8 s |
52+
| `predict_batch_grouped` (KV-cache + early-exit) | **9.0** | **18.0 s** |
53+
54+
**≈ 17× faster** than stock `predict_batch()` on this run. In the full pipeline
55+
(where recognition shares the GPU with layout/table/detection stages), the
56+
largest OCR batches went from ~150 ms/crop to ~50 ms/crop (≈ 3×), with per-step
57+
GPU-kernel time reduced ~4× by the KV-cache.
58+
59+
Numbers depend on document content (line lengths), GPU thermal state and
60+
hardware, and vary run-to-run (measured 17–26× across runs); treat them as
61+
representative rather than exact.

0 commit comments

Comments
 (0)