File size: 4,264 Bytes
751d397
18147f2
 
751d397
18147f2
 
 
 
 
751d397
18147f2
 
 
 
 
 
 
 
751d397
18147f2
 
 
 
 
 
 
 
 
 
 
 
751d397
18147f2
 
 
b361107
18147f2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
751d397
18147f2
 
 
 
 
751d397
18147f2
 
 
751d397
b361107
18147f2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b361107
18147f2
 
 
751d397
b361107
18147f2
 
 
 
 
751d397
18147f2
751d397
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# 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()