Arabic OCR Post-Correction (Qwen2.5-0.5B + LoRA)

A 0.5B-parameter adapter that repairs Arabic OCR output — the step between "we scanned the archive" and "the archive is searchable".

raw OCR   وأضا ف أن "بريطايا يمكن أن تؤثر على الاتحاد الأوروبى عبر الـعمل هع شركائها".
corrected وأضاف أن "بريطانيا يمكن أن تؤثر على الاتحاد الأوروبي عبر العمل مع شركائها".

Why Arabic OCR needs this

Many Arabic letters share an identical skeleton (rasm) and differ only by dots (i'jam): ب ت ث ن are the same stroke. A scanner that loses or invents one dot changes the letter. The script is also cursive, so word boundaries are narrow and words get split or fused.

The consequence is that an 8% character error rate carries a 41% word error rate — and search, indexing and extraction all operate on words.

Results

200 held-out segments, greedy decoding.

CER WER
Raw OCR (do nothing) 0.0808 0.4112
Untuned Qwen2.5-0.5B 1.8123 2.2680
This adapter 0.0724 0.1765
+ drift guardrail 0.0671 0.2042

Word error rate falls 41.1% → 20.4% (a 50.3% reduction) while character accuracy simultaneously improves 16.9%.

The untuned base model scores CER 1.81 — above 1.0 means its output is unrelated text rather than a damaged version of the truth. It answers the instruction conversationally instead of restoring, so every gain here is attributable to the finetune.

Improvement holds across all severity bands:

severity baseline CER model CER
light (<8%) 0.0438 0.0404
medium (8–14%) 0.0813 0.0713
heavy (>14%) 0.1211 0.1092

Usage

import torch
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer

BASE = "Qwen/Qwen2.5-0.5B-Instruct"
tok = AutoTokenizer.from_pretrained(BASE)
model = PeftModel.from_pretrained(
    AutoModelForCausalLM.from_pretrained(BASE, dtype=torch.bfloat16, device_map="auto"),
    "Sheeda/arabic-ocr-post-correction-0.5b",
).eval()

INSTRUCTION = "صحّح أخطاء المسح الضوئي في النص التالي وأعد كتابته بشكل صحيح:"
noisy = 'وأضا ف أن "بريطايا يمكن أن تؤثر على الاتحاد الأوروبى عبر الـعمل هع شركائها".'

prompt = tok.apply_chat_template(
    [{"role": "user", "content": f"{INSTRUCTION}\n\n{noisy}"}],
    tokenize=False, add_generation_prompt=True,
)
inputs = tok(prompt, return_tensors="pt").to(model.device)
out = model.generate(**inputs, max_new_tokens=256, do_sample=False)
print(tok.decode(out[0][inputs["input_ids"].shape[-1]:], skip_special_tokens=True))

Use greedy decoding. This is restoration with one correct answer; sampling only invents text that was never on the page.

Guardrail

A generative model can rewrite rather than repair. Reject any correction that diverges too far from its input — measured against the input, never a reference, so it works in production:

from Levenshtein import distance  # or any edit distance

def guard(source, prediction, max_drift=0.20):
    if distance(source, prediction) / max(len(source), 1) <= max_drift:
        return prediction
    return source

max_drift=0.20 is near-optimal on both metrics. Tighten it where nothing may be made worse; loosen it where findability outweighs character fidelity.

Training

  • Base: Qwen/Qwen2.5-0.5B-Instruct
  • Method: LoRA (r=32, alpha=64, dropout=0.05) on all attention and MLP projections
  • Data: 56,931 synthetic (noisy → clean) pairs from AraSum
  • Schedule: 2 epochs, effective batch 32, lr 2e-4 cosine, bf16
  • Hardware: single RTX 5070 Ti (12GB), ~80 minutes

No public Arabic OCR-correction corpus exists, so training pairs are synthesized: clean Arabic text corrupted by a confusion model weighted toward how the script actually fails — 42% dot/skeleton confusion, 22% word splitting and merging, the rest dropped characters, kashida insertion and spurious diacritics. Severity is sampled per example between 4% and 18%.

Limitations

  • The corruption is synthetic. It models Arabic OCR failure from the structure of the script; it is not a recording of a specific engine's output. Calibrating against real Tesseract or PaddleOCR output on scanned pages is the honest next step, and until that is done these numbers describe performance on synthetic corruption.
  • Trained on Modern Standard Arabic news text. Dialectal and heavily classical text are out of distribution.
  • The guardrail catches wholesale rewrites, not small confident wrong edits. Those need token-level confidence or a lexicon check.

Links

Code, data pipeline and evaluation harness: https://github.com/Crypto47/arabic-ocr-post-correction

License

MIT. The base model's license governs the merged weights independently.

Downloads last month
-
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for Sheeda/arabic-ocr-post-correction-0.5b

Adapter
(788)
this model