tikenM/ecg-linear-separability

GitHub: tikenM/ecg-linear-separability

面向心电图心律失常检测的混合特征工程与可解释性分析框架,提供论文「Hybrid Feature Engineering for Resource-Efficient Arrhythmia Detection」的完整可复现代码与统计验证。

Stars: 0 | Forks: 0

# ECG-XAI:可解释且资源高效的 arrhythmia 框架 这是一个用于在 MIT-BIH 和 INCART 上进行 AAMI 分类 arrhythmia 的混合 feature-engineering pipeline,围绕四个核心正确性修复而构建,并根据同行评审的意见进行了第二轮扩展。其核心论点是:**separability + transparency + 廉价地恢复 black-box explanations**,并通过可部署临床声明所需的统计严谨性进行了评估。 ## 仓库结构 ``` ecg_xai/ config.py constants: AAMI classes, MITDB splits, feature families data_io.py WFDB loading, patient-disjoint splits, INCART patient grouping preprocessing.py zero-phase bandpass features.py feature extraction by family (time/freq/morph/wavelet/hrv/graph) pipeline.py leakage-safe imblearn pipeline, AdaptiveSMOTEENN, metrics pipeline_v1.py (not included here -- see "External dependencies" below) separability.py linear separability evidence + kernel-gap CI + tolerance sweep ablation.py feature-family ablation + graph cost-benefit significance.py paired CV tests, McNemar, patient-level bootstrap/Wilcoxon cluster_separability.py silhouette / Davies-Bouldin / Calinski-Harabasz + t-SNE threshold_analysis.py minority-class PR curves for the linear SVC smote_report.py class-wise distributions before/after AdaptiveSMOTEENN detection_eval.py R-peak detector scoring + reference-to-detected label transfer cnn_baseline.py 1D-CNN black-box baseline (torch, optional) xai_alignment.py linear-vs-CNN frequency-band explanation agreement analysis_extra.py (not included here -- see "External dependencies" below) run_experiments.py main end-to-end driver run_smote_report.py standalone SMOTE-ENN distribution report smoke_test.py synthetic-data sanity check, no real data required extract_reviewer_numbers.py pulls response-to-reviewers numbers out of results.json response_to_reviewers.md point-by-point reviewer response, code-linked requirements.txt ``` ## 安装 ``` pip install -r requirements.txt # torch only needed for the CNN/XAI stage python -c "import wfdb; wfdb.dl_database('mitdb', 'data/mitdb')" python -c "import wfdb; wfdb.dl_database('incartdb', 'data/incartdb')" ``` ## 运行 ``` python -m ecg_xai.run_experiments \ --mitdb data/mitdb --incartdb data/incartdb \ --out results/ --cnn --epochs 30 ``` Flags 互相独立;如果您不需要这些阶段,可以去掉 `--incartdb` 或 `--cnn`。`--extra` 运行 leakage/headroom/causality/runtime/window-sweep 实验(需要 `analysis_extra.py`,见下文)。 写入 `results/results.json` 以及两张图表:`results/tsne_feature_space.png` 和 `results/pr_curve_minority_classes.png`。 ### 独立运行:SMOTE-ENN 类别分布报告 独立于主驱动程序运行: ``` python -m ecg_xai.run_smote_report --mitdb data/mitdb --classifier svc --out results/smote_report.json ``` 打印 SMOTE 之前 / 之后以及 ENN 之后的类别计数,既作为单一的 DS1 训练集表格,也按患者分组的 CV fold 输出(与 `cross_val_grouped` 使用的确切 fold 相匹配,因为 `AdaptiveSMOTEENN` 所选择的 `k` 及其回退到普通过采样的分支可能会因 fold 而异)。 ## `results.json` 包含的内容 | Key | From | 解决的 Fix / comment | |---|---|---| | `mitdb.interpatient`, `mitdb.grouped_cv` | original | inter-patient eval,无 leakage | | `mitdb.separability` | `separability.py` | linear/RBF kernel gap,现包含 per-fold CI、paired test 和 epsilon-tolerance sweep | | `mitdb.ablation` | `ablation.py` | leave-one-family-out / only-one-in | | `mitdb.significance_cv` | `significance.py` | 在相同 CV fold 上进行 paired classifier comparison | | `mitdb.significance_mcnemar_descriptive` | `significance.py` | per-beat McNemar -- **descriptive only**,在 within-patient correlation 下会夸大证据 | | `mitdb.significance_patient_cluster_bootstrap`, `mitdb.significance_patientwise` | `significance.py` | patient-level inference;这些是主要的显著性结果 | | `mitdb.separability_extended` | `cluster_separability.py` | silhouette / Davies-Bouldin / Calinski-Harabasz + t-SNE,独立于任何已拟合的 classifier | | `mitdb.minority_class_pr` | `threshold_analysis.py` | linear SVC 的 SVEB/VEB precision-recall 曲线及 threshold sweep | | `incart.allocation` | `data_io.py` | patient-level(而非 record-level)hold-out,包含每个分区的确切 patient/record ID 以及严格的互斥性检查 | | `xai_alignment` | `xai_alignment.py`, `cnn_baseline.py` | linear-vs-CNN frequency-band agreement(需要 `--cnn`) | | `extra.*` | `analysis_extra.py` | leakage 压力测试、非线性 headroom、causal-vs-acausal graph、runtime profile、window sweep(需要 `--extra`) | ## 针对评审意见的补充 本次修订回应了六条评审意见。从意见到代码的映射,以及还需要哪些真实数据运行才能完成的工作,都在 [`response_to_reviewers.md`](./response_to_reviewers.md) 中进行了完整记录。摘要如下: 1. **Patient-level 显著性** (`significance.py`)。Per-beat McNemar 将患者内相关的 beat 视为独立的,从而夸大了证据;现已由 patient cluster bootstrap 和 patient-wise paired Wilcoxon test 取代,两者均经过 Holm-Bonferroni 校正。 2. **Kernel-gap 不确定性** (`separability.py`)。Linear-vs-RBF gap 现在会报告 per-fold 95% CI、paired significance test 以及对五种 separability tolerances (epsilon) 的 sweep,从而展示结论对任意选择的 epsilon 的敏感性,而不是凭空断言。“separability certificate”一词已被停用,替换为证据实际支持的语言表述。 3. **“总体风险的无偏估计 (Unbiased estimate of population risk)”声明。** 仅存在于正文中;没有代码对此进行断言。修订后的命题请参阅 `response_to_reviewers.md`。 4. **Graph 特征成本效益** (`ablation.py`)。分离出 *在已有 HRV 的情况下* graph 的边际效益(而不仅仅是 leave-one-out),并将每个家族的 F1 delta 与其测量的 per-beat 成本相关联。 5. **INCART patient-level 分组** (`data_io.py`)。最初的 hold-out 是按 *record* 分组的;INCART 的 75 条 record 仅来自 32 名患者,因此这可能会将同一名患者的 record 拆分到训练集和测试集中。通过从每个 `.hea` 文件解析患者编号并以此进行分组已修复此问题,并在 runtime 期间进行严格断言,确保没有任何一名患者同时出现在两个分区中。 6. **检测到的 peak 评估** (`detection_eval.py`)。提供了一个框架 —— 包含 AAMI 风格的 R-peak 匹配、detection 评分以及从参考到检测的标签转移 —— 用于在您的 ensemble detector 输出而不是参考注释上运行分类基准测试。此功能未接入 CLI:请将您的 detector 作为 callable 提供。 ## 本仓库快照中未包含的外部依赖 有两个模块被 `run_experiments.py` 导入,但未包含在本次修订过程中: - **`pipeline_v1.py`** -- 导入用于 `build_pipeline`, `evaluate_interpatient`, `cross_val_grouped`, `classifier_size_kb`。本次修订中的所有测试均使用 `pipeline.py` 的实现作为这四个名称的替代;在信任真实运行中的连接逻辑之前,请确认 `pipeline_v1.py` 的签名是否匹配。 - **`analysis_extra.py`** -- `--extra` 所需,并且是 `--cnn` (`alignment_significance`) 的一部分。在本次修订中未进行任何测试。 ## 测试状态 针对 reviewer-response 补充中的每个函数,均在合成的 patient-grouped beat 数据上使用了真实的 `data_io.py` / `features.py` / `pipeline.py` 进行了运行测试,并在可能产生静默错误的地方进行了手动正确性验证:例如,已将 `decision_function` 的列顺序与 `pipe.predict()` 进行了核对(要求 100% 一致),已检查 INCART patient grouping 以防止被留出的患者的 record 出现在训练集中,并且已针对一个已知 TP/FP/FN 数量的手动构建案例验证了 `detection_eval.py` 中的 R-peak 匹配。 以上所有内容都不能替代在真实 MIT-BIH/INCART 数据上的运行。`response_to_reviewers.md` 中的数字被标记为 `[TO FILL]` 而非估计值,应在真实运行后通过 `extract_reviewer_numbers.py` 进行填充 —— 关于哪个 `results.json` key 对应哪个占位符,请参见该脚本的 docstring。
标签:Apex, TruffleHog, 凭据扫描, 医疗AI, 可解释AI, 学术论文, 心电图分析, 机器学习, 逆向工具