Completed Machine Learning

Mental Health Prediction App

An ensemble ML pipeline predicting mental health risk from behavioral and workplace survey data — 85%+ accuracy with a real-time Streamlit interface for clinicians and researchers.

Python XGBoost scikit-learn Streamlit pandas SMOTE matplotlib
Mental Health Prediction App interface

Overview

What it does

Predicts whether an individual is at risk for a mental health condition using behavioral and workplace survey responses, with real-time inference via a Streamlit dashboard.

Problem solved

Mental health screening is slow, expensive, and inaccessible. This turns a structured survey into an instant, private risk assessment — no clinician required for initial triage.

Built for

Healthcare researchers, HR teams running workplace wellness programs, and CS students exploring applied ML on real-world health datasets.

The project uses the Open Sourcing Mental Illness (OSMI) dataset, which contains ~1,400 survey responses from tech industry workers. After extensive preprocessing — handling missing values, encoding ordinal features, and applying SMOTE to correct a 70/30 class imbalance — four classifiers were trained and compared: Logistic Regression, Random Forest, SVM, and XGBoost.

XGBoost was the top performer across all metrics. The final pipeline is serialized with joblib and served through a Streamlit UI that collects the same survey inputs, runs inference in real time, and visualizes feature importance so users can understand why the model flagged a risk.


Technical Architecture

Data flow

Survey CSV
Preprocessing
SMOTE
XGBoost Model
Risk Score
Streamlit UI

Key components

  • Preprocessing pipeline — label encoding, one-hot encoding for categoricals, StandardScaler for numeric features
  • SMOTE oversampling — synthetic minority oversampling to fix the 70/30 class split before training
  • Model ensemble — XGBoost, Random Forest, SVM, Logistic Regression compared with k-fold CV
  • Streamlit app — form UI, real-time inference, feature importance chart, downloadable report

Model selection

Four algorithms were evaluated using 5-fold stratified cross-validation. XGBoost outperformed all others on F1 and AUC-ROC. Chi-squared tests and mutual information scoring reduced features from 27 to the 16 most predictive columns.


Key Features

85%+ prediction accuracy

XGBoost with hyperparameter tuning via GridSearchCV achieves 85%+ accuracy and 0.91 AUC-ROC on held-out test data.

Real-time inference

Streamlit form collects inputs and returns a risk probability score in under 100ms — no server required.

Explainable predictions

Feature importance bar chart shows which survey answers drove the prediction — critical for clinical trust.

Model comparison dashboard

Side-by-side accuracy, F1, precision, and recall metrics for all four tested classifiers.


Challenges & Solutions

Class imbalance in the dataset

Challenge: 70% of respondents had no mental health concern, causing the model to default to predicting the majority class.

Solution: Applied SMOTE (Synthetic Minority Oversampling Technique) inside each CV fold to prevent data leakage while generating synthetic minority samples. Improved recall on the positive class from 61% to 81%.

High-cardinality categorical features

Challenge: The "Country" and "Work_interfere" columns had many categories, and naive one-hot encoding inflated the feature space.

Solution: Used chi-squared testing and mutual information to identify and drop low-signal categories. Grouped rare countries into an "Other" bin, reducing dimensionality by 40%.

Streamlit state management

Challenge: The model pipeline (scaler + encoder + classifier) needed to be loaded once, not re-instantiated on every rerun.

Solution: Used @st.cache_resource to load the joblib pipeline once and keep it in memory across sessions.


Results & Impact

85%+

Prediction accuracy

0.91

AUC-ROC score

0.83

F1 score

4

Models compared

XGBoost consistently outperformed the other three classifiers. The model generalizes well on the held-out test set, with no significant gap between training and test metrics — indicating minimal overfitting despite the SMOTE augmentation.

What I'd do differently

  • Optimize for recall, not accuracy. Accuracy is dominated by the majority class. For mental health screening, a false negative — missing someone who needs help — is far more costly than a false positive. I'd set the classification threshold to 0.3 and report per-class recall prominently.
  • Surface SHAP explanations in the UI. The current app shows feature importance globally. SHAP would show per-prediction explanations — which specific survey answers drove this person's score — making the output clinically actionable rather than just a number.
  • Add confidence intervals, not just a point estimate. A model this size on 1,400 samples has real variance. Showing "65–80% risk" rather than "74% risk" is more honest about what the data actually supports.

Code Highlight

Applying SMOTE inside each CV fold prevents data leakage while correcting class imbalance.

from xgboost import XGBClassifier
from sklearn.model_selection import StratifiedKFold, cross_val_score
from imblearn.over_sampling import SMOTE
from imblearn.pipeline import Pipeline as ImbPipeline

# SMOTE inside the pipeline ensures no leakage across CV folds
pipeline = ImbPipeline([
    ('smote', SMOTE(random_state=42)),
    ('scaler', StandardScaler()),
    ('model', XGBClassifier(
        n_estimators=200,
        max_depth=6,
        learning_rate=0.05,
        scale_pos_weight=2.3,    # extra weight on positive class
        use_label_encoder=False,
        eval_metric='auc'
    ))
])

cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(pipeline, X, y, cv=cv, scoring='roc_auc')
print(f"AUC-ROC: {scores.mean():.3f} ± {scores.std():.3f}")