ArabicNewsAnalyzer's picture
Update app.py
aa9b884 verified
Raw
History Blame Contribute Delete
5.73 kB
import os
import re
import functools
import torch
import gradio as gr
from transformers import AutoTokenizer, AutoModelForSequenceClassification
# โ”€โ”€ Model & Tokenizer Configuration โ”€โ”€
MODEL_ID = os.getenv("HF_MODEL_ID", "ArabicNewsAnalyzer/MARBERTv2-Single-Arabic-Dialect-Scrapped-MSA-Mask-epoch3")
LOCAL_MODEL_PATH = os.path.join(os.path.dirname(__file__), "..", "results", "best_model")
MAX_LENGTH = 128
COUNTRY_DETAILS = {
"Algeria": {"flag": "๐Ÿ‡ฉ๐Ÿ‡ฟ", "name_ar": "ุงู„ุฌุฒุงุฆุฑ", "region": "Maghrebi"},
"Bahrain": {"flag": "๐Ÿ‡ง๐Ÿ‡ญ", "name_ar": "ุงู„ุจุญุฑูŠู†", "region": "Gulf"},
"Egypt": {"flag": "๐Ÿ‡ช๐Ÿ‡ฌ", "name_ar": "ู…ุตุฑ", "region": "Egyptian"},
"Iraq": {"flag": "๐Ÿ‡ฎ๐Ÿ‡ถ", "name_ar": "ุงู„ุนุฑุงู‚", "region": "Iraqi"},
"Jordan": {"flag": "๐Ÿ‡ฏ๐Ÿ‡ด", "name_ar": "ุงู„ุฃุฑุฏู†", "region": "Levantine"},
"Kuwait": {"flag": "๐Ÿ‡ฐ๐Ÿ‡ผ", "name_ar": "ุงู„ูƒูˆูŠุช", "region": "Gulf"},
"Lebanon": {"flag": "๐Ÿ‡ฑ๐Ÿ‡ง", "name_ar": "ู„ุจู†ุงู†", "region": "Levantine"},
"Libya": {"flag": "๐Ÿ‡ฑ๐Ÿ‡พ", "name_ar": "ู„ูŠุจูŠุง", "region": "Maghrebi"},
"Morocco": {"flag": "๐Ÿ‡ฒ๐Ÿ‡ฆ", "name_ar": "ุงู„ู…ุบุฑุจ", "region": "Maghrebi"},
"Oman": {"flag": "๐Ÿ‡ด๐Ÿ‡ฒ", "name_ar": "ุนูู…ุงู†", "region": "Gulf"},
"Palestine": {"flag": "๐Ÿ‡ต๐Ÿ‡ธ", "name_ar": "ูู„ุณุทูŠู†", "region": "Levantine"},
"Qatar": {"flag": "๐Ÿ‡ถ๐Ÿ‡ฆ", "name_ar": "ู‚ุทุฑ", "region": "Gulf"},
"Saudi_Arabia": {"flag": "๐Ÿ‡ธ๐Ÿ‡ฆ", "name_ar": "ุงู„ุณุนูˆุฏูŠุฉ", "region": "Gulf"},
"Sudan": {"flag": "๐Ÿ‡ธ๐Ÿ‡ฉ", "name_ar": "ุงู„ุณูˆุฏุงู†", "region": "Nilo-Saharan"},
"Syria": {"flag": "๐Ÿ‡ธ๐Ÿ‡พ", "name_ar": "ุณูˆุฑูŠุง", "region": "Levantine"},
"Tunisia": {"flag": "๐Ÿ‡น๐Ÿ‡ณ", "name_ar": "ุชูˆู†ุณ", "region": "Maghrebi"},
"UAE": {"flag": "๐Ÿ‡ฆ๐Ÿ‡ช", "name_ar": "ุงู„ุฅู…ุงุฑุงุช", "region": "Gulf"},
"Yemen": {"flag": "๐Ÿ‡พ๐Ÿ‡ช", "name_ar": "ุงู„ูŠู…ู†", "region": "Peninsular"},
"MSA": {"flag": "๐Ÿ“", "name_ar": "ุงู„ูุตุญู‰", "region": "MSA"}
}
# โ”€โ”€ Cache Loading โ”€โ”€
@functools.lru_cache(maxsize=1)
def load_model_and_tokenizer():
path_to_load = MODEL_ID
if os.path.exists(LOCAL_MODEL_PATH) and os.path.exists(os.path.join(LOCAL_MODEL_PATH, "config.json")):
path_to_load = LOCAL_MODEL_PATH
tokenizer = AutoTokenizer.from_pretrained(path_to_load)
model = AutoModelForSequenceClassification.from_pretrained(path_to_load)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
model.eval()
id2label = model.config.id2label
if not id2label or isinstance(list(id2label.keys())[0], str):
id2label = {int(k): str(v) for k, v in id2label.items()}
return tokenizer, model, id2label, device
# โ”€โ”€ Text Preprocessing โ”€โ”€
def preprocess_text(text: str) -> str:
if not text:
return ""
text = re.sub(r"[\u200b\u200c\u200d\u200e\u200f\ufeff\u00ad]", "", text)
return re.sub(r"\s+", " ", text).strip()
# โ”€โ”€ Inference Function โ”€โ”€
@torch.inference_mode()
def predict_dialect(text: str) -> dict:
cleaned_text = preprocess_text(text)
if not cleaned_text:
return {"error": "Invalid or empty text input."}
tokenizer, model, id2label, device = load_model_and_tokenizer()
inputs = tokenizer(
cleaned_text,
return_tensors="pt",
truncation=True,
max_length=MAX_LENGTH
)
inputs = {k: v.to(device) for k, v in inputs.items()}
outputs = model(**inputs)
probs = torch.softmax(outputs.logits, dim=-1).squeeze(0).cpu().numpy()
# Sort probabilities in descending order
sorted_indices = probs.argsort()[::-1]
all_predictions = []
for idx in sorted_indices:
label = id2label[int(idx)]
details = COUNTRY_DETAILS.get(label, {"flag": "๐Ÿณ๏ธ", "name_ar": label, "region": "Unknown"})
all_predictions.append({
"country": label,
"name_ar": details["name_ar"],
"flag": details["flag"],
"region": details["region"],
"confidence": round(float(probs[idx]), 4)
})
top_prediction = all_predictions[0]
return {
"text": cleaned_text,
"prediction": top_prediction["country"],
"confidence": top_prediction["confidence"],
"details": top_prediction,
"probabilities": {p["country"]: p["confidence"] for p in all_predictions}
}
# โ”€โ”€ Examples List โ”€โ”€
examples = [
["ูˆุงุด ูƒุงูŠู† ุงู„ุฒูŠู† ูู‡ุงุฏ ุงู„ุจู„ุงุฏ ุบุงุจ ุนู„ูŠู†ุง ู‡ุงุฏ ุงู„ุฃูŠุงู…"],
["ูŠุง ุฒู„ู…ุฉ ุดูˆ ู‡ุงู„ุญูƒูŠ ุงู„ูุงุถูŠ ุดูˆ ุนู… ุชุญูƒูŠ ุงู†ุช"],
["ุดู†ูˆ ู‡ุฐุง ุงู„ุญุฌูŠ ู…ุง ูŠุตูŠุฑ ู‡ูŠุฌ ุงุจุฏ ูˆุงู„ู„ู‡"],
["ุงูŠูˆุง ูƒุฏู‡ ุงู„ูƒู„ุงู… ุฏู‡ ุญู„ูˆ ุงูˆูŠ ุชุณู„ู… ุงูŠุฏูƒ ูŠุง ูู†ุงู†"],
["ูˆุงู„ู„ู‡ ูŠุง ุฎูˆูŠ ู‡ุงู„ู…ุทุนู… ุฃูƒู„ู‡ ุทูŠุจ ุจุฒุงู ุชุจุงุฑูƒ ุงู„ู„ู‡"],
["ุงูŠู‡ ูˆุงู„ู„ู‡ ูƒู„ุงู…ูƒ ุตุญ ูŠุงู„ุบุงู„ูŠ ู…ุง ู‚ุตุฑุช ุฑุจูŠ ูŠุญูุธูƒ"],
["ูƒูŠููƒ ุญุจูŠุจูŠ ุดู„ูˆู†ูƒ ุงู„ูŠูˆู… ุนุณุงูƒ ุทูŠุจ"],
["ู‡ุงุฐูŠ ุงู„ุณุงู„ูุฉ ู…ุง ุชู†ูุน ุฎู„ุงุต ู„ุงุฒู… ู†ู„ู‚ู‰ ุญู„ ุณุฑูŠุน"],
["ูˆูƒุงู„ุฉ ุชุณู†ูŠู… ุนู† ู…ุตุงุฏุฑ ุฅูŠุฑุงู†ูŠุฉ: ุฏูˆูŠ ุงู†ูุฌุงุฑูŠู† ููŠ ุฌุฒูŠุฑุฉ ู‚ุดู… ุณุจุจู‡ ุงู„ุชุตุฏูŠ ู„ุฃู‡ุฏุงู ู…ุนุงุฏูŠุฉ ุนู†ุฏ ู…ุฏุฎู„ ู…ุถูŠู‚ ู‡ุฑู…ุฒ"],
]
# โ”€โ”€ Minimal Gradio Interface โ”€โ”€
demo = gr.Interface(
fn=predict_dialect,
inputs=gr.Textbox(lines=3, placeholder="ุฃุฏุฎู„ ุงู„ู†ุต ุงู„ุนุฑุจูŠ ู‡ู†ุง...", label="Input Text"),
outputs=gr.JSON(label="API Response"),
examples=examples,
cache_examples=False,
title="Arabic Dialect Classifier",
api_name="predict"
)
if __name__ == "__main__":
demo.launch()