GPAPredictor / app.py
WarTitan2077's picture
Update app.py
18147f2 verified
# 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()