""" 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()