niobures commited on
Commit
0100200
·
verified ·
1 Parent(s): 9e670fc

CATT & CATT-Whisper (code, dataset, models, papers, spaces)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +4 -0
  2. Abjad AI at NADI 2025. CATT-Whisper. Multimodal Diacritic Restoration Using Text and Speech Representations.pdf +3 -0
  3. CATT. Character-based Arabic Tashkeel Transformer.pdf +3 -0
  4. NADI 2025. The First Multidialectal Arabic Speech Processing Shared Task.pdf +3 -0
  5. Thaka at KSAA-2026 Task 2. Regularized Fine-Tuning for Arabic Speech Diacritization.pdf +3 -0
  6. code/Arabic-Romanizer (ArabicTransliterator +8).zip +3 -0
  7. code/ArabicTransliterator.zip +3 -0
  8. code/catt-whisper.zip +3 -0
  9. code/catt.zip +3 -0
  10. code/mishkal.zip +3 -0
  11. code/saara.zip +3 -0
  12. dataset/dataset.zip +3 -0
  13. models/CATT-Whisper/catt_whisper_base_model_v1_epoch_26_with_spec_augment.pt +3 -0
  14. models/CATT-Whisper/source.txt +1 -0
  15. models/CATT/best_ed_epoch_175.pt +3 -0
  16. models/CATT/best_ed_mlm_epoch_175.pt +3 -0
  17. models/CATT/best_ed_mlm_ns_epoch_178.pt +3 -0
  18. models/CATT/best_eo_epoch_192.pt +3 -0
  19. models/CATT/best_eo_mlm_epoch_192.pt +3 -0
  20. models/CATT/best_eo_mlm_ns_epoch_193.pt +3 -0
  21. models/CATT/char_bert_model_pretrained.pt +3 -0
  22. models/CATT/ed_epoch_5.pt +3 -0
  23. models/CATT/ed_mlm_epoch_5.pt +3 -0
  24. models/CATT/ed_model_onnx.zip +3 -0
  25. models/CATT/eo_epoch_5.pt +3 -0
  26. models/CATT/eo_mlm_epoch_5.pt +3 -0
  27. models/CATT/eo_model_onnx.zip +3 -0
  28. models/CATT/source.txt +1 -0
  29. models/catt-diacritizer/.gitattributes +35 -0
  30. models/catt-diacritizer/README.md +43 -0
  31. models/catt-diacritizer/bw2ar.py +126 -0
  32. models/catt-diacritizer/catt_ed_decoder.int8.onnx +3 -0
  33. models/catt-diacritizer/catt_ed_decoder.onnx +3 -0
  34. models/catt-diacritizer/catt_ed_encoder.int8.onnx +3 -0
  35. models/catt-diacritizer/catt_ed_encoder.onnx +3 -0
  36. models/catt-diacritizer/catt_eo.int8.onnx +3 -0
  37. models/catt-diacritizer/catt_eo.onnx +3 -0
  38. models/catt-diacritizer/catt_models_onnx.py +273 -0
  39. models/catt-diacritizer/source.txt +1 -0
  40. models/catt-diacritizer/tashkeel_tokenizer_onnx.py +158 -0
  41. models/catt-diacritizer/utils.py +139 -0
  42. spaces/arabic-auto-tashkeel/.gitattributes +35 -0
  43. spaces/arabic-auto-tashkeel/README.md +13 -0
  44. spaces/arabic-auto-tashkeel/app.py +104 -0
  45. spaces/arabic-auto-tashkeel/bw2ar.py +126 -0
  46. spaces/arabic-auto-tashkeel/ed.py +63 -0
  47. spaces/arabic-auto-tashkeel/ed_pl.py +164 -0
  48. spaces/arabic-auto-tashkeel/eo.py +50 -0
  49. spaces/arabic-auto-tashkeel/eo_pl.py +151 -0
  50. spaces/arabic-auto-tashkeel/models/best_ed_mlm_ns_epoch_178.pt +3 -0
.gitattributes CHANGED
@@ -33,3 +33,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ Abjad[[:space:]]AI[[:space:]]at[[:space:]]NADI[[:space:]]2025.[[:space:]]CATT-Whisper.[[:space:]]Multimodal[[:space:]]Diacritic[[:space:]]Restoration[[:space:]]Using[[:space:]]Text[[:space:]]and[[:space:]]Speech[[:space:]]Representations.pdf filter=lfs diff=lfs merge=lfs -text
37
+ CATT.[[:space:]]Character-based[[:space:]]Arabic[[:space:]]Tashkeel[[:space:]]Transformer.pdf filter=lfs diff=lfs merge=lfs -text
38
+ NADI[[:space:]]2025.[[:space:]]The[[:space:]]First[[:space:]]Multidialectal[[:space:]]Arabic[[:space:]]Speech[[:space:]]Processing[[:space:]]Shared[[:space:]]Task.pdf filter=lfs diff=lfs merge=lfs -text
39
+ Thaka[[:space:]]at[[:space:]]KSAA-2026[[:space:]]Task[[:space:]]2.[[:space:]]Regularized[[:space:]]Fine-Tuning[[:space:]]for[[:space:]]Arabic[[:space:]]Speech[[:space:]]Diacritization.pdf filter=lfs diff=lfs merge=lfs -text
Abjad AI at NADI 2025. CATT-Whisper. Multimodal Diacritic Restoration Using Text and Speech Representations.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:16235cec951d5a337c988d5fa66b809946d070ea69244f16242d93f49057cc48
3
+ size 296424
CATT. Character-based Arabic Tashkeel Transformer.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fbbef8cd0bfa0f7a721af845316e82da4bcc735635b079127933d14455e14610
3
+ size 400230
NADI 2025. The First Multidialectal Arabic Speech Processing Shared Task.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:633ffcd7c2dcdb1b3c9e7c16e0ec9c0850285f313c882e076e286e684b1ba45a
3
+ size 1215064
Thaka at KSAA-2026 Task 2. Regularized Fine-Tuning for Arabic Speech Diacritization.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b063a23f75d4104f1a34087ab00b81a4641fe2195edf3936e5624dc854917ad5
3
+ size 159709
code/Arabic-Romanizer (ArabicTransliterator +8).zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:98fed58e6d6f1fad9f245a5f915061bcd11f69a85f193da2dde63aa3ad83e853
3
+ size 22718916
code/ArabicTransliterator.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:81ef1cbdc5c266d6e501010b39f9998ef3d3a03dec095a5c722fe2d0c9b679ec
3
+ size 46528355
code/catt-whisper.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:220f178a205ca4bfff5f4cbf752979dc273e38a6b29f724c045d84945d5a9e9e
3
+ size 1271147
code/catt.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:93f4f236ba71de710cf2316558f8bf0b0d3e5853c84a13659f8505c13bc90771
3
+ size 8552916
code/mishkal.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d9243591c7a437ee16dccc343d51e61e3d1b6b2bd9383a42863d95c9ec108495
3
+ size 117913533
code/saara.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c8fcb5eef9e6ad428cb1129481175b1af32009a072e8f353d2eb79d10460a059
3
+ size 46530545
dataset/dataset.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fa332ca9d3118ed8bc974debc681ba3f9d5ebca3cb824073b24bef0314e5fb00
3
+ size 209268505
models/CATT-Whisper/catt_whisper_base_model_v1_epoch_26_with_spec_augment.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f7ca2e8107eaab302b2c48a91f4d4f3ec8c151f96ee5ffd27750778a51f5ecac
3
+ size 159223099
models/CATT-Whisper/source.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ https://github.com/abjadai/catt-whisper
models/CATT/best_ed_epoch_175.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5125495c8fd6c5480c8443a2d73b766be00c1c85988d6b67ec4e5dc7c17bdd5a
3
+ size 88403216
models/CATT/best_ed_mlm_epoch_175.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a250c29acdb8cbdd9e729287a6631710986d8243d487b2fa5a25b996cbaa84be
3
+ size 88403608
models/CATT/best_ed_mlm_ns_epoch_178.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c6297ec54f9af518a59d4b9e5831d3ad4da304435b8c7fb969f6e3a16816ac23
3
+ size 88403510
models/CATT/best_eo_epoch_192.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:dbe3b98ca741382fbdb7acdf5a2314b7a93bc295b2e3364c6343b9a7c8985081
3
+ size 75761995
models/CATT/best_eo_mlm_epoch_192.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4f21dfe38acf867b1531be3e20ab76c6b2e438eaae6e1ff108607be986518d11
3
+ size 75762311
models/CATT/best_eo_mlm_ns_epoch_193.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4ad3e811b1bf7ecda252dc9284d2bf3087c1795add47be5ff34cb13688413322
3
+ size 75762232
models/CATT/char_bert_model_pretrained.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:440b0dcad32278a607d34d110a00a0ff20d9ad7d67f7dea8b5e323645976c9c4
3
+ size 75807234
models/CATT/ed_epoch_5.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:47b811b4db7675876a83356af04c1f61b2d60aab4ce57ebfbe848aa95f4b7a05
3
+ size 88402530
models/CATT/ed_mlm_epoch_5.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8f5534cf55306e31ad7cbdd89f931a1ae1b4724f04d6d68391b4e699f52fc1df
3
+ size 88402922
models/CATT/ed_model_onnx.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:75d2a6360725c7e0bbb238b5a950f621fb8e30b21ad7c65e91e0f36759bcb3ce
3
+ size 85926550
models/CATT/eo_epoch_5.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0997479729f9cbbfa0b10070e0099fcc88ff540e9dcd113b2e0c27bd59f0ca8b
3
+ size 75761442
models/CATT/eo_mlm_epoch_5.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f2bd85659b985949849c7377ddd941f3965bec60d0b8eb60f5a012f10f46bec3
3
+ size 75761758
models/CATT/eo_model_onnx.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5343bfdfda70c981b587811b79348b8dde91f65c0a551ca98f232ea5cb62ceac
3
+ size 72109998
models/CATT/source.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ https://github.com/abjadai/catt
models/catt-diacritizer/.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
models/catt-diacritizer/README.md ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ language: ar
4
+ library_name: onnx
5
+ tags:
6
+ - arabic
7
+ - diacritization
8
+ - tashkeel
9
+ - onnx
10
+ - transformer
11
+ ---
12
+
13
+ # CATT-EO — Arabic diacritizer (stitched single ONNX)
14
+
15
+ Single-file ONNX of the **encoder-only [CATT](https://github.com/abjadai/catt)** model
16
+ (Abjad AI, Apache-2.0). CATT-EO is non-autoregressive — a transformer encoder followed by
17
+ a linear classifier head — so [TigreGotico](https://huggingface.co/TigreGotico)
18
+ **stitched the two stages into one ONNX** with `onnx.compose`. The stitched graph is
19
+ **byte-identical** (max|Δ| = 0.0) to the original two-stage encoder→head pipeline.
20
+
21
+ | file | precision | size |
22
+ |------|-----------|-----:|
23
+ | `catt_eo.onnx` | fp32 | 78 MB |
24
+ | `catt_eo.int8.onnx` | dynamic int8 | 21.6 MB |
25
+
26
+ **I/O.** Inputs `src` (int64, `[batch, seq]` Buckwalter ids) and `src_mask`
27
+ (bool, `[batch, 1, seq, seq]`). Output `[batch, seq, 18]` tag logits. Tokenizer:
28
+ `tashkeel_tokenizer_onnx.py` (+ `bw2ar.py`, `utils.py`) — Buckwalter encoding and the
29
+ 18-tag tashkeel scheme. See `catt_models_onnx.py` for an end-to-end example.
30
+
31
+ **Benchmark.** ~4.27 % DER on the broad
32
+ [`TigreGotico/arabic_diacritized_text`](https://huggingface.co/datasets/TigreGotico/arabic_diacritized_text)
33
+ test (much lower on CATT's own narrow benchmark — distribution-dependent). The int8 file
34
+ is dynamic-quantized; expect small divergence from fp32.
35
+
36
+ Part of the
37
+ [Arabic Diacritizers / Tashkeel collection](https://huggingface.co/collections/TigreGotico/arabic-diacritizers-tashkeel-6a247318559bcc49e128aa5f).
38
+ Original CATT © Abjad AI (Apache-2.0); this is a stitched re-export with attribution.
39
+
40
+
41
+ ## Encoder-decoder variant (CATT-ED)
42
+
43
+ `catt_ed_encoder.onnx` + `catt_ed_decoder.onnx` (+ `.int8`) are the **autoregressive** encoder-decoder model — the decoder runs once per output position (causal mask), so it is two ONNX (not stitchable) and much slower than CATT-EO. In `text2tashkeel` it is the `catt-ed` / `catt-ed-int8` model.
models/catt-diacritizer/bw2ar.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ functions to convert Arabic words/text into buckwalter encoding and vice versa
3
+ """
4
+
5
+ import sys
6
+ import re
7
+ import utils
8
+
9
+ buck2uni = {
10
+ "'": u"\u0621", # hamza-on-the-line
11
+ "|": u"\u0622", # madda
12
+ ">": u"\u0623", # hamza-on-'alif
13
+ "&": u"\u0624", # hamza-on-waaw
14
+ "<": u"\u0625", # hamza-under-'alif
15
+ "}": u"\u0626", # hamza-on-yaa'
16
+ "A": u"\u0627", # bare 'alif
17
+ "b": u"\u0628", # baa'
18
+ "p": u"\u0629", # taa' marbuuTa
19
+ "t": u"\u062A", # taa'
20
+ "v": u"\u062B", # thaa'
21
+ "j": u"\u062C", # jiim
22
+ "H": u"\u062D", # Haa'
23
+ "x": u"\u062E", # khaa'
24
+ "d": u"\u062F", # daal
25
+ "*": u"\u0630", # dhaal
26
+ "r": u"\u0631", # raa'
27
+ "z": u"\u0632", # zaay
28
+ "s": u"\u0633", # siin
29
+ "$": u"\u0634", # shiin
30
+ "S": u"\u0635", # Saad
31
+ "D": u"\u0636", # Daad
32
+ "T": u"\u0637", # Taa'
33
+ "Z": u"\u0638", # Zaa' (DHaa')
34
+ "E": u"\u0639", # cayn
35
+ "g": u"\u063A", # ghayn
36
+ "_": u"\u0640", # taTwiil
37
+ "f": u"\u0641", # faa'
38
+ "q": u"\u0642", # qaaf
39
+ "k": u"\u0643", # kaaf
40
+ "l": u"\u0644", # laam
41
+ "m": u"\u0645", # miim
42
+ "n": u"\u0646", # nuun
43
+ "h": u"\u0647", # haa'
44
+ "w": u"\u0648", # waaw
45
+ "Y": u"\u0649", # 'alif maqSuura
46
+ "y": u"\u064A", # yaa'
47
+ "F": u"\u064B", # fatHatayn
48
+ "N": u"\u064C", # Dammatayn
49
+ "K": u"\u064D", # kasratayn
50
+ "a": u"\u064E", # fatHa
51
+ "u": u"\u064F", # Damma
52
+ "i": u"\u0650", # kasra
53
+ "~": u"\u0651", # shaddah
54
+ "o": u"\u0652", # sukuun
55
+ "`": u"\u0670", # dagger 'alif
56
+ "{": u"\u0671", # waSla
57
+ }
58
+
59
+ # For a reverse transliteration (Unicode -> Buckwalter), a dictionary
60
+ # which is the reverse of the above buck2uni is essential.
61
+ uni2buck = {}
62
+
63
+ # Iterate through all the items in the buck2uni dict.
64
+ for (key, value) in buck2uni.items():
65
+ # The value from buck2uni becomes a key in uni2buck, and vice
66
+ # versa for the keys.
67
+ uni2buck[value] = key
68
+
69
+ # add special characters
70
+ uni2buck[u"\ufefb"] = "lA"
71
+ uni2buck[u"\ufef7"] = "l>"
72
+ uni2buck[u"\ufef5"] = "l|"
73
+ uni2buck[u"\ufef9"] = "l<"
74
+
75
+ # clean the arabic text from unwanted characters that may cause problem while building the language model
76
+ def clean_text(text):
77
+ text = re.sub(u"[\ufeff]", "", text, flags=re.UNICODE) # strip Unicode Character 'ZERO WIDTH NO-BREAK SPACE' (U+FEFF). For more info, check http://www.fileformat.info/info/unicode/char/feff/index.htm
78
+ text = utils.remove_non_arabic(text)
79
+ text = utils.strip_tashkeel(text)
80
+ text = utils.strip_tatweel(text)
81
+ return text
82
+
83
+ # convert a single word into buckwalter and vice versa
84
+ def transliterate_word(input_word, direction='bw2ar'):
85
+ output_word = ''
86
+ # Loop over each character in the string, bw_word.
87
+ for char in input_word:
88
+ # Look up current char in the dictionary to get its
89
+ # respective value. If there is no match, e.g., chars like
90
+ # spaces, then just stick with the current char without any
91
+ # conversion.
92
+ # if type(char) == bytes:
93
+ # char = char.decode('ascii')
94
+ if direction == 'bw2ar':
95
+ #print('in bw2ar')
96
+ output_word += buck2uni.get(char, char)
97
+ elif direction == 'ar2bw':
98
+ #print('in ar2bw')
99
+ output_word += uni2buck.get(char, char)
100
+ else:
101
+ sys.stderr.write('Error: invalid direction!')
102
+ sys.exit()
103
+ return output_word
104
+
105
+
106
+ # convert a text into buckwalter and vice versa
107
+ def transliterate_text(input_text, direction='bw2ar'):
108
+ output_text = ''
109
+ for input_word in input_text.split(' '):
110
+ output_text += transliterate_word(input_word, direction) + ' '
111
+
112
+ return output_text[:-1] # remove the last space ONLY
113
+
114
+
115
+ if __name__ == '__main__':
116
+ if len(sys.argv) < 2:
117
+ sys.stderr.write('Usage: INPUT TEXT | python {} DIRECTION(bw2ar|ar2bw)'.format(sys.argv[1]))
118
+ exit(1)
119
+ for line in sys.stdin:
120
+ line = line if sys.argv[1] == 'bw2ar' else clean_text(line)
121
+ output_text = transliterate_text(line, direction=str(sys.argv[1]))
122
+ if output_text.strip() != '':
123
+ sys.stdout.write('{}\n'.format(output_text.strip()))
124
+
125
+
126
+
models/catt-diacritizer/catt_ed_decoder.int8.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b5819a139e8b27ed2bd8d0209b6a75206e86f1a3d3dc1b359f75b4fcbcfa2e4e
3
+ size 15158542
models/catt-diacritizer/catt_ed_decoder.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:14b5580ecb33f8687cdccb14edb120afa2a2cfbf7caf4444fa4a2a86548c375c
3
+ size 52991044
models/catt-diacritizer/catt_ed_encoder.int8.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d5dff46db736a1b1c17babad46db499322cd0c30da65121fd45446b03e806db8
3
+ size 11845630
models/catt-diacritizer/catt_ed_encoder.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3ec637243feb55f3b83b35264a5daa9c23e1261097ce42ecc85163fba5e1d962
3
+ size 40235317
models/catt-diacritizer/catt_eo.int8.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:59ba20d02a4b507a6b816a9b46b1230ba73d341c6731aa48da5de99388a39fca
3
+ size 21564456
models/catt-diacritizer/catt_eo.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0f4040ae643c2a5aa40221bb845e146d8826690a95501c9250f72dbc5a12479c
3
+ size 78308696
models/catt-diacritizer/catt_models_onnx.py ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import math
3
+ import numpy as np
4
+ from tqdm import tqdm
5
+ from pathlib import Path
6
+ import onnxruntime as ort
7
+ from abc import ABC, abstractmethod
8
+ from utils import remove_non_arabic
9
+ from tashkeel_tokenizer_onnx import TashkeelTokenizer
10
+
11
+
12
+ class BaseONNXTashkeel(ABC):
13
+ """
14
+ Base class for ONNX-based Arabic text diacritization models
15
+
16
+ This abstract base class provides common functionality for Arabic text
17
+ diacritization using pre-trained ONNX models.
18
+ """
19
+
20
+ def __init__(self, encoder_path=None, decoder_path=None, tokenizer=None,
21
+ auto_preprocess=True, model_type="ed_model"):
22
+ """
23
+ Initialize ONNX Tashkeel model
24
+
25
+ Args:
26
+ encoder_path (str, optional): Path to encoder ONNX file
27
+ decoder_path (str, optional): Path to decoder ONNX file
28
+ tokenizer (TashkeelTokenizer, optional): Tokenizer instance
29
+ auto_preprocess (bool): Automatically preprocess text (remove non-Arabic)
30
+ model_type (str): Type of model ("ed_model" or "eo_model")
31
+ """
32
+ self.tokenizer = tokenizer or TashkeelTokenizer()
33
+ self.auto_preprocess = auto_preprocess
34
+ self.src_pad_idx = self.tokenizer.letters_map['<PAD>']
35
+
36
+ # Set model-specific paths
37
+ encoder_path, decoder_path = self._get_model_paths(
38
+ encoder_path, decoder_path, model_type
39
+ )
40
+
41
+ # Load ONNX sessions
42
+ self._load_onnx_sessions(encoder_path, decoder_path)
43
+
44
+ def _get_model_paths(self, encoder_path, decoder_path, model_type):
45
+ """Get default model paths if not provided"""
46
+ if encoder_path is None or decoder_path is None:
47
+ package_dir = Path(__file__).parent
48
+ model_dir = package_dir / "onnx_models" / model_type
49
+ encoder_path = encoder_path or str(model_dir / "encoder.onnx")
50
+ decoder_path = decoder_path or str(model_dir / "decoder.onnx")
51
+ return encoder_path, decoder_path
52
+
53
+ def _load_onnx_sessions(self, encoder_path, decoder_path):
54
+ """Load ONNX runtime sessions"""
55
+ if not os.path.exists(encoder_path):
56
+ raise FileNotFoundError(f"Encoder ONNX file not found: {encoder_path}")
57
+ if not os.path.exists(decoder_path):
58
+ raise FileNotFoundError(f"Decoder ONNX file not found: {decoder_path}")
59
+
60
+ _opt = ort.SessionOptions()
61
+ import os as _os
62
+ _t = _os.environ.get("TT_ORT_THREADS")
63
+ if _t:
64
+ _opt.intra_op_num_threads = int(_t); _opt.inter_op_num_threads = int(_t)
65
+ self.encoder_session = ort.InferenceSession(encoder_path, sess_options=_opt)
66
+ self.decoder_session = ort.InferenceSession(decoder_path, sess_options=_opt)
67
+ print("✅ Loaded model successfully!")
68
+
69
+ def _preprocess_text(self, text):
70
+ """Preprocess text if auto_preprocess is enabled"""
71
+ return remove_non_arabic(text) if self.auto_preprocess else text
72
+
73
+ def _make_pad_mask(self, q, k, q_pad_idx, k_pad_idx):
74
+ """Create padding mask (numpy version)"""
75
+ len_q, len_k = q.shape[1], k.shape[1]
76
+ k_mask = (k != k_pad_idx).reshape(-1, 1, 1, len_k)
77
+ k_mask = np.repeat(k_mask, len_q, axis=2)
78
+ q_mask = (q != q_pad_idx).reshape(-1, 1, len_q, 1)
79
+ q_mask = np.repeat(q_mask, len_k, axis=3)
80
+ return k_mask & q_mask
81
+
82
+ def _make_no_peak_mask(self, q, k):
83
+ """Create no-peak mask (numpy version)"""
84
+ len_q, len_k = q.shape[1], k.shape[1]
85
+ return np.tril(np.ones((len_q, len_k))).astype(bool)
86
+
87
+ def _run_encoder(self, src_np):
88
+ """Run encoder using ONNX"""
89
+ src_mask_np = self._make_pad_mask(
90
+ src_np, src_np, self.src_pad_idx, self.src_pad_idx
91
+ ).astype(bool)
92
+
93
+ encoder_inputs = {'src': src_np, 'src_mask': src_mask_np}
94
+ return self.encoder_session.run(None, encoder_inputs)[0]
95
+
96
+ def _pad_seq(self, sequences, batch_first=True, padding_value=0.0, prepadding=True):
97
+ """Pad sequences to same length"""
98
+ lens = [seq.shape[0] for seq in sequences]
99
+ max_len = max(lens)
100
+
101
+ # Create padded array
102
+ if len(sequences[0].shape) == 1:
103
+ padded_sequences = np.full((len(sequences), max_len), padding_value, dtype=sequences[0].dtype)
104
+ else:
105
+ shape = (len(sequences), max_len) + sequences[0].shape[1:]
106
+ padded_sequences = np.full(shape, padding_value, dtype=sequences[0].dtype)
107
+
108
+ # Fill with actual sequence data
109
+ for i, seq in enumerate(sequences):
110
+ padded_sequences[i, :lens[i]] = seq
111
+
112
+ if prepadding:
113
+ for i in range(len(lens)):
114
+ padded_sequences[i] = np.roll(padded_sequences[i], max_len - lens[i], axis=0)
115
+
116
+ if not batch_first:
117
+ padded_sequences = padded_sequences.transpose(1, 0, *range(2, padded_sequences.ndim))
118
+
119
+ return padded_sequences
120
+
121
+ def _get_batches(self, X, batch_size=16):
122
+ """Generate batches from list"""
123
+ num_batches = math.ceil(len(X) / batch_size)
124
+ for i in range(num_batches):
125
+ yield X[i*batch_size : (i+1)*batch_size]
126
+
127
+ def _prepare_batch_input(self, texts_mini):
128
+ """Prepare input batch for processing"""
129
+ input_ids_list = []
130
+ for text in texts_mini:
131
+ input_ids, _ = self.tokenizer.encode(text, test_match=False)
132
+ input_ids_list.append(input_ids)
133
+
134
+ return self._pad_seq(
135
+ input_ids_list,
136
+ batch_first=True,
137
+ padding_value=self.tokenizer.letters_map['<PAD>'],
138
+ prepadding=False
139
+ )
140
+
141
+ def _apply_space_mask(self, predictions, input_ids):
142
+ """Apply space masking to predictions"""
143
+ space_mask = (input_ids == self.tokenizer.letters_map[' '])
144
+ predictions[space_mask] = self.tokenizer.tashkeel_map[self.tokenizer.no_tashkeel_tag]
145
+ return predictions
146
+
147
+ @abstractmethod
148
+ def _process_batch(self, batch_input_ids):
149
+ """Process a batch of input IDs - to be implemented by subclasses"""
150
+ pass
151
+
152
+ def do_tashkeel_batch(self, texts, batch_size=16, verbose=True):
153
+ """
154
+ Add tashkeel to multiple Arabic texts
155
+
156
+ Args:
157
+ texts (list): List of input Arabic texts
158
+ batch_size (int): Batch size for processing
159
+ verbose (bool): Show progress bar
160
+
161
+ Returns:
162
+ list: List of texts with tashkeel
163
+ """
164
+ processed_texts = [self._preprocess_text(text) for text in texts]
165
+ text_with_tashkeel = []
166
+ data_iter = self._get_batches(processed_texts, batch_size)
167
+
168
+ if verbose:
169
+ num_batches = math.ceil(len(processed_texts) / batch_size)
170
+ data_iter = tqdm(data_iter, total=num_batches)
171
+
172
+ for texts_mini in data_iter:
173
+ batch_input_ids = self._prepare_batch_input(texts_mini)
174
+ predictions = self._process_batch(batch_input_ids)
175
+ text_with_tashkeel_mini = self.tokenizer.decode(batch_input_ids, predictions)
176
+ text_with_tashkeel.extend(text_with_tashkeel_mini)
177
+
178
+ return text_with_tashkeel
179
+
180
+ def do_tashkeel(self, text, verbose=True):
181
+ """
182
+ Add tashkeel to Arabic text
183
+
184
+ Args:
185
+ text (str): Input Arabic text
186
+
187
+ Returns:
188
+ str: Text with tashkeel
189
+ """
190
+ return self.do_tashkeel_batch([text], verbose=False)[0]
191
+
192
+
193
+ class CATTEncoderDecoder(BaseONNXTashkeel):
194
+ """
195
+ ONNX-based Arabic text diacritization model using encoder-decoder architecture
196
+ """
197
+
198
+ def __init__(self, encoder_path=None, decoder_path=None, tokenizer=None,
199
+ auto_preprocess=True):
200
+ """Initialize encoder-decoder model"""
201
+ super().__init__(encoder_path, decoder_path, tokenizer, auto_preprocess, "ed_model")
202
+ self.trg_pad_idx = self.tokenizer.tashkeel_map['<PAD>']
203
+
204
+ def _run_decoder(self, trg_np, enc_src_np, src_np):
205
+ """Run decoder using ONNX"""
206
+ src_trg_mask_np = self._make_pad_mask(
207
+ trg_np, src_np, self.trg_pad_idx, self.src_pad_idx
208
+ ).astype(bool)
209
+
210
+ trg_mask_np = self._make_pad_mask(
211
+ trg_np, trg_np, self.trg_pad_idx, self.trg_pad_idx
212
+ ).astype(bool)
213
+
214
+ no_peak_mask_np = self._make_no_peak_mask(trg_np, trg_np)
215
+ trg_mask_np = trg_mask_np & no_peak_mask_np
216
+
217
+ decoder_inputs = {
218
+ 'trg': trg_np,
219
+ 'enc_src': enc_src_np,
220
+ 'trg_mask': trg_mask_np,
221
+ 'src_trg_mask': src_trg_mask_np
222
+ }
223
+
224
+ return self.decoder_session.run(None, decoder_inputs)[0]
225
+
226
+ def _process_batch(self, batch_input_ids):
227
+ """Process batch using encoder-decoder with autoregressive decoding"""
228
+ src = batch_input_ids
229
+ enc_src = self._run_encoder(src)
230
+
231
+ # Initialize target with BOS token
232
+ target_ids = np.array([[self.tokenizer.tashkeel_map['<BOS>']]] * len(batch_input_ids), dtype=np.int64)
233
+
234
+ # Autoregressive decoding loop
235
+ for i in range(src.shape[1] - 1):
236
+ preds = self._run_decoder(target_ids, enc_src, src)
237
+ next_tokens = np.argmax(preds[:, -1, :], axis=1)
238
+ target_ids = np.concatenate([target_ids, np.expand_dims(next_tokens, 1)], axis=1)
239
+
240
+ # Apply space handling logic
241
+ target_ids = self._apply_space_mask(target_ids, src[:, :target_ids.shape[1]])
242
+
243
+ return target_ids
244
+
245
+
246
+ class CATTEncoderOnly(BaseONNXTashkeel):
247
+ """
248
+ ONNX-based Arabic text diacritization model using encoder-only architecture
249
+ """
250
+
251
+ def __init__(self, encoder_path=None, decoder_path=None, tokenizer=None,
252
+ auto_preprocess=True):
253
+ """Initialize encoder-only model"""
254
+ super().__init__(encoder_path, decoder_path, tokenizer, auto_preprocess, "eo_model")
255
+
256
+ def _run_decoder(self, enc_src_np):
257
+ """Run decoder using ONNX"""
258
+ decoder_inputs = {'enc_src': enc_src_np}
259
+ return self.decoder_session.run(None, decoder_inputs)[0]
260
+
261
+ def _process_batch(self, batch_input_ids):
262
+ """Process batch using encoder-only architecture"""
263
+ # Remove BOS and EOS tokens
264
+ batch_input_ids = batch_input_ids[:, 1:-1]
265
+
266
+ enc_src = self._run_encoder(batch_input_ids)
267
+ preds = self._run_decoder(enc_src)
268
+ predictions = np.argmax(preds, axis=-1)
269
+
270
+ # Apply space masking
271
+ predictions = self._apply_space_mask(predictions, batch_input_ids)
272
+
273
+ return predictions
models/catt-diacritizer/source.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ https://huggingface.co/TigreGotico/catt-diacritizer
models/catt-diacritizer/tashkeel_tokenizer_onnx.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import re
3
+ import numpy as np
4
+ import bw2ar
5
+ from utils import HARAKAT_PAT
6
+
7
+
8
+ class TashkeelTokenizer:
9
+
10
+ def __init__(self):
11
+ self.letters = [' ', '$', '&', "'", '*', '<', '>', 'A', 'D', 'E', 'H', 'S', 'T', 'Y', 'Z',
12
+ 'b', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't',
13
+ 'v', 'w', 'x', 'y', 'z', '|', '}'
14
+ ]
15
+ self.letters = ['<PAD>', '<BOS>', '<EOS>'] + self.letters + ['<MASK>']
16
+
17
+ self.no_tashkeel_tag = '<NT>'
18
+ self.tashkeel_list = ['<NT>', '<SD>', '<SDD>', '<SF>', '<SFF>', '<SK>',
19
+ '<SKK>', 'F', 'K', 'N', 'a', 'i', 'o', 'u', '~']
20
+
21
+ self.tashkeel_list = ['<PAD>', '<BOS>', '<EOS>'] + self.tashkeel_list
22
+
23
+ self.tashkeel_map = {c:i for i,c in enumerate(self.tashkeel_list)}
24
+ self.letters_map = {c:i for i,c in enumerate(self.letters)}
25
+ self.inverse_tags = {
26
+ '~a': '<SF>', # shaddah and fatHa
27
+ '~u': '<SD>', # shaddah and Damma
28
+ '~i': '<SK>', # shaddah and kasra
29
+ '~F': '<SFF>', # shaddah and fatHatayn
30
+ '~N': '<SDD>', # shaddah and Dammatayn
31
+ '~K': '<SKK>' # shaddah and kasratayn
32
+ }
33
+ self.tags = {v:k for k,v in self.inverse_tags.items()}
34
+ self.shaddah_last = ['a~', 'u~', 'i~', 'F~', 'N~', 'K~']
35
+ self.shaddah_first = ['~a', '~u', '~i', '~F', '~N', '~K']
36
+ self.tahkeel_chars = ['F','N','K','a', 'u', 'i', '~', 'o']
37
+
38
+ def clean_text(self, text):
39
+ text = re.sub(u'[%s]' % u'\u0640', '', text) # strip tatweel
40
+ text = text.replace('ٱ', 'ا')
41
+ return ' '.join(re.sub(u"[^\u0621-\u063A\u0640-\u0652\u0670\u0671\ufefb\ufef7\ufef5\ufef9 ]", " ", text, flags=re.UNICODE).split())
42
+
43
+ def check_match(self, text_with_tashkeel, letter_n_tashkeel_pairs):
44
+ text_with_tashkeel = text_with_tashkeel.strip()
45
+ # test if the reconstructed text with tashkeel is the same as the original one
46
+ syn_text = self.combine_tashkeel_with_text(letter_n_tashkeel_pairs)
47
+ return syn_text == text_with_tashkeel or syn_text == self.unify_shaddah_position(text_with_tashkeel)
48
+
49
+ def unify_shaddah_position(self, text_with_tashkeel):
50
+ # unify the order of shaddah and the harakah to make shaddah always at the beginning
51
+ for i in range(len(self.shaddah_first)):
52
+ text_with_tashkeel = text_with_tashkeel.replace(self.shaddah_last[i], self.shaddah_first[i])
53
+ return text_with_tashkeel
54
+
55
+ def split_tashkeel_from_text(self, text_with_tashkeel, test_match=True):
56
+ text_with_tashkeel = self.clean_text(text_with_tashkeel)
57
+ text_with_tashkeel = bw2ar.transliterate_text(text_with_tashkeel, 'ar2bw')
58
+ text_with_tashkeel = text_with_tashkeel.replace('`', '') # remove dagger 'alif
59
+
60
+ # unify the order of shaddah and the harakah to make shaddah always at the beginning
61
+ text_with_tashkeel = self.unify_shaddah_position(text_with_tashkeel)
62
+
63
+ # remove duplicated harakat
64
+ for i in range(len(self.tahkeel_chars)):
65
+ text_with_tashkeel = text_with_tashkeel.replace(self.tahkeel_chars[i]*2, self.tahkeel_chars[i])
66
+
67
+ letter_n_tashkeel_pairs = []
68
+ for i in range(len(text_with_tashkeel)): # go over the whole text
69
+ # check if the first character is a normal letter and the second character is a tashkeel
70
+ if i < (len(text_with_tashkeel) - 1) and not text_with_tashkeel[i] in self.tashkeel_list and text_with_tashkeel[i+1] in self.tashkeel_list:
71
+ # IMPORTANT: check if tashkeel is Shaddah, then there might be another Tashkeel char associated with it. If so,
72
+ # replace both Shaddah and the Tashkeel chars with the appropriate tag
73
+ if text_with_tashkeel[i+1] == '~':
74
+ # IMPORTANT: the following if statement depends on the concept of short circuit!!
75
+ # The first condition checks if there are still more chars before it access position i+2
76
+ # "text_with_tashkeel[i+2]" since it causes "index out of range" exception. Notice that
77
+ # Shaddah here is put in the first position before the Harakah.
78
+ if i+2 < len(text_with_tashkeel) and f'~{text_with_tashkeel[i+2]}' in self.inverse_tags:
79
+ letter_n_tashkeel_pairs.append((text_with_tashkeel[i], self.inverse_tags[f'~{text_with_tashkeel[i+2]}']))
80
+ else:
81
+ # if it is only Shaddah, just add it to the list
82
+ letter_n_tashkeel_pairs.append((text_with_tashkeel[i], '~'))
83
+ else:
84
+ letter_n_tashkeel_pairs.append((text_with_tashkeel[i], text_with_tashkeel[i+1]))
85
+ # if the character at position i is a normal letter and has no Tashkeel, then add
86
+ # it with the tag "self.no_tashkeel_tag"
87
+ # IMPORTANT: this elif block ensures also that there is no two or more consecutive tashkeel other than shaddah
88
+ elif not text_with_tashkeel[i] in self.tashkeel_list:
89
+ letter_n_tashkeel_pairs.append((text_with_tashkeel[i], self.no_tashkeel_tag))
90
+
91
+ if test_match:
92
+ # test if the split is done correctly by ensuring that we can retrieve back the original text
93
+ assert self.check_match(text_with_tashkeel, letter_n_tashkeel_pairs)
94
+ return [('<BOS>', '<BOS>')] + letter_n_tashkeel_pairs + [('<EOS>', '<EOS>')]
95
+
96
+ def combine_tashkeel_with_text(self, letter_n_tashkeel_pairs):
97
+ combined_with_tashkeel = []
98
+ for letter, tashkeel in letter_n_tashkeel_pairs:
99
+ combined_with_tashkeel.append(letter)
100
+ if tashkeel in self.tags:
101
+ combined_with_tashkeel.append(self.tags[tashkeel])
102
+ elif tashkeel != self.no_tashkeel_tag:
103
+ combined_with_tashkeel.append(tashkeel)
104
+ text = ''.join(combined_with_tashkeel)
105
+ return text
106
+
107
+ def encode(self, text_with_tashkeel, test_match=True):
108
+ letter_n_tashkeel_pairs = self.split_tashkeel_from_text(text_with_tashkeel, test_match)
109
+ text, tashkeel = zip(*letter_n_tashkeel_pairs)
110
+ input_ids = [self.letters_map[c] for c in text]
111
+ target_ids = [self.tashkeel_map[c] for c in tashkeel]
112
+ return np.array(input_ids, dtype=np.int64), np.array(target_ids, dtype=np.int64)
113
+
114
+ def filter_tashkeel(self, tashkeel):
115
+ tmp = []
116
+ for i, t in enumerate(tashkeel):
117
+ if i != 0 and t == '<BOS>':
118
+ t = self.no_tashkeel_tag
119
+ elif i != (len(tashkeel) - 1) and t == '<EOS>':
120
+ t = self.no_tashkeel_tag
121
+ tmp.append(t)
122
+ tashkeel = tmp
123
+ return tashkeel
124
+
125
+ def decode(self, input_ids, target_ids):
126
+ ar_texts = []
127
+ for j in range(len(input_ids)):
128
+ letters = [self.letters[i] for i in input_ids[j]]
129
+ tashkeel = [self.tashkeel_list[i] for i in target_ids[j]]
130
+
131
+ letters = list(filter(lambda x: x != '<BOS>' and x != '<EOS>' and x != '<PAD>', letters))
132
+ tashkeel = self.filter_tashkeel(tashkeel)
133
+ tashkeel = list(filter(lambda x: x != '<BOS>' and x != '<EOS>' and x != '<PAD>', tashkeel))
134
+
135
+ # VERY IMPORTANT NOTE: zip takes min(len(letters), len(tashkeel)) and discard the reset of letters / tashkeels
136
+ letter_n_tashkeel_pairs = list(zip(letters, tashkeel))
137
+ bw_text = self.combine_tashkeel_with_text(letter_n_tashkeel_pairs)
138
+ ar_text = bw2ar.transliterate_text(bw_text, 'bw2ar')
139
+ ar_texts.append(ar_text)
140
+ return ar_texts
141
+
142
+ def get_tashkeel_with_case_ending(self, text, case_ending=True):
143
+ text_split = self.split_tashkeel_from_text(text, test_match=False)
144
+ text_spaces_indecies = [i for i, el in enumerate(text_split) if el == (' ', '<NT>')]
145
+ new_text_split = []
146
+ for i, el in enumerate(text_split):
147
+ if not case_ending and (i+1) in text_spaces_indecies:
148
+ el = (el[0], '<NT>') # no case ending
149
+ new_text_split.append(el)
150
+ letters, tashkeel = zip(*new_text_split)
151
+ return letters, tashkeel
152
+
153
+ def remove_tashkeel(self, text):
154
+ text = HARAKAT_PAT.sub('', text)
155
+ text = re.sub(u"[\u064E]", "", text, flags=re.UNICODE) # fattha
156
+ text = re.sub(u"[\u0671]", "", text, flags=re.UNICODE) # waSla
157
+ return text
158
+
models/catt-diacritizer/utils.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ note: this code is used in bw2ar.py file
3
+ """
4
+
5
+ #!/usr/bin/python
6
+ # -*- coding=utf-8 -*-
7
+ #---
8
+ # $Id: arabic.py,v 1.6 2003/04/22 17:18:22 elzubeir Exp $
9
+ #
10
+ # ------------
11
+ # Description:
12
+ # ------------
13
+ #
14
+ # Arabic codes
15
+ #
16
+ # (C) Copyright 2003, Arabeyes, Mohammed Elzubeir
17
+ # (C) Copyright 2019, Faris Abdullah Alasmary
18
+ # -----------------
19
+ # Revision Details: (Updated by Revision Control System)
20
+ # -----------------
21
+ # $Date: 2003/04/22 17:18:22 $
22
+ # $Author: elzubeir $
23
+ # $Revision: 1.6 $
24
+ # $Source: /home/arabeyes/cvs/projects/duali/pyduali/pyduali/arabic.py,v $
25
+ #
26
+ # This program is written under the BSD License.
27
+ #---
28
+ """ Constants for arabic """
29
+ import re
30
+ COMMA = u'\u060C'
31
+ SEMICOLON = u'\u061B'
32
+ QUESTION = u'\u061F'
33
+ HAMZA = u'\u0621'
34
+ ALEF_MADDA = u'\u0622'
35
+ ALEF_HAMZA_ABOVE = u'\u0623'
36
+ WAW_HAMZA = u'\u0624'
37
+ ALEF_HAMZA_BELOW = u'\u0625'
38
+ YEH_HAMZA = u'\u0626'
39
+ ALEF = u'\u0627'
40
+ BEH = u'\u0628'
41
+ TEH_MARBUTA = u'\u0629'
42
+ TEH = u'\u062a'
43
+ THEH = u'\u062b'
44
+ JEEM = u'\u062c'
45
+ HAH = u'\u062d'
46
+ KHAH = u'\u062e'
47
+ DAL = u'\u062f'
48
+ THAL = u'\u0630'
49
+ REH = u'\u0631'
50
+ ZAIN = u'\u0632'
51
+ SEEN = u'\u0633'
52
+ SHEEN = u'\u0634'
53
+ SAD = u'\u0635'
54
+ DAD = u'\u0636'
55
+ TAH = u'\u0637'
56
+ ZAH = u'\u0638'
57
+ AIN = u'\u0639'
58
+ GHAIN = u'\u063a'
59
+ TATWEEL = u'\u0640'
60
+ FEH = u'\u0641'
61
+ QAF = u'\u0642'
62
+ KAF = u'\u0643'
63
+ LAM = u'\u0644'
64
+ MEEM = u'\u0645'
65
+ NOON = u'\u0646'
66
+ HEH = u'\u0647'
67
+ WAW = u'\u0648'
68
+ ALEF_MAKSURA = u'\u0649'
69
+ YEH = u'\u064a'
70
+ MADDA_ABOVE = u'\u0653'
71
+ HAMZA_ABOVE = u'\u0654'
72
+ HAMZA_BELOW = u'\u0655'
73
+ ZERO = u'\u0660'
74
+ ONE = u'\u0661'
75
+ TWO = u'\u0662'
76
+ THREE = u'\u0663'
77
+ FOUR = u'\u0664'
78
+ FIVE = u'\u0665'
79
+ SIX = u'\u0666'
80
+ SEVEN = u'\u0667'
81
+ EIGHT = u'\u0668'
82
+ NINE = u'\u0669'
83
+ PERCENT = u'\u066a'
84
+ DECIMAL = u'\u066b'
85
+ THOUSANDS = u'\u066c'
86
+ STAR = u'\u066d'
87
+ MINI_ALEF = u'\u0670'
88
+ ALEF_WASLA = u'\u0671'
89
+ FULL_STOP = u'\u06d4'
90
+ BYTE_ORDER_MARK = u'\ufeff'
91
+
92
+ # Diacritics
93
+ FATHATAN = u'\u064b'
94
+ DAMMATAN = u'\u064c'
95
+ KASRATAN = u'\u064d'
96
+ FATHA = u'\u064e'
97
+ DAMMA = u'\u064f'
98
+ KASRA = u'\u0650'
99
+ SHADDA = u'\u0651'
100
+ SUKUN = u'\u0652'
101
+
102
+ #Ligatures
103
+ LAM_ALEF = u'\ufefb'
104
+ LAM_ALEF_HAMZA_ABOVE = u'\ufef7'
105
+ LAM_ALEF_HAMZA_BELOW = u'\ufef9'
106
+ LAM_ALEF_MADDA_ABOVE = u'\ufef5'
107
+ SIMPLE_LAM_ALEF = u'\u0644\u0627'
108
+ SIMPLE_LAM_ALEF_HAMZA_ABOVE = u'\u0644\u0623'
109
+ SIMPLE_LAM_ALEF_HAMZA_BELOW = u'\u0644\u0625'
110
+ SIMPLE_LAM_ALEF_MADDA_ABOVE = u'\u0644\u0622'
111
+
112
+
113
+ HARAKAT_PAT = re.compile(u"["+u"".join([FATHATAN, DAMMATAN, KASRATAN,
114
+ FATHA, DAMMA, KASRA, SUKUN,
115
+ SHADDA])+u"]")
116
+ HAMZAT_PAT = re.compile(u"["+u"".join([WAW_HAMZA, YEH_HAMZA])+u"]")
117
+ ALEFAT_PAT = re.compile(u"["+u"".join([ALEF_MADDA, ALEF_HAMZA_ABOVE,
118
+ ALEF_HAMZA_BELOW, HAMZA_ABOVE,
119
+ HAMZA_BELOW])+u"]")
120
+ LAMALEFAT_PAT = re.compile(u"["+u"".join([LAM_ALEF,
121
+ LAM_ALEF_HAMZA_ABOVE,
122
+ LAM_ALEF_HAMZA_BELOW,
123
+ LAM_ALEF_MADDA_ABOVE])+u"]")
124
+
125
+ def strip_tashkeel(text):
126
+ text = HARAKAT_PAT.sub('', text)
127
+ text = re.sub(u"[\u064E]", "", text, flags=re.UNICODE) # fattha
128
+ text = re.sub(u"[\u0671]", "", text, flags=re.UNICODE) # waSla
129
+ return text
130
+
131
+ def strip_tatweel(text):
132
+ return re.sub(u'[%s]' % TATWEEL, '', text)
133
+
134
+ # remove removing Tashkeel + removing Tatweel + non Arabic chars
135
+ def remove_non_arabic(text):
136
+ text = strip_tashkeel(text)
137
+ text = strip_tatweel(text)
138
+ return ' '.join(re.sub(u"[^\u0621-\u063A\u0641-\u064A ]", " ", text, flags=re.UNICODE).split())
139
+
spaces/arabic-auto-tashkeel/.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
spaces/arabic-auto-tashkeel/README.md ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Arabic Auto Tashkeel
3
+ emoji: 🔥
4
+ colorFrom: indigo
5
+ colorTo: yellow
6
+ sdk: gradio
7
+ sdk_version: 4.27.0
8
+ app_file: app.py
9
+ pinned: false
10
+ license: apache-2.0
11
+ ---
12
+
13
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
spaces/arabic-auto-tashkeel/app.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import spaces
2
+ import gradio as gr
3
+ from shakkala import Shakkala
4
+ from pathlib import Path
5
+ import torch
6
+ from eo_pl import TashkeelModel as TashkeelModelEO
7
+ from ed_pl import TashkeelModel as TashkeelModelED
8
+ from tashkeel_tokenizer import TashkeelTokenizer
9
+ from utils import remove_non_arabic
10
+
11
+ # Initialize the Shakkala model
12
+ sh = Shakkala(version=3)
13
+ model, graph = sh.get_model()
14
+
15
+ def infer_shakkala(input_text):
16
+ input_int = sh.prepare_input(input_text)
17
+ logits = model.predict(input_int)[0]
18
+ predicted_harakat = sh.logits_to_text(logits)
19
+ final_output = sh.get_final_text(input_text, predicted_harakat)
20
+ print(final_output)
21
+ return final_output
22
+
23
+ # Initialize the CaTT model and tokenizer
24
+ tokenizer = TashkeelTokenizer()
25
+ eo_ckpt_path = Path(__file__).parent / 'models/best_eo_mlm_ns_epoch_193.pt'
26
+ ed_ckpt_path = Path(__file__).parent / 'models/best_ed_mlm_ns_epoch_178.pt'
27
+
28
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
29
+ max_seq_len = 1024
30
+ print('Creating Model...')
31
+ eo_model = TashkeelModelEO(tokenizer, max_seq_len=max_seq_len, n_layers=6, learnable_pos_emb=False)
32
+ ed_model = TashkeelModelED(tokenizer, max_seq_len=max_seq_len, n_layers=3, learnable_pos_emb=False)
33
+
34
+ eo_model.load_state_dict(torch.load(eo_ckpt_path, map_location=device))
35
+ eo_model.eval().to(device)
36
+ ed_model.load_state_dict(torch.load(ed_ckpt_path, map_location=device))
37
+ ed_model.eval().to(device)
38
+
39
+ @spaces.GPU()
40
+ def infer_catt(input_text, choose_model):
41
+ input_text = remove_non_arabic(input_text)
42
+ batch_size = 16
43
+ verbose = True
44
+ if choose_model == 'Encoder-Only':
45
+ output_text = eo_model.do_tashkeel_batch([input_text], batch_size, verbose)
46
+ else:
47
+ output_text = ed_model.do_tashkeel_batch([input_text], batch_size, verbose)
48
+
49
+ return output_text[0]
50
+
51
+
52
+
53
+ examples = ["السلام عليكم ورحمة الله وبركاته", "العلم نور", "الحمد لله"]
54
+
55
+ with gr.Blocks(title="Arabic Tashkeel") as demo:
56
+ gr.HTML("<center><h1>Arabic Tashkeel</h1></center>")
57
+ gr.HTML(
58
+ "<center><p>Compare different methods for adding tashkeel to Arabic text.</p></center>"
59
+ )
60
+ with gr.Tab(label="CATT"):
61
+ gr.HTML("<center><h2>CATT: Character-based Arabic Tashkeel Transformer</h2></center>")
62
+ gr.HTML("<center><a href='https://github.com/abjadai/catt'>GitHub</a> - <a href='https://arxiv.org/abs/2407.03236'>Arxiv Paper</a></center>")
63
+
64
+ with gr.Row():
65
+ with gr.Column():
66
+ text_input1 = gr.Textbox(label="Input Text", rtl=True, text_align="right")
67
+ choose_model = gr.Radio(
68
+ label="Choose Model",
69
+ value="Encoder-Decoder",
70
+ choices=["Encoder-Only", "Encoder-Decoder"],
71
+ )
72
+ with gr.Row():
73
+ clear_button1 = gr.Button(value="Clear", variant="secondary")
74
+ submit_button1 = gr.Button(value="Add Tashkeel", variant="primary")
75
+
76
+ with gr.Column():
77
+ text_output1 = gr.Textbox(label="Output Text", rtl=True, text_align="right")
78
+
79
+ gr.Examples(examples, text_input1, cache_examples=False)
80
+
81
+ submit_button1.click(infer_catt, inputs=[text_input1, choose_model], outputs=text_output1)
82
+ clear_button1.click(lambda: ("", ""), outputs=[text_input1, text_output1])
83
+
84
+ with gr.Tab(label="Shakkala"):
85
+ gr.HTML("<center><h2>Shakkala: Arabic Diacritization</h2></center>")
86
+ gr.HTML("<center><a href='https://github.com/Barqawiz/Shakkala'>GitHub</a> - <a href='https://pypi.org/project/shakkala/'>PyPi Package</a></center>")
87
+ with gr.Row():
88
+ with gr.Column():
89
+ text_input2 = gr.Textbox(label="Input Text", rtl=True, text_align="right")
90
+ with gr.Row():
91
+ clear_button2 = gr.Button(value="Clear", variant="secondary")
92
+ submit_button2 = gr.Button(value="Apply Tashkeel", variant="primary")
93
+ with gr.Column():
94
+ text_output2 = gr.Textbox(
95
+ lines=1, label="Output Text", rtl=True, text_align="right"
96
+ )
97
+
98
+ submit_button2.click(infer_shakkala, inputs=text_input2, outputs=text_output2)
99
+ clear_button2.click(lambda: ("", ""), outputs=[text_input2, text_output2])
100
+
101
+ gr.Examples(examples, text_input2, cache_examples=False)
102
+
103
+ if __name__ == '__main__':
104
+ demo.queue().launch()
spaces/arabic-auto-tashkeel/bw2ar.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ functions to convert Arabic words/text into buckwalter encoding and vice versa
3
+ """
4
+
5
+ import sys
6
+ import re
7
+ import utils
8
+
9
+ buck2uni = {
10
+ "'": u"\u0621", # hamza-on-the-line
11
+ "|": u"\u0622", # madda
12
+ ">": u"\u0623", # hamza-on-'alif
13
+ "&": u"\u0624", # hamza-on-waaw
14
+ "<": u"\u0625", # hamza-under-'alif
15
+ "}": u"\u0626", # hamza-on-yaa'
16
+ "A": u"\u0627", # bare 'alif
17
+ "b": u"\u0628", # baa'
18
+ "p": u"\u0629", # taa' marbuuTa
19
+ "t": u"\u062A", # taa'
20
+ "v": u"\u062B", # thaa'
21
+ "j": u"\u062C", # jiim
22
+ "H": u"\u062D", # Haa'
23
+ "x": u"\u062E", # khaa'
24
+ "d": u"\u062F", # daal
25
+ "*": u"\u0630", # dhaal
26
+ "r": u"\u0631", # raa'
27
+ "z": u"\u0632", # zaay
28
+ "s": u"\u0633", # siin
29
+ "$": u"\u0634", # shiin
30
+ "S": u"\u0635", # Saad
31
+ "D": u"\u0636", # Daad
32
+ "T": u"\u0637", # Taa'
33
+ "Z": u"\u0638", # Zaa' (DHaa')
34
+ "E": u"\u0639", # cayn
35
+ "g": u"\u063A", # ghayn
36
+ "_": u"\u0640", # taTwiil
37
+ "f": u"\u0641", # faa'
38
+ "q": u"\u0642", # qaaf
39
+ "k": u"\u0643", # kaaf
40
+ "l": u"\u0644", # laam
41
+ "m": u"\u0645", # miim
42
+ "n": u"\u0646", # nuun
43
+ "h": u"\u0647", # haa'
44
+ "w": u"\u0648", # waaw
45
+ "Y": u"\u0649", # 'alif maqSuura
46
+ "y": u"\u064A", # yaa'
47
+ "F": u"\u064B", # fatHatayn
48
+ "N": u"\u064C", # Dammatayn
49
+ "K": u"\u064D", # kasratayn
50
+ "a": u"\u064E", # fatHa
51
+ "u": u"\u064F", # Damma
52
+ "i": u"\u0650", # kasra
53
+ "~": u"\u0651", # shaddah
54
+ "o": u"\u0652", # sukuun
55
+ "`": u"\u0670", # dagger 'alif
56
+ "{": u"\u0671", # waSla
57
+ }
58
+
59
+ # For a reverse transliteration (Unicode -> Buckwalter), a dictionary
60
+ # which is the reverse of the above buck2uni is essential.
61
+ uni2buck = {}
62
+
63
+ # Iterate through all the items in the buck2uni dict.
64
+ for (key, value) in buck2uni.items():
65
+ # The value from buck2uni becomes a key in uni2buck, and vice
66
+ # versa for the keys.
67
+ uni2buck[value] = key
68
+
69
+ # add special characters
70
+ uni2buck[u"\ufefb"] = "lA"
71
+ uni2buck[u"\ufef7"] = "l>"
72
+ uni2buck[u"\ufef5"] = "l|"
73
+ uni2buck[u"\ufef9"] = "l<"
74
+
75
+ # clean the arabic text from unwanted characters that may cause problem while building the language model
76
+ def clean_text(text):
77
+ text = re.sub(u"[\ufeff]", "", text, flags=re.UNICODE) # strip Unicode Character 'ZERO WIDTH NO-BREAK SPACE' (U+FEFF). For more info, check http://www.fileformat.info/info/unicode/char/feff/index.htm
78
+ text = utils.remove_non_arabic(text)
79
+ text = utils.strip_tashkeel(text)
80
+ text = utils.strip_tatweel(text)
81
+ return text
82
+
83
+ # convert a single word into buckwalter and vice versa
84
+ def transliterate_word(input_word, direction='bw2ar'):
85
+ output_word = ''
86
+ # Loop over each character in the string, bw_word.
87
+ for char in input_word:
88
+ # Look up current char in the dictionary to get its
89
+ # respective value. If there is no match, e.g., chars like
90
+ # spaces, then just stick with the current char without any
91
+ # conversion.
92
+ # if type(char) == bytes:
93
+ # char = char.decode('ascii')
94
+ if direction == 'bw2ar':
95
+ #print('in bw2ar')
96
+ output_word += buck2uni.get(char, char)
97
+ elif direction == 'ar2bw':
98
+ #print('in ar2bw')
99
+ output_word += uni2buck.get(char, char)
100
+ else:
101
+ sys.stderr.write('Error: invalid direction!')
102
+ sys.exit()
103
+ return output_word
104
+
105
+
106
+ # convert a text into buckwalter and vice versa
107
+ def transliterate_text(input_text, direction='bw2ar'):
108
+ output_text = ''
109
+ for input_word in input_text.split(' '):
110
+ output_text += transliterate_word(input_word, direction) + ' '
111
+
112
+ return output_text[:-1] # remove the last space ONLY
113
+
114
+
115
+ if __name__ == '__main__':
116
+ if len(sys.argv) < 2:
117
+ sys.stderr.write('Usage: INPUT TEXT | python {} DIRECTION(bw2ar|ar2bw)'.format(sys.argv[1]))
118
+ exit(1)
119
+ for line in sys.stdin:
120
+ line = line if sys.argv[1] == 'bw2ar' else clean_text(line)
121
+ output_text = transliterate_text(line, direction=str(sys.argv[1]))
122
+ if output_text.strip() != '':
123
+ sys.stdout.write('{}\n'.format(output_text.strip()))
124
+
125
+
126
+
spaces/arabic-auto-tashkeel/ed.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import torch.nn as nn
3
+ from transformer import *
4
+
5
+ class Transformer(nn.Module):
6
+
7
+ def __init__(self, src_pad_idx, trg_pad_idx, enc_voc_size, dec_voc_size, d_model, n_head, max_len,
8
+ ffn_hidden, n_layers, drop_prob, learnable_pos_emb=True):
9
+ super().__init__()
10
+ self.src_pad_idx = src_pad_idx
11
+ self.trg_pad_idx = trg_pad_idx
12
+ self.encoder = Encoder(d_model=d_model,
13
+ n_head=n_head,
14
+ max_len=max_len,
15
+ ffn_hidden=ffn_hidden,
16
+ enc_voc_size=enc_voc_size,
17
+ drop_prob=drop_prob,
18
+ n_layers=n_layers,
19
+ padding_idx=src_pad_idx,
20
+ learnable_pos_emb=learnable_pos_emb)
21
+
22
+ self.decoder = Decoder(d_model=d_model,
23
+ n_head=n_head,
24
+ max_len=max_len,
25
+ ffn_hidden=ffn_hidden,
26
+ dec_voc_size=dec_voc_size,
27
+ drop_prob=drop_prob,
28
+ n_layers=n_layers,
29
+ padding_idx=trg_pad_idx,
30
+ learnable_pos_emb=learnable_pos_emb)
31
+
32
+ def get_device(self):
33
+ return next(self.parameters()).device
34
+
35
+ def forward(self, src, trg):
36
+ device = self.get_device()
37
+ src_mask = self.make_pad_mask(src, src, self.src_pad_idx, self.src_pad_idx).to(device)
38
+ src_trg_mask = self.make_pad_mask(trg, src, self.trg_pad_idx, self.src_pad_idx).to(device)
39
+ trg_mask = self.make_pad_mask(trg, trg, self.trg_pad_idx, self.trg_pad_idx).to(device) * \
40
+ self.make_no_peak_mask(trg, trg).to(device)
41
+ enc_src = self.encoder(src, src_mask)
42
+ output = self.decoder(trg, enc_src, trg_mask, src_trg_mask)
43
+ return output
44
+
45
+ def make_pad_mask(self, q, k, q_pad_idx, k_pad_idx):
46
+ len_q, len_k = q.size(1), k.size(1)
47
+ # batch_size x 1 x 1 x len_k
48
+ k = k.ne(k_pad_idx).unsqueeze(1).unsqueeze(2)
49
+ # batch_size x 1 x len_q x len_k
50
+ k = k.repeat(1, 1, len_q, 1)
51
+ # batch_size x 1 x len_q x 1
52
+ q = q.ne(q_pad_idx).unsqueeze(1).unsqueeze(3)
53
+ # batch_size x 1 x len_q x len_k
54
+ q = q.repeat(1, 1, 1, len_k)
55
+ mask = k & q
56
+ return mask
57
+
58
+ def make_no_peak_mask(self, q, k):
59
+ len_q, len_k = q.size(1), k.size(1)
60
+ # len_q x len_k
61
+ mask = torch.tril(torch.ones(len_q, len_k)).type(torch.BoolTensor)
62
+ return mask
63
+
spaces/arabic-auto-tashkeel/ed_pl.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import torch
3
+ import torch.nn as nn
4
+ import pytorch_lightning as pl
5
+ from torch.nn import functional as F
6
+ from torch.utils.data import DataLoader
7
+ from ed import Transformer
8
+ from tqdm import tqdm
9
+ import math
10
+ import torch
11
+ import torch.nn as nn
12
+
13
+ from torch.nn.utils.rnn import pad_sequence
14
+ # sequences is a list of tensors of shape TxH where T is the seqlen and H is the feats dim
15
+ def pad_seq(sequences, batch_first=True, padding_value=0.0, prepadding=True):
16
+ lens = [i.shape[0]for i in sequences]
17
+ padded_sequences = pad_sequence(sequences, batch_first=True, padding_value=padding_value) # NxTxH
18
+ if prepadding:
19
+ for i in range(len(lens)):
20
+ padded_sequences[i] = padded_sequences[i].roll(-lens[i])
21
+ if not batch_first:
22
+ padded_sequences = padded_sequences.transpose(0, 1) # TxNxH
23
+ return padded_sequences
24
+
25
+
26
+
27
+ def get_batches(X, batch_size=16):
28
+ num_batches = math.ceil(len(X) / batch_size)
29
+ for i in range(num_batches):
30
+ x = X[i*batch_size : (i+1)*batch_size]
31
+ yield x
32
+
33
+
34
+ class TashkeelModel(pl.LightningModule):
35
+ def __init__(self, tokenizer, max_seq_len, d_model=512, n_layers=3, n_heads=16, drop_prob=0.1, learnable_pos_emb=True):
36
+
37
+ super(TashkeelModel, self).__init__()
38
+
39
+ ffn_hidden = 4 * d_model
40
+ src_pad_idx = tokenizer.letters_map['<PAD>']
41
+ trg_pad_idx = tokenizer.tashkeel_map['<PAD>']
42
+ enc_voc_size = len(tokenizer.letters_map) # 37 + 3
43
+ dec_voc_size = len(tokenizer.tashkeel_map) # 15 + 3
44
+ self.transformer = Transformer(src_pad_idx=src_pad_idx,
45
+ trg_pad_idx=trg_pad_idx,
46
+ d_model=d_model,
47
+ enc_voc_size=enc_voc_size,
48
+ dec_voc_size=dec_voc_size,
49
+ max_len=max_seq_len,
50
+ ffn_hidden=ffn_hidden,
51
+ n_head=n_heads,
52
+ n_layers=n_layers,
53
+ drop_prob=drop_prob,
54
+ learnable_pos_emb=learnable_pos_emb
55
+ )
56
+
57
+ self.criterion = nn.CrossEntropyLoss(ignore_index=tokenizer.tashkeel_map['<PAD>'])
58
+ self.tokenizer = tokenizer
59
+
60
+
61
+ def forward(self, x, y=None):
62
+ y_pred = self.transformer(x, y)
63
+ return y_pred
64
+
65
+
66
+ def training_step(self, batch, batch_idx):
67
+ input_ids, target_ids = batch
68
+ input_ids = input_ids[:, :-1]
69
+ y_in = target_ids[:, :-1]
70
+ y_out = target_ids[:, 1:]
71
+ y_pred = self(input_ids, y_in)
72
+ loss = self.criterion(y_pred.transpose(1, 2), y_out)
73
+
74
+ self.log('train_loss', loss, prog_bar=True)
75
+ sch = self.lr_schedulers()
76
+ sch.step()
77
+ self.log('lr', sch.get_last_lr()[0], prog_bar=True)
78
+ return loss
79
+
80
+
81
+ def validation_step(self, batch, batch_idx):
82
+ input_ids, target_ids = batch
83
+ input_ids = input_ids[:, :-1]
84
+ y_in = target_ids[:, :-1]
85
+ y_out = target_ids[:, 1:]
86
+ y_pred = self(input_ids, y_in)
87
+ loss = self.criterion(y_pred.transpose(1, 2), y_out)
88
+
89
+ pred_text_with_tashkeels = self.tokenizer.decode(input_ids, y_pred.argmax(2).squeeze())
90
+ true_text_with_tashkeels = self.tokenizer.decode(input_ids, y_out)
91
+ total_val_der_distance = 0
92
+ total_val_der_ref_length = 0
93
+ for i in range(len(true_text_with_tashkeels)):
94
+ pred_text_with_tashkeel = pred_text_with_tashkeels[i]
95
+ true_text_with_tashkeel = true_text_with_tashkeels[i]
96
+ val_der = self.tokenizer.compute_der(true_text_with_tashkeel, pred_text_with_tashkeel)
97
+ total_val_der_distance += val_der['distance']
98
+ total_val_der_ref_length += val_der['ref_length']
99
+
100
+ total_der_error = total_val_der_distance / total_val_der_ref_length
101
+ self.log('val_loss', loss)
102
+ self.log('val_der', torch.FloatTensor([total_der_error]))
103
+ self.log('val_der_distance', torch.FloatTensor([total_val_der_distance]))
104
+ self.log('val_der_ref_length', torch.FloatTensor([total_val_der_ref_length]))
105
+
106
+
107
+ def test_step(self, batch, batch_idx):
108
+ input_ids, target_ids = batch
109
+ y_pred = self(input_ids, None)
110
+ loss = self.criterion(y_pred.transpose(1, 2), target_ids)
111
+ self.log('test_loss', loss)
112
+
113
+
114
+ def configure_optimizers(self):
115
+ optimizer = torch.optim.AdamW(self.parameters(), lr=3e-4)
116
+ #max_iters = 10000
117
+ #lr_scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=max_iters, eta_min=3e-6)
118
+ gamma = 1 / 1.000001
119
+ #gamma = 1 / 1.0001
120
+ lr_scheduler = torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma)
121
+ opts = {"optimizer": optimizer, "lr_scheduler": lr_scheduler}
122
+ return opts
123
+
124
+
125
+ @torch.no_grad()
126
+ def do_tashkeel_batch(self, texts, batch_size=16, verbose=True):
127
+ self.eval()
128
+ device = next(self.parameters()).device
129
+ text_with_tashkeel = []
130
+ data_iter = get_batches(texts, batch_size)
131
+ if verbose:
132
+ num_batches = math.ceil(len(texts) / batch_size)
133
+ data_iter = tqdm(data_iter, total=num_batches)
134
+ for texts_mini in data_iter:
135
+ input_ids_list = []
136
+ for text in texts_mini:
137
+ input_ids, _ = self.tokenizer.encode(text, test_match=False)
138
+ input_ids_list.append(input_ids)
139
+ batch_input_ids = pad_seq(input_ids_list, batch_first=True, padding_value=self.tokenizer.letters_map['<PAD>'], prepadding=False)
140
+ target_ids = torch.LongTensor([[self.tokenizer.tashkeel_map['<BOS>']]] * len(texts_mini)).to(device)
141
+ src = batch_input_ids.to(device)
142
+
143
+ src_mask = self.transformer.make_pad_mask(src, src, self.transformer.src_pad_idx, self.transformer.src_pad_idx).to(device)
144
+ enc_src = self.transformer.encoder(src, src_mask)
145
+
146
+ for i in range(src.shape[1] - 1):
147
+ trg = target_ids
148
+ src_trg_mask = self.transformer.make_pad_mask(trg, src, self.transformer.trg_pad_idx, self.transformer.src_pad_idx).to(device)
149
+ trg_mask = self.transformer.make_pad_mask(trg, trg, self.transformer.trg_pad_idx, self.transformer.trg_pad_idx).to(device) * \
150
+ self.transformer.make_no_peak_mask(trg, trg).to(device)
151
+
152
+ preds = self.transformer.decoder(trg, enc_src, trg_mask, src_trg_mask)
153
+ # IMPORTANT NOTE: the following code snippet is to FORCE the prediction of the input space char to output no_tashkeel tag '<NT>'
154
+ target_ids = torch.cat([target_ids, preds[:, -1].argmax(1).unsqueeze(1)], axis=1)
155
+ target_ids[self.tokenizer.letters_map[' '] == src[:, :target_ids.shape[1]]] = self.tokenizer.tashkeel_map[self.tokenizer.no_tashkeel_tag]
156
+ # target_ids = torch.cat([target_ids, preds[:, -1].argmax(1).unsqueeze(1)], axis=1)
157
+ text_with_tashkeel_mini = self.tokenizer.decode(src, target_ids)
158
+ text_with_tashkeel += text_with_tashkeel_mini
159
+ return text_with_tashkeel
160
+
161
+
162
+ @torch.no_grad()
163
+ def do_tashkeel(self, text):
164
+ return self.do_tashkeel_batch([text])[0]
spaces/arabic-auto-tashkeel/eo.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch.nn as nn
2
+ from transformer import *
3
+
4
+ class Transformer(nn.Module):
5
+ def __init__(self, src_pad_idx, trg_pad_idx, enc_voc_size, dec_voc_size, d_model, n_head, max_len,
6
+ ffn_hidden, n_layers, drop_prob, learnable_pos_emb=True):
7
+ super().__init__()
8
+ self.src_pad_idx = src_pad_idx
9
+ self.trg_pad_idx = trg_pad_idx
10
+ self.encoder = Encoder(d_model=d_model,
11
+ n_head=n_head,
12
+ max_len=max_len,
13
+ ffn_hidden=ffn_hidden,
14
+ enc_voc_size=enc_voc_size,
15
+ drop_prob=drop_prob,
16
+ n_layers=n_layers,
17
+ padding_idx=src_pad_idx,
18
+ learnable_pos_emb=learnable_pos_emb)
19
+
20
+ self.decoder = nn.Linear(d_model, dec_voc_size)
21
+
22
+ def get_device(self):
23
+ return next(self.parameters()).device
24
+
25
+ def forward(self, src):
26
+ device = self.get_device()
27
+ src_mask = self.make_pad_mask(src, src, self.src_pad_idx, self.src_pad_idx).to(device)
28
+ enc_src = self.encoder(src, src_mask)
29
+ output = self.decoder(enc_src)
30
+ return output
31
+
32
+ def make_pad_mask(self, q, k, q_pad_idx, k_pad_idx):
33
+ len_q, len_k = q.size(1), k.size(1)
34
+ # batch_size x 1 x 1 x len_k
35
+ k = k.ne(k_pad_idx).unsqueeze(1).unsqueeze(2)
36
+ # batch_size x 1 x len_q x len_k
37
+ k = k.repeat(1, 1, len_q, 1)
38
+ # batch_size x 1 x len_q x 1
39
+ q = q.ne(q_pad_idx).unsqueeze(1).unsqueeze(3)
40
+ # batch_size x 1 x len_q x len_k
41
+ q = q.repeat(1, 1, 1, len_k)
42
+ mask = k & q
43
+ return mask
44
+
45
+ def make_no_peak_mask(self, q, k):
46
+ len_q, len_k = q.size(1), k.size(1)
47
+ # len_q x len_k
48
+ mask = torch.tril(torch.ones(len_q, len_k)).type(torch.BoolTensor)
49
+ return mask
50
+
spaces/arabic-auto-tashkeel/eo_pl.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import torch
3
+ import torch.nn as nn
4
+ import pytorch_lightning as pl
5
+ from torch.nn import functional as F
6
+ from torch.utils.data import DataLoader
7
+ from eo import Transformer
8
+ from tqdm import tqdm
9
+ import math
10
+ import torch
11
+ import torch.nn as nn
12
+
13
+ from torch.nn.utils.rnn import pad_sequence
14
+ # sequences is a list of tensors of shape TxH where T is the seqlen and H is the feats dim
15
+ def pad_seq(sequences, batch_first=True, padding_value=0.0, prepadding=True):
16
+ lens = [i.shape[0]for i in sequences]
17
+ padded_sequences = pad_sequence(sequences, batch_first=True, padding_value=padding_value) # NxTxH
18
+ if prepadding:
19
+ for i in range(len(lens)):
20
+ padded_sequences[i] = padded_sequences[i].roll(-lens[i])
21
+ if not batch_first:
22
+ padded_sequences = padded_sequences.transpose(0, 1) # TxNxH
23
+ return padded_sequences
24
+
25
+
26
+
27
+ def get_batches(X, batch_size=16):
28
+ num_batches = math.ceil(len(X) / batch_size)
29
+ for i in range(num_batches):
30
+ x = X[i*batch_size : (i+1)*batch_size]
31
+ yield x
32
+
33
+
34
+ class TashkeelModel(pl.LightningModule):
35
+ def __init__(self, tokenizer, max_seq_len, d_model=512, n_layers=3, n_heads=16, drop_prob=0.1, learnable_pos_emb=True):
36
+
37
+ super(TashkeelModel, self).__init__()
38
+
39
+ ffn_hidden = 4 * d_model
40
+ src_pad_idx = tokenizer.letters_map['<PAD>']
41
+ trg_pad_idx = tokenizer.tashkeel_map['<PAD>']
42
+ enc_voc_size = len(tokenizer.letters_map) # 37 + 3
43
+ dec_voc_size = len(tokenizer.tashkeel_map) # 15 + 3
44
+ self.transformer = Transformer(src_pad_idx=src_pad_idx,
45
+ trg_pad_idx=trg_pad_idx,
46
+ d_model=d_model,
47
+ enc_voc_size=enc_voc_size,
48
+ dec_voc_size=dec_voc_size,
49
+ max_len=max_seq_len,
50
+ ffn_hidden=ffn_hidden,
51
+ n_head=n_heads,
52
+ n_layers=n_layers,
53
+ drop_prob=drop_prob,
54
+ learnable_pos_emb=learnable_pos_emb
55
+ )
56
+
57
+ self.criterion = nn.CrossEntropyLoss(ignore_index=tokenizer.tashkeel_map['<PAD>'])
58
+ self.tokenizer = tokenizer
59
+
60
+
61
+ def forward(self, x):
62
+ y_pred = self.transformer(x)
63
+ return y_pred
64
+
65
+
66
+ def training_step(self, batch, batch_idx):
67
+ input_ids, target_ids = batch
68
+ input_ids = input_ids[:, 1:-1]
69
+ y_out = target_ids[:, 1:-1]
70
+ y_pred = self(input_ids)
71
+ loss = self.criterion(y_pred.transpose(1, 2), y_out)
72
+
73
+ self.log('train_loss', loss, prog_bar=True)
74
+ # sch = self.lr_schedulers()
75
+ # sch.step()
76
+ # self.log('lr', sch.get_last_lr()[0], prog_bar=True)
77
+ return loss
78
+
79
+
80
+ def validation_step(self, batch, batch_idx):
81
+ input_ids, target_ids = batch
82
+ input_ids = input_ids[:, 1:-1]
83
+ y_out = target_ids[:, 1:-1]
84
+ y_pred = self(input_ids)
85
+ loss = self.criterion(y_pred.transpose(1, 2), y_out)
86
+
87
+ pred_text_with_tashkeels = self.tokenizer.decode(input_ids, y_pred.argmax(2).squeeze())
88
+ true_text_with_tashkeels = self.tokenizer.decode(input_ids, y_out)
89
+ total_val_der_distance = 0
90
+ total_val_der_ref_length = 0
91
+ for i in range(len(true_text_with_tashkeels)):
92
+ pred_text_with_tashkeel = pred_text_with_tashkeels[i]
93
+ true_text_with_tashkeel = true_text_with_tashkeels[i]
94
+ val_der = self.tokenizer.compute_der(true_text_with_tashkeel, pred_text_with_tashkeel)
95
+ total_val_der_distance += val_der['distance']
96
+ total_val_der_ref_length += val_der['ref_length']
97
+
98
+ total_der_error = total_val_der_distance / total_val_der_ref_length
99
+ self.log('val_loss', loss)
100
+ self.log('val_der', torch.FloatTensor([total_der_error]))
101
+ self.log('val_der_distance', torch.FloatTensor([total_val_der_distance]))
102
+ self.log('val_der_ref_length', torch.FloatTensor([total_val_der_ref_length]))
103
+
104
+
105
+ def test_step(self, batch, batch_idx):
106
+ input_ids, target_ids = batch
107
+ y_pred = self(input_ids, None)
108
+ loss = self.criterion(y_pred.transpose(1, 2), target_ids)
109
+ self.log('test_loss', loss)
110
+
111
+
112
+ def configure_optimizers(self):
113
+ optimizer = torch.optim.AdamW(self.parameters(), lr=3e-5)
114
+ #max_iters = 10000
115
+ #lr_scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=max_iters, eta_min=3e-6)
116
+ gamma = 1 / 1.000001
117
+ #gamma = 1 / 1.0001
118
+ #lr_scheduler = torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma)
119
+ opts = {"optimizer": optimizer} #, "lr_scheduler": lr_scheduler}
120
+ return opts
121
+
122
+
123
+ @torch.no_grad()
124
+ def do_tashkeel_batch(self, texts, batch_size=16, verbose=True):
125
+ self.eval()
126
+ device = next(self.parameters()).device
127
+ text_with_tashkeel = []
128
+ data_iter = get_batches(texts, batch_size)
129
+ if verbose:
130
+ num_batches = math.ceil(len(texts) / batch_size)
131
+ data_iter = tqdm(data_iter, total=num_batches)
132
+ for texts_mini in data_iter:
133
+ input_ids_list = []
134
+ for text in texts_mini:
135
+ input_ids, _ = self.tokenizer.encode(text, test_match=False)
136
+ input_ids_list.append(input_ids)
137
+ batch_input_ids = pad_seq(input_ids_list, batch_first=True, padding_value=self.tokenizer.letters_map['<PAD>'], prepadding=False)
138
+ batch_input_ids = batch_input_ids[:, 1:-1].to(device)
139
+ y_pred = self(batch_input_ids)
140
+ y_pred = y_pred.argmax(-1)
141
+ # IMPORTANT NOTE: the following code snippet is to FORCE the prediction of the input space char to output no_tashkeel tag '<NT>'
142
+ y_pred[self.tokenizer.letters_map[' '] == batch_input_ids] = self.tokenizer.tashkeel_map[self.tokenizer.no_tashkeel_tag]
143
+ text_with_tashkeel_mini = self.tokenizer.decode(batch_input_ids, y_pred)
144
+ text_with_tashkeel += text_with_tashkeel_mini
145
+
146
+ return text_with_tashkeel
147
+
148
+
149
+ @torch.no_grad()
150
+ def do_tashkeel(self, text):
151
+ return self.do_tashkeel_batch([text])[0]
spaces/arabic-auto-tashkeel/models/best_ed_mlm_ns_epoch_178.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c6297ec54f9af518a59d4b9e5831d3ad4da304435b8c7fb969f6e3a16816ac23
3
+ size 88403510