stressUp / app_stress.py
YureiYuri's picture
app and req.txt
3125bb9 verified
# ============================================================
# HuggingFace Space β€” Stress Level Classifier Demo
# File: app.py (deploy as a Gradio Space)
# ============================================================
# How to deploy:
# 1. Go to https://huggingface.co/spaces β†’ Create new Space
# 2. Choose SDK: Gradio
# 3. Upload this file as app.py + requirements.txt
# 4. Space auto-builds and launches
#
# requirements.txt:
# torch>=2.1.0
# gradio>=4.0.0
# huggingface_hub>=0.22.0
# numpy>=1.24.0
# ============================================================
import json
import zipfile
import shutil
import tempfile
import numpy as np
import torch
import torch.nn as nn
import gradio as gr
from huggingface_hub import hf_hub_download
from pathlib import Path
# ── Config ────────────────────────────────────────────────────────────────────
MODEL_REPO = "your-hf-username/stress-level-classifier-mlp" # ← update this
FEATURE_COLS = [
"anxiety_level", "self_esteem", "mental_health_history", "depression",
"headache", "blood_pressure", "sleep_quality", "breathing_problem",
"noise_level", "living_conditions", "safety", "basic_needs",
"academic_performance", "study_load", "teacher_student_relationship",
"future_career_concerns", "social_support", "peer_pressure",
"extracurricular_activities", "bullying",
]
FEATURE_RANGES = {
"anxiety_level": (0, 21),
"self_esteem": (0, 30),
"mental_health_history": (0, 1),
"depression": (0, 27),
"headache": (0, 5),
"blood_pressure": (1, 3),
"sleep_quality": (1, 5),
"breathing_problem": (1, 5),
"noise_level": (0, 5),
"living_conditions": (1, 5),
"safety": (1, 5),
"basic_needs": (1, 5),
"academic_performance": (1, 5),
"study_load": (1, 5),
"teacher_student_relationship":(1, 5),
"future_career_concerns": (1, 5),
"social_support": (1, 5),
"peer_pressure": (1, 5),
"extracurricular_activities": (0, 5),
"bullying": (1, 5),
}
STRESS_EMOJIS = {"low": "🟒", "medium": "🟑", "high": "πŸ”΄"}
STRESS_LABELS = ["low", "medium", "high"]
ID2LABEL = {"0": "low", "1": "medium", "2": "high"}
# ── Model definition (must match training) ────────────────────────────────────
class StressMLP(nn.Module):
def __init__(self, input_dim, num_classes, dropout=0.3):
super().__init__()
self.net = nn.Sequential(
nn.Linear(input_dim, 256), nn.BatchNorm1d(256), nn.ReLU(), nn.Dropout(dropout),
nn.Linear(256, 128), nn.BatchNorm1d(128), nn.ReLU(), nn.Dropout(dropout),
nn.Linear(128, 64), nn.BatchNorm1d(64), nn.ReLU(), nn.Dropout(dropout / 2),
nn.Linear(64, num_classes),
)
def forward(self, x):
return self.net(x)
# ── Load model from Hub ───────────────────────────────────────────────────────
print(f"Loading model from {MODEL_REPO} …")
cfg_path = hf_hub_download(MODEL_REPO, "config.json")
scaler_path = hf_hub_download(MODEL_REPO, "scaler.json")
weights_path = hf_hub_download(MODEL_REPO, "model_weights.pt")
with open(cfg_path) as f: cfg = json.load(f)
with open(scaler_path) as f: scaler = json.load(f)
MEAN = np.array(scaler["mean"], dtype=np.float32)
SCALE = np.array(scaler["scale"], dtype=np.float32)
model = StressMLP(cfg["input_dim"], cfg["num_classes"], cfg["dropout"])
model.load_state_dict(torch.load(weights_path, map_location="cpu"))
model.eval()
print("Model loaded βœ…")
# ── Build downloadable zip on startup ────────────────────────────────────────
DOWNLOAD_ZIP = Path("/tmp/stress_model_package.zip")
def build_download_zip():
"""Package the cached HF model files into a single zip for users."""
if DOWNLOAD_ZIP.exists():
return
files_to_zip = {
"model_weights.pt": weights_path,
"config.json": cfg_path,
"scaler.json": scaler_path,
}
with zipfile.ZipFile(DOWNLOAD_ZIP, "w", zipfile.ZIP_DEFLATED) as zf:
for arcname, src in files_to_zip.items():
zf.write(src, arcname=arcname)
print(f"Download zip ready: {DOWNLOAD_ZIP}")
build_download_zip()
# ── Inference ─────────────────────────────────────────────────────────────────
def predict(*feature_values):
arr = np.array(feature_values, dtype=np.float32).reshape(1, -1)
arr = (arr - MEAN) / SCALE
with torch.no_grad():
logits = model(torch.tensor(arr))
probs = torch.softmax(logits, dim=-1).squeeze().numpy()
pred_id = int(probs.argmax())
pred_label = STRESS_LABELS[pred_id]
emoji = STRESS_EMOJIS[pred_label]
confidence = probs[pred_id] * 100
result = f"## {emoji} Stress Level: **{pred_label.upper()}**\n"
result += f"Confidence: **{confidence:.1f}%**"
scores = {
f"{STRESS_EMOJIS[lbl]} {lbl.capitalize()}": float(probs[i]) * 100
for i, lbl in enumerate(STRESS_LABELS)
}
return result, scores
def get_download():
return str(DOWNLOAD_ZIP)
# ── Build sliders from feature ranges ────────────────────────────────────────
def make_label(col):
return col.replace("_", " ").title()
sliders = [
gr.Slider(
minimum=lo, maximum=hi, value=(lo + hi) // 2, step=1,
label=make_label(col),
)
for col, (lo, hi) in FEATURE_RANGES.items()
]
# ── Gradio UI ─────────────────────────────────────────────────────────────────
with gr.Blocks(
theme=gr.themes.Soft(),
title="Stress Level Classifier",
css="""
.result-md { font-size: 1.35rem; padding: 14px 18px;
border-radius: 10px; background: var(--block-background-fill); }
.dl-btn { margin-top: 8px; }
footer { display: none !important; }
""",
) as demo:
gr.Markdown(
"""
# 🧠 Student Stress Level Classifier
Predicts stress level (**Low / Medium / High**) from 20 psychosocial &
physiological indicators using a fine-tuned MLP.
"""
)
with gr.Row():
# ── Left: inputs ──────────────────────────────────────────────────────
with gr.Column(scale=3):
gr.Markdown("### πŸ“‹ Input Features")
with gr.Accordion("🧬 Psychological Factors", open=True):
s_anxiety = sliders[0]
s_esteem = sliders[1]
s_mh_hist = sliders[2]
s_depress = sliders[3]
with gr.Accordion("🩺 Physical Indicators", open=False):
s_headache = sliders[4]
s_bp = sliders[5]
s_sleep = sliders[6]
s_breath = sliders[7]
with gr.Accordion("🏠 Environmental Factors", open=False):
s_noise = sliders[8]
s_living = sliders[9]
s_safety = sliders[10]
s_basic = sliders[11]
with gr.Accordion("πŸŽ“ Academic Factors", open=False):
s_acad = sliders[12]
s_study = sliders[13]
s_teacher = sliders[14]
s_career = sliders[15]
with gr.Accordion("πŸ‘₯ Social Factors", open=False):
s_social = sliders[16]
s_peer = sliders[17]
s_extra = sliders[18]
s_bully = sliders[19]
predict_btn = gr.Button("πŸ” Predict Stress Level", variant="primary", size="lg")
# ── Right: outputs ────────────────────────────────────────────────────
with gr.Column(scale=2):
gr.Markdown("### πŸ“Š Results")
result_md = gr.Markdown(
value="*Adjust sliders and click Predict.*",
elem_classes=["result-md"],
)
scores_out = gr.Label(
label="Confidence per Class (%)",
num_top_classes=3,
)
gr.Markdown("---")
gr.Markdown("### πŸ“₯ Download Model")
gr.Markdown(
"Download all model files (weights, config, scaler) as a single ZIP."
)
download_btn = gr.DownloadButton(
label="⬇️ Download stress_model_package.zip",
value=get_download,
variant="secondary",
elem_classes=["dl-btn"],
)
gr.Markdown(
f"Or browse individual files on the "
f"[HuggingFace model page πŸ€—](https://huggingface.co/{MODEL_REPO})."
)
# ── Examples ──────────────────────────────────────────────────────────────
gr.Markdown("---")
gr.Examples(
examples=[
# anxiety, esteem, mh_hist, depress, headache, bp, sleep, breath,
# noise, living, safety, basic, acad, study, teacher, career,
# social, peer, extra, bully
[4, 26, 0, 6, 1, 2, 4, 1, 1, 4, 4, 4, 5, 1, 4, 1, 3, 2, 2, 1], # Low stress
[13, 22, 1, 12, 3, 1, 2, 4, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 2, 2], # Medium stress
[20, 3, 1, 22, 5, 3, 1, 5, 3, 1, 1, 1, 1, 5, 2, 5, 1, 5, 4, 5], # High stress
],
inputs=sliders,
label="Quick examples (Low / Medium / High stress)",
)
gr.Markdown(
"""
---
**Features:** 20 psychosocial & physiological indicators Β·
**Model:** PyTorch MLP fine-tuned for 10 epochs Β·
**Classes:** 🟒 Low Β· 🟑 Medium Β· πŸ”΄ High
"""
)
predict_btn.click(
fn=predict,
inputs=sliders,
outputs=[result_md, scores_out],
)
if __name__ == "__main__":
demo.launch()