SLP-AI-sa / app.py
Fulwah's picture
Update app.py
265ec44 verified
Raw
History Blame Contribute Delete
8.51 kB
import gradio as gr
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import re
model_path = "."
tokenizer = AutoTokenizer.from_pretrained(model_path, local_files_only=True)
model = AutoModelForSequenceClassification.from_pretrained(model_path, local_files_only=True)
id2label = {
0: 'Fluency Disorder (تأتأة)',
1: 'Language Disorder (تأخر لغة)',
2: 'Motor Speech Disorder (اضطراب حركي)',
3: 'Other/Medical (أخرى/طبية)',
4: 'Social Communication/ASD (تواصل واجتماعي)',
5: 'Speech Sound Disorder (مخارج الحروف)'
}
# (Data Source: Amayreh & Dyson 1998 + ASHA) ---
SOUND_NORMS = {
# المجموعة الأولى (تكتمل حوالي 3 سنوات)
'ب': 3, 'م': 3, 'ن': 3, 'و': 3, 'ه': 3, 'د': 3, 'ت': 3, 'ك': 3, 'ف': 3, 'ل': 3, 'ي': 3,
# المجموعة الثانية (تكتمل بين 4 - 5 سنوات)
'س': 5, 'ز': 5, 'ش': 5, 'ج': 5,
# المجموعة الثالثة (تكتمل حوالي 6 سنوات)
'ر': 6, 'ث': 6, 'ذ': 6, 'ص': 6, 'ض': 6, 'ط': 6, 'ظ': 6, 'ع': 6, 'غ': 6, 'خ': 6
}
# (Phonological Processes)
PHONO_PROCESSES = {
'fronting': {'age': 3.5, 'keywords': ['كاف', 'قاف', 'غين', 'خاء'], 'desc': 'التقديم (Fronting): استبدال الأصوات الخلفية بأمامية (مثل: كتاب -> تتاب) طبيعي حتى 3.5 سنوات.'},
'gliding': {'age': 6.0, 'keywords': ['راء', 'لام', 'واو', 'ياء'], 'desc': 'الانزلاق (Gliding): إبدال الراء أو اللام بـ (واو/ياء) عملية طبيعية قد تستمر حتى 6 سنوات.'},
'final_deletion': {'age': 3.0, 'keywords': ['اخر الكلمة', 'يحذف', 'نهاية'], 'desc': 'حذف الحرف الأخير (Final Consonant Deletion) طبيعي حتى عمر 3 سنوات فقط.'},
'stopping': {'age': 4.0, 'keywords': ['فاء', 'سين', 'شين', 'صاد'], 'desc': 'الوقف (Stopping): استبدال الأصوات الاحتكاكية بأصوات انفجارية طبيعي حتى 3-4 سنوات.'}
}
# (Logic Layer)
def extract_age(text):
text = text.replace('١', '1').replace('٢', '2').replace('٣', '3').replace('٤', '4').replace('٥', '5').replace('٦', '6')
match_half = re.search(r'(\d+)\s*(ونصف|و نص)', text)
if match_half:
return float(match_half.group(1)) + 0.5
numbers = re.findall(r'\d+', text)
if numbers: return float(numbers[0])
text = text.lower()
if 'سنتين' in text or 'عامين' in text: return 2.0
if 'سنة' in text or 'عام' in text: return 1.0
if 'ثلاث' in text: return 3.0
if 'أربع' in text: return 4.0
if 'خمس' in text: return 5.0
if 'ست' in text: return 6.0
return None
def check_clinical_logic(age, text, predicted_label):
text = text.lower()
if age is None:
return predicted_label, None
# Gliding
if any(k in text for k in PHONO_PROCESSES['gliding']['keywords']) and ('يبدل' in text or 'يقلب' in text or 'ما يقول' in text):
if age < PHONO_PROCESSES['gliding']['age']:
return "Phonological Process (عملية نطقية)", PHONO_PROCESSES['gliding']['desc']
# Fronting
if any(k in text for k in PHONO_PROCESSES['fronting']['keywords']) and ('يبدل' in text or 'تاء' in text or 'دال' in text):
if age <= PHONO_PROCESSES['fronting']['age']:
return "Phonological Process (عملية نطقية)", PHONO_PROCESSES['fronting']['desc']
# 2. Sound Norms
for sound, norm_age in SOUND_NORMS.items():
patterns = [f"حرف ال{sound}", f"حرف {sound}", f"صوت ال{sound}", f"ال{sound}"]
if any(p in text for p in patterns):
if age < norm_age:
return f"Age Appropriate Error (خطأ تطوري طبيعي)", f"حسب معايير (Amayreh & Dyson)، اكتساب صوت ({sound}) يكتمل عند {norm_age} سنوات. وعمر الطفل ({age}) سنوات، فالأمر طبيعي."
if age < 4 and any(w in text for w in ['قصة', 'سالفة', 'يسرد']):
return "Normal Development (تطور طبيعي)", "مهارة السرد القصصي تكتمل عادة بعد عمر 4 سنوات."
if (2 <= age <= 4) and any(w in text for w in ['تأتأة', 'يعلق', 'يكرر']):
return "Developmental Disfluency (تأتأة نمائية)", "التأتأة الطبيعية شائعة بين 2-4 سنوات وتختفي غالباً تلقائياً."
return predicted_label, None
# -----------------------------------------
def predict_speech_issue(text):
if not text.strip(): return "الرجاء كتابة وصف الحالة"
# AI Prediction
inputs = tokenizer(text, return_tensors="pt", truncation=True, padding=True)
with torch.no_grad():
outputs = model(**inputs)
probs = torch.nn.functional.softmax(outputs.logits, dim=-1)
pred_id = torch.argmax(probs).item()
confidence = probs[0][pred_id].item()
ai_label = id2label[pred_id]
# Clinical Logic Check
age = extract_age(text)
final_label, logic_reason = check_clinical_logic(age, text, ai_label)
# Formatting
if logic_reason:
advice = logic_reason
color = "#27ae60" # Green
badge = "✅ تحليل نمائي (Evidence-Based)"
confidence_display = "استناداً للمعايير العلمية"
else:
# Default AI Recommendations
recommendations = {
'Speech Sound Disorder (مخارج الحروف)': "ينصح بتقييم مخارج الحروف وفحص أعضاء النطق (Oral Motor Exam).",
'Language Disorder (تأخر لغة)': "ينصح بعمل اختبار لغة شامل (مثل PLS-4 أو معرب).",
'Social Communication/ASD (تواصل واجتماعي)': "ينصح بمراجعة طبيب نمو وسلوك وعمل مقياس كارز أو ADOS.",
'Fluency Disorder (تأتأة)': "ينصح بتقييم الطلاقة ومراقبة البيئة التواصلية.",
'Motor Speech Disorder (اضطراب حركي)': "يحتاج تقييم دقيق (Apraxia/Dysarthria) من قبل مختص.",
'Other/Medical (أخرى/طبية)': "⛔ يفضل استشارة طبيب أنف وأذن وحنجرة (ENT) لفحص السمع أو الحبال الصوتية."
}
advice = recommendations.get(final_label, "يرجى استشارة مختص.")
color = "#c0392b" if "Medical" in final_label else "#2980b9"
badge = "🤖 تصنيف الذكاء الاصطناعي"
confidence_display = f"{confidence:.1%}"
return f"""
<div style="direction: rtl; text-align: right; padding: 20px; background-color: #fff; border-radius: 12px; border-right: 6px solid {color}; box-shadow: 0 4px 15px rgba(0,0,0,0.05);">
<div style="display: flex; justify-content: space-between; align-items: center;">
<h2 style="color: {color}; margin: 0;">{final_label}</h2>
<span style="background-color: #f1f2f6; padding: 6px 12px; border-radius: 20px; font-size: 0.8em; color: #555;">{badge}</span>
</div>
<p style="color: #7f8c8d; margin-top: 5px;"><b>الثقة:</b> {confidence_display}</p>
<hr style="border-top: 1px dashed #ddd; margin: 15px 0;">
<div style="background-color: #fcfcfc; padding: 10px; border-radius: 8px;">
<p style="font-size: 1.1em; color: #2c3e50; line-height: 1.6;"><b>💡 الرأي المختص:</b><br>{advice}</p>
</div>
<p style="font-size: 0.7em; color: #95a5a6; margin-top: 15px;">* المصادر: Amayreh & Dyson (1998), ASHA Guidelines.</p>
</div>
"""
with gr.Blocks(theme=gr.themes.Soft()) as demo:
gr.Markdown("<h1 style='text-align: center'>🇸🇦 أداة الفرز الذكية لاضطرابات النطق - المراعية للهجات السعودية</h1>")
input_txt = gr.Textbox(label="وصف الحالة (يرجى ذكر العمر)", placeholder="مثال: ولدي عمره 3 سنوات ونص ويبدل الكاف تاء")
btn = gr.Button("تحليل الحالة", variant="primary")
output = gr.HTML(label="التقرير")
btn.click(fn=predict_speech_issue, inputs=input_txt, outputs=output)
if __name__ == "__main__":
demo.launch()