Spaces:
Sleeping
Sleeping
Upload 3 files
Browse files- app.py +106 -0
- best_model_v2_calibrated.joblib +3 -0
- requirements.txt +6 -0
app.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import joblib
|
| 2 |
+
import pandas as pd
|
| 3 |
+
import gradio as gr
|
| 4 |
+
|
| 5 |
+
# ---------------- Load model bundle ----------------
|
| 6 |
+
bundle = joblib.load("best_model_v2_calibrated.joblib")
|
| 7 |
+
model = bundle["model"]
|
| 8 |
+
threshold = float(bundle["threshold"])
|
| 9 |
+
|
| 10 |
+
# ---------------- Output formatting ----------------
|
| 11 |
+
def format_output(lang, label, proba):
|
| 12 |
+
if lang == "বাংলা":
|
| 13 |
+
risk_text = "উচ্চ ঝুঁকি" if label == "High Risk" else "কম ঝুঁকি"
|
| 14 |
+
return (
|
| 15 |
+
f"### ফলাফল: **{risk_text}**\n"
|
| 16 |
+
f"- ঝুঁকির সম্ভাবনা (Probability): **{proba:.3f}**\n"
|
| 17 |
+
f"- Threshold: **{threshold:.2f}**\n\n"
|
| 18 |
+
"⚠️ এটি একটি **স্ক্রিনিং টুল**, চিকিৎসা নির্ণয় নয়। সমস্যা থাকলে ডাক্তারের পরামর্শ নিন।"
|
| 19 |
+
)
|
| 20 |
+
else:
|
| 21 |
+
return (
|
| 22 |
+
f"### Result: **{label}**\n"
|
| 23 |
+
f"- Risk probability: **{proba:.3f}**\n"
|
| 24 |
+
f"- Threshold: **{threshold:.2f}**\n\n"
|
| 25 |
+
"⚠️ This is a **screening tool**, not a medical diagnosis. If you have symptoms, consult a clinician."
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
def web_speak_html(text, lang):
|
| 29 |
+
voice_lang = "bn-BD" if lang == "বাংলা" else "en-US"
|
| 30 |
+
safe = text.replace("`", "").replace("\\", "\\\\").replace("'", "\\'")
|
| 31 |
+
return f"""
|
| 32 |
+
<div style="display:flex;gap:10px;align-items:center;">
|
| 33 |
+
<button onclick="(function(){{
|
| 34 |
+
const msg = new SpeechSynthesisUtterance('{safe}');
|
| 35 |
+
msg.lang = '{voice_lang}';
|
| 36 |
+
window.speechSynthesis.cancel();
|
| 37 |
+
window.speechSynthesis.speak(msg);
|
| 38 |
+
}})()" style="padding:10px 14px;border-radius:10px;border:1px solid #ccc;cursor:pointer;">
|
| 39 |
+
🔊 Speak / শোনান
|
| 40 |
+
</button>
|
| 41 |
+
<span style="opacity:0.7;">(Voice depends on browser installed voices)</span>
|
| 42 |
+
</div>
|
| 43 |
+
"""
|
| 44 |
+
|
| 45 |
+
# ---------------- Prediction ----------------
|
| 46 |
+
def predict(lang, age, gender, bmi, bp_sys, bp_dia, phys_days, dpq, smoking, alcohol, diabetes, cycle):
|
| 47 |
+
sample = pd.DataFrame([{
|
| 48 |
+
"Age": float(age),
|
| 49 |
+
"Gender": gender,
|
| 50 |
+
"BMI": float(bmi),
|
| 51 |
+
"BP_SYS": float(bp_sys),
|
| 52 |
+
"BP_DIA": float(bp_dia),
|
| 53 |
+
"Phys_Activity_Days": float(phys_days),
|
| 54 |
+
"DPQ_Score": float(dpq),
|
| 55 |
+
"Smoking_Indicator": float(smoking),
|
| 56 |
+
"Alcohol_Feature": float(alcohol),
|
| 57 |
+
"Diabetes_Indicator": float(diabetes),
|
| 58 |
+
"Cycle": cycle
|
| 59 |
+
}])
|
| 60 |
+
|
| 61 |
+
proba = float(model.predict_proba(sample)[:, 1][0])
|
| 62 |
+
pred = int(proba >= threshold)
|
| 63 |
+
label = "High Risk" if pred == 1 else "Low Risk"
|
| 64 |
+
|
| 65 |
+
md = format_output(lang, label, proba)
|
| 66 |
+
speak = web_speak_html(md, lang)
|
| 67 |
+
return md, speak
|
| 68 |
+
|
| 69 |
+
# ---------------- UI ----------------
|
| 70 |
+
with gr.Blocks(title="SleepGuardAI – Sleep Risk Screening") as demo:
|
| 71 |
+
gr.Markdown("# SleepGuardAI – Sleep Risk Screening (NHANES-based)")
|
| 72 |
+
gr.Markdown("Fill the form → get risk score. Bilingual output + Speak button included.")
|
| 73 |
+
|
| 74 |
+
lang = gr.Radio(["English", "বাংলা"], value="English", label="Language / ভাষা")
|
| 75 |
+
|
| 76 |
+
with gr.Row():
|
| 77 |
+
age = gr.Slider(10, 90, value=30, label="Age / বয়স")
|
| 78 |
+
gender = gr.Dropdown(["Male", "Female"], value="Male", label="Gender")
|
| 79 |
+
|
| 80 |
+
with gr.Row():
|
| 81 |
+
bmi = gr.Slider(10, 50, value=25, label="BMI")
|
| 82 |
+
bp_sys = gr.Slider(80, 220, value=120, label="Systolic BP")
|
| 83 |
+
bp_dia = gr.Slider(40, 140, value=80, label="Diastolic BP")
|
| 84 |
+
|
| 85 |
+
phys = gr.Slider(0, 7, value=3, step=1, label="Physical Activity Days/Week")
|
| 86 |
+
|
| 87 |
+
gr.Markdown("### Optional (improves accuracy if known)")
|
| 88 |
+
with gr.Row():
|
| 89 |
+
dpq = gr.Slider(0, 27, value=0, step=1, label="DPQ Depression Score (0–27)")
|
| 90 |
+
smoking = gr.Dropdown([1, 2], value=2, label="Smoking (1=Yes, 2=No)")
|
| 91 |
+
diabetes = gr.Dropdown([1, 2], value=2, label="Diabetes (1=Yes, 2=No)")
|
| 92 |
+
alcohol = gr.Slider(0, 30, value=0, step=1, label="Alcohol feature (proxy)")
|
| 93 |
+
|
| 94 |
+
cycle = gr.Dropdown(["G", "H", "I", "J"], value="J", label="NHANES Cycle")
|
| 95 |
+
|
| 96 |
+
btn = gr.Button("Predict / ফলাফল দেখুন")
|
| 97 |
+
out_md = gr.Markdown()
|
| 98 |
+
out_speak = gr.HTML()
|
| 99 |
+
|
| 100 |
+
btn.click(
|
| 101 |
+
predict,
|
| 102 |
+
inputs=[lang, age, gender, bmi, bp_sys, bp_dia, phys, dpq, smoking, alcohol, diabetes, cycle],
|
| 103 |
+
outputs=[out_md, out_speak]
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
demo.launch()
|
best_model_v2_calibrated.joblib
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:53ef30d0d2726ffdee40a15bbd4ac1f412f724010442586e119094491ca8b0c9
|
| 3 |
+
size 3936773
|
requirements.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio
|
| 2 |
+
pandas
|
| 3 |
+
numpy
|
| 4 |
+
scikit-learn
|
| 5 |
+
joblib
|
| 6 |
+
xgboost
|