Model Card for Ottoman_Print_OCR_QWEN4B
This model is a vision-language adapter for Qwen3.5-4B with LoRA (via Unsloth) for Ottoman Turkish printed texts in printed Naskh font. It mainly operates at the line level. It might still work on multi-line level.
The output is not modern Turkish transliteration but the original Perso-Arabic script. In this way, researchers can analyze the ortographic patterns in Ottoman Turkish directly, such as how the question suffix, +mI, was written etc.
Since there are different ways to transliterate a text (e.g., IJMES Transliteration and Modern Turkish alphabet) this approach preserves the information on the text as its original form and lets other researchers to build their of transliteration tools by using the output of this OCR model.
The model processes the data with 96% character-level accuracy and 80% word-level accuracy. With further normalization, these values go to 98% character-level and 88% word-level.
You can find an example on how this model can be used
here
Through this link, you can find
Eşar-ı Ziya as processed for OCR. You can search for a word or phrase. Such an interface can be expandable to other works with this model as well.
Data
The training data consists of 48,306 words in 5,375 lines across nine printed Ottoman Turkish works (
Eşar-ı Ziya to represent poetry,
New Testament (vol 2, 1857 edition) for religious/narrative works,
Medhal-i Fıkh for Islamic law language,
Muahedat Mecmuası for diplomatic language),
Solakzade Tarihi and
Muhtasar Tarih-i Umumi for history domain,
Mir'at-ı Alem for the language in magazines,
Kozmoğrafya, yahut, İlm-i ahval-i sema for science/astrology domain, and
Mecmua-i Fünûn-i Askeriyye for military domain).
The OCR training data was manually prepared by the author.
Since the training data does not cover all genres, it might not be comprehensively effective in all Ottoman Turkish documents.
The distribution of data per work can be seen below.
Training Hyperparameters
The following hyperparameters were used during training:
| Hyperparameter | Value |
|---|
| Learning Rate | 2e-05 |
| Train Batch Size (per device) | 4 |
| Gradient Accumulation Steps | 4 |
| Optimizer | 8-bit AdamW (adamw_8bit) |
| LR Scheduler Type | Cosine (cosine) |
| Warmup Ratio | 0.1 |
| Weight Decay | 0.05 |
| Number of Epochs | 3 |
| Max Sequence Length | 256 |
| LoRA R | 64 |
| LoRA Alpha | 64 |
| Seed | 3407 |
Training Results
The training process was stopped at 550 steps, achieving the following best evaluation metric:
- Best Validation Loss: 0.139433
Evaluation Results
The model was evaluated on a test set of 807 lines.
| Metric | Baseline | Normalized (Ye + Kef) |
|---|
| CER (Character Error Rate) | 4.12% | 2.37% |
| WER (Word Error Rate) | 19.9% | 12.13% |
- Mean Line CER: 3.03%
- Median Line CER: 1.69%
- Perfect Matches: 350 out of 807 lines (CER = 0)
Normalization Process
To achieve the best results, the following character normalization is recommended for the output:
\u064a and \u0649 $\rightarrow$ \u06cc (Persian/Arabic Ye)
\u0643 $\rightarrow$ \u06a9 (Kef/Keheh)
- Removal of ZWNJ (
\u200c) and ZWJ (\u200d)
The first two processes do not make any significant change on how the data looks like. The last one is optional. It is basically the invisible character to write a letter in the word-ending word in the middle of a word (like داىٔرهده). The model is supposed to put this character in its output.
How to Use
This model requires the unsloth library and torch.
1from unsloth import FastVisionModel
2import torch
3from PIL import Image
4
5# 1. Load Model & Tokenizer
6model, tokenizer = FastVisionModel.from_pretrained(
7 "enesyila/Ottoman-Print-OCR-Qwen4B",
8 load_in_4bit=True,
9)
10FastVisionModel.for_inference(model)
11tokenizer.padding_side = "left"
12
13# 2. Load Image
14image_path = "path_to_your_line_image.png" # You should put the path of your image here.
15image = Image.open(image_path).convert("RGB")
16
17# 3. Prepare Prompt
18instruction = "Transcribe the Ottoman Turkish printed text in this image." # Please do not change this instruction. The model was trained with this instruction so it should work the best with this.
19messages = [
20 {"role": "user", "content": [
21 {"type": "image"},
22 {"type": "text", "text": instruction},
23 ]}
24]
25prompt = tokenizer.apply_chat_template(messages, add_generation_prompt=True, enable_thinking=False) # disabling thinking ensures that the model will only give the OCR output and not its reasoning.
26
27# 4. Tokenize & Generate
28inputs = tokenizer(
29 images=[image],
30 text=[prompt],
31 add_special_tokens=False,
32 return_tensors="pt",
33).to("cuda")
34
35if "pixel_values" in inputs:
36 inputs["pixel_values"] = inputs["pixel_values"].to(torch.bfloat16)
37
38with torch.inference_mode():
39 output_ids = model.generate(
40 **inputs,
41 max_new_tokens=128, # This value is enough for just one line. If you want to process larger pieces of text, you can increase this value but I can't guarantee that the model will work on multi-line level.
42 use_cache=True,
43 do_sample=False,
44 temperature=0.0,
45 repetition_penalty=1.1,
46 )
47
48# 5. Decode Result
49input_len = inputs.input_ids.shape[1]
50result = tokenizer.decode(output_ids[0][input_len:], skip_special_tokens=True).strip()
51
52print("Transcription:", result)
Copyright
This model is licensed under Creative Commons Attribution Non Commercial 4.0. Therefore, the commercial use is strictly forbidden.
However, you can use this model for your research freely.
Contact