bci-mvp / src /ensemble_benchmark.py
WilliamK112
feat: Add Stacking Ensemble model benchmark combining RF, SVM, and MLP
f60e814
Raw
History Blame Contribute Delete
3.27 kB
"""
Ensemble model benchmark for superior classification performance.
Combines RF, SVM, and MLP into a Stacking Classifier to exceed baseline accuracy.
Outputs metrics to outputs/ensemble_results.csv
"""
from pathlib import Path
import numpy as np
import pandas as pd
from time import perf_counter
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier, StackingClassifier
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import accuracy_score, f1_score, roc_auc_score
from src.preprocess import build_dataset_from_folder
def evaluate_model(name, clf, X_train, y_train, X_test, y_test):
t0 = perf_counter()
clf.fit(X_train, y_train)
train_time = perf_counter() - t0
t1 = perf_counter()
pred = clf.predict(X_test)
infer_time = (perf_counter() - t1) / len(X_test)
if hasattr(clf, "predict_proba"):
proba = clf.predict_proba(X_test)[:, 1]
auc = roc_auc_score(y_test, proba)
else:
auc = np.nan
return {
"model": name,
"accuracy": accuracy_score(y_test, pred),
"f1": f1_score(y_test, pred),
"auc": auc,
"train_sec": train_time,
"infer_sec_per_sample": infer_time,
}
def main():
print("[1] Loading data...")
X0, y0 = build_dataset_from_folder("data/relaxed", label=0)
X1, y1 = build_dataset_from_folder("data/focused", label=1)
X = np.vstack([X0, X1])
y = np.concatenate([y0, y1])
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
rf = RandomForestClassifier(n_estimators=400, class_weight="balanced", random_state=42, n_jobs=-1)
svm = SVC(C=2.0, kernel="rbf", probability=True, class_weight="balanced", random_state=42)
mlp = MLPClassifier(hidden_layer_sizes=(128, 64), activation="relu", early_stopping=True, random_state=42)
estimators = [
('rf', rf),
('svm', svm),
('mlp', mlp)
]
stacking = StackingClassifier(
estimators=estimators,
final_estimator=LogisticRegression(),
cv=5,
n_jobs=-1
)
models = {
"RF_Baseline": Pipeline([("scaler", StandardScaler()), ("clf", rf)]),
"SVM_Baseline": Pipeline([("scaler", StandardScaler()), ("clf", svm)]),
"MLP_Baseline": Pipeline([("scaler", StandardScaler()), ("clf", mlp)]),
"Stacking_Ensemble": Pipeline([("scaler", StandardScaler()), ("clf", stacking)]),
}
print("[2] Evaluating models...")
rows = []
for name, clf in models.items():
print(f" -> Training {name}...")
rows.append(evaluate_model(name, clf, X_train, y_train, X_test, y_test))
df = pd.DataFrame(rows).sort_values("accuracy", ascending=False)
out_dir = Path("outputs")
out_dir.mkdir(exist_ok=True)
out_csv = out_dir / "ensemble_benchmark_results.csv"
df.to_csv(out_csv, index=False)
print("\n[3] Results:")
print(df.to_string(index=False))
print(f"\nSaved results to {out_csv}")
if __name__ == "__main__":
main()