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