File size: 5,410 Bytes
d5980d3
 
 
 
 
 
 
fbd9939
d5980d3
 
fbd9939
 
 
d5980d3
 
 
 
 
 
 
fbd9939
 
 
d5980d3
 
 
 
 
 
 
 
 
5ca84fc
 
 
 
d5980d3
 
fbd9939
 
 
 
 
5ca84fc
fbd9939
 
 
 
 
 
d5980d3
fbd9939
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d5980d3
fbd9939
 
 
d5980d3
 
 
 
 
 
 
 
 
 
 
 
 
5ca84fc
 
 
 
d5980d3
 
 
 
 
 
5ca84fc
d5980d3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5ca84fc
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import os
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
os.environ["TF_ENABLE_ONEDNN_OPTS"] = "0"

import numpy as np
import gradio as gr
import joblib
import spaces  # Still needed for the dummy function
from tensorflow.keras.models import load_model

# ============================================================
# LOAD MODELS & SCALERS
# ============================================================
knn_model = joblib.load("knn_model.pkl")
svm_model = joblib.load("rbf_svm_model.pkl")
ann_model = load_model("ann_model.keras")

scaler_rfe = joblib.load("scaler_rfe.pkl")
selected_features = joblib.load("selected_features.pkl")

# ============================================================
# ENCODING MAPS
# ============================================================
gender_map = {"Female": 0, "Male": 1}
binary_map = {"No": 0, "Yes": 1}
ordinal_map = {"Low": 0, "Medium": 1, "High": 2}
alcohol_map = {"None": 0, "Low": 1, "Medium": 2, "High": 3}

binary_features = {'smoking', 'family_hd', 'diabetes', 'high_bp', 'low_hdl', 'high_ldl'}
ordinal_features = {'exercise', 'stress', 'sugar'}

def encode_input_value(feature, value):
    if feature == 'gender': return gender_map[value]
    if feature in binary_features: return binary_map[value]
    if feature in ordinal_features: return ordinal_map[value]
    if feature == 'alcohol': return alcohol_map[value]
    return float(value)

# ============================================================
# ZERO-GPU WORKAROUND
# ============================================================
# This satisfies Hugging Face's ZeroGPU requirement without 
# crashing your actual Scikit-Learn/TensorFlow models.
@spaces.GPU
def dummy_startup():
    pass

# ============================================================
# PREDICTION FUNCTION (NO GPU DECORATOR)
# ============================================================
def predict_heart_disease(model_choice, *feature_values):
    try:
        encoded_inputs = [encode_input_value(f, v) for f, v in zip(selected_features, feature_values)]
        X_input = np.array([encoded_inputs], dtype=float)
        X_scaled = scaler_rfe.transform(X_input)

        if model_choice == "KNN":
            y_pred = knn_model.predict(X_scaled)[0]
            y_prob = knn_model.predict_proba(X_scaled)[0][1]
        elif model_choice == "SVM":
            y_pred = svm_model.predict(X_scaled)[0]
            if hasattr(svm_model, "predict_proba"):
                y_prob = svm_model.predict_proba(X_scaled)[0][1]
            else:
                score = svm_model.decision_function(X_scaled)[0]
                y_prob = 1 / (1 + np.exp(-score))
        else: # ANN
            y_prob = float(ann_model.predict(X_scaled, verbose=0)[0][0])
            y_pred = int(y_prob >= 0.5)

        status = "Yes" if int(y_pred) == 1 else "No"
        probability_text = f"{y_prob:.4f} ({y_prob * 100:.2f}%)"

        return status, probability_text, model_choice
        
    except Exception as e:
        # If an error happens, this will print it directly to the Gradio UI
        return f"Error: {str(e)}", "Error", model_choice

# ============================================================
# UI DEFINITION
# ============================================================
feature_labels = {
    'age': 'Age', 'gender': 'Gender', 'blood_pressure': 'Blood Pressure',
    'cholesterol': 'Cholesterol Level', 'exercise': 'Exercise Habits',
    'smoking': 'Smoking', 'family_hd': 'Family Heart Disease', 'diabetes': 'Diabetes',
    'bmi': 'BMI', 'high_bp': 'High Blood Pressure', 'low_hdl': 'Low HDL Cholesterol',
    'high_ldl': 'High LDL Cholesterol', 'alcohol': 'Alcohol Consumption',
    'stress': 'Stress Level', 'sleep_hours': 'Sleep Hours', 'sugar': 'Sugar Consumption',
    'triglyceride': 'Triglyceride Level', 'fasting_bs': 'Fasting Blood Sugar',
    'crp': 'CRP Level', 'homocysteine': 'Homocysteine Level'
}

def create_feature_input(feature):
    label = feature_labels.get(feature, feature)
    if feature == 'gender': return gr.Dropdown(["Female", "Male"], label=label, value="Female")
    if feature in binary_features: return gr.Dropdown(["No", "Yes"], label=label, value="No")
    if feature in ordinal_features: return gr.Dropdown(["Low", "Medium", "High"], label=label, value="Medium")
    if feature == 'alcohol': return gr.Dropdown(["None", "Low", "Medium", "High"], label=label, value="None")
    return gr.Number(label=label, value=0.0)

with gr.Blocks() as demo:
    gr.Markdown("<h2 style='text-align:center'>💓 Heart Disease Prediction 💓</h2>")
    
    with gr.Row():
        model_input = gr.Dropdown(["ANN", "SVM", "KNN"], label="Choose Model", value="ANN")

    feature_inputs = []
    for start in range(0, len(selected_features), 5):
        with gr.Row():
            for feature in selected_features[start:start + 5]:
                feature_inputs.append(create_feature_input(feature))

    predict_button = gr.Button("Predict 💡", variant="primary")

    with gr.Row():
        status_output = gr.Textbox(label="Heart Disease Status")
        prob_output = gr.Textbox(label="Prediction Probability")
        model_output = gr.Textbox(label="Model Selected")

    predict_button.click(
        fn=predict_heart_disease,
        inputs=[model_input] + feature_inputs,
        outputs=[status_output, prob_output, model_output]
    )

demo.launch(server_name="0.0.0.0", server_port=7860, ssr_mode=False)