bci-mvp / src /cross_dataset_eval.py
WilliamK112
feat: cross-dataset evaluation + Hugging Face Spaces public demo scaffold
f95c38e
Raw
History Blame Contribute Delete
2.89 kB
"""
Cross-dataset generalization evaluation.
Expected structure:
data/
dataset_a/
relaxed/*.edf
focused/*.edf
dataset_b/
relaxed/*.edf
focused/*.edf
Usage:
python src/cross_dataset_eval.py --train dataset_a --test dataset_b
"""
from pathlib import Path
import argparse
import json
import numpy as np
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, f1_score, roc_auc_score
from src.preprocess import build_dataset_from_folder
def load_split(root: Path, name: str):
base = root / name
x0, y0 = build_dataset_from_folder(str(base / "relaxed"), label=0)
x1, y1 = build_dataset_from_folder(str(base / "focused"), label=1)
X = np.vstack([x0, x1])
y = np.concatenate([y0, y1])
return X, y
def evaluate(model, X_train, y_train, X_test, y_test):
model.fit(X_train, y_train)
pred = model.predict(X_test)
result = {
"accuracy": float(accuracy_score(y_test, pred)),
"f1": float(f1_score(y_test, pred)),
}
if hasattr(model, "predict_proba"):
proba = model.predict_proba(X_test)[:, 1]
result["auc"] = float(roc_auc_score(y_test, proba))
else:
result["auc"] = None
return result
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--data-root", default="data")
parser.add_argument("--train", required=True, help="train dataset folder name under data/")
parser.add_argument("--test", required=True, help="test dataset folder name under data/")
parser.add_argument("--out", default="outputs/cross_dataset_results.json")
args = parser.parse_args()
root = Path(args.data_root)
X_train, y_train = load_split(root, args.train)
X_test, y_test = load_split(root, args.test)
models = {
"RF": Pipeline([
("scaler", StandardScaler()),
("clf", RandomForestClassifier(n_estimators=400, class_weight="balanced", random_state=42, n_jobs=-1)),
]),
"SVM": Pipeline([
("scaler", StandardScaler()),
("clf", SVC(C=2.0, kernel="rbf", probability=True, class_weight="balanced", random_state=42)),
]),
}
results = {
"train_dataset": args.train,
"test_dataset": args.test,
"train_samples": int(len(y_train)),
"test_samples": int(len(y_test)),
"models": {},
}
for name, model in models.items():
results["models"][name] = evaluate(model, X_train, y_train, X_test, y_test)
out = Path(args.out)
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(json.dumps(results, indent=2), encoding="utf-8")
print(json.dumps(results, indent=2))
print(f"Saved {out}")
if __name__ == "__main__":
main()