Spaces:
Sleeping
Sleeping
| # app.py | |
| import gradio as gr | |
| import joblib, pandas as pd, numpy as np, shap, json | |
| # ------------------------------ | |
| # Load optimized model + metadata | |
| # ------------------------------ | |
| MODEL_PATH = "rf_gpa_model_optimized.joblib" | |
| META_PATH = "model_meta.json" | |
| model = joblib.load(MODEL_PATH) | |
| with open(META_PATH, "r") as f: | |
| meta = json.load(f) | |
| # ------------------------------ | |
| # Helper: GPA → Letter grade | |
| # ------------------------------ | |
| def gpa_to_letter(g): | |
| g = float(np.clip(g, 0, 4)) | |
| if g >= 3.7: return "A" | |
| if g >= 3.3: return "A-" | |
| if g >= 3.0: return "B+" | |
| if g >= 2.7: return "B" | |
| if g >= 2.3: return "B-" | |
| if g >= 2.0: return "C+" | |
| if g >= 1.7: return "C" | |
| if g >= 1.3: return "C-" | |
| if g >= 1.0: return "D+" | |
| if g >= 0.7: return "D" | |
| return "F" | |
| # ------------------------------ | |
| # Predictor function with SHAP explainability | |
| # ------------------------------ | |
| def predict_gpa(*inputs, explain=False): | |
| cols = [ | |
| "grade_level","semester","subject_type","subject","difficulty_level", | |
| "past_avg_gpa","avg_test_score","avg_quiz_grade","attendance_pct", | |
| "completion_pct","hours_studied","confidence_before","assignment_type" | |
| ] | |
| X = pd.DataFrame([dict(zip(cols, inputs[:-1]))]) | |
| # Predict GPA | |
| pred = model.predict(X)[0] | |
| letter = gpa_to_letter(pred) | |
| # Optional explanation | |
| explanation = "" | |
| if inputs[-1]: # "Explain" checkbox | |
| try: | |
| explainer = shap.TreeExplainer(model.named_steps["rf"]) | |
| X_enc = model.named_steps["pre"].transform(X) | |
| shap_vals = explainer(X_enc) | |
| vals = shap_vals.values[0] | |
| top_idx = np.argsort(-np.abs(vals))[:3] | |
| feat_names = model.named_steps["pre"].get_feature_names_out() | |
| explanation = "Top contributing features:\n" + "\n".join( | |
| [f"{feat_names[i]}: {vals[i]:.3f}" for i in top_idx] | |
| ) | |
| except Exception as e: | |
| explanation = f"SHAP explanation unavailable: {e}" | |
| return round(float(pred), 2), letter, explanation | |
| # ------------------------------ | |
| # Gradio UI | |
| # ------------------------------ | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# 🎓 Academic GPA Predictor (Optimized Model)") | |
| gr.Markdown(f"**Model Performance:** RMSE={meta['rmse']:.3f}, R²={meta['r2']:.3f}, Letter Acc={meta['letter_acc']:.2f}") | |
| with gr.Row(): | |
| with gr.Column(): | |
| grade_level = gr.Slider(9, 12, 11, step=1, label="Grade Level") | |
| semester = gr.Radio([1, 2], value=1, label="Semester") | |
| subject_type = gr.Dropdown(["Core", "Elective"], value="Core", label="Subject Type") | |
| subject = gr.Dropdown(["English","Math","Science","History","Foreign Language","Art","Computer Science","PE"], value="English", label="Subject") | |
| difficulty = gr.Radio([1, 2, 3], value=1, label="Difficulty (1=Reg, 2=Honors, 3=AP)") | |
| past_avg_gpa = gr.Slider(0, 4, 3, step=0.01, label="Past GPA") | |
| avg_test_score = gr.Slider(0, 100, 80, step=1, label="Average Test Score %") | |
| avg_quiz = gr.Slider(0, 100, 82, step=1, label="Average Quiz Score %") | |
| attendance = gr.Slider(0, 100, 93, step=1, label="Attendance %") | |
| completion = gr.Slider(0, 100, 95, step=1, label="Completion %") | |
| hours = gr.Slider(0, 40, 2, step=1, label="Hours Studied (per week)") | |
| confidence = gr.Slider(0, 10, 6, step=1, label="Confidence Before Assignment") | |
| assignment = gr.Dropdown(["Assignment","Quiz","Test","Project","Exam"], value="Assignment", label="Assignment Type") | |
| explain = gr.Checkbox(label="Explain with SHAP") | |
| btn = gr.Button("Predict GPA") | |
| with gr.Column(): | |
| gpa_out = gr.Textbox(label="Predicted GPA") | |
| letter_out = gr.Textbox(label="Predicted Letter Grade") | |
| exp_out = gr.Textbox(label="Explanation", lines=6) | |
| def run(*args): return predict_gpa(*args) | |
| btn.click(run, [ | |
| grade_level,semester,subject_type,subject,difficulty, | |
| past_avg_gpa,avg_test_score,avg_quiz,attendance,completion, | |
| hours,confidence,assignment,explain | |
| ], [gpa_out,letter_out,exp_out]) | |
| if __name__ == "__main__": | |
| demo.launch() | |