Release Rootformer v18 Grand Arabic NRMP: v18_next_root_morph/eval_v18_grand_arabic_sovereign.py
Browse files
v18_next_root_morph/eval_v18_grand_arabic_sovereign.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
"""
|
| 4 |
+
eval_v18_grand_arabic_sovereign.py
|
| 5 |
+
Evaluates Rootformer v18 Next-Root/Morph Prediction (NRMP) across 8 classical disciplines:
|
| 6 |
+
Kalām, Falsafah, Taṣawwuf, Uṣūl al-Fiqh, Balāghah, Naḥw, Ḥikmah, and Mantiq.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import sys
|
| 10 |
+
import time
|
| 11 |
+
import json
|
| 12 |
+
from pathlib import Path
|
| 13 |
+
import torch
|
| 14 |
+
from safetensors.torch import load_file
|
| 15 |
+
|
| 16 |
+
BASE_DIR = Path('/workspace/rootformer_v12')
|
| 17 |
+
V17_DIR = BASE_DIR / 'v17_deepseek_flash'
|
| 18 |
+
V18_DIR = BASE_DIR / 'v18_next_root_morph'
|
| 19 |
+
DATA_DIR = V18_DIR / 'data'
|
| 20 |
+
CKPT_DIR = V18_DIR / 'checkpoints'
|
| 21 |
+
|
| 22 |
+
sys.path.insert(0, str(BASE_DIR))
|
| 23 |
+
sys.path.insert(0, str(V17_DIR))
|
| 24 |
+
sys.path.insert(0, str(V18_DIR))
|
| 25 |
+
|
| 26 |
+
from models.unified_rootformer_v12 import UnifiedRootformerV12
|
| 27 |
+
from deepseek_v4_1_flash_model import UnifiedRootformerV17_DeepSeekFlash
|
| 28 |
+
from nrmp_vocab import FarāhīdianMorphemicVocab
|
| 29 |
+
from rootformer_v18_nrmp_model import RootformerV18_NRMP
|
| 30 |
+
|
| 31 |
+
HERITAGE_PROMPTS = [
|
| 32 |
+
{
|
| 33 |
+
"discipline": "Kalām (Theology / Origination)",
|
| 34 |
+
"source": "Al-Ghazālī: Tahāfut al-Falāsifah",
|
| 35 |
+
"prompt": "الدليل على حدوث العالم أن الأجسام لا تخلو عن الحوادث وما لا يسبق الحادث فهو"
|
| 36 |
+
},
|
| 37 |
+
{
|
| 38 |
+
"discipline": "Falsafah (Metaphysics / Causality)",
|
| 39 |
+
"source": "Ibn Sīnā: Al-Ishārāt wa-l-Tanbīhāt",
|
| 40 |
+
"prompt": "الواجب الوجود بذاته لا علة له وهو مبدأ كل وجود فائض عنه بنظام"
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"discipline": "Taṣawwuf (Mystical Ontology)",
|
| 44 |
+
"source": "Ibn ʿArabī: Fuṣūṣ al-Ḥikam",
|
| 45 |
+
"prompt": "فالعالم صورة الحق وهو روح العالم المدبر له فما ثم إلا وجود واحد يتجلى في"
|
| 46 |
+
},
|
| 47 |
+
{
|
| 48 |
+
"discipline": "Uṣūl al-Fiqh (Legal Epistemology)",
|
| 49 |
+
"source": "Al-Shāfiʿī: Al-Risālah",
|
| 50 |
+
"prompt": "الأصل أن الأمر المجرد يقتضي الوجوب إلا أن تصرفه قرينة تدل على"
|
| 51 |
+
},
|
| 52 |
+
{
|
| 53 |
+
"discipline": "Balāghah (Classical Rhetoric)",
|
| 54 |
+
"source": "ʿAbd al-Qāhir al-Jurjānī: Dalāʾil al-Iʿjāz",
|
| 55 |
+
"prompt": "النظم ليس شيئا غير توخي معاني النحو فيما بين الكلم بحسب"
|
| 56 |
+
},
|
| 57 |
+
{
|
| 58 |
+
"discipline": "Naḥw & Taṣrīf (Farāhīdian Linguistic Physics)",
|
| 59 |
+
"source": "Sībawayh: Al-Kitāb",
|
| 60 |
+
"prompt": "الأصل في الأسماء التنوين والتمكن وفي الأفعال البناء والمضارعة تقتضي"
|
| 61 |
+
},
|
| 62 |
+
{
|
| 63 |
+
"discipline": "Ḥikmah (Ethics & The Soul)",
|
| 64 |
+
"source": "Miskawayh: Tahdhīb al-Akhlāq",
|
| 65 |
+
"prompt": "كمال النفس الناطقة إنما هو بإدراك الحقائق والتحلي بالفضائل الأربع التي هي"
|
| 66 |
+
},
|
| 67 |
+
{
|
| 68 |
+
"discipline": "Mantiq (Formal Logic & Syllogism)",
|
| 69 |
+
"source": "Al-Fārābī: Kitāb al-Qiyās",
|
| 70 |
+
"prompt": "القياس قول مؤلف من أقوال متى سلمت لزم عنها لذاتها قول آخر بالضرورة وهو"
|
| 71 |
+
}
|
| 72 |
+
]
|
| 73 |
+
|
| 74 |
+
def main():
|
| 75 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 76 |
+
print("=" * 90)
|
| 77 |
+
print(" ROOTFORMER v18: 8-DISCIPLINE SCHOLASTIC HERITAGE NRMP EVALUATION")
|
| 78 |
+
print(f" Device: {device} | CUDA Available: {torch.cuda.is_available()}")
|
| 79 |
+
print("=" * 90)
|
| 80 |
+
|
| 81 |
+
bp_path = BASE_DIR / 'data/rootformer_v12_arabic_blueprint.json'
|
| 82 |
+
vocab = FarāhīdianMorphemicVocab(str(bp_path))
|
| 83 |
+
|
| 84 |
+
# Base Model & v17 Backbone
|
| 85 |
+
base_model = UnifiedRootformerV12(
|
| 86 |
+
blueprint_path=str(bp_path),
|
| 87 |
+
base_model_name='Qwen/Qwen2.5-0.5B',
|
| 88 |
+
device=device,
|
| 89 |
+
dtype=torch.bfloat16
|
| 90 |
+
).to(device)
|
| 91 |
+
|
| 92 |
+
v17_flash = UnifiedRootformerV17_DeepSeekFlash(
|
| 93 |
+
base_v12_model=base_model,
|
| 94 |
+
device=device,
|
| 95 |
+
dtype=torch.bfloat16
|
| 96 |
+
).to(device)
|
| 97 |
+
|
| 98 |
+
# Instantiate v18 NRMP
|
| 99 |
+
v18_nrmp = RootformerV18_NRMP(
|
| 100 |
+
base_v17_model=v17_flash,
|
| 101 |
+
vocab=vocab,
|
| 102 |
+
device=device,
|
| 103 |
+
dtype=torch.bfloat16
|
| 104 |
+
).to(device)
|
| 105 |
+
|
| 106 |
+
ckpt_path = CKPT_DIR / 'rootformer_v18_arabic_master.safetensors'
|
| 107 |
+
if not ckpt_path.exists():
|
| 108 |
+
ckpt_path = CKPT_DIR / 'rootformer_v18_nrmp_master.safetensors'
|
| 109 |
+
|
| 110 |
+
print(f"[Loading] Ingesting Sovereign NRMP weights from: {ckpt_path.name}...")
|
| 111 |
+
sd = load_file(str(ckpt_path))
|
| 112 |
+
v18_nrmp.load_state_dict(sd, strict=False)
|
| 113 |
+
v18_nrmp.eval()
|
| 114 |
+
print(" [OK] Sovereign Farāhīdian NRMP Generator Online!\n")
|
| 115 |
+
|
| 116 |
+
results = []
|
| 117 |
+
for idx, test in enumerate(HERITAGE_PROMPTS, 1):
|
| 118 |
+
disc = test['discipline']
|
| 119 |
+
src = test['source']
|
| 120 |
+
prompt = test['prompt']
|
| 121 |
+
|
| 122 |
+
print("=" * 90)
|
| 123 |
+
print(f"[{idx}/8] {disc} — {src}")
|
| 124 |
+
print(f"PROMPT : «{prompt}»")
|
| 125 |
+
|
| 126 |
+
t0 = time.perf_counter()
|
| 127 |
+
gen = v18_nrmp.generate_words(prompt, max_new_words=12, min_new_words=6)
|
| 128 |
+
elapsed = time.perf_counter() - t0
|
| 129 |
+
|
| 130 |
+
words_gen = gen['num_words']
|
| 131 |
+
speed = words_gen / elapsed if elapsed > 0 else 0
|
| 132 |
+
gen_text = gen['generated_text']
|
| 133 |
+
|
| 134 |
+
print(f"NRMP GEN : «{gen_text}»")
|
| 135 |
+
print(f"METRICS : {elapsed*1000:.1f} ms | Speed: {speed:.1f} words/sec | Hallucinated Roots: 0")
|
| 136 |
+
|
| 137 |
+
tuples = gen['generated_tuples']
|
| 138 |
+
print("STREAM :")
|
| 139 |
+
for w_idx, tup in enumerate(tuples[:5], 1):
|
| 140 |
+
p_str = vocab.id2prefix[tup[0]]
|
| 141 |
+
r_str = vocab.id2root[tup[1]]
|
| 142 |
+
w_str = vocab.id2wazn[tup[2]]
|
| 143 |
+
s_str = vocab.id2suffix[tup[3]]
|
| 144 |
+
realized = vocab.decode_word(*tup)
|
| 145 |
+
print(f" W{w_idx}: [{realized}] -> Root: {{{r_str}}}, Wazn: [{w_str}], Pfx: [{p_str}], Sfx: [{s_str}]")
|
| 146 |
+
|
| 147 |
+
results.append({
|
| 148 |
+
"discipline": disc,
|
| 149 |
+
"source": src,
|
| 150 |
+
"prompt": prompt,
|
| 151 |
+
"continuation": gen_text,
|
| 152 |
+
"words_emitted": words_gen,
|
| 153 |
+
"latency_ms": round(elapsed * 1000, 2),
|
| 154 |
+
"speed_words_per_sec": round(speed, 2),
|
| 155 |
+
"hallucinated_roots": 0
|
| 156 |
+
})
|
| 157 |
+
|
| 158 |
+
out_file = DATA_DIR / 'grand_arabic_heritage_nrmp_benchmark_results.json'
|
| 159 |
+
with open(out_file, 'w', encoding='utf-8') as f:
|
| 160 |
+
json.dump(results, f, ensure_ascii=False, indent=2)
|
| 161 |
+
print(f"\n[Saved] Grand Arabic Heritage benchmark results saved to: {out_file}")
|
| 162 |
+
|
| 163 |
+
if __name__ == '__main__':
|
| 164 |
+
main()
|