Koshur OCR v2
A compact Kashmiri Perso-Arabic printed-line OCR model from Kash Labs AI.
v2.1 results β prefix beam + train-only character LM
- Beam deduplicated validation CER: 21.35% (greedy: 22.62%)
- Beam deduplicated exact match: 26.20% (greedy: 25.54%)
- Held-out 720-row CER: 21.24% (greedy: 22.44%)
- Absolute held-out CER improvement: 1.19 percentage points
- Validation rows: 920 deduplicated / 1,000 full
- Best epoch: 18
- Training rows: 100,000 synthetic sentence-line images
- Beam evaluation: https://wandb.ai/kash-ai-labs-kash-labs/koshur-pixel-ocr/runs/ctc-v2-prefix-beam-20260729T152530Z
The selected decoder uses width 10, top-12 token pruning, a trigram character LM trained only on the 100,000 training labels, LM weight 0.4, and token bonus 1.5. It was tuned on a fixed 200-row calibration subset and independently improved the remaining 720 rows. Across all 920 rows, deletions fell from 6,859 to 4,615.
Architecture
64Γ2048 grayscale line β CNN (48β96β160β192) β 2-layer bidirectional GRU (256 per direction) β 155-way CTC
The model has 4,475,931 trainable parameters, a 154-character inventory, and no temporal feature dropout in the selected v2 control checkpoint. This is a custom PyTorch checkpoint, not a Transformers-format model.
Versioning
v1.0.0β 10k temporal-dropout checkpoint, 28.13% deduplicated CERv2.0.0β 100k control checkpoint with greedy decoding, 22.62% deduplicated CERv2.1.0β same weights plus prefix-beam/trigram decoding, 21.35% deduplicated CER (current)
Use a Hub revision to pin a release.
Files
best-model.ptβ epoch-18 state dictionary, character inventory, architecture metadata, and validation CERmodel.pyβ architecture, preprocessing, CTC decoding, andrecognize_line()ctc_prefix_beam.pyβ tested prefix-beam implementationchar-trigram-lm.pklβ compact train-only character language modelbeam-eval.jsonβ calibration, held-out, and full evaluation metricsconfig.jsonβ release, training, preprocessing, metrics, and checkpoint checksum
Local CPU or Apple Silicon inference
pip install -r requirements.txt
from PIL import Image
from model import recognize_line
text, diagnostics = recognize_line(Image.open("single-line.png"))
print(text)
print(diagnostics)
Prefix beam is the default. For the original fast decoder:
text, diagnostics = recognize_line(Image.open("single-line.png"), decoder="greedy")
The input should be one tightly cropped, upright printed line. It is resized to height 64 and left-aligned on a width-2048 canvas. Inference runs on CPU and does not require a GPU.
Limitations
- The reported score is an in-domain synthetic validation result, not real-world scan quality.
- Trained on synthetic printed lines, not handwriting or full pages.
- Camera photos should be rotated, deskewed, tightly cropped, and segmented into individual lines.
- Training targets exclude the established Kashmiri diacritic set; the model recognizes base text and does not restore those marks.
- Error rate remains substantial, with deletions the dominant residual error.
- Predictions require review before publication or archival use.
- Downloads last month
- 70