mallelamanoj75 commited on
Commit
fbf9d7d
·
verified ·
1 Parent(s): ded4fbc

Deploy MediPredict AI Gradio app

Browse files
Files changed (7) hide show
  1. .gitignore +32 -0
  2. README.md +186 -6
  3. app.py +182 -0
  4. data/.gitkeep +1 -0
  5. model/.gitkeep +1 -0
  6. requirements.txt +6 -0
  7. train.py +185 -0
.gitignore ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Virtual environments
7
+ .venv/
8
+ venv/
9
+ env/
10
+
11
+ # Environment and credentials
12
+ .env
13
+ .env.*
14
+ kaggle.json
15
+
16
+ # Local notebooks and editor files
17
+ .ipynb_checkpoints/
18
+ .vscode/
19
+ .idea/
20
+
21
+ # OS files
22
+ .DS_Store
23
+ Thumbs.db
24
+
25
+ # Generated dataset and model artifacts
26
+ data/*
27
+ !data/.gitkeep
28
+ model/*
29
+ !model/.gitkeep
30
+
31
+ # Logs
32
+ *.log
README.md CHANGED
@@ -1,12 +1,192 @@
1
  ---
2
  title: MediPredict AI
3
- emoji: 🐢
4
- colorFrom: indigo
5
  colorTo: green
6
  sdk: gradio
7
- sdk_version: 6.13.0
8
- app_file: app.py
9
- pinned: false
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: MediPredict AI
3
+ emoji: 🏥
4
+ colorFrom: blue
5
  colorTo: green
6
  sdk: gradio
7
+ sdk_version: "4.0.0"
8
+ python_version: "3.10"
 
9
  ---
10
 
11
+ # MediPredict AI - Disease Prediction System
12
+
13
+ MediPredict AI is an end-to-end machine learning project that predicts diabetes risk using the Pima Indians Diabetes Database. It trains Logistic Regression, Random Forest, and XGBoost classifiers, evaluates them with standard classification metrics, saves the best model, and serves predictions through a Gradio web interface.
14
+
15
+ This project is built for Python 3.10, VS Code, and Hugging Face Spaces.
16
+
17
+ ## Project Structure
18
+
19
+ ```text
20
+ disease-prediction-app/
21
+ ├── data/
22
+ │ └── diabetes.csv
23
+ ├── model/
24
+ │ ├── model.pkl
25
+ │ └── scaler.pkl
26
+ ├── app.py
27
+ ├── train.py
28
+ ├── requirements.txt
29
+ └── README.md
30
+ ```
31
+
32
+ `diabetes.csv`, `model.pkl`, and `scaler.pkl` are generated after running `train.py`.
33
+
34
+ ## Dataset
35
+
36
+ Dataset: Pima Indians Diabetes Database
37
+ Kaggle slug: `uciml/pima-indians-diabetes-database`
38
+
39
+ The training script downloads the dataset automatically with the Kaggle API.
40
+
41
+ ## Kaggle API Setup
42
+
43
+ 1. Install the Kaggle package:
44
+
45
+ ```bash
46
+ pip install kaggle
47
+ ```
48
+
49
+ 2. Go to your Kaggle account settings:
50
+
51
+ ```text
52
+ https://www.kaggle.com/settings
53
+ ```
54
+
55
+ 3. Create a new API token. Kaggle downloads a file named `kaggle.json`.
56
+
57
+ 4. Place `kaggle.json` in the correct folder.
58
+
59
+ On Windows:
60
+
61
+ ```text
62
+ C:\Users\<your-username>\.kaggle\kaggle.json
63
+ ```
64
+
65
+ On macOS or Linux:
66
+
67
+ ```text
68
+ ~/.kaggle/kaggle.json
69
+ ```
70
+
71
+ 5. On macOS or Linux, set file permissions:
72
+
73
+ ```bash
74
+ chmod 600 ~/.kaggle/kaggle.json
75
+ ```
76
+
77
+ ## Local Setup in VS Code
78
+
79
+ Open the project folder in VS Code:
80
+
81
+ ```bash
82
+ cd disease-prediction-app
83
+ ```
84
+
85
+ Create a virtual environment:
86
+
87
+ ```bash
88
+ python -m venv .venv
89
+ ```
90
+
91
+ Activate the virtual environment on Windows PowerShell:
92
+
93
+ ```bash
94
+ .\.venv\Scripts\Activate.ps1
95
+ ```
96
+
97
+ Activate the virtual environment on macOS or Linux:
98
+
99
+ ```bash
100
+ source .venv/bin/activate
101
+ ```
102
+
103
+ Install dependencies:
104
+
105
+ ```bash
106
+ pip install -r requirements.txt
107
+ ```
108
+
109
+ Train the model:
110
+
111
+ ```bash
112
+ python train.py
113
+ ```
114
+
115
+ Run the Gradio app:
116
+
117
+ ```bash
118
+ python app.py
119
+ ```
120
+
121
+ Open the local URL shown in the terminal, usually:
122
+
123
+ ```text
124
+ http://127.0.0.1:7860
125
+ ```
126
+
127
+ ## Model Training
128
+
129
+ `train.py` performs the full ML workflow:
130
+
131
+ - Downloads `diabetes.csv` from Kaggle.
132
+ - Loads the data into a pandas DataFrame.
133
+ - Replaces zero values in `Glucose`, `BloodPressure`, `SkinThickness`, `Insulin`, and `BMI` with `NaN`.
134
+ - Fills missing values with column means.
135
+ - Scales features with `StandardScaler`.
136
+ - Trains Logistic Regression, Random Forest, and XGBoost.
137
+ - Evaluates models using accuracy, precision, recall, F1-score, and ROC-AUC.
138
+ - Saves the best model to `model/model.pkl`.
139
+ - Saves the scaler to `model/scaler.pkl`.
140
+
141
+ ## Gradio App
142
+
143
+ `app.py` loads the saved model and scaler, accepts patient health inputs, scales the input, predicts diabetes risk, and displays:
144
+
145
+ - Prediction: `Low Risk` or `High Risk`
146
+ - Probability score
147
+
148
+ The app uses Gradio only. It does not use Streamlit.
149
+
150
+ ## Hugging Face Spaces Deployment
151
+
152
+ 1. Train the model locally first:
153
+
154
+ ```bash
155
+ python train.py
156
+ ```
157
+
158
+ 2. Confirm these files exist:
159
+
160
+ ```text
161
+ data/diabetes.csv
162
+ model/model.pkl
163
+ model/scaler.pkl
164
+ ```
165
+
166
+ 3. Create a new Hugging Face Space:
167
+
168
+ - Go to `https://huggingface.co/spaces`
169
+ - Click **Create new Space**
170
+ - Select **Gradio** as the SDK
171
+ - Use Python 3.10
172
+
173
+ 4. Upload or push these files to the Space repository:
174
+
175
+ ```text
176
+ app.py
177
+ train.py
178
+ requirements.txt
179
+ README.md
180
+ data/diabetes.csv
181
+ model/model.pkl
182
+ model/scaler.pkl
183
+ ```
184
+
185
+ 5. Hugging Face Spaces will install dependencies from `requirements.txt` and run `app.py` automatically.
186
+
187
+ ## Important Notes
188
+
189
+ - This app is for educational use only.
190
+ - It is not a medical diagnosis tool.
191
+ - The Gradio app uses a default Diabetes Pedigree Function value of `0.47` because the requested UI inputs do not include that dataset feature.
192
+ - For production medical use, consult clinical experts, validate the model rigorously, and follow healthcare compliance requirements.
app.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pickle
2
+ from pathlib import Path
3
+
4
+ import gradio as gr
5
+ import pandas as pd
6
+
7
+
8
+ BASE_DIR = Path(__file__).resolve().parent
9
+ MODEL_FILE = BASE_DIR / "model" / "model.pkl"
10
+ SCALER_FILE = BASE_DIR / "model" / "scaler.pkl"
11
+
12
+ DEFAULT_DPF = 0.47
13
+ FEATURE_COLUMNS = [
14
+ "Pregnancies",
15
+ "Glucose",
16
+ "BloodPressure",
17
+ "SkinThickness",
18
+ "Insulin",
19
+ "BMI",
20
+ "DiabetesPedigreeFunction",
21
+ "Age",
22
+ ]
23
+
24
+
25
+ def load_pickle(path: Path):
26
+ if not path.exists():
27
+ raise FileNotFoundError(
28
+ f"{path.name} was not found in {path.parent}. Run `python train.py` first."
29
+ )
30
+
31
+ with path.open("rb") as file:
32
+ return pickle.load(file)
33
+
34
+
35
+ model = load_pickle(MODEL_FILE)
36
+ scaler = load_pickle(SCALER_FILE)
37
+
38
+
39
+ def validate_inputs(values: list[float]) -> str | None:
40
+ labels = [
41
+ "Pregnancies",
42
+ "Glucose",
43
+ "Blood Pressure",
44
+ "Skin Thickness",
45
+ "Insulin",
46
+ "BMI",
47
+ "Age",
48
+ ]
49
+
50
+ for label, value in zip(labels, values):
51
+ if value is None:
52
+ return f"Please enter a value for {label}."
53
+ if value < 0:
54
+ return f"{label} cannot be negative."
55
+
56
+ if values[5] == 0:
57
+ return "BMI must be greater than 0."
58
+
59
+ return None
60
+
61
+
62
+ def predict_diabetes(
63
+ pregnancies: float,
64
+ glucose: float,
65
+ blood_pressure: float,
66
+ skin_thickness: float,
67
+ insulin: float,
68
+ bmi: float,
69
+ age: float,
70
+ ) -> tuple[str, str]:
71
+ values = [
72
+ pregnancies,
73
+ glucose,
74
+ blood_pressure,
75
+ skin_thickness,
76
+ insulin,
77
+ bmi,
78
+ age,
79
+ ]
80
+
81
+ error = validate_inputs(values)
82
+ if error:
83
+ return "Invalid Input", error
84
+
85
+ input_data = pd.DataFrame(
86
+ [
87
+ {
88
+ "Pregnancies": pregnancies,
89
+ "Glucose": glucose,
90
+ "BloodPressure": blood_pressure,
91
+ "SkinThickness": skin_thickness,
92
+ "Insulin": insulin,
93
+ "BMI": bmi,
94
+ "DiabetesPedigreeFunction": DEFAULT_DPF,
95
+ "Age": age,
96
+ }
97
+ ],
98
+ columns=FEATURE_COLUMNS,
99
+ )
100
+
101
+ scaled_input = scaler.transform(input_data)
102
+ prediction = int(model.predict(scaled_input)[0])
103
+ probability = float(model.predict_proba(scaled_input)[0][1])
104
+
105
+ risk_label = "High Risk" if prediction == 1 else "Low Risk"
106
+ risk_message = (
107
+ "The model predicts a higher likelihood of diabetes."
108
+ if prediction == 1
109
+ else "The model predicts a lower likelihood of diabetes."
110
+ )
111
+
112
+ return risk_label, f"{probability * 100:.2f}% probability. {risk_message}"
113
+
114
+
115
+ custom_css = """
116
+ .gradio-container {
117
+ max-width: 960px !important;
118
+ margin: auto !important;
119
+ }
120
+ .risk-box {
121
+ border-radius: 8px;
122
+ }
123
+ """
124
+
125
+
126
+ with gr.Blocks(theme=gr.themes.Soft(), css=custom_css, title="MediPredict AI") as demo:
127
+ gr.Markdown(
128
+ """
129
+ # 🏥 MediPredict AI
130
+ A disease prediction system that estimates diabetes risk using a trained machine learning classifier.
131
+ Enter patient health values below to get a risk category and probability score.
132
+ """
133
+ )
134
+
135
+ with gr.Row():
136
+ with gr.Column():
137
+ pregnancies = gr.Slider(
138
+ minimum=0,
139
+ maximum=20,
140
+ value=1,
141
+ step=1,
142
+ label="Pregnancies",
143
+ )
144
+ glucose = gr.Number(value=120, label="Glucose")
145
+ blood_pressure = gr.Number(value=70, label="Blood Pressure")
146
+ skin_thickness = gr.Number(value=20, label="Skin Thickness")
147
+
148
+ with gr.Column():
149
+ insulin = gr.Number(value=80, label="Insulin")
150
+ bmi = gr.Number(value=25.0, label="BMI")
151
+ age = gr.Number(value=30, label="Age")
152
+
153
+ predict_button = gr.Button("Predict Diabetes Risk", variant="primary")
154
+
155
+ with gr.Row():
156
+ prediction_output = gr.Textbox(label="Prediction", interactive=False)
157
+ probability_output = gr.Textbox(label="Probability Score", interactive=False)
158
+
159
+ predict_button.click(
160
+ fn=predict_diabetes,
161
+ inputs=[
162
+ pregnancies,
163
+ glucose,
164
+ blood_pressure,
165
+ skin_thickness,
166
+ insulin,
167
+ bmi,
168
+ age,
169
+ ],
170
+ outputs=[prediction_output, probability_output],
171
+ )
172
+
173
+ gr.Markdown(
174
+ """
175
+ **Note:** This app is for educational purposes only and is not a medical diagnosis tool.
176
+ Please consult a qualified healthcare professional for medical advice.
177
+ """
178
+ )
179
+
180
+
181
+ if __name__ == "__main__":
182
+ demo.launch()
data/.gitkeep ADDED
@@ -0,0 +1 @@
 
 
1
+
model/.gitkeep ADDED
@@ -0,0 +1 @@
 
 
1
+
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ pandas
2
+ numpy
3
+ scikit-learn
4
+ xgboost
5
+ gradio
6
+ kaggle
train.py ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import pickle
3
+ import zipfile
4
+ from pathlib import Path
5
+
6
+ import numpy as np
7
+ import pandas as pd
8
+ from kaggle.api.kaggle_api_extended import KaggleApi
9
+ from sklearn.ensemble import RandomForestClassifier
10
+ from sklearn.linear_model import LogisticRegression
11
+ from sklearn.metrics import (
12
+ accuracy_score,
13
+ f1_score,
14
+ precision_score,
15
+ recall_score,
16
+ roc_auc_score,
17
+ )
18
+ from sklearn.model_selection import train_test_split
19
+ from sklearn.preprocessing import StandardScaler
20
+ from xgboost import XGBClassifier
21
+
22
+
23
+ BASE_DIR = Path(__file__).resolve().parent
24
+ DATA_DIR = BASE_DIR / "data"
25
+ MODEL_DIR = BASE_DIR / "model"
26
+ DATASET_SLUG = "uciml/pima-indians-diabetes-database"
27
+ DATA_FILE = DATA_DIR / "diabetes.csv"
28
+ MODEL_FILE = MODEL_DIR / "model.pkl"
29
+ SCALER_FILE = MODEL_DIR / "scaler.pkl"
30
+
31
+ FEATURE_COLUMNS = [
32
+ "Pregnancies",
33
+ "Glucose",
34
+ "BloodPressure",
35
+ "SkinThickness",
36
+ "Insulin",
37
+ "BMI",
38
+ "DiabetesPedigreeFunction",
39
+ "Age",
40
+ ]
41
+
42
+ ZERO_AS_MISSING_COLUMNS = [
43
+ "Glucose",
44
+ "BloodPressure",
45
+ "SkinThickness",
46
+ "Insulin",
47
+ "BMI",
48
+ ]
49
+
50
+
51
+ def download_dataset() -> None:
52
+ """Download the Pima Indians Diabetes dataset using Kaggle API."""
53
+ DATA_DIR.mkdir(parents=True, exist_ok=True)
54
+
55
+ if DATA_FILE.exists():
56
+ print(f"Dataset already exists: {DATA_FILE}")
57
+ return
58
+
59
+ print("Downloading dataset from Kaggle...")
60
+ api = KaggleApi()
61
+ api.authenticate()
62
+ api.dataset_download_files(DATASET_SLUG, path=str(DATA_DIR), unzip=False)
63
+
64
+ zip_path = DATA_DIR / "pima-indians-diabetes-database.zip"
65
+ if not zip_path.exists():
66
+ raise FileNotFoundError(
67
+ "Kaggle download finished, but the expected zip file was not found."
68
+ )
69
+
70
+ with zipfile.ZipFile(zip_path, "r") as zip_ref:
71
+ zip_ref.extractall(DATA_DIR)
72
+
73
+ zip_path.unlink(missing_ok=True)
74
+
75
+ if not DATA_FILE.exists():
76
+ raise FileNotFoundError("diabetes.csv was not found after extracting the dataset.")
77
+
78
+ print(f"Dataset saved to: {DATA_FILE}")
79
+
80
+
81
+ def load_and_preprocess_data() -> tuple[np.ndarray, np.ndarray, StandardScaler]:
82
+ """Load data, clean missing medical values, split features, and scale."""
83
+ df = pd.read_csv(DATA_FILE)
84
+
85
+ missing_columns = [col for col in FEATURE_COLUMNS + ["Outcome"] if col not in df.columns]
86
+ if missing_columns:
87
+ raise ValueError(f"Dataset is missing required columns: {missing_columns}")
88
+
89
+ df[ZERO_AS_MISSING_COLUMNS] = df[ZERO_AS_MISSING_COLUMNS].replace(0, np.nan)
90
+ df[ZERO_AS_MISSING_COLUMNS] = df[ZERO_AS_MISSING_COLUMNS].fillna(
91
+ df[ZERO_AS_MISSING_COLUMNS].mean()
92
+ )
93
+
94
+ x = df[FEATURE_COLUMNS]
95
+ y = df["Outcome"]
96
+
97
+ scaler = StandardScaler()
98
+ x_scaled = scaler.fit_transform(x)
99
+
100
+ return x_scaled, y.to_numpy(), scaler
101
+
102
+
103
+ def evaluate_model(name: str, model, x_test: np.ndarray, y_test: np.ndarray) -> dict:
104
+ """Calculate common classification metrics for a trained model."""
105
+ predictions = model.predict(x_test)
106
+
107
+ if hasattr(model, "predict_proba"):
108
+ probabilities = model.predict_proba(x_test)[:, 1]
109
+ else:
110
+ probabilities = predictions
111
+
112
+ metrics = {
113
+ "model": name,
114
+ "accuracy": accuracy_score(y_test, predictions),
115
+ "precision": precision_score(y_test, predictions, zero_division=0),
116
+ "recall": recall_score(y_test, predictions, zero_division=0),
117
+ "f1_score": f1_score(y_test, predictions, zero_division=0),
118
+ "roc_auc": roc_auc_score(y_test, probabilities),
119
+ }
120
+ return metrics
121
+
122
+
123
+ def train_models() -> None:
124
+ """Train candidate models and save the best model by ROC-AUC."""
125
+ MODEL_DIR.mkdir(parents=True, exist_ok=True)
126
+ download_dataset()
127
+
128
+ x, y, scaler = load_and_preprocess_data()
129
+ x_train, x_test, y_train, y_test = train_test_split(
130
+ x,
131
+ y,
132
+ test_size=0.2,
133
+ random_state=42,
134
+ stratify=y,
135
+ )
136
+
137
+ models = {
138
+ "Logistic Regression": LogisticRegression(max_iter=1000, random_state=42),
139
+ "Random Forest": RandomForestClassifier(
140
+ n_estimators=200,
141
+ random_state=42,
142
+ class_weight="balanced",
143
+ ),
144
+ "XGBoost": XGBClassifier(
145
+ n_estimators=200,
146
+ learning_rate=0.05,
147
+ max_depth=3,
148
+ subsample=0.9,
149
+ colsample_bytree=0.9,
150
+ eval_metric="logloss",
151
+ random_state=42,
152
+ ),
153
+ }
154
+
155
+ results = []
156
+ trained_models = {}
157
+
158
+ for name, model in models.items():
159
+ print(f"\nTraining {name}...")
160
+ model.fit(x_train, y_train)
161
+ trained_models[name] = model
162
+ metrics = evaluate_model(name, model, x_test, y_test)
163
+ results.append(metrics)
164
+
165
+ results_df = pd.DataFrame(results).sort_values(by="roc_auc", ascending=False)
166
+ print("\nModel Evaluation Results")
167
+ print(results_df.to_string(index=False))
168
+
169
+ best_model_name = results_df.iloc[0]["model"]
170
+ best_model = trained_models[best_model_name]
171
+
172
+ with MODEL_FILE.open("wb") as model_file:
173
+ pickle.dump(best_model, model_file)
174
+
175
+ with SCALER_FILE.open("wb") as scaler_file:
176
+ pickle.dump(scaler, scaler_file)
177
+
178
+ print(f"\nBest model: {best_model_name}")
179
+ print(f"Model saved to: {MODEL_FILE}")
180
+ print(f"Scaler saved to: {SCALER_FILE}")
181
+
182
+
183
+ if __name__ == "__main__":
184
+ os.environ.setdefault("KAGGLE_CONFIG_DIR", str(Path.home() / ".kaggle"))
185
+ train_models()