Feraoun-Pro / README.md
ainouche-abderahmane's picture
Upload folder using huggingface_hub
085b9be verified
|
Raw
History Blame Contribute Delete
14 kB
---
language:
- kab
license: apache-2.0
tags:
- kabyle
- taqbaylit
- berber
- amazigh
- ocr
- image-to-text
- tifinagh
- document-understanding
- low-resource
- ctc
pipeline_tag: image-to-text
metrics:
- cer
- wer
- exact_match
model-index:
- name: Feraoun-Pro
results:
- task:
type: image-to-text
name: Line-level document OCR, Kabyle Latin
dataset:
type: agbalu/KabTifinagh
name: 25,484 held-out rendered lines, Latin
metrics:
- type: cer
value: 0.0734
name: Character error rate
- task:
type: image-to-text
name: Line-level document OCR, Kabyle Neo-Tifinagh
dataset:
type: agbalu/KabTifinagh
name: 4,516 held-out rendered lines, Neo-Tifinagh
metrics:
- type: cer
value: 0.0091
name: Character error rate
---
# Feraoun-Pro
A 51M-parameter CTC recogniser that reads a **line of printed Kabyle** (Taqbaylit, `kab`)
from a pixel strip and writes it out as text — in the Berber Latin orthography and in
Neo-Tifinagh, from one checkpoint, without being told which script it is looking at, in a
single forward pass.
It exists because the Kabyle written record is on paper. Novels, grammars, periodicals and
the archive scans that carry a century of prose are images, and general-purpose OCR was
never trained on the language: the emphatic consonants `ḍ ḥ ṛ ṣ ṭ ẓ` are a base letter plus
a sub-dot that other engines drop or normalise away, and dropping the dot changes the word.
`ţ` — Boulifa's original transcription convention — has a slot in this vocabulary. It has
never had one before.
**What it adds over [`agbalu/Feraoun-36M`](https://huggingface.co/agbalu/Feraoun-36M),
which also reads both scripts.** A 173-symbol table against 171 — `ţ` and `Ţ`, Boulifa's
convention, and 33 Neo-Tifinagh codepoints against 31. A single CTC forward pass in place of
one autoregressive decoder step per character. A variable-width canvas up to 1,024 px, so a
long line is no longer squeezed to fit. 600,000 training lines against 80,000. The two
models are separate artifacts on the same task and both stay published; Results says how to
choose.
## Results
**30,000 held-out lines, rendered**, drawn at a fixed seed over the full 600,000-line pool —
85% Latin, 15% Neo-Tifinagh, matching the training mix. The model saw none of these
sentences during training.
| | Latin (`n` = 25,484) | Neo-Tifinagh (`n` = 4,516) |
|---|---|---|
| Character error rate | **7.34%** | **0.91%** |
**Line exact match over all 30,000 lines: 81.77%.**
Three things the table does not say on its own.
**`Feraoun-36M` publishes 2.85% Latin CER, 1.64% Neo-Tifinagh CER and 70.20% exact match,
and none of those is comparable with the figures above.** The two models were scored on
different held-out draws from different line sets, and neither has been run on the other's —
so the pair is two measurements, not a ranking, and this card does not turn it into one.
Choose by page: this model takes a line up to 1,024 px without squeezing it, has `ţ` in its
alphabet, and decodes in one pass; the 36M model is smaller and is the one whose font
generalisation has been tested.
**The Tifinagh number is a retention result, not a difficulty result.** Neo-Tifinagh writes
no sub-dots and no capitals and its lines are shorter, so sub-1% there is not a better score
on the same task — it is an easier task, and the figure's value is that it proves the script
survived training alongside Latin rather than fading.
**Checkpoint selection was gated on both scripts at once.** The combined error
`0.5 · Latin CER + 0.5 · Tifinagh CER` had to improve while neither script regressed past
its baseline. A checkpoint that reads Tifinagh better by forgetting Latin is not selected,
which is the failure this model's whole training design is built around.
## What the numbers cover, and what they do not
Every figure above is measured on rendered lines. What that leaves open, stated so you can
plan around it rather than discover it:
- **Real scanned pages are not in the evaluation.** Training and validation are both
synthetic renders, as is `Feraoun-36M`'s 2.85%. Performance on Adlis book scans — genuine
century-old print — is open for both models.
- **The head-to-head against `Feraoun-36M` has not been run**, for the reason above.
- **`ţ`/`Ţ` have vocabulary slots and no per-glyph score.** The validation pool holds too
few `ţ` lines to report one. The slots are what make the glyph representable at all; no
other Kabyle OCR system has them.
- **Font generalisation is not quantified.** Training and evaluation draw from the same set
of container faces, so a typeface the model has never seen is outside what these numbers
describe. Expect it to be the axis that moves most.
## Architecture
| | |
|---|---|
| Parameters | **51,440,589**, plus 132,036 elements of buffer — the sinusoidal position table and the stem's batch-norm statistics — for 51,572,625 tensor elements in the weights file. The training checkpoint holds 60,158,014: the difference is an 8,585,389-parameter auxiliary decoder that supplies gradients and is not exported |
| Stem | 5-layer convolutional front end, 64 px fixed height, variable width to 1,024 px |
| Encoder | 16-layer pre-norm Transformer, 512 hidden, 8 heads |
| Head | CTC over 173 classes, greedy decode |
| Input | `(1, 3, 64, W)`, pixel range `[−1, 1]`, aspect ratio preserved |
| Vocabulary | 173 classes — 4 special, Latin, Kabyle extended and sub-dot (`ḍ ḥ ṛ ṣ ṭ ẓ`), accented Latin, `ţ`/`Ţ`, digits, punctuation, 33 Neo-Tifinagh |
**No decoder steps, no beam search, no key-value cache.** A line is one forward pass:
convolutional stem, Transformer encoder, linear projection, CTC argmax. The auxiliary
autoregressive decoder existed only to keep gradients reaching the early convolutional
layers, and it is not in the published weights.
**173 classes, not subwords.** A subword tokeniser completes vocabulary words where the ink
is broken, which is precisely the failure an OCR system must not have: it produces fluent
Kabyle that is not what the page says.
## Intended use
Turning printed Kabyle into text: scanned novels, grammars, periodicals and archive
material, in the Latin orthography or in Neo-Tifinagh, from the same model.
**Not suitable for**: handwriting, absent from the training data; multi-column layout, which
`transcribe_page` reads straight across; any decision about a person; or any language other
than Kabyle. Tarifit, Tashelhit and Central Atlas Tamazight share part of this orthography
and are not evaluated here.
## Usage
`transformers`, `torch`, `pillow` and `numpy`, nothing else. The architecture is not one of
the library's own, so the modelling code travels in this repository and
`trust_remote_code=True` is what loads it.
```python
from PIL import Image
from transformers import AutoModel
model = AutoModel.from_pretrained("agbalu/Feraoun-Pro", trust_remote_code=True).eval()
print(model.transcribe(Image.open("line_crop.png")))
# 'Taqbaylit d tutlayt tayemmat nneɣ.'
print(model.transcribe(Image.open("tifinagh_line.png")))
# 'ⵜⴰⵇⴱⴰⵢⵍⵉⵜ ⴷ ⵜⵓⵜⵍⴰⵢⵜ ⵜⴰⵢⴻⵎⵎⴰⵜ ⵏⵏⴻⵖ.'
```
`transcribe` does the preprocessing as well as the decoding, and that matters: the model was
fitted on lines scaled to 64 px of height with aspect ratio preserved, so a caller who builds
the tensor themselves at another scale is measuring a different model.
For a full page, `transcribe_page` segments on the horizontal ink profile first. That
segmentation assumes a single column of roughly horizontal lines; it has not been evaluated
on multi-column layouts, marginalia or skewed scans.
## Training data
Lines are **rendered, not scanned.** There is no page of real Kabyle print with
character-level ground truth to train against.
| | |
|---|---|
| Latin text | AƔBALU-Text v1, not publicly released |
| Neo-Tifinagh text | [`agbalu/KabTifinagh`](https://huggingface.co/datasets/agbalu/KabTifinagh) `script_conversion` train split, CC-BY-2.0 |
| Objective | CTC, plus an auxiliary autoregressive decoder whose gradients reach the encoder and which is **not** exported |
The renderer composites text set at 24–38 pt in the faces available in the training container
onto generated paper grain, then degrades by blur, exposure, JPEG artefacting and a slight
skew. Height is fixed at 64 px; width follows the line up to 1,024 px, so glyphs are never
squashed to a square canvas.
**The degradation is deliberately modest**, and the model has not been shown genuinely hard
inputs: no bleed-through from the reverse of the page, no ink starvation, no page-edge
shadow, no show-through from facing pages. Expect real archival scans to be harder than
anything in training.
**The corpus composition is uneven.** AƔBALU-Text v1 is a 42-source mixture and the sources
are not weighted equally. The line set is a seeded draw over the full pool rather than a
prefix, which is what keeps it from being one source, but it does not flatten the source
distribution: a novel's long sentences and a newspaper's headlines are both in the pool and
both outside what a 30,000-line validation draw can fully represent.
## Training recipe
One A10G throughout, 600,000 rendered lines per stage, 570,000 train / 30,000 validation on
a fixed seed, batch 64, one epoch per stage, cosine schedule with 5% warmup.
| stage | mix | peak lr | what it does |
|---|---|---|---|
| 1 | Latin only | `2e-5` | builds the Latin recogniser and the sub-dot distinctions |
| 2 | 60% Latin / 40% Tifinagh | `2e-5` | introduces the 33 Neo-Tifinagh codepoints |
| 3 | 85% Latin / 15% Tifinagh | `5e-6``2.5e-7` | settles both scripts; 8,907 steps |
**The staging is the design, not a schedule that happened to be split up.** Introducing
Neo-Tifinagh at 40% costs Latin accuracy — that is where this model's 7.34% Latin CER comes
from, and it is why stage 3 drops the Tifinagh share to a rehearsal fraction and the learning
rate by 4×. The 15% rehearsal is enough to keep the Tifinagh codepoints from fading while the
Latin weights settle. Every stage's checkpoint is gated on the combined two-script error with
a per-script regression ceiling, so no stage can trade one script for the other.
## Limitations
**7.34% Latin CER on synthetic renders is not 7.34% on your documents.** The validation set
is drawn from the same rendered distribution as training. Real scanned pages have ink bleed,
page curvature, show-through and aging that the renderer approximates at modest levels.
Whether the gap is one point or ten is not known.
**Latin character accuracy is the weak axis.** It is the price of dual-script capacity in a
model this size, and it is stated in Results rather than buried here.
**Punctuation after dense glyph clusters is a consistent weakness.** In Neo-Tifinagh,
`ⴰⵣⵓⵍ ⴼⵍⵍ-ⴰⵡⴻⵏ, ⴰⵎⴻⴽ ⵜⴻⵍⵍⴰⵎ?` came back as `ⴰⵣⵓⵍ ⴼⵍⵍ,ⴰⵡⴻⵏ, ⴰⵎⴻⴽ ⵜⴻⵍⵍⴰⵎ!` — the hyphen read
as a comma and the closing `?` as `!`, both at marks that follow glyphs without a full
inter-word space.
**Whitespace boundaries are not reliable.** The validation ground truth is stripped, so
trailing-space errors introduced by the renderer's post-processing do not appear in the CER.
They will appear in your output if the downstream consumer is whitespace-sensitive.
**Page layout is a projection profile, nothing more.** `transcribe_page` finds horizontal ink
bands and crops them. There is no column detection, no reading-order model and no table or
figure handling, so a two-column periodical is read straight across.
## Files
| file | contents |
|---|---|
| `model.safetensors` | the convolutional stem, the 16-layer encoder and the CTC head — 51,440,589 parameters and 132,036 elements of buffer, 206 MB fp32. `from_pretrained` reports no missing and no unexpected keys |
| `config.json` | architecture and the `auto_map` that points at the code below |
| `configuration_feraoun_pro.py`, `modeling_feraoun_pro.py` | the architecture and the preprocessing, importing only `torch`, `transformers`, `pillow` and `numpy` |
| `tokenizer.json`, `tokenizer_config.json` | the 173-symbol table, id for id with the one the model was trained on |
Training cannot be resumed from the published files: the optimizer, scheduler, step and epoch
stay in the source checkpoint on the training volume and are not exported.
## Reproduction
```bash
make modal-ocr-pro RUN=latin EPOCHS=1 LR=2e-5 RATIO=0.0 LINES=600000
make modal-ocr-pro RUN=dual EPOCHS=1 LR=2e-5 RATIO=0.40 RESUME=latin
make modal-ocr-pro RUN=feraoun-pro EPOCHS=1 LR=5e-6 RATIO=0.15 RESUME=dual
make modal-ocr-pro-pull RUN=feraoun-pro
make push REPO=feraoun-pro
```
## The name
**Mouloud Feraoun** (1913–1962), the schoolteacher from Tizi Hibel who wrote *Le Fils du
pauvre* and put Si Mohand's oral poetry onto the printed page — the direction this model
reverses. He was assassinated by the OAS on 15 March 1962, three days before the Évian
Accords were signed. The naming is homage and carries no endorsement.
## Citation
```bibtex
@software{agbalu_feraoun_pro_2026,
title = {Feraoun-Pro: dual-script CTC document OCR for Kabyle},
author = {AƔBALU},
year = {2026},
url = {https://huggingface.co/agbalu/Feraoun-Pro},
note = {51,572,625 parameters; Latin CER 7.34\%, Neo-Tifinagh CER 0.91\%,
line exact match 81.77\%, over 30,000 held-out rendered lines}
}
```
## Licence
**Apache-2.0** on the weights and the code. The training text is CC-BY-2.0 on the Tifinagh
side and a mixed corpus of uneven provenance on the Latin side. A permissive grant on the
weights makes no claim about the text behind them, so the corpus composition is stated above
rather than assumed.
Part of [AƔBALU](https://huggingface.co/agbalu), a Kabyle NLP collection.