From 287fc2944f6279c553f1c9d2779c85c016b8c67f Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 5 Apr 2026 18:11:28 -0400 Subject: [PATCH 01/39] Create pyhealth.datasets.PTBXLDataset.rst --- docs/api/datasets/pyhealth.datasets.PTBXLDataset.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 docs/api/datasets/pyhealth.datasets.PTBXLDataset.rst diff --git a/docs/api/datasets/pyhealth.datasets.PTBXLDataset.rst b/docs/api/datasets/pyhealth.datasets.PTBXLDataset.rst new file mode 100644 index 000000000..36dfedb95 --- /dev/null +++ b/docs/api/datasets/pyhealth.datasets.PTBXLDataset.rst @@ -0,0 +1,11 @@ +pyhealth.datasets.PTBXLDataset +=================================== + +PTB-XL is a publically available electrocardiography dataset. Contains 21837 samples from 18885 patients, all approximately 10 seconds in duration. For more information see `here _. + +Kaggle: https://www.kaggle.com/datasets/physionet/ptbxl-electrocardiography-database + +.. autoclass:: pyhealth.datasets.PTBXLDataset + :members: + :undoc-members: + :show-inheritance: From 8fb5fc986037c3ba6ee935cc5214b4554f431074 Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 5 Apr 2026 18:16:08 -0400 Subject: [PATCH 02/39] Update pyhealth.datasets.PTBXLDataset.rst --- docs/api/datasets/pyhealth.datasets.PTBXLDataset.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/datasets/pyhealth.datasets.PTBXLDataset.rst b/docs/api/datasets/pyhealth.datasets.PTBXLDataset.rst index 36dfedb95..9ef9b3cea 100644 --- a/docs/api/datasets/pyhealth.datasets.PTBXLDataset.rst +++ b/docs/api/datasets/pyhealth.datasets.PTBXLDataset.rst @@ -1,7 +1,7 @@ pyhealth.datasets.PTBXLDataset -=================================== +============================== -PTB-XL is a publically available electrocardiography dataset. Contains 21837 samples from 18885 patients, all approximately 10 seconds in duration. For more information see `here _. +PTB-XL is a publically available electrocardiography dataset. Contains 21837 samples from 18885 patients, all approximately 10 seconds in duration. For more information see `here `_. Kaggle: https://www.kaggle.com/datasets/physionet/ptbxl-electrocardiography-database From b38da528f935b448032a5eab2522546ab7e004e0 Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 5 Apr 2026 18:34:25 -0400 Subject: [PATCH 03/39] Add PTBXLDataset to datasets.rst --- docs/api/datasets.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api/datasets.rst b/docs/api/datasets.rst index b02439d26..4fa3c0d6d 100644 --- a/docs/api/datasets.rst +++ b/docs/api/datasets.rst @@ -238,6 +238,7 @@ Available Datasets datasets/pyhealth.datasets.BMDHSDataset datasets/pyhealth.datasets.COVID19CXRDataset datasets/pyhealth.datasets.ChestXray14Dataset + datasets/pyhealth.datasets.PTBXLDataset datasets/pyhealth.datasets.TUABDataset datasets/pyhealth.datasets.TUEVDataset datasets/pyhealth.datasets.ClinVarDataset From e813ff877f28c1a2901d6c8ce1fd678aa4bc1275 Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Thu, 9 Apr 2026 00:56:05 -0400 Subject: [PATCH 04/39] Create ptbxl.py --- pyhealth/datasets/ptbxl.py | 84 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 pyhealth/datasets/ptbxl.py diff --git a/pyhealth/datasets/ptbxl.py b/pyhealth/datasets/ptbxl.py new file mode 100644 index 000000000..260651cb3 --- /dev/null +++ b/pyhealth/datasets/ptbxl.py @@ -0,0 +1,84 @@ +import pandas as pd +from pathlib import Path + +from .base_dataset import BaseDataset + +logger = logging.getLogger(__name__) + +class PTBXLDataset(BaseDataset): + """Base dataset for the PTB-XL ECG dataset + + PTB-XL is a publically available electrocardiography dataset. Contains 21837 samples from 18885 patients, all approximately 10 seconds in duration. + + Dataset is available here: https://www.kaggle.com/datasets/physionet/ptbxl-electrocardiography-database + """ + + def __init__( + self, + root: str, + dataset_name: Optional[str] = None, + **kwargs, + ) -> None: + super().__init__( + root=root, + tables=["ptbxl"], + dataset_name="ptbxl", + config_path=None, + **kwargs, + ) + + def load_data(self) -> dd.DataFrame: + """Returns a dataframe with each individual row corresponding to each .hea/.mat file combination in the PTB-XL dataset. + + Returns: + dd.DataFrame: Dataframe with one row per record + """ + root_path = Path(self.root) + files = root_path.glob("*.hea") + + if not files: + raise FileNotFoundError(f"No .hea files found in {self.root}. Are you sure you have the right directory?") + + logger.info(f"Found {len(files)} .hea files") + + rows = [] + for hea_file in files: + age = None + sex = None + dx = [] + + with open(hea_file, "r") as f: + for line in f: + line = line.strip() + + # Parse individual lines + if line.startswith("#Age:"): + try: + age = int(line.split(":")[1].strip()) + except ValueError: + age = None + elif line.startswith("#Sex:"): + sex = line.split(":")[1].strip() + elif line.startswith("#Dx:"): + dx = [x.strip() for x in line.split(":")[1].split(",")] + + # May need a line here for the dx abbreviations + + rows.append({ + "patient_id": hea_file.stem, + "event_type": "ptbxl", + "timestamp": pd.NaT, + "ptbxl/mat": str(root_path / f"{hea_file.stem}.mat"), + "ptbxl/age": age, + "ptbxl/sex": sex, + "ptbxl/dx_codes": ".".join(dx) + }) + + df = pd.Dataframe(rows) + # TBD - Will need train and test splits + + logger.info(f"Parsed {len(df)} records.") + return dd.from_pandas(df, npartitions=1) + + + From 8795145c3983c13e346398bec1ae6067d3d1bf5b Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Fri, 10 Apr 2026 02:21:15 -0400 Subject: [PATCH 05/39] Update ptbxl.py --- pyhealth/datasets/ptbxl.py | 130 ++++++++++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 3 deletions(-) diff --git a/pyhealth/datasets/ptbxl.py b/pyhealth/datasets/ptbxl.py index 260651cb3..45403b9cd 100644 --- a/pyhealth/datasets/ptbxl.py +++ b/pyhealth/datasets/ptbxl.py @@ -5,6 +5,124 @@ logger = logging.getLogger(__name__) +"""Full list of possible diagnoses for the PTB-XL dataset provided here: https://github.com/physionetchallenges/physionetchallenges.github.io/blob/master/2020/Dx_map.csv + +Not all codes are present in the data but they are included for completeness, as referenced in the Data Description section here: https://physionet.org/content/challenge-2020/1.0.2/ +""" +SNOMED_CT_ABBREVIATION = { + "270492004": IAVB, + "195042002": IIAVB, + "164951009": abQRS, + "426664006": AJR, + "57054005": AMI, + "413444003": AMIs, + "426434006": AnMIs, + "54329005": AnMI, + "251173003": AB, + "164889003": AF, + "195080001": AFAFL, + "164890007": AFL, + "195126007": AH, + "251268003": AP, + "713422000": ATach, + "29320008": AVJR, + "233917008": AVB, + "251170000": BPAC, + "74615001": BTS, + "426627000": Brady, + "6374002": BBB, + "698247007": CD, + "426749004": CAF, + "413844008": CMI, + "27885002": CHB, + "713427006": CRBBB, + "204384007": CIAHB, + "53741008": CHD, + "77867006": SQT, + "82226007": DIB, + "428417006": ERe, + "13640000": FB, + "84114007": HF, + "368009": HVD, + "251259000": HTV, + "49260003": IR, + "251120003": ILBBB, + "713426002": IRBBB, + "251200008": ICA, + "425419005": IIs, + "704997005": ISTD, + "426995002": JE, + "251164006": JPC, + "426648003": JTach, + "425623009": LIs, + "445118002": LAnFB, + "253352002": LAA, + "67741000119109": LAE, + "446813000": LAH, + "39732003": LAD, + "164909002": LBBB, + "445211001": LPFB, + "164873001": LVH, + "370365005": LVS, + "251146004": LQRSV, + "54016002": MoI, + "164865005": MI, + "164861001": MIs, + "698252002": NSIVCB, + "428750005": NSSTTA, + "164867002": OldMI, + "10370003": PR, + "251182009": VPVC, + "282825002": PAF, + "67198005": PSVT, + "425856008": PVT, + "284470004": PAC, + "427172004": PVC, + "17338001": VPB, + "164947007": LPR, + "111975006": LQT, + "164917005": QAb, + "164921003": RAb, + "314208002": RAF, + "253339007": RAAb, + "446358003": RAH, + "47665007": RAD, + "59118001": RBBB, + "89792004": RVH, + "55930002": STC, + "49578007": SPRI, + "65778007": SAB, + "427393009": SA, + "426177001": SB, + "60423000": SND, + "426783006": NSR, + "427084000": STach, + "429622005": STD, + "164931005": STE, + "164930006": STIAb, + "251168009": SVB, + "63593006": SVPB, + "426761007": SVT, + "251139008": ALR, + "164934002": TAb, + "59931005": TInv, + "266257000": TIA, + "164937009": UAb, + "11157007": VBig, + "164884008": VEB, + "75532003": VEsB, + "81898007": VEsR, + "164896001": VF, + "111288001": VFL, + "266249003": VH, + "251266004": VPP, + "195060002": VPEx, + "164895002": VTach, + "251180001": VTrig, + "195101003": WAP, + "74390002": WPW +} + class PTBXLDataset(BaseDataset): """Base dataset for the PTB-XL ECG dataset @@ -39,6 +157,9 @@ def load_data(self) -> dd.DataFrame: if not files: raise FileNotFoundError(f"No .hea files found in {self.root}. Are you sure you have the right directory?") + # Should all be in the same order to make the training / validation / test splits deterministic + files = sorted(files) + logger.info(f"Found {len(files)} .hea files") rows = [] @@ -62,9 +183,11 @@ def load_data(self) -> dd.DataFrame: elif line.startswith("#Dx:"): dx = [x.strip() for x in line.split(":")[1].split(",")] - # May need a line here for the dx abbreviations + # Map diagnosis codes to the abbreviations (may need them for tasks) + dx_abbreviations = [SNOMED_CT_ABBREVIATION[x] for x in dx if x in SNOMED_CT_ABBREVIATION] - rows.append({ + # Append required data to the list + rows.append({ "patient_id": hea_file.stem, "event_type": "ptbxl", "timestamp": pd.NaT, @@ -72,7 +195,8 @@ def load_data(self) -> dd.DataFrame: "ptbxl/age": age, "ptbxl/sex": sex, "ptbxl/dx_codes": ".".join(dx) - }) + "ptbxl/dx_abbreviations": ","./join(dx_abbreviations) + }) df = pd.Dataframe(rows) # TBD - Will need train and test splits From 3cc1da9c9da8d47afca946f4bf995fa1eda5a2f7 Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 19 Apr 2026 01:14:50 -0400 Subject: [PATCH 06/39] Update ptbxl.py --- pyhealth/datasets/ptbxl.py | 269 ++++++++++++++++++++----------------- 1 file changed, 145 insertions(+), 124 deletions(-) diff --git a/pyhealth/datasets/ptbxl.py b/pyhealth/datasets/ptbxl.py index 45403b9cd..6baaf5c0b 100644 --- a/pyhealth/datasets/ptbxl.py +++ b/pyhealth/datasets/ptbxl.py @@ -1,5 +1,8 @@ +import logging import pandas as pd +import dask.dataframe as dd from pathlib import Path +from typing import Optional from .base_dataset import BaseDataset @@ -10,117 +13,117 @@ Not all codes are present in the data but they are included for completeness, as referenced in the Data Description section here: https://physionet.org/content/challenge-2020/1.0.2/ """ SNOMED_CT_ABBREVIATION = { - "270492004": IAVB, - "195042002": IIAVB, - "164951009": abQRS, - "426664006": AJR, - "57054005": AMI, - "413444003": AMIs, - "426434006": AnMIs, - "54329005": AnMI, - "251173003": AB, - "164889003": AF, - "195080001": AFAFL, - "164890007": AFL, - "195126007": AH, - "251268003": AP, - "713422000": ATach, - "29320008": AVJR, - "233917008": AVB, - "251170000": BPAC, - "74615001": BTS, - "426627000": Brady, - "6374002": BBB, - "698247007": CD, - "426749004": CAF, - "413844008": CMI, - "27885002": CHB, - "713427006": CRBBB, - "204384007": CIAHB, - "53741008": CHD, - "77867006": SQT, - "82226007": DIB, - "428417006": ERe, - "13640000": FB, - "84114007": HF, - "368009": HVD, - "251259000": HTV, - "49260003": IR, - "251120003": ILBBB, - "713426002": IRBBB, - "251200008": ICA, - "425419005": IIs, - "704997005": ISTD, - "426995002": JE, - "251164006": JPC, - "426648003": JTach, - "425623009": LIs, - "445118002": LAnFB, - "253352002": LAA, - "67741000119109": LAE, - "446813000": LAH, - "39732003": LAD, - "164909002": LBBB, - "445211001": LPFB, - "164873001": LVH, - "370365005": LVS, - "251146004": LQRSV, - "54016002": MoI, - "164865005": MI, - "164861001": MIs, - "698252002": NSIVCB, - "428750005": NSSTTA, - "164867002": OldMI, - "10370003": PR, - "251182009": VPVC, - "282825002": PAF, - "67198005": PSVT, - "425856008": PVT, - "284470004": PAC, - "427172004": PVC, - "17338001": VPB, - "164947007": LPR, - "111975006": LQT, - "164917005": QAb, - "164921003": RAb, - "314208002": RAF, - "253339007": RAAb, - "446358003": RAH, - "47665007": RAD, - "59118001": RBBB, - "89792004": RVH, - "55930002": STC, - "49578007": SPRI, - "65778007": SAB, - "427393009": SA, - "426177001": SB, - "60423000": SND, - "426783006": NSR, - "427084000": STach, - "429622005": STD, - "164931005": STE, - "164930006": STIAb, - "251168009": SVB, - "63593006": SVPB, - "426761007": SVT, - "251139008": ALR, - "164934002": TAb, - "59931005": TInv, - "266257000": TIA, - "164937009": UAb, - "11157007": VBig, - "164884008": VEB, - "75532003": VEsB, - "81898007": VEsR, - "164896001": VF, - "111288001": VFL, - "266249003": VH, - "251266004": VPP, - "195060002": VPEx, - "164895002": VTach, - "251180001": VTrig, - "195101003": WAP, - "74390002": WPW + "270492004": "IAVB", + "195042002": "IIAVB", + "164951009": "abQRS", + "426664006": "AJR", + "57054005": "AMI", + "413444003": "AMIs", + "426434006": "AnMIs", + "54329005": "AnMI", + "251173003": "AB", + "164889003": "AF", + "195080001": "AFAFL", + "164890007": "AFL", + "195126007": "AH", + "251268003": "AP", + "713422000": "ATach", + "29320008": "AVJR", + "233917008": "AVB", + "251170000": "BPAC", + "74615001": "BTS", + "426627000": "Brady", + "6374002": "BBB", + "698247007": "CD", + "426749004": "CAF", + "413844008": "CMI", + "27885002": "CHB", + "713427006": "CRBBB", + "204384007": "CIAHB", + "53741008": "CHD", + "77867006": "SQT", + "82226007": "DIB", + "428417006": "ERe", + "13640000": "FB", + "84114007": "HF", + "368009": "HVD", + "251259000": "HTV", + "49260003": "IR", + "251120003": "ILBBB", + "713426002": "IRBBB", + "251200008": "ICA", + "425419005": "IIs", + "704997005": "ISTD", + "426995002": "JE", + "251164006": "JPC", + "426648003": "JTach", + "425623009": "LIs", + "445118002": "LAnFB", + "253352002": "LAA", + "67741000119109": "LAE", + "446813000": "LAH", + "39732003": "LAD", + "164909002": "LBBB", + "445211001": "LPFB", + "164873001": "LVH", + "370365005": "LVS", + "251146004": "LQRSV", + "54016002": "MoI", + "164865005": "MI", + "164861001": "MIs", + "698252002": "NSIVCB", + "428750005": "NSSTTA", + "164867002": "OldMI", + "10370003": "PR", + "251182009": "VPVC", + "282825002": "PAF", + "67198005": "PSVT", + "425856008": "PVT", + "284470004": "PAC", + "427172004": "PVC", + "17338001": "VPB", + "164947007": "LPR", + "111975006": "LQT", + "164917005": "QAb", + "164921003": "RAb", + "314208002": "RAF", + "253339007": "RAAb", + "446358003": "RAH", + "47665007": "RAD", + "59118001": "RBBB", + "89792004": "RVH", + "55930002": "STC", + "49578007": "SPRI", + "65778007": "SAB", + "427393009": "SA", + "426177001": "SB", + "60423000": "SND", + "426783006": "NSR", + "427084000": "STach", + "429622005": "STD", + "164931005": "STE", + "164930006": "STIAb", + "251168009": "SVB", + "63593006": "SVPB", + "426761007": "SVT", + "251139008": "ALR", + "164934002": "TAb", + "59931005": "TInv", + "266257000": "TIA", + "164937009": "UAb", + "11157007": "VBig", + "164884008": "VEB", + "75532003": "VEsB", + "81898007": "VEsR", + "164896001": "VF", + "111288001": "VFL", + "266249003": "VH", + "251266004": "VPP", + "195060002": "VPEx", + "164895002": "VTach", + "251180001": "VTrig", + "195101003": "WAP", + "74390002": "WPW" } class PTBXLDataset(BaseDataset): @@ -130,11 +133,12 @@ class PTBXLDataset(BaseDataset): Dataset is available here: https://www.kaggle.com/datasets/physionet/ptbxl-electrocardiography-database """ - + + CLASSES = list(SNOMED_CT_ABBREVIATION.values()) + def __init__( self, root: str, - dataset_name: Optional[str] = None, **kwargs, ) -> None: super().__init__( @@ -152,16 +156,18 @@ def load_data(self) -> dd.DataFrame: dd.DataFrame: Dataframe with one row per record """ root_path = Path(self.root) - files = root_path.glob("*.hea") + files = sorted(root_path.glob("*.hea")) + # Check existence of required .hea files if not files: raise FileNotFoundError(f"No .hea files found in {self.root}. Are you sure you have the right directory?") - - # Should all be in the same order to make the training / validation / test splits deterministic - files = sorted(files) - logger.info(f"Found {len(files)} .hea files") + # Check existence of required .csv file (for the train/test/val splits) + if not (root_path / "ptbxl_database.csv").exists(): + raise FileNotFoundError(f"No ptbxl_database.csv file found in {self.root}. Does it exist in this directory?") + db = pd.read_csv(root_path / "ptbxl_database.csv", index_col="ecg_id") + rows = [] for hea_file in files: age = None @@ -186,6 +192,15 @@ def load_data(self) -> dd.DataFrame: # Map diagnosis codes to the abbreviations (may need them for tasks) dx_abbreviations = [SNOMED_CT_ABBREVIATION[x] for x in dx if x in SNOMED_CT_ABBREVIATION] + # Train / test / validation splits using the strat_fold column in ptbxl_database.csv + strat_fold = db.loc[int(hea_file.stem.replace("HR","")), "strat_fold"] + if fold <= 8: + split = "train" + elif fold == 9: + split = "val" + else: + split = "test" + # Append required data to the list rows.append({ "patient_id": hea_file.stem, @@ -194,15 +209,21 @@ def load_data(self) -> dd.DataFrame: "ptbxl/mat": str(root_path / f"{hea_file.stem}.mat"), "ptbxl/age": age, "ptbxl/sex": sex, - "ptbxl/dx_codes": ".".join(dx) - "ptbxl/dx_abbreviations": ","./join(dx_abbreviations) + "ptbxl/dx_codes": ",".join(dx), + "ptbxl/dx_abbreviations": ",".join(dx_abbreviations), + "ptbxl/split": split }) - df = pd.Dataframe(rows) - # TBD - Will need train and test splits + df = pd.DataFrame(rows) logger.info(f"Parsed {len(df)} records.") return dd.from_pandas(df, npartitions=1) + + @property + def default_task(self) -> PTBXLMultilabelClassification: + """Returns the default task for the PTBXL dataset: PTBXLMultilabelClassification. - - + Returns: + PTBXLMultilabelClassification: The default task instance created with the default label type and sampling rate. + """ + return PTBXLMultilabelClassification() From 45ad4932a63802f9ca023ec0dd2c7229be43f9a7 Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 19 Apr 2026 01:27:30 -0400 Subject: [PATCH 07/39] Update __init__.py Add PTBXLDataset to __init__.py --- pyhealth/datasets/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyhealth/datasets/__init__.py b/pyhealth/datasets/__init__.py index 54e77670c..b4cd3c659 100644 --- a/pyhealth/datasets/__init__.py +++ b/pyhealth/datasets/__init__.py @@ -61,6 +61,7 @@ def __init__(self, *args, **kwargs): from .mimic4 import MIMIC4CXRDataset, MIMIC4Dataset, MIMIC4EHRDataset, MIMIC4NoteDataset from .mimicextract import MIMICExtractDataset from .omop import OMOPDataset +from .ptbxl import PTBXLDataset from .sample_dataset import SampleBuilder, SampleDataset, create_sample_dataset from .shhs import SHHSDataset from .sleepedf import SleepEDFDataset From fa0a4e0ab0143297aee52f9464302c49bd46f3dd Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 19 Apr 2026 01:40:10 -0400 Subject: [PATCH 08/39] Fixed indents --- pyhealth/datasets/ptbxl.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pyhealth/datasets/ptbxl.py b/pyhealth/datasets/ptbxl.py index 6baaf5c0b..a5ceef553 100644 --- a/pyhealth/datasets/ptbxl.py +++ b/pyhealth/datasets/ptbxl.py @@ -164,9 +164,9 @@ def load_data(self) -> dd.DataFrame: logger.info(f"Found {len(files)} .hea files") # Check existence of required .csv file (for the train/test/val splits) - if not (root_path / "ptbxl_database.csv").exists(): - raise FileNotFoundError(f"No ptbxl_database.csv file found in {self.root}. Does it exist in this directory?") - db = pd.read_csv(root_path / "ptbxl_database.csv", index_col="ecg_id") + if not (root_path / "ptbxl_database.csv").exists(): + raise FileNotFoundError(f"No ptbxl_database.csv file found in {self.root}. Does it exist in this directory?") + db = pd.read_csv(root_path / "ptbxl_database.csv", index_col="ecg_id") rows = [] for hea_file in files: @@ -193,13 +193,13 @@ def load_data(self) -> dd.DataFrame: dx_abbreviations = [SNOMED_CT_ABBREVIATION[x] for x in dx if x in SNOMED_CT_ABBREVIATION] # Train / test / validation splits using the strat_fold column in ptbxl_database.csv - strat_fold = db.loc[int(hea_file.stem.replace("HR","")), "strat_fold"] - if fold <= 8: - split = "train" - elif fold == 9: - split = "val" - else: - split = "test" + strat_fold = db.loc[int(hea_file.stem.replace("HR","")), "strat_fold"] + if fold <= 8: + split = "train" + elif fold == 9: + split = "val" + else: + split = "test" # Append required data to the list rows.append({ @@ -219,11 +219,11 @@ def load_data(self) -> dd.DataFrame: logger.info(f"Parsed {len(df)} records.") return dd.from_pandas(df, npartitions=1) - @property + @property def default_task(self) -> PTBXLMultilabelClassification: - """Returns the default task for the PTBXL dataset: PTBXLMultilabelClassification. + """Returns the default task for the PTBXL dataset: PTBXLMultilabelClassification. Returns: PTBXLMultilabelClassification: The default task instance created with the default label type and sampling rate. """ - return PTBXLMultilabelClassification() + return PTBXLMultilabelClassification() \ No newline at end of file From 4f71fed852cc2bb507630a4e91caec4cb70913a4 Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 19 Apr 2026 01:45:36 -0400 Subject: [PATCH 09/39] Update __init__.py Add the multilabel classification task definition --- pyhealth/tasks/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyhealth/tasks/__init__.py b/pyhealth/tasks/__init__.py index 797988377..52ce0bc06 100644 --- a/pyhealth/tasks/__init__.py +++ b/pyhealth/tasks/__init__.py @@ -45,6 +45,7 @@ MortalityPredictionStageNetMIMIC4, ) from .patient_linkage import patient_linkage_mimic3_fn +from .ptbxl_multilabel_classification import PTBXLMultilabelClassification from .readmission_prediction import ( ReadmissionPredictionEICU, ReadmissionPredictionMIMIC3, From a7bee56c13dded30bff0d641d3e37b8981382e7b Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 19 Apr 2026 01:50:00 -0400 Subject: [PATCH 10/39] Missing line --- pyhealth/datasets/ptbxl.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyhealth/datasets/ptbxl.py b/pyhealth/datasets/ptbxl.py index a5ceef553..67527c4ea 100644 --- a/pyhealth/datasets/ptbxl.py +++ b/pyhealth/datasets/ptbxl.py @@ -5,6 +5,7 @@ from typing import Optional from .base_dataset import BaseDataset +from pyhealth.tasks import PTBXLMultilabelClassification logger = logging.getLogger(__name__) From 4e2a34f1ba2ea7033185120759bb83c4b3646da2 Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 19 Apr 2026 02:06:21 -0400 Subject: [PATCH 11/39] Add ptbxl_multilabel_classification to get the dataset tasks to work. --- .../tasks/ptbxl_multilabel_classification.py | 364 ++++++++++++++++++ 1 file changed, 364 insertions(+) create mode 100644 pyhealth/tasks/ptbxl_multilabel_classification.py diff --git a/pyhealth/tasks/ptbxl_multilabel_classification.py b/pyhealth/tasks/ptbxl_multilabel_classification.py new file mode 100644 index 000000000..f95c69c83 --- /dev/null +++ b/pyhealth/tasks/ptbxl_multilabel_classification.py @@ -0,0 +1,364 @@ +"""PTB-XL multi-label ECG classification task. + +This module provides :class:`PTBXLMultilabelClassification`, a +:class:`~pyhealth.tasks.BaseTask` subclass that turns a +:class:`~pyhealth.datasets.PTBXLDataset` into a multi-label classification +problem. + +Two label spaces are supported, selected via the ``label_type`` constructor +argument. This design enables the **ablation study** described in the project +paper: hold the model and training hyper-parameters constant and vary only the +label granularity (and optionally the signal sampling rate) to observe how +label coarseness affects downstream ROC-AUC and F1 performance. + +Mathematical framing +-------------------- +Let :math:`X \\in \\mathbb{R}^{C \\times T}` be a single ECG recording with +:math:`C = 12` leads and :math:`T` time-steps (1,000 at 100 Hz or 5,000 at +500 Hz). Given a label universe of :math:`K` classes, the ground-truth +annotation is a binary vector :math:`y \\in \\{0, 1\\}^K` (multi-hot). + +A model :math:`f_\\theta` maps the ECG to per-class logit scores: + +.. math:: + + \\hat{y} = \\sigma\\!\\left(f_\\theta(X) W^\\top + b\\right) \\in [0,1]^K + +Training minimises the element-wise **binary cross-entropy**: + +.. math:: + + \\mathcal{L} = -\\frac{1}{K} \\sum_{k=1}^{K} + \\Bigl[ y_k \\log \\hat{y}_k + (1 - y_k) \\log (1 - \\hat{y}_k) \\Bigr] + +Evaluation uses **macro-averaged ROC-AUC**: + +.. math:: + + \\overline{\\text{AUC}} = \\frac{1}{K} \\sum_{k=1}^{K} + \\int_0^1 \\text{TPR}_k(t)\\, d\\text{FPR}_k(t) + +and **macro-averaged F1** (at threshold 0.5): + +.. math:: + + \\overline{F_1} = \\frac{1}{K} \\sum_{k=1}^{K} + \\frac{2 \\cdot \\text{TP}_k}{2 \\cdot \\text{TP}_k + \\text{FP}_k + \\text{FN}_k} + +Label spaces +------------ +``"superdiagnostic"`` (:data:`SUPERDIAG_CLASSES` — 5 classes) + Directly mirrors the five PTB-XL superdiagnostic categories from + Strodthoff et al. (2020). SNOMED-CT codes from every recording's + ``# Dx:`` list are mapped to one or more of NORM / MI / STTC / CD / HYP + using :data:`SNOMED_TO_SUPERDIAG`. Records with no mappable code are + skipped. + +``"diagnostic"`` (:data:`CHALLENGE_SNOMED_CLASSES` — 27 classes) + Uses the 27 SNOMED-CT codes that were officially scored in the + PhysioNet/CinC Challenge 2020. Each code present in a recording's + ``# Dx:`` list that falls within this vocabulary becomes a positive label. + Records with no scored codes are skipped. + +Ablation axes +------------- +The two constructor arguments create the natural ablation grid: + ++-------------------+-----------+------------------------+ +| ``label_type`` | ``sampling_rate`` | Description | ++===================+===========+========================+ +| ``"superdiagnostic"`` | 100 | 5-class / 100 Hz | ++-------------------+-----------+------------------------+ +| ``"superdiagnostic"`` | 500 | 5-class / 500 Hz | ++-------------------+-----------+------------------------+ +| ``"diagnostic"`` | 100 | 27-class / 100 Hz | ++-------------------+-----------+------------------------+ +| ``"diagnostic"`` | 500 | 27-class / 500 Hz | ++-------------------+-----------+------------------------+ + +Author: + CS-598 DLH Project Team +""" + +import logging +from typing import Dict, List, Optional + +import numpy as np + +from pyhealth.data import Patient +from pyhealth.tasks import BaseTask + +logger = logging.getLogger(__name__) + +# --------------------------------------------------------------------------- +# Label-space definitions +# --------------------------------------------------------------------------- + +#: Mapping from SNOMED-CT code (string) to one of the 5 PTB-XL superdiagnostic +#: classes. Codes absent from this dict are silently ignored during label +#: construction. The mapping follows Table 1 of Strodthoff et al. (2020) and +#: the PhysioNet Challenge 2020 label alignment documented in the challenge +#: description paper. +SNOMED_TO_SUPERDIAG: Dict[str, str] = { + # ------ NORM — Normal sinus rhythm ----------------------------------- # + "426783006": "NORM", + # ------ MI — Myocardial Infarction ----------------------------------- # + "57054005": "MI", # Acute myocardial infarction + "164865005": "MI", # Myocardial infarction + "413444003": "MI", # Acute MI of anterolateral wall + "413867000": "MI", # Acute MI of inferior wall + "164861001": "MI", # Anterior MI + "164857002": "MI", # Inferior MI + "164860000": "MI", # Anteroseptal MI + "164864009": "MI", # Posterior MI + "164867002": "MI", # Lateral MI + # ------ STTC — ST/T-wave Change -------------------------------------- # + "164931005": "STTC", # ST elevation + "164934002": "STTC", # ST depression + "59931005": "STTC", # Inverted T-wave / T-wave abnormality + "164947007": "STTC", # Prolonged PR interval + "164917005": "STTC", # Prolonged QT interval + "251268003": "STTC", # Early repolarisation pattern + "428750005": "STTC", # Non-specific ST-T change + # ------ CD — Conduction Disturbance / Rhythm Disorder ---------------- # + "270492004": "CD", # First-degree AV block + "195042002": "CD", # Second-degree AV block + "27885002": "CD", # Third-degree AV block + "6374002": "CD", # Bundle branch block (unspecified) + "713427006": "CD", # Complete right bundle branch block (CRBBB) + "713426002": "CD", # Complete left bundle branch block (CLBBB) + "164909002": "CD", # Left bundle branch block + "59118001": "CD", # Right bundle branch block + "698252002": "CD", # Non-specific intraventricular conduction disturbance + "445118002": "CD", # Left anterior fascicular block (LAFB) + "10370003": "CD", # Pacing rhythm + "164889003": "CD", # Atrial fibrillation + "164890007": "CD", # Atrial flutter + "426627000": "CD", # Bradycardia + "427393009": "CD", # Sinus arrhythmia + "426177001": "CD", # Sinus bradycardia + "427084000": "CD", # Sinus tachycardia + "63593006": "CD", # Supraventricular premature beats + "17338001": "CD", # Ventricular premature beats + "284470004": "CD", # Premature atrial contraction + "427172004": "CD", # Premature ventricular contraction + # ------ HYP — Hypertrophy / Axis Deviation --------------------------- # + "55827005": "HYP", # Left ventricular hypertrophy + "446358003": "HYP", # Right ventricular hypertrophy + "73282002": "HYP", # Biventricular hypertrophy + "67751000119106": "HYP", # Left atrial enlargement + "446813000": "HYP", # Right atrial enlargement + "39732003": "HYP", # Left axis deviation + "47665007": "HYP", # Right axis deviation + "251146004": "HYP", # Low QRS voltage +} + +#: Ordered list of the 5 superdiagnostic class names. The ordering is +#: deterministic so that model outputs are consistently interpretable. +SUPERDIAG_CLASSES: List[str] = ["NORM", "MI", "STTC", "CD", "HYP"] + +#: The 27 SNOMED-CT codes officially scored in the PhysioNet/CinC Challenge +#: 2020 (alphabetically sorted by their clinical abbreviation for readability). +#: These form the label universe for ``label_type="diagnostic"``. +CHALLENGE_SNOMED_CLASSES: List[str] = sorted( + [ + "270492004", # IAVB — First-degree atrioventricular block + "164889003", # AF — Atrial fibrillation + "164890007", # AFL — Atrial flutter + "6374002", # BBB — Bundle branch block (unspecified) + "426627000", # Brady — Bradycardia + "713427006", # CRBBB — Complete right bundle branch block + "713426002", # CLBBB — Complete left bundle branch block + "445118002", # LAnFB — Left anterior fascicular block + "39732003", # LAD — Left axis deviation + "164909002", # LBBB — Left bundle branch block + "251146004", # LQRSV — Low QRS voltage + "698252002", # NSIVCB — Non-specific intraventricular conduction dist. + "10370003", # PR — Pacing rhythm + "164947007", # LPR — Prolonged PR interval + "164917005", # LQT — Prolonged QT interval + "47665007", # RAD — Right axis deviation + "427393009", # SA — Sinus arrhythmia + "426177001", # SB — Sinus bradycardia + "426783006", # NSR — Normal sinus rhythm + "427084000", # ST — Sinus tachycardia + "63593006", # SVPB — Supraventricular premature beats + "164934002", # STD — ST depression + "59931005", # TWA — T-wave abnormality + "164931005", # STE — ST elevation + "17338001", # VPB — Ventricular premature beats + "284470004", # PAC — Premature atrial contraction + "427172004", # PVC — Premature ventricular contraction + ] +) + +_CHALLENGE_SET: frozenset = frozenset(CHALLENGE_SNOMED_CLASSES) + + +# --------------------------------------------------------------------------- +# Task class +# --------------------------------------------------------------------------- + + +class PTBXLMultilabelClassification(BaseTask): + """Multi-label 12-lead ECG classification on PTB-XL. + + For each ECG recording this task: + + 1. Loads the ``.mat`` signal matrix via :func:`scipy.io.loadmat` + (shape ``(12, 5000)`` at 500 Hz). + 2. Optionally decimates the signal to 100 Hz (shape ``(12, 1000)``). + 3. Parses SNOMED-CT codes from the ``scp_codes`` event attribute. + 4. Maps those codes to the chosen label space (superdiagnostic or + full Challenge 27-class). + 5. Returns one sample dict per valid recording:: + + { + "signal": np.ndarray, # shape (12, T), float32 + "labels": List[str], # positive class names / SNOMED strings + } + + Args: + sampling_rate (int): Target sampling rate in Hz. Accepted values are + ``100`` (decimation ×5 from the native 500 Hz; yields ``T = 1000``) + and ``500`` (no resampling; yields ``T = 5000``). + Defaults to ``100``. + label_type (str): Label vocabulary to use. ``"superdiagnostic"`` + yields 5 classes (NORM, MI, STTC, CD, HYP); + ``"diagnostic"`` yields 27 SNOMED-CT classes from the PhysioNet + Challenge 2020 scoring list. Defaults to ``"superdiagnostic"``. + + Raises: + ValueError: If ``sampling_rate`` is not 100 or 500. + ValueError: If ``label_type`` is not ``"superdiagnostic"`` or + ``"diagnostic"``. + + Examples: + Superdiagnostic task at 100 Hz (default):: + + >>> from pyhealth.datasets import PTBXLDataset + >>> from pyhealth.tasks import PTBXLMultilabelClassification + >>> dataset = PTBXLDataset(root="/data/.../training/ptb-xl/") + >>> task = PTBXLMultilabelClassification() + >>> sample_ds = dataset.set_task(task) + >>> sample_ds[0]["labels"] # e.g. ["NORM"] or ["CD", "STTC"] + + 27-class diagnostic task at 500 Hz (ablation variant):: + + >>> task_27 = PTBXLMultilabelClassification( + ... sampling_rate=500, label_type="diagnostic" + ... ) + >>> sample_ds_27 = dataset.set_task(task_27) + + See Also: + :data:`SNOMED_TO_SUPERDIAG`, :data:`SUPERDIAG_CLASSES`, + :data:`CHALLENGE_SNOMED_CLASSES` + """ + + task_name: str = "PTBXLMultilabelClassification" + input_schema: Dict[str, str] = {"signal": "tensor"} + output_schema: Dict[str, str] = {"labels": "multilabel"} + + def __init__( + self, + sampling_rate: int = 100, + label_type: str = "superdiagnostic", + ) -> None: + super().__init__() + + if sampling_rate not in (100, 500): + raise ValueError( + f"sampling_rate must be 100 or 500, got {sampling_rate}." + ) + if label_type not in ("superdiagnostic", "diagnostic"): + raise ValueError( + "label_type must be 'superdiagnostic' or 'diagnostic', " + f"got '{label_type}'." + ) + + self.sampling_rate = sampling_rate + self.label_type = label_type + + # Disambiguate the task_name so that cached SampleDatasets from + # different configurations do not collide on disk. + self.task_name = ( + f"PTBXLSuperDiagnostic_{sampling_rate}Hz" + if label_type == "superdiagnostic" + else f"PTBXLDiagnostic27_{sampling_rate}Hz" + ) + + # ------------------------------------------------------------------ + # Core logic + # ------------------------------------------------------------------ + + def __call__(self, patient: Patient) -> List[Dict]: + """Extract samples from one patient (= one ECG recording in PTB-XL). + + Args: + patient: A :class:`~pyhealth.data.Patient` object whose events + have ``event_type="ptbxl"`` and carry attributes + ``signal_file``, ``scp_codes``, ``age``, and ``sex``. + + Returns: + A list with at most one sample dict + ``{"signal": np.ndarray, "labels": List[str]}``, or an empty list + if the recording should be skipped (missing file, unrecognised + codes, etc.). + """ + # In PTBXLDataset each patient has exactly one event in the "ptbxl" + # table (record == patient). + events = patient.get_events(event_type="ptbxl") + samples = [] + + for event in events: + # ---- 1. Load the .mat signal -------------------------------- + signal_file = getattr(event, "signal_file", None) + if not signal_file: + logger.debug("Skip %s: no signal_file attribute.", event) + continue + + try: + from scipy.io import loadmat as _loadmat + mat = _loadmat(signal_file) + signal = mat["val"].astype(np.float32) # (12, 5000) @ 500 Hz + except Exception as exc: + logger.warning("Cannot load signal from %s: %s", signal_file, exc) + continue + + if signal.ndim != 2 or signal.shape[0] != 12: + logger.warning( + "Unexpected signal shape %s in %s; skipping.", + signal.shape, + signal_file, + ) + continue + + # ---- 2. Resample if needed (decimation only) ---------------- + # Native rate is 500 Hz (5000 samples / 10 s). + # Decimation by 5 gives 100 Hz (1000 samples / 10 s). + if self.sampling_rate == 100: + signal = signal[:, ::5] # shape (12, 1000) + + # ---- 3. Parse SNOMED-CT codes -------------------------------- + raw_codes: str = str(getattr(event, "scp_codes", "") or "") + codes = [c.strip() for c in raw_codes.split(",") if c.strip()] + + # ---- 4. Map to chosen label space --------------------------- + if self.label_type == "superdiagnostic": + labels = list( + { + SNOMED_TO_SUPERDIAG[c] + for c in codes + if c in SNOMED_TO_SUPERDIAG + } + ) + else: # "diagnostic" — 27-class Challenge vocabulary + labels = [c for c in codes if c in _CHALLENGE_SET] + + if not labels: + # No recognised labels → skip (consistent with other tasks). + continue + + samples.append({"signal": signal, "labels": labels}) + + return samples \ No newline at end of file From 98a569c53d14a090868a1caf96f7140cf80ebd98 Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 19 Apr 2026 02:19:06 -0400 Subject: [PATCH 12/39] Typos --- pyhealth/datasets/ptbxl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyhealth/datasets/ptbxl.py b/pyhealth/datasets/ptbxl.py index 67527c4ea..e5612b097 100644 --- a/pyhealth/datasets/ptbxl.py +++ b/pyhealth/datasets/ptbxl.py @@ -195,9 +195,9 @@ def load_data(self) -> dd.DataFrame: # Train / test / validation splits using the strat_fold column in ptbxl_database.csv strat_fold = db.loc[int(hea_file.stem.replace("HR","")), "strat_fold"] - if fold <= 8: + if strat_fold <= 8: split = "train" - elif fold == 9: + elif strat_fold == 9: split = "val" else: split = "test" From 56326f19a425cc270997b444870e9c6fc09f6229 Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 19 Apr 2026 07:33:48 -0400 Subject: [PATCH 13/39] Fix test failure + test_ptbxl.py stub --- pyhealth/datasets/ptbxl.py | 2 +- tests/core/test_ptbxl.py | 0 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 tests/core/test_ptbxl.py diff --git a/pyhealth/datasets/ptbxl.py b/pyhealth/datasets/ptbxl.py index e5612b097..f2275b4f1 100644 --- a/pyhealth/datasets/ptbxl.py +++ b/pyhealth/datasets/ptbxl.py @@ -145,7 +145,7 @@ def __init__( super().__init__( root=root, tables=["ptbxl"], - dataset_name="ptbxl", + dataset_name=dataset_name or "ptbxl", config_path=None, **kwargs, ) diff --git a/tests/core/test_ptbxl.py b/tests/core/test_ptbxl.py new file mode 100644 index 000000000..e69de29bb From f4af64aed7827af5521f18c200e6e050674025fc Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 19 Apr 2026 07:46:38 -0400 Subject: [PATCH 14/39] Typo --- pyhealth/datasets/ptbxl.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyhealth/datasets/ptbxl.py b/pyhealth/datasets/ptbxl.py index f2275b4f1..e87ab01bb 100644 --- a/pyhealth/datasets/ptbxl.py +++ b/pyhealth/datasets/ptbxl.py @@ -140,6 +140,7 @@ class PTBXLDataset(BaseDataset): def __init__( self, root: str, + dataset_name: Optional[str] = None, **kwargs, ) -> None: super().__init__( From b8e0668cf6823f207c48caba65aa8511fc84247f Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 19 Apr 2026 09:18:48 -0400 Subject: [PATCH 15/39] Commit test file - only dataset tests currently --- tests/core/test_ptbxl.py | 224 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 224 insertions(+) diff --git a/tests/core/test_ptbxl.py b/tests/core/test_ptbxl.py index e69de29bb..2e73d3845 100644 --- a/tests/core/test_ptbxl.py +++ b/tests/core/test_ptbxl.py @@ -0,0 +1,224 @@ +import tempfile +import shutil +import unittest +from pathlib import Path + +import dask.dataframe as dd +import numpy as np +import pandas as pd +import scipy.io + +from pyhealth.datasets import PTBXLDataset +from pyhealth.tasks.ptbxl_multilabel_classification import PTBXLMultilabelClassification + +def write_hea_file(path, record_id, age, sex, dx): + with open(path, "w") as f: + f.write(f"{record_id} 12 500 5000\n") + for lead in ["I", "II", "III", "aVR", "aVL", "aVF", "V1", "V2", "V3", "V4", "V5", "V6"]: + f.write(f"{record_id}.mat 16+24 200/mV 16 0 0 0 0 {lead}\n") + f.write(f"#Age: {age}\n") + f.write(f"#Sex: {sex}\n") + f.write(f"#Dx: {dx}\n") + f.write("#Rx: Unknown\n") + f.write("#Hx: Unknown\n") + f.write("#Sx: Unknown\n") + +def write_mat_file(path): + scipy.io.savemat(str(path), {"val": np.random.randn(12, 5000)}) + +def write_database_csv(path, records): + pd.DataFrame({ + "ecg_id": [int(r[0].replace("HR", "")) for r in records], + "strat_fold": [r[4] for r in records], + }).to_csv(path, index=False) + +class TestPTBXLDataset(unittest.TestCase): + """Test PTBXLDataset with synthetic test data""" + + # Create records with (record_id, age, sex, dx_codes, strat_fold); at minimum need 1 and 8 for + RECORDS = [ + ("HR00001", 56, "Female", "251146004,426783006", 1), # train + ("HR00002", 37, "Female", "426783006", 8), # train + ("HR00003", 24, "Male", "426783006", 9), # val + ("HR00004", 45, "Female", "164889003", 10), # test + ] + + def setUp(self): + """Create a temporary directory with 4 synthetic .hea/.mat pairs and a matching ptbxl_database.csv""" + self.test_dir = tempfile.mkdtemp() + for record_id, age, sex, dx, _ in self.RECORDS: + write_hea_file( + Path(self.test_dir) / f"{record_id}.hea", + record_id, age, sex, dx + ) + write_mat_file(Path(self.test_dir) / f"{record_id}.mat") + write_database_csv( + Path(self.test_dir) / "ptbxl_database.csv", + self.RECORDS + ) + + def tearDown(self): + shutil.rmtree(self.test_dir) + + def test_instantiation(self): + """Test 1 - Dataset can be instantiated""" + dataset = PTBXLDataset(root=self.test_dir) + self.assertIsNotNone(dataset) + + def test_dataset_name_default(self): + """Test 2 - Default dataset name is ptbxl""" + dataset = PTBXLDataset(root=self.test_dir) + self.assertEqual(dataset.dataset_name, "ptbxl") + + def test_dataset_name_custom(self): + """Test 3 - Can set a custom dataset name""" + dataset = PTBXLDataset(root=self.test_dir, dataset_name="my_ptbxl") + self.assertEqual(dataset.dataset_name, "my_ptbxl") + + def test_default_task_returns_task_instance(self): + """Test 4 - default_task() returns a PTBXLMultilabelClassification instance""" + ds = PTBXLDataset.__new__(PTBXLDataset) + task = ds.default_task + self.assertIsInstance(task, PTBXLMultilabelClassification) + + def test_classes_attribute(self): + """Test 5 - The list of strings CLASSES exists and is not empty""" + self.assertIsInstance(PTBXLDataset.CLASSES, list) + self.assertGreater(len(PTBXLDataset.CLASSES), 0) + self.assertTrue(all(isinstance(c, str) for c in PTBXLDataset.CLASSES)) + + def test_load_data_returns_dask_dataframe(self): + """Test 6 - load_data() returns a Dask DataFrame""" + dataset = PTBXLDataset(root=self.test_dir) + self.assertIsInstance(dataset.load_data(), dd.DataFrame) + + def test_load_data_row_count(self): + """Test 7 - load_data() returns one row per .hea file""" + df = PTBXLDataset(root=self.test_dir).load_data().compute() + self.assertEqual(len(df), len(self.RECORDS)) + + def test_load_data_required_columns(self): + """Test 8 - load_data() output contains all required BaseDataset columns""" + df = PTBXLDataset(root=self.test_dir).load_data().compute() + for col in ["patient_id", "event_type", "timestamp"]: + self.assertIn(col, df.columns, f"Missing required column: {col}") + + def test_load_data_attribute_columns(self): + """Test 9 - load_data() output contains all ptbxl/ attribute columns""" + df = PTBXLDataset(root=self.test_dir).load_data().compute() + for col in ["ptbxl/mat", "ptbxl/age", "ptbxl/sex", + "ptbxl/dx_codes", "ptbxl/dx_abbreviations", "ptbxl/split"]: + self.assertIn(col, df.columns, f"Missing attribute column: {col}") + + def test_load_data_event_type(self): + """Test 10 - All rows have event_type == ptbxl""" + df = PTBXLDataset(root=self.test_dir).load_data().compute() + self.assertTrue((df["event_type"] == "ptbxl").all()) + + def test_age_parsed_correctly(self): + """Test 11 - Ages are parsed correctly from .hea files""" + df = PTBXLDataset(root=self.test_dir).load_data().compute() + df = df.set_index("patient_id") + self.assertEqual(df.loc["HR00001", "ptbxl/age"], 56) + + def test_sex_parsed_correctly(self): + """Test 12 - Sex is parsed correctly from .hea files""" + df = PTBXLDataset(root=self.test_dir).load_data().compute() + df = df.set_index("patient_id") + self.assertEqual(df.loc["HR00001", "ptbxl/sex"], "Female") + self.assertEqual(df.loc["HR00003", "ptbxl/sex"], "Male") + + def test_dx_codes_parsed_correctly(self): + """Test 13 - SNOMED CT codes are parsed correctly from .hea files.""" + df = PTBXLDataset(root=self.test_dir).load_data().compute() + df = df.set_index("patient_id") + self.assertEqual(df.loc["HR00001", "ptbxl/dx_codes"], "251146004,426783006") + self.assertEqual(df.loc["HR00003", "ptbxl/dx_codes"], "426783006") + + def test_dx_abbreviations_mapped_correctly(self): + """Test 14 - SNOMED CT codes are mapped to correct abbreviations""" + df = PTBXLDataset(root=self.test_dir).load_data().compute() + df = df.set_index("patient_id") + self.assertIn("NSR", df.loc["HR00001", "ptbxl/dx_abbreviations"]) + self.assertIn("AF", df.loc["HR00004", "ptbxl/dx_abbreviations"]) + + def test_mat_file_path_correct(self): + """Test 15 - .mat file paths point to the correct location""" + df = PTBXLDataset(root=self.test_dir).load_data().compute() + df = df.set_index("patient_id") + expected = str(Path(self.test_dir) / "HR00001.mat") + self.assertEqual(df.loc["HR00001", "ptbxl/mat"], expected) + + def test_split_values(self): + """Test 16 - Split column only contains train, val, or test""" + df = PTBXLDataset(root=self.test_dir).load_data().compute() + self.assertTrue(df["ptbxl/split"].isin(["train", "val", "test"]).all()) + + def test_split_from_strat_fold(self): + """Test 17 - Splits are correctly assigned from strat_fold values""" + df = PTBXLDataset(root=self.test_dir).load_data().compute() + df = df.set_index("patient_id") + self.assertEqual(df.loc["HR00001", "ptbxl/split"], "train") # fold 1 + self.assertEqual(df.loc["HR00002", "ptbxl/split"], "train") # fold 8 + self.assertEqual(df.loc["HR00003", "ptbxl/split"], "val") # fold 9 + self.assertEqual(df.loc["HR00004", "ptbxl/split"], "test") # fold 10 + + def test_unknown_snomed_code_skipped(self): + """Test 18 - SNOMED codes not in mapping are skipped without error""" + write_hea_file( + Path(self.test_dir) / "HR00099.hea", + "HR00099", 30, "Male", "999999999,426783006" + ) + write_mat_file(Path(self.test_dir) / "HR00099.mat") + pd.DataFrame({ + "ecg_id": [int(r[0].replace("HR", "")) for r in self.RECORDS] + [99], + "strat_fold": [r[4] for r in self.RECORDS] + [1], + }).to_csv(Path(self.test_dir) / "ptbxl_database.csv", index=False) + + df = PTBXLDataset(root=self.test_dir).load_data().compute() + df = df.set_index("patient_id") + self.assertEqual(df.loc["HR00099", "ptbxl/dx_abbreviations"], "NSR") + + def test_invalid_age_handled(self): + """Test 19 - Non-integer age values result in None without error""" + write_hea_file( + Path(self.test_dir) / "HR00098.hea", + "HR00098", "NaN", "Female", "426783006" + ) + write_mat_file(Path(self.test_dir) / "HR00098.mat") + pd.DataFrame({ + "ecg_id": [int(r[0].replace("HR", "")) for r in self.RECORDS] + [98], + "strat_fold": [r[4] for r in self.RECORDS] + [1], + }).to_csv(Path(self.test_dir) / "ptbxl_database.csv", index=False) + + df = PTBXLDataset(root=self.test_dir).load_data().compute() + df = df.set_index("patient_id") + self.assertTrue(pd.isna(df.loc["HR00098", "ptbxl/age"])) + + def test_no_hea_files_raises_error(self): + """Test 20 - FileNotFoundError raised if no .hea files found""" + empty_dir = tempfile.mkdtemp() + try: + dataset = PTBXLDataset(root=empty_dir) + with self.assertRaises(FileNotFoundError): + dataset.load_data().compute() + finally: + shutil.rmtree(empty_dir) + + def test_missing_csv_raises_error(self): + """Test 21 - FileNotFoundError raised if ptbxl_database.csv is missing""" + no_csv_dir = tempfile.mkdtemp() + try: + write_hea_file( + Path(no_csv_dir) / "HR00001.hea", + "HR00001", 56, "Female", "426783006" + ) + write_mat_file(Path(no_csv_dir) / "HR00001.mat") + dataset = PTBXLDataset(root=no_csv_dir) + with self.assertRaises(FileNotFoundError): + dataset.load_data().compute() + finally: + shutil.rmtree(no_csv_dir) + +if __name__ == "__main__": + unittest.main() \ No newline at end of file From eba50c9404bfd47f330c019ae93eefe7b63708ce Mon Sep 17 00:00:00 2001 From: "Kent R. Spillner" Date: Sat, 11 Apr 2026 12:01:29 -0500 Subject: [PATCH 16/39] Add Resnet-18, SE-ResNet, and Lambda-ResNet models All three models based on the implementations used in "In-depth Benchmarking of Deep Neural Network Architectures for ECG Diagnosis." Common code extracted to ecg_resnet_base.py. Assisted-by: Claude:Claude-4.6-Sonnet --- pyhealth/models/__init__.py | 4 + pyhealth/models/lambda_resnet.py | 388 +++++++++++++++++++++++++++++ pyhealth/models/resnet.py | 89 +++++++ pyhealth/models/resnet_ecg_base.py | 369 +++++++++++++++++++++++++++ pyhealth/models/se_resnet.py | 213 ++++++++++++++++ 5 files changed, 1063 insertions(+) create mode 100644 pyhealth/models/lambda_resnet.py create mode 100644 pyhealth/models/resnet.py create mode 100644 pyhealth/models/resnet_ecg_base.py create mode 100644 pyhealth/models/se_resnet.py diff --git a/pyhealth/models/__init__.py b/pyhealth/models/__init__.py index 5233b1726..fbc561ec3 100644 --- a/pyhealth/models/__init__.py +++ b/pyhealth/models/__init__.py @@ -9,6 +9,7 @@ from .embedding import EmbeddingModel from .gamenet import GAMENet, GAMENetLayer from .jamba_ehr import JambaEHR, JambaLayer +from .lambda_resnet import LambdaConv1d, LambdaBottleneck1d, LambdaResNet1d, LambdaResNet18ECG from .logistic_regression import LogisticRegression from .gan import GAN from .gnn import GAT, GCN @@ -19,9 +20,12 @@ from .micron import MICRON, MICRONLayer from .mlp import MLP from .molerec import MoleRec, MoleRecLayer +from .resnet_ecg_base import BasicBlock1d, Bottleneck1d, ResNet1d, ECGBackboneModel +from .resnet import ResNet18ECG from .retain import MultimodalRETAIN, RETAIN, RETAINLayer from .rnn import MultimodalRNN, RNN, RNNLayer from .safedrug import SafeDrug, SafeDrugLayer +from .se_resnet import SEModule1d, SEResNetBottleneck1d, SEResNet50ECG from .sparcnet import DenseBlock, DenseLayer, SparcNet, TransitionLayer from .stagenet import StageNet, StageNetLayer from .stagenet_mha import StageAttentionNet, StageNetAttentionLayer diff --git a/pyhealth/models/lambda_resnet.py b/pyhealth/models/lambda_resnet.py new file mode 100644 index 000000000..0860429dd --- /dev/null +++ b/pyhealth/models/lambda_resnet.py @@ -0,0 +1,388 @@ +"""1-D Lambda-ResNet-18 ECG model. + +Implements the ``lambda_resnet1d18`` backbone used in: + + Nonaka N. & Seita J. (2021). In-depth Benchmarking of Deep Neural Network + Architectures for ECG Diagnosis. *PMLR* 149:1–19. + https://proceedings.mlr.press/v149/nonaka21a.html + +The lambda layer is described in: + + Bello I. (2021). LambdaNetworks: Modeling Long-Range Interactions Without + Attention. *ICLR 2021* (Spotlight). + https://openreview.net/forum?id=xTJEN-ggl1b + +**Architecture notes** (from the reference ``lambdanet1d.py``): + +* The model uses **bottleneck blocks** (expansion = 4) for all four stages, + not basic blocks — giving effective channel widths of 256 / 512 / 1024 / 2048. +* Every bottleneck replaces its middle 3×1 convolution with a + :class:`LambdaConv1d` layer followed by optional average-pooling (for + downsampling stages) and BN + ReLU. +* ``dim_u = 4`` (intra-depth dimension) and ``nhead = 4``. +* The local positional context uses a learnable ``nn.Parameter`` shaped as a + Conv2d weight ``(dim_k, dim_u, 1, dim_m)`` applied via ``F.conv2d``, + exactly as in the reference. +* A ``Dropout(0.3)`` is placed *inside* the backbone's final FC (before the + linear projection to ``backbone_out_dim``). +* Input is clamped to ``[-20, 20]`` at the start of each forward pass and + after each of the first three stages — matching the reference's numerical + stability guards. + +See :mod:`pyhealth.models.resnet_ecg_base` for shared building blocks. +""" + +from typing import List, Optional + +import torch +import torch.nn as nn +import torch.nn.functional as F + +from pyhealth.datasets import SampleDataset +from pyhealth.models.resnet_ecg_base import ECGBackboneModel + +_CLAMP = 20.0 + + +# --------------------------------------------------------------------------- +# Lambda convolution layer +# --------------------------------------------------------------------------- + +class LambdaConv1d(nn.Module): + """1-D lambda layer (Bello, 2021). + + Captures content-based and position-based long-range interactions across + the full temporal sequence without materialising an attention map. + + Matches ``LambdaConv1d`` in the reference ``lambdanet1d.py``: + + * Queries projected and BN-normalised. + * Keys projected (no BN), softmax-normalised over the time dimension. + * Values projected and BN-normalised. + * **Content lambda**: ``λ_c = softmax(K)ᵀ V`` → ``(B, dim_k, dim_v)``. + * **Position lambda**: learnable ``nn.Parameter`` shaped as a Conv2d + weight ``(dim_k, dim_u, 1, dim_m)`` applied to the reshaped values via + ``F.conv2d``, producing per-position context ``(B, dim_k, dim_v, N)``. + + Args: + in_channels (int): Number of input channels. + out_channels (int): Number of output channels (must be divisible by + ``nhead``). + nhead (int): Number of query heads. + dim_k (int): Key/query depth. Default ``16``. + dim_u (int): Intra-depth dimension. Default ``4``. + dim_m (int): Receptive field for the local positional embedding + (must be odd; ``0`` disables the positional term). Default ``7``. + + Examples: + >>> layer = LambdaConv1d(256, 256, nhead=4) + >>> layer(torch.randn(2, 256, 312)).shape + torch.Size([2, 256, 312]) + """ + + def __init__( + self, + in_channels: int, + out_channels: int, + nhead: int, + dim_k: int = 16, + dim_u: int = 4, + dim_m: int = 7, + ) -> None: + super().__init__() + + assert out_channels % nhead == 0, ( + f"out_channels ({out_channels}) must be divisible by nhead ({nhead})" + ) + + self.nhead = nhead + self.dim_k = dim_k + self.dim_u = dim_u + self.dim_m = dim_m + self.dim_v = out_channels // nhead + + self.local_context = dim_m > 0 + self.padding = (dim_m - 1) // 2 + + # Projections + self.to_queries = nn.Sequential( + nn.Conv1d(in_channels, dim_k * nhead, kernel_size=1, bias=False), + nn.BatchNorm1d(dim_k * nhead), + ) + self.to_keys = nn.Sequential( + nn.Conv1d(in_channels, dim_k * dim_u, kernel_size=1, bias=False), + ) + self.to_values = nn.Sequential( + nn.Conv1d(in_channels, self.dim_v * dim_u, kernel_size=1, bias=False), + nn.BatchNorm1d(self.dim_v * dim_u), + ) + + # Positional embedding: stored as a Conv2d-shaped parameter so that + # F.conv2d can apply it efficiently, matching the reference exactly. + if self.local_context: + self.embedding = nn.Parameter( + torch.randn(dim_k, dim_u, 1, dim_m), requires_grad=True + ) + else: + self.embedding = nn.Parameter( + torch.randn(dim_k, dim_u), requires_grad=True + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + B, _, N = x.shape + k, u, v = self.dim_k, self.dim_u, self.dim_v + h = self.nhead + + queries = self.to_queries(x).view(B, h, k, N) # (B, h, k, N) + + keys = self.to_keys(x).view(B, k, u, N) # (B, k, u, N) + keys = F.softmax(keys, dim=-1) # softmax over N + + values = self.to_values(x).view(B, v, u, N) # (B, v, u, N) + + # Content lambda: λ_c = keys^T values → (B, k, v) + lambda_c = torch.einsum("bkum,bvum->bkv", keys, values) + y_c = torch.einsum("bhkn,bkv->bhvn", queries, lambda_c) # (B, h, v, N) + + # Position lambda + if self.local_context: + # values reshaped to (B, u, v, N) then treated as a 2D feature + # map of size (v, N) with u channels for the Conv2d application. + values_2d = values.view(B, u, v, N) # (B, u, v, N) + # F.conv2d: weight (k, u, 1, dim_m) applied to (B, u, v, N) + # → (B, k, v, N) + lambda_p = F.conv2d(values_2d, self.embedding, + padding=(0, self.padding)) # (B, k, v, N) + y_p = torch.einsum("bhkn,bkvn->bhvn", queries, lambda_p) + else: + lambda_p = torch.einsum("ku,bvun->bkvn", self.embedding, values) + y_p = torch.einsum("bhkn,bkvn->bhvn", queries, lambda_p) + + return (y_c + y_p).contiguous().view(B, h * v, N) # (B, out_ch, N) + + +# --------------------------------------------------------------------------- +# Lambda bottleneck block +# --------------------------------------------------------------------------- + +class LambdaBottleneck1d(nn.Module): + """1-D Lambda bottleneck block. + + Three-conv bottleneck where the middle 3×1 convolution is replaced by a + :class:`LambdaConv1d` layer, following ``LambdaBottleneck1d`` in the + reference ``lambdanet1d.py``. + + For downsampling stages (``stride > 1``) an ``AvgPool1d`` is appended + after the lambda layer (before BN + ReLU), matching the reference. + + Args: + in_planes (int): Number of input channels. + planes (int): Base channel width; output is ``planes * 4``. + stride (int): Downsampling stride. Default ``1``. + + Examples: + >>> block = LambdaBottleneck1d(64, 64) # 64 → 256 channels + >>> block(torch.randn(4, 64, 312)).shape + torch.Size([4, 256, 312]) + """ + + expansion: int = 4 + + def __init__( + self, + in_planes: int, + planes: int, + stride: int = 1, + ) -> None: + super().__init__() + + # 1×1 bottleneck-down + self.conv1 = nn.Conv1d(in_planes, planes, kernel_size=1, bias=False) + self.bn1 = nn.BatchNorm1d(planes) + + # Lambda layer (replaces 3×1 conv) + optional avg-pool + BN + ReLU + lambda_layers: List[nn.Module] = [ + LambdaConv1d(planes, planes, nhead=4) + ] + if stride != 1 or in_planes != self.expansion * planes: + lambda_layers.append( + nn.AvgPool1d(kernel_size=3, stride=stride, padding=1) + ) + lambda_layers.append(nn.BatchNorm1d(planes)) + lambda_layers.append(nn.ReLU()) + self.conv2 = nn.Sequential(*lambda_layers) + + # 1×1 bottleneck-up + self.conv3 = nn.Conv1d(planes, self.expansion * planes, + kernel_size=1, bias=False) + self.bn3 = nn.BatchNorm1d(self.expansion * planes) + + # Shortcut + self.shortcut: nn.Module + if stride != 1 or in_planes != self.expansion * planes: + self.shortcut = nn.Sequential( + nn.Conv1d(in_planes, self.expansion * planes, + kernel_size=1, stride=stride), + nn.BatchNorm1d(self.expansion * planes), + ) + else: + self.shortcut = nn.Identity() + + def forward(self, x: torch.Tensor) -> torch.Tensor: + out = F.relu(self.bn1(self.conv1(x))) + out = self.conv2(out) + out = self.bn3(self.conv3(out)) + out = out + self.shortcut(x) + return F.relu(out) + + +# --------------------------------------------------------------------------- +# LambdaResNet1d backbone +# --------------------------------------------------------------------------- + +class LambdaResNet1d(nn.Module): + """1-D Lambda-ResNet backbone. + + All four stages use :class:`LambdaBottleneck1d` blocks, matching + ``LambdaResNet1d`` in the reference ``lambdanet1d.py``. + + The backbone's final FC wraps the linear projection with + ``Dropout(0.3)``, and ``torch.clamp([-20, 20])`` is applied after the + stem and after each of the first three stages for numerical stability + (both matching the reference). + + Args: + num_blocks (List[int]): Blocks per stage. + num_lead (int): Number of input channels (ECG leads). Default ``12``. + backbone_out_dim (int): Projection output dimension. Default ``256``. + + Examples: + >>> bb = LambdaResNet1d([2, 2, 2, 2]) + >>> bb(torch.randn(2, 12, 1250)).shape + torch.Size([2, 256]) + """ + + def __init__( + self, + num_blocks: List[int], + num_lead: int = 12, + backbone_out_dim: int = 256, + ) -> None: + super().__init__() + self.in_planes = 64 + + self.conv1 = nn.Conv1d(num_lead, 64, + kernel_size=7, stride=2, padding=3, bias=False) + self.bn1 = nn.BatchNorm1d(64) + self.maxpool = nn.MaxPool1d(kernel_size=3, stride=2, padding=1) + + self.layer1 = self._make_layer(LambdaBottleneck1d, 64, num_blocks[0]) + self.layer2 = self._make_layer(LambdaBottleneck1d, 128, num_blocks[1], stride=2) + self.layer3 = self._make_layer(LambdaBottleneck1d, 256, num_blocks[2], stride=2) + self.layer4 = self._make_layer(LambdaBottleneck1d, 512, num_blocks[3], stride=2) + + self.avgpool = nn.AdaptiveAvgPool1d(1) + # Dropout inside the backbone FC, matching the reference. + self.fc = nn.Sequential( + nn.Dropout(0.3), + nn.Linear(512 * LambdaBottleneck1d.expansion, backbone_out_dim), + ) + + def _make_layer( + self, + block: type, + planes: int, + num_blocks: int, + stride: int = 1, + ) -> nn.Sequential: + strides = [stride] + [1] * (num_blocks - 1) + layers: List[nn.Module] = [] + for s in strides: + layers.append(block(self.in_planes, planes, s)) + self.in_planes = planes * block.expansion + return nn.Sequential(*layers) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + x = torch.clamp(x, min=-_CLAMP, max=_CLAMP) + x = F.relu(self.bn1(self.conv1(x))) + x = self.maxpool(x) + + x = self.layer1(x) + x = torch.clamp(x, min=-_CLAMP, max=_CLAMP) + x = self.layer2(x) + x = torch.clamp(x, min=-_CLAMP, max=_CLAMP) + x = self.layer3(x) + x = torch.clamp(x, min=-_CLAMP, max=_CLAMP) + x = self.layer4(x) + + x = self.avgpool(x) + x = torch.flatten(x, 1) + return self.fc(x) + + +# --------------------------------------------------------------------------- +# LambdaResNet18ECG (PyHealth BaseModel) +# --------------------------------------------------------------------------- + +class LambdaResNet18ECG(ECGBackboneModel): + """Lambda-ResNet-18 backbone for ECG classification (Nonaka & Seita, 2021). + + Replaces the 3×1 convolution in every bottleneck block with a + :class:`LambdaConv1d` layer, enabling global temporal context modelling + without explicit attention maps (Bello, 2021). + + This is the ``lambda_resnet1d18`` variant from the reference code: four + stages of :class:`LambdaBottleneck1d` with layer counts ``[2, 2, 2, 2]`` + and effective channel widths of 256 / 512 / 1024 / 2048 (bottleneck + expansion = 4). + + All training/evaluation conventions (prediction head, sliding-window + protocol) are identical to :class:`~pyhealth.models.ResNet18ECG`. + + Args: + dataset (SampleDataset): Dataset used to infer feature/label keys, + output size, and loss function. + in_channels (int): Number of ECG leads. Default ``12``. + backbone_output_dim (int): Backbone projection output dimension. + Default ``256``. + dropout (float): Dropout probability in the prediction head. + Default ``0.25``. + + Examples: + >>> import numpy as np + >>> from pyhealth.datasets import create_sample_dataset, get_dataloader + >>> samples = [ + ... {"patient_id": "p0", "visit_id": "v0", + ... "signal": np.random.randn(12, 1250).astype(np.float32), + ... "label": [1, 0, 1, 0, 0]}, + ... {"patient_id": "p1", "visit_id": "v1", + ... "signal": np.random.randn(12, 1250).astype(np.float32), + ... "label": [0, 1, 0, 1, 0]}, + ... ] + >>> dataset = create_sample_dataset( + ... samples=samples, + ... input_schema={"signal": "tensor"}, + ... output_schema={"label": "multilabel"}, + ... dataset_name="test", + ... ) + >>> model = LambdaResNet18ECG(dataset=dataset) + >>> out = model(**next(iter(get_dataloader(dataset, batch_size=2)))) + >>> sorted(out.keys()) + ['logit', 'loss', 'y_prob', 'y_true'] + """ + + def __init__( + self, + dataset: SampleDataset, + in_channels: int = 12, + backbone_output_dim: int = 256, + dropout: float = 0.25, + ) -> None: + super().__init__(dataset=dataset) + + self.backbone = LambdaResNet1d( + num_blocks=[2, 2, 2, 2], + num_lead=in_channels, + backbone_out_dim=backbone_output_dim, + ) + self._build_head(backbone_output_dim, dropout) diff --git a/pyhealth/models/resnet.py b/pyhealth/models/resnet.py new file mode 100644 index 000000000..f1e2a467e --- /dev/null +++ b/pyhealth/models/resnet.py @@ -0,0 +1,89 @@ +"""Plain 1-D ResNet-18 ECG model. + +Implements the ``resnet1d18`` backbone used in: + + Nonaka N. & Seita J. (2021). In-depth Benchmarking of Deep Neural Network + Architectures for ECG Diagnosis. *PMLR* 149:1–19. + https://proceedings.mlr.press/v149/nonaka21a.html + +See :mod:`pyhealth.models.resnet_ecg_base` for the shared building blocks. +""" + +from pyhealth.datasets import SampleDataset +from pyhealth.models.resnet_ecg_base import BasicBlock1d, ECGBackboneModel, ResNet1d + + +class ResNet18ECG(ECGBackboneModel): + """ResNet-18 backbone for ECG classification (Nonaka & Seita, 2021). + + Standard 1-D ResNet-18 with a two-layer prediction head. + + **Backbone** (``resnet1d18`` in the reference code): + + * Stem: ``Conv1d(12, 64, 7, stride=2) → BN → ReLU → MaxPool1d(3, stride=2)`` + * Four stages of :class:`~pyhealth.models.BasicBlock1d` with layer counts + ``[2, 2, 2, 2]`` and channel widths ``[64, 128, 256, 512]``. + * ``AdaptiveAvgPool1d(1)`` → ``Linear(512, backbone_output_dim)``. + + **Head** (``HeadModule`` in the reference code): + + ``Linear(256, 128) → ReLU → BN(128) → Dropout(0.25) → Linear(128, n_classes)`` + + **Windowing** (Section 4.2): + + During *training* a random 2.5-second window is cropped from each + recording (handle in the task preprocessing / collate function). + During *evaluation* use :meth:`forward_sliding_window` for the 50 %-overlap + sliding-window protocol from the paper. + + Args: + dataset (SampleDataset): Dataset used to infer feature/label keys, + output size, and loss function. + in_channels (int): Number of ECG leads. Default ``12``. + base_channels (int): Width of the first residual stage. Default ``64``. + backbone_output_dim (int): Backbone projection output dimension. + Default ``256``. + dropout (float): Dropout probability in the prediction head. + Default ``0.25``. + + Examples: + >>> import numpy as np + >>> from pyhealth.datasets import create_sample_dataset, get_dataloader + >>> samples = [ + ... {"patient_id": "p0", "visit_id": "v0", + ... "signal": np.random.randn(12, 1250).astype(np.float32), + ... "label": [1, 0, 1, 0, 0]}, + ... {"patient_id": "p1", "visit_id": "v1", + ... "signal": np.random.randn(12, 1250).astype(np.float32), + ... "label": [0, 1, 0, 1, 0]}, + ... ] + >>> dataset = create_sample_dataset( + ... samples=samples, + ... input_schema={"signal": "tensor"}, + ... output_schema={"label": "multilabel"}, + ... dataset_name="test", + ... ) + >>> model = ResNet18ECG(dataset=dataset) + >>> out = model(**next(iter(get_dataloader(dataset, batch_size=2)))) + >>> sorted(out.keys()) + ['logit', 'loss', 'y_prob', 'y_true'] + """ + + def __init__( + self, + dataset: SampleDataset, + in_channels: int = 12, + base_channels: int = 64, + backbone_output_dim: int = 256, + dropout: float = 0.25, + ) -> None: + super().__init__(dataset=dataset) + + self.backbone = ResNet1d( + in_channels=in_channels, + layers=[2, 2, 2, 2], + block=BasicBlock1d, + base_channels=base_channels, + output_dim=backbone_output_dim, + ) + self._build_head(backbone_output_dim, dropout) diff --git a/pyhealth/models/resnet_ecg_base.py b/pyhealth/models/resnet_ecg_base.py new file mode 100644 index 000000000..8635d8e04 --- /dev/null +++ b/pyhealth/models/resnet_ecg_base.py @@ -0,0 +1,369 @@ +"""Shared building blocks for 1-D ResNet-based ECG models. + +This module provides: + +* :class:`BasicBlock1d` – the two-conv residual block for ResNet-18/34. +* :class:`Bottleneck1d` – the three-conv bottleneck block for ResNet-50+. +* :class:`ResNet1d` – a generic 1-D ResNet backbone whose block type and + layer counts are fully configurable via constructor arguments. +* :class:`ECGBackboneModel` – an abstract :class:`~pyhealth.models.BaseModel` + that owns the shared prediction head, :meth:`forward`, and + :meth:`forward_sliding_window` inherited by every ECG ResNet variant. + +References: + He K. et al. (2016). Deep Residual Learning for Image Recognition. *CVPR*. + + Nonaka N. & Seita J. (2021). In-depth Benchmarking of Deep Neural Network + Architectures for ECG Diagnosis. *PMLR* 149:1–19. + https://proceedings.mlr.press/v149/nonaka21a.html +""" + +from typing import Callable, Dict, List, Optional, Type, Union + +import torch +import torch.nn as nn +import torch.nn.functional as F + +from pyhealth.datasets import SampleDataset +from pyhealth.models import BaseModel + + +# --------------------------------------------------------------------------- +# BasicBlock1d +# --------------------------------------------------------------------------- + +class BasicBlock1d(nn.Module): + """1-D two-conv residual basic block (ResNet-18/34). + + Directly mirrors ``torchvision.models.resnet.BasicBlock`` with all 2-D + operations replaced by 1-D equivalents. + + Args: + in_channels (int): Number of input channels. + out_channels (int): Number of output channels. + stride (int): Stride of the first convolution. Default ``1``. + + Examples: + >>> block = BasicBlock1d(64, 128, stride=2) + >>> block(torch.randn(4, 64, 500)).shape + torch.Size([4, 128, 250]) + """ + + expansion: int = 1 + + def __init__( + self, + in_channels: int, + out_channels: int, + stride: int = 1, + ) -> None: + super().__init__() + + self.conv1 = nn.Conv1d( + in_channels, out_channels, + kernel_size=3, stride=stride, padding=1, bias=False, + ) + self.bn1 = nn.BatchNorm1d(out_channels) + self.relu = nn.ReLU(inplace=True) + self.conv2 = nn.Conv1d( + out_channels, out_channels, + kernel_size=3, stride=1, padding=1, bias=False, + ) + self.bn2 = nn.BatchNorm1d(out_channels) + + self.downsample: Optional[nn.Sequential] = None + if stride != 1 or in_channels != out_channels: + self.downsample = nn.Sequential( + nn.Conv1d(in_channels, out_channels, + kernel_size=1, stride=stride, bias=False), + nn.BatchNorm1d(out_channels), + ) + + def _conv_branch(self, x: torch.Tensor) -> torch.Tensor: + """Conv1 → BN1 → ReLU → Conv2 → BN2. + + Extracted so SE/Lambda subclasses can insert attention after BN2 + and before the residual addition. + """ + out = self.relu(self.bn1(self.conv1(x))) + return self.bn2(self.conv2(out)) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + identity = x if self.downsample is None else self.downsample(x) + return self.relu(self._conv_branch(x) + identity) + + +# --------------------------------------------------------------------------- +# Bottleneck1d +# --------------------------------------------------------------------------- + +class Bottleneck1d(nn.Module): + """1-D three-conv bottleneck residual block (ResNet-50+). + + Mirrors ``torchvision.models.resnet.Bottleneck`` with 1-D operations. + The channel expansion factor is 4 (``planes * 4`` output channels). + + Args: + in_channels (int): Number of input channels. + planes (int): Base channel width; output is ``planes * 4``. + stride (int): Stride of the 3×1 convolution. Default ``1``. + + Examples: + >>> block = Bottleneck1d(64, 64) # 64 → 256 channels + >>> block(torch.randn(4, 64, 500)).shape + torch.Size([4, 256, 500]) + """ + + expansion: int = 4 + + def __init__( + self, + in_channels: int, + planes: int, + stride: int = 1, + ) -> None: + super().__init__() + out_channels = planes * self.expansion + + self.conv1 = nn.Conv1d(in_channels, planes, kernel_size=1, bias=False) + self.bn1 = nn.BatchNorm1d(planes) + self.conv2 = nn.Conv1d(planes, planes, + kernel_size=3, stride=stride, padding=1, bias=False) + self.bn2 = nn.BatchNorm1d(planes) + self.conv3 = nn.Conv1d(planes, out_channels, kernel_size=1, bias=False) + self.bn3 = nn.BatchNorm1d(out_channels) + self.relu = nn.ReLU(inplace=True) + + self.downsample: Optional[nn.Sequential] = None + if stride != 1 or in_channels != out_channels: + self.downsample = nn.Sequential( + nn.Conv1d(in_channels, out_channels, + kernel_size=1, stride=stride, bias=False), + nn.BatchNorm1d(out_channels), + ) + + def _conv_branch(self, x: torch.Tensor) -> torch.Tensor: + out = self.relu(self.bn1(self.conv1(x))) + out = self.relu(self.bn2(self.conv2(out))) + return self.bn3(self.conv3(out)) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + identity = x if self.downsample is None else self.downsample(x) + return self.relu(self._conv_branch(x) + identity) + + +# --------------------------------------------------------------------------- +# ResNet1d +# --------------------------------------------------------------------------- + +BlockType = Union[Type[BasicBlock1d], Type[Bottleneck1d]] + + +class ResNet1d(nn.Module): + """Generic configurable 1-D ResNet backbone. + + Args: + in_channels (int): Input channels (ECG leads). + layers (List[int]): Blocks per stage, e.g. ``[2, 2, 2, 2]``. + block (BlockType): Block constructor (:class:`BasicBlock1d` or + :class:`Bottleneck1d`, or an augmented subclass). + base_channels (int): Width of the first residual stage. Default ``64``. + output_dim (int, optional): Projection output dimension. ``None`` + returns the raw GAP output. Default ``256``. + block_kwargs (dict, optional): Extra keyword arguments forwarded to + every block constructor call. + + Examples: + >>> bb = ResNet1d(12, [2, 2, 2, 2], BasicBlock1d, output_dim=256) + >>> bb(torch.randn(4, 12, 1250)).shape + torch.Size([4, 256]) + """ + + def __init__( + self, + in_channels: int, + layers: List[int], + block: BlockType, + base_channels: int = 64, + output_dim: Optional[int] = 256, + block_kwargs: Optional[dict] = None, + ) -> None: + super().__init__() + block_kwargs = block_kwargs or {} + + self.stem = nn.Sequential( + nn.Conv1d(in_channels, base_channels, + kernel_size=7, stride=2, padding=3, bias=False), + nn.BatchNorm1d(base_channels), + nn.ReLU(inplace=True), + nn.MaxPool1d(kernel_size=3, stride=2, padding=1), + ) + + channel_widths = [base_channels * (2 ** i) for i in range(4)] + strides = [1, 2, 2, 2] + + self.stages = nn.ModuleList() + in_ch = base_channels + for n_blocks, stride, out_ch in zip(layers, strides, channel_widths): + # First block may change spatial resolution and/or channel width; + # remaining blocks keep stride=1. + stage: List[nn.Module] = [ + block(in_ch, out_ch, stride=stride, **block_kwargs) + ] + in_ch = out_ch * block.expansion # type: ignore[attr-defined] + for _ in range(1, n_blocks): + stage.append(block(in_ch, out_ch, stride=1, **block_kwargs)) + self.stages.append(nn.Sequential(*stage)) + + self.gap = nn.AdaptiveAvgPool1d(1) + + self.proj: Optional[nn.Linear] = None + final_ch = channel_widths[-1] * block.expansion # type: ignore[attr-defined] + if output_dim is not None: + self.proj = nn.Linear(final_ch, output_dim) + self.out_channels = output_dim + else: + self.out_channels = final_ch + + self._init_weights() + + def _init_weights(self) -> None: + for m in self.modules(): + if isinstance(m, nn.Conv1d): + nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity="relu") + elif isinstance(m, nn.BatchNorm1d): + nn.init.constant_(m.weight, 1) + nn.init.constant_(m.bias, 0) + elif isinstance(m, nn.Linear): + nn.init.constant_(m.bias, 0) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + x = self.stem(x) + for stage in self.stages: + x = stage(x) + x = self.gap(x).squeeze(-1) + if self.proj is not None: + x = self.proj(x) + return x + + +# --------------------------------------------------------------------------- +# ECGBackboneModel – shared PyHealth BaseModel +# --------------------------------------------------------------------------- + +class ECGBackboneModel(BaseModel): + """Abstract base class for ECG ResNet-variant PyHealth models. + + Subclass template:: + + class MyECGModel(ECGBackboneModel): + def __init__(self, dataset, ...): + super().__init__(dataset) + self.backbone = MyBackbone(...) + self._build_head(backbone_output_dim, dropout) + + Provides :meth:`_build_head`, :meth:`forward`, and + :meth:`forward_sliding_window`. Subclasses only need to set + ``self.backbone`` and call ``self._build_head()``. + """ + + def __init__(self, dataset: SampleDataset) -> None: + super().__init__(dataset=dataset) + assert len(self.feature_keys) == 1, ( + f"{type(self).__name__} expects exactly one feature key." + ) + assert len(self.label_keys) == 1, ( + f"{type(self).__name__} expects exactly one label key." + ) + self.feature_key = self.feature_keys[0] + self.label_key = self.label_keys[0] + self.backbone: nn.Module # assigned by subclass before _build_head() + + def _build_head(self, backbone_output_dim: int, dropout: float = 0.25) -> None: + """Build the prediction head from Nonaka & Seita (2021). + + ``Linear(d, 128) → ReLU → BN(128) → Dropout(0.25) → Linear(128, n_classes)`` + + Must be called after ``self.backbone`` has been assigned. + + Args: + backbone_output_dim (int): Output dimension of the backbone. + dropout (float): Dropout probability. Default ``0.25``. + """ + output_size = self.get_output_size() + self.head = nn.Sequential( + nn.Linear(backbone_output_dim, 128), + nn.ReLU(inplace=True), + nn.BatchNorm1d(128), + nn.Dropout(p=dropout), + nn.Linear(128, output_size), + ) + + def forward(self, **kwargs) -> Dict[str, torch.Tensor]: + """Forward pass for a single fixed-length window. + + Args: + **kwargs: Must contain the feature key (tensor ``(batch, n_leads, + window_length)``) and the label key. + + Returns: + Dict with keys ``loss``, ``y_prob``, ``y_true``, ``logit``, and + optionally ``embed`` when ``kwargs["embed"]`` is ``True``. + """ + x: torch.Tensor = kwargs[self.feature_key].to(self.device) + emb = self.backbone(x) + logits = self.head(emb) + + y_true = kwargs[self.label_key].to(self.device) + loss = self.get_loss_function()(logits, y_true) + y_prob = self.prepare_y_prob(logits) + + results: Dict[str, torch.Tensor] = { + "loss": loss, + "y_prob": y_prob, + "y_true": y_true, + "logit": logits, + } + if kwargs.get("embed", False): + results["embed"] = emb + return results + + def forward_sliding_window( + self, + signal: torch.Tensor, + window_size: int, + step_size: Optional[int] = None, + ) -> torch.Tensor: + """Sliding-window evaluation (Nonaka & Seita, 2021, Section 4.2). + + Splits *signal* into overlapping windows, runs the model on each, and + returns the per-class **maximum** probability across windows. + + Args: + signal (torch.Tensor): ``(batch, n_leads, total_length)``. + window_size (int): Samples per window (e.g. ``1250`` for 2.5 s at + 500 Hz). + step_size (int, optional): Stride between windows. Defaults to + ``window_size // 2`` (50 % overlap). + + Returns: + torch.Tensor: ``(batch, n_classes)``. + """ + if step_size is None: + step_size = window_size // 2 + + total_length = signal.shape[-1] + starts = list(range(0, total_length - window_size + 1, step_size)) + if not starts: + signal = F.pad(signal, (0, window_size - total_length)) + starts = [0] + + all_probs: List[torch.Tensor] = [] + self.eval() + with torch.no_grad(): + for start in starts: + window = signal[..., start: start + window_size].to(self.device) + logits = self.head(self.backbone(window)) + all_probs.append(self.prepare_y_prob(logits)) + + return torch.stack(all_probs, dim=1).max(dim=1).values diff --git a/pyhealth/models/se_resnet.py b/pyhealth/models/se_resnet.py new file mode 100644 index 000000000..cf3ff1b8f --- /dev/null +++ b/pyhealth/models/se_resnet.py @@ -0,0 +1,213 @@ +"""1-D SE-ResNet-50 ECG model. + +Implements the ``se_resnet1d50`` backbone used in: + + Nonaka N. & Seita J. (2021). In-depth Benchmarking of Deep Neural Network + Architectures for ECG Diagnosis. *PMLR* 149:1–19. + https://proceedings.mlr.press/v149/nonaka21a.html + +The SE block is described in: + + Hu J., Shen L. & Sun G. (2018). Squeeze-and-Excitation Networks. *CVPR*. + +The paper benchmarks SE-ResNet-**50** (not SE-ResNet-18). The backbone uses +three-conv bottleneck blocks (expansion = 4) with an SE module applied after +the third convolution and before the residual addition (Figure 3 of Hu et al.). + +The reference implementation (``senet1d.py``) uses ``Conv1d(1×1)`` rather +than ``nn.Linear`` for the SE excitation bottleneck, and the downsample +convolution in each stage uses ``kernel_size=1, padding=0`` (not 3/1 as in +the SENet-154 variant). + +See :mod:`pyhealth.models.resnet_ecg_base` for shared building blocks. +""" + +from typing import Optional + +import torch +import torch.nn as nn + +from pyhealth.datasets import SampleDataset +from pyhealth.models.resnet_ecg_base import ECGBackboneModel, ResNet1d + + +# --------------------------------------------------------------------------- +# SE module +# --------------------------------------------------------------------------- + +class SEModule1d(nn.Module): + """1-D Squeeze-and-Excitation module (Hu et al., 2018). + + Uses ``Conv1d(1×1)`` projections (matching the reference ``senet1d.py``). + + Squeeze: ``AdaptiveAvgPool1d(1)`` → ``(batch, C, 1)`` + Excitation: ``Conv1d(C, C//r, 1) → ReLU → Conv1d(C//r, C, 1) → Sigmoid`` + Scale: element-wise multiply input by the channel weights. + + Args: + channels (int): Number of input channels. + reduction (int): Bottleneck reduction ratio. Default ``16``. + + Examples: + >>> se = SEModule1d(256) + >>> se(torch.randn(4, 256, 312)).shape + torch.Size([4, 256, 312]) + """ + + def __init__(self, channels: int, reduction: int = 16) -> None: + super().__init__() + self.avg_pool = nn.AdaptiveAvgPool1d(1) + self.fc1 = nn.Conv1d(channels, channels // reduction, + kernel_size=1, padding=0) + self.relu = nn.ReLU(inplace=True) + self.fc2 = nn.Conv1d(channels // reduction, channels, + kernel_size=1, padding=0) + self.sigmoid = nn.Sigmoid() + + def forward(self, x: torch.Tensor) -> torch.Tensor: + s = self.avg_pool(x) + s = self.relu(self.fc1(s)) + s = self.sigmoid(self.fc2(s)) + return x * s + + +# --------------------------------------------------------------------------- +# SE bottleneck block +# --------------------------------------------------------------------------- + +class SEResNetBottleneck1d(nn.Module): + """1-D SE-ResNet bottleneck block. + + Three-conv bottleneck (1×1 → 3×1 → 1×1) with an SE module applied + after the third convolution and before the residual addition. + + Matches ``SEResNetBottleneck1d`` in the reference ``senet1d.py``. + + Args: + in_channels (int): Number of input channels. + planes (int): Base channel width; output is ``planes * 4``. + stride (int): Stride of the 3×1 convolution. Default ``1``. + reduction (int): SE reduction ratio. Default ``16``. + + Examples: + >>> block = SEResNetBottleneck1d(64, 64) # 64 → 256 channels + >>> block(torch.randn(4, 64, 500)).shape + torch.Size([4, 256, 500]) + """ + + expansion: int = 4 + + def __init__( + self, + in_channels: int, + planes: int, + stride: int = 1, + reduction: int = 16, + ) -> None: + super().__init__() + out_channels = planes * self.expansion + + # Reference uses stride in conv1 (Caffe convention), not in conv2. + self.conv1 = nn.Conv1d(in_channels, planes, + kernel_size=1, stride=stride, bias=False) + self.bn1 = nn.BatchNorm1d(planes) + self.conv2 = nn.Conv1d(planes, planes, + kernel_size=3, padding=1, bias=False) + self.bn2 = nn.BatchNorm1d(planes) + self.conv3 = nn.Conv1d(planes, out_channels, kernel_size=1, bias=False) + self.bn3 = nn.BatchNorm1d(out_channels) + self.relu = nn.ReLU(inplace=True) + self.se_module = SEModule1d(out_channels, reduction=reduction) + + self.downsample: Optional[nn.Sequential] = None + if stride != 1 or in_channels != out_channels: + # Reference uses kernel_size=1, padding=0 for SE-ResNet variants. + self.downsample = nn.Sequential( + nn.Conv1d(in_channels, out_channels, + kernel_size=1, stride=stride, padding=0, bias=False), + nn.BatchNorm1d(out_channels), + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + identity = x if self.downsample is None else self.downsample(x) + + out = self.relu(self.bn1(self.conv1(x))) + out = self.relu(self.bn2(self.conv2(out))) + out = self.bn3(self.conv3(out)) + out = self.se_module(out) + identity + return self.relu(out) + + +# --------------------------------------------------------------------------- +# SEResNet50ECG (PyHealth BaseModel) +# --------------------------------------------------------------------------- + +class SEResNet50ECG(ECGBackboneModel): + """SE-ResNet-50 backbone for ECG classification (Nonaka & Seita, 2021). + + Augments a ResNet-50 backbone by inserting a + :class:`~pyhealth.models.SEModule1d` channel-attention gate into every + bottleneck block, following Hu et al. (2018). + + This is the variant the paper benchmarks as "SE-ResNet" (``se_resnet1d50`` + in the reference code). It uses three-conv bottleneck blocks + (``expansion = 4``) with layer counts ``[3, 4, 6, 3]``. + + All training/evaluation conventions (backbone output dimension, prediction + head, sliding-window protocol) are identical to + :class:`~pyhealth.models.ResNet18ECG`. + + Args: + dataset (SampleDataset): Dataset used to infer feature/label keys, + output size, and loss function. + in_channels (int): Number of ECG leads. Default ``12``. + base_channels (int): Width of the first residual stage. Default ``64``. + backbone_output_dim (int): Backbone projection output dimension. + Default ``256``. + dropout (float): Dropout probability in the prediction head. + Default ``0.25``. + reduction (int): SE bottleneck reduction ratio. Default ``16``. + + Examples: + >>> import numpy as np + >>> from pyhealth.datasets import create_sample_dataset, get_dataloader + >>> samples = [ + ... {"patient_id": "p0", "visit_id": "v0", + ... "signal": np.random.randn(12, 1250).astype(np.float32), + ... "label": [1, 0, 1, 0, 0]}, + ... {"patient_id": "p1", "visit_id": "v1", + ... "signal": np.random.randn(12, 1250).astype(np.float32), + ... "label": [0, 1, 0, 1, 0]}, + ... ] + >>> dataset = create_sample_dataset( + ... samples=samples, + ... input_schema={"signal": "tensor"}, + ... output_schema={"label": "multilabel"}, + ... dataset_name="test", + ... ) + >>> model = SEResNet50ECG(dataset=dataset) + >>> out = model(**next(iter(get_dataloader(dataset, batch_size=2)))) + >>> sorted(out.keys()) + ['logit', 'loss', 'y_prob', 'y_true'] + """ + + def __init__( + self, + dataset: SampleDataset, + in_channels: int = 12, + base_channels: int = 64, + backbone_output_dim: int = 256, + dropout: float = 0.25, + reduction: int = 16, + ) -> None: + super().__init__(dataset=dataset) + + self.backbone = ResNet1d( + in_channels=in_channels, + layers=[3, 4, 6, 3], # ResNet-50 layer counts + block=SEResNetBottleneck1d, + base_channels=base_channels, + output_dim=backbone_output_dim, + block_kwargs={"reduction": reduction}, + ) + self._build_head(backbone_output_dim, dropout) From 461d6d4fb607c746817e13d746e415f40c370301 Mon Sep 17 00:00:00 2001 From: "Kent R. Spillner" Date: Sat, 18 Apr 2026 14:29:44 -0500 Subject: [PATCH 17/39] Add tests for ResNet models for ECG diagnosis Includes tests for ResNet-18, Lambda-ResNet, and SE-ResNet models. Assisted-by: Claude:Claude-4.6-Sonnet --- tests/core/test_resnet_ecg.py | 598 ++++++++++++++++++++++++++++++++++ 1 file changed, 598 insertions(+) create mode 100644 tests/core/test_resnet_ecg.py diff --git a/tests/core/test_resnet_ecg.py b/tests/core/test_resnet_ecg.py new file mode 100644 index 000000000..7157b222e --- /dev/null +++ b/tests/core/test_resnet_ecg.py @@ -0,0 +1,598 @@ +"""Tests for 1-D ResNet-based ECG models. + +Covers ResNet18ECG, SEResNet50ECG, and LambdaResNet18ECG, exercising: + - model initialisation and attribute checks + - forward pass output keys and shapes + - backward pass (gradient flow) + - embed flag + - custom hyperparameter variants + - all three output modes (multilabel, multiclass, binary) + - forward_sliding_window evaluation helper +""" + +import unittest + +import numpy as np +import torch + +from pyhealth.datasets import create_sample_dataset, get_dataloader +from pyhealth.models.resnet import ResNet18ECG +from pyhealth.models.se_resnet import SEResNet50ECG +from pyhealth.models.lambda_resnet import LambdaResNet18ECG + +# --------------------------------------------------------------------------- +# Shared fixture helpers +# --------------------------------------------------------------------------- + +_N_LEADS = 12 +_LENGTH = 1250 # 2.5 s @ 500 Hz — matches the paper's window size +_N_LABELS = 5 # number of multilabel classes used in tests + + +def _make_samples(n: int, rng: np.random.RandomState, + label_mode: str = "multilabel") -> list: + """Return ``n`` synthetic ECG samples for the given label mode. + + For multilabel, PyHealth's MultiLabelProcessor expects each label to be a + list of *active class indices* (set-style encoding), not a fixed-length + binary vector. E.g. ``[1, 3]`` means classes 1 and 3 are active. The + processor builds its vocabulary from the union of all class indices seen + across the dataset, so every class in ``range(_N_LABELS)`` must appear at + least once to guarantee a full-size output vector. + """ + samples = [] + for i in range(n): + if label_mode == "multilabel": + # Sample a random subset of class indices; ensure each class + # appears in at least one sample by cycling through them. + active = [j for j in range(_N_LABELS) if rng.randint(0, 2)] + # Guarantee the i-th class (mod _N_LABELS) is always present so + # the full vocabulary is established across the dataset. + forced = i % _N_LABELS + if forced not in active: + active.append(forced) + label = sorted(active) + elif label_mode == "multiclass": + label = int(rng.randint(0, 3)) + else: # binary + label = int(rng.randint(0, 2)) + samples.append({ + "patient_id": f"p{i}", + "visit_id": "v0", + "signal": rng.randn(_N_LEADS, _LENGTH).astype(np.float32), + "label": label, + }) + return samples + + +def _make_dataset(samples: list, label_mode: str): + return create_sample_dataset( + samples=samples, + input_schema={"signal": "tensor"}, + output_schema={"label": label_mode}, + dataset_name=f"test_ecg_{label_mode}", + ) + + +def _assert_forward_output(tc: unittest.TestCase, ret: dict, + batch_size: int, n_classes: int) -> None: + """Assert standard forward-output contract.""" + tc.assertIn("loss", ret) + tc.assertIn("y_prob", ret) + tc.assertIn("y_true", ret) + tc.assertIn("logit", ret) + tc.assertEqual(ret["loss"].dim(), 0) + tc.assertEqual(ret["y_prob"].shape[0], batch_size) + tc.assertEqual(ret["y_prob"].shape[1], n_classes) + tc.assertEqual(ret["y_true"].shape[0], batch_size) + tc.assertEqual(ret["logit"].shape[0], batch_size) + tc.assertEqual(ret["logit"].shape[1], n_classes) + tc.assertTrue(torch.isfinite(ret["loss"])) + + +# --------------------------------------------------------------------------- +# ResNet-18 +# --------------------------------------------------------------------------- + +class TestResNet18ECG(unittest.TestCase): + """Tests for ResNet18ECG.""" + + def setUp(self): + rng = np.random.RandomState(0) + samples = _make_samples(4, rng, "multilabel") + self.dataset = _make_dataset(samples, "multilabel") + self.model = ResNet18ECG(dataset=self.dataset) + self.batch = next(iter(get_dataloader(self.dataset, batch_size=4, shuffle=False))) + + # -- initialisation ------------------------------------------------------- + + def test_initialization(self): + self.assertIsInstance(self.model, ResNet18ECG) + self.assertEqual(len(self.model.feature_keys), 1) + self.assertIn("signal", self.model.feature_keys) + self.assertEqual(len(self.model.label_keys), 1) + self.assertIn("label", self.model.label_keys) + # backbone: 4 stages + self.assertEqual(len(self.model.backbone.stages), 4) + # head ends with a linear layer + self.assertIsInstance(list(self.model.head.children())[-1], torch.nn.Linear) + + def test_backbone_output_dim(self): + x = torch.randn(2, _N_LEADS, _LENGTH) + with torch.no_grad(): + out = self.model.backbone(x) + self.assertEqual(out.shape, (2, 256)) + + # -- forward -------------------------------------------------------------- + + def test_forward_multilabel(self): + with torch.no_grad(): + ret = self.model(**self.batch) + _assert_forward_output(self, ret, batch_size=4, n_classes=_N_LABELS) + # multilabel y_prob in [0, 1] + self.assertTrue(torch.all(ret["y_prob"] >= 0)) + self.assertTrue(torch.all(ret["y_prob"] <= 1)) + + def test_forward_multiclass(self): + rng = np.random.RandomState(1) + n_classes = 4 + samples = _make_samples(4, rng, "multiclass") + # ensure all classes represented so tokeniser has correct size + for i, s in enumerate(samples): + s["label"] = i % n_classes + ds = _make_dataset(samples, "multiclass") + model = ResNet18ECG(dataset=ds) + batch = next(iter(get_dataloader(ds, batch_size=4, shuffle=False))) + with torch.no_grad(): + ret = model(**batch) + _assert_forward_output(self, ret, batch_size=4, n_classes=n_classes) + # multiclass y_prob rows sum to ~1 + self.assertTrue(torch.allclose(ret["y_prob"].sum(dim=1), + torch.ones(4), atol=1e-5)) + + def test_forward_binary(self): + rng = np.random.RandomState(2) + samples = _make_samples(4, rng, "binary") + for i, s in enumerate(samples): + s["label"] = i % 2 + ds = _make_dataset(samples, "binary") + model = ResNet18ECG(dataset=ds) + batch = next(iter(get_dataloader(ds, batch_size=4, shuffle=False))) + with torch.no_grad(): + ret = model(**batch) + _assert_forward_output(self, ret, batch_size=4, n_classes=1) + self.assertTrue(torch.all(ret["y_prob"] >= 0)) + self.assertTrue(torch.all(ret["y_prob"] <= 1)) + + # -- backward ------------------------------------------------------------- + + def test_backward(self): + ret = self.model(**self.batch) + ret["loss"].backward() + has_grad = any( + p.requires_grad and p.grad is not None + for p in self.model.parameters() + ) + self.assertTrue(has_grad, "No parameters received gradients") + + # -- embed ---------------------------------------------------------------- + + def test_embed_flag(self): + batch = dict(self.batch, embed=True) + with torch.no_grad(): + ret = self.model(**batch) + self.assertIn("embed", ret) + self.assertEqual(ret["embed"].shape, (4, 256)) + + # -- hyperparameters ------------------------------------------------------ + + def test_custom_hyperparameters(self): + model = ResNet18ECG( + dataset=self.dataset, + in_channels=_N_LEADS, + base_channels=32, + backbone_output_dim=128, + dropout=0.1, + ) + x = torch.randn(2, _N_LEADS, _LENGTH) + with torch.no_grad(): + emb = model.backbone(x) + self.assertEqual(emb.shape, (2, 128)) + batch = next(iter(get_dataloader(self.dataset, batch_size=2, shuffle=False))) + with torch.no_grad(): + ret = model(**batch) + self.assertIn("loss", ret) + + # -- sliding window ------------------------------------------------------- + + def test_forward_sliding_window(self): + # Full-length recording is 5 s @ 500 Hz = 2500 samples + signal = torch.randn(2, _N_LEADS, 2500) + self.model.eval() + probs = self.model.forward_sliding_window(signal, window_size=_LENGTH) + self.assertEqual(probs.shape, (2, _N_LABELS)) + self.assertTrue(torch.all(probs >= 0)) + self.assertTrue(torch.all(probs <= 1)) + + def test_forward_sliding_window_short_signal(self): + """Signal shorter than one window is zero-padded and processed.""" + signal = torch.randn(2, _N_LEADS, 500) + self.model.eval() + probs = self.model.forward_sliding_window(signal, window_size=_LENGTH) + self.assertEqual(probs.shape, (2, _N_LABELS)) + + def test_forward_sliding_window_custom_step(self): + """Custom step size produces the same output shape.""" + signal = torch.randn(2, _N_LEADS, 2500) + self.model.eval() + probs = self.model.forward_sliding_window( + signal, window_size=_LENGTH, step_size=250) + self.assertEqual(probs.shape, (2, _N_LABELS)) + + +# --------------------------------------------------------------------------- +# SE-ResNet-50 +# --------------------------------------------------------------------------- + +class TestSEResNet50ECG(unittest.TestCase): + """Tests for SEResNet50ECG.""" + + def setUp(self): + rng = np.random.RandomState(3) + samples = _make_samples(4, rng, "multilabel") + self.dataset = _make_dataset(samples, "multilabel") + self.model = SEResNet50ECG(dataset=self.dataset) + self.batch = next(iter(get_dataloader(self.dataset, batch_size=4, shuffle=False))) + + # -- initialisation ------------------------------------------------------- + + def test_initialization(self): + self.assertIsInstance(self.model, SEResNet50ECG) + self.assertIn("signal", self.model.feature_keys) + self.assertIn("label", self.model.label_keys) + # SE-ResNet-50 has 4 stages + self.assertEqual(len(self.model.backbone.stages), 4) + + def test_se_blocks_present(self): + """Every bottleneck block in the backbone contains an SEModule1d.""" + from pyhealth.models.se_resnet import SEResNetBottleneck1d, SEModule1d + for stage in self.model.backbone.stages: + for block in stage.children(): + self.assertIsInstance(block, SEResNetBottleneck1d) + self.assertIsInstance(block.se_module, SEModule1d) + + def test_backbone_output_dim(self): + x = torch.randn(2, _N_LEADS, _LENGTH) + with torch.no_grad(): + out = self.model.backbone(x) + self.assertEqual(out.shape, (2, 256)) + + # -- forward -------------------------------------------------------------- + + def test_forward_multilabel(self): + with torch.no_grad(): + ret = self.model(**self.batch) + _assert_forward_output(self, ret, batch_size=4, n_classes=_N_LABELS) + self.assertTrue(torch.all(ret["y_prob"] >= 0)) + self.assertTrue(torch.all(ret["y_prob"] <= 1)) + + def test_forward_multiclass(self): + rng = np.random.RandomState(4) + n_classes = 3 + samples = _make_samples(4, rng, "multiclass") + for i, s in enumerate(samples): + s["label"] = i % n_classes + ds = _make_dataset(samples, "multiclass") + model = SEResNet50ECG(dataset=ds) + batch = next(iter(get_dataloader(ds, batch_size=4, shuffle=False))) + with torch.no_grad(): + ret = model(**batch) + _assert_forward_output(self, ret, batch_size=4, n_classes=n_classes) + self.assertTrue(torch.allclose(ret["y_prob"].sum(dim=1), + torch.ones(4), atol=1e-5)) + + def test_forward_binary(self): + rng = np.random.RandomState(5) + samples = _make_samples(4, rng, "binary") + for i, s in enumerate(samples): + s["label"] = i % 2 + ds = _make_dataset(samples, "binary") + model = SEResNet50ECG(dataset=ds) + batch = next(iter(get_dataloader(ds, batch_size=4, shuffle=False))) + with torch.no_grad(): + ret = model(**batch) + _assert_forward_output(self, ret, batch_size=4, n_classes=1) + + # -- backward ------------------------------------------------------------- + + def test_backward(self): + ret = self.model(**self.batch) + ret["loss"].backward() + has_grad = any( + p.requires_grad and p.grad is not None + for p in self.model.parameters() + ) + self.assertTrue(has_grad, "No parameters received gradients") + + # -- embed ---------------------------------------------------------------- + + def test_embed_flag(self): + batch = dict(self.batch, embed=True) + with torch.no_grad(): + ret = self.model(**batch) + self.assertIn("embed", ret) + self.assertEqual(ret["embed"].shape, (4, 256)) + + # -- hyperparameters ------------------------------------------------------ + + def test_custom_reduction_ratio(self): + """SE reduction ratio is forwarded to every SEModule1d.""" + from pyhealth.models.se_resnet import SEModule1d + model = SEResNet50ECG(dataset=self.dataset, reduction=8) + for m in model.backbone.modules(): + if isinstance(m, SEModule1d): + # fc1 maps C → C//8 + in_ch = m.fc1.in_channels + out_ch = m.fc1.out_channels + self.assertEqual(out_ch, in_ch // 8) + + def test_custom_hyperparameters(self): + model = SEResNet50ECG( + dataset=self.dataset, + backbone_output_dim=128, + dropout=0.1, + reduction=8, + ) + batch = next(iter(get_dataloader(self.dataset, batch_size=2, shuffle=False))) + with torch.no_grad(): + ret = model(**batch) + self.assertIn("loss", ret) + self.assertEqual(ret["y_prob"].shape[1], _N_LABELS) + + # -- sliding window ------------------------------------------------------- + + def test_forward_sliding_window(self): + signal = torch.randn(2, _N_LEADS, 2500) + self.model.eval() + probs = self.model.forward_sliding_window(signal, window_size=_LENGTH) + self.assertEqual(probs.shape, (2, _N_LABELS)) + self.assertTrue(torch.all(probs >= 0)) + self.assertTrue(torch.all(probs <= 1)) + + def test_forward_sliding_window_short_signal(self): + signal = torch.randn(2, _N_LEADS, 500) + self.model.eval() + probs = self.model.forward_sliding_window(signal, window_size=_LENGTH) + self.assertEqual(probs.shape, (2, _N_LABELS)) + + +# --------------------------------------------------------------------------- +# Lambda-ResNet-18 +# --------------------------------------------------------------------------- + +class TestLambdaResNet18ECG(unittest.TestCase): + """Tests for LambdaResNet18ECG.""" + + def setUp(self): + rng = np.random.RandomState(6) + samples = _make_samples(4, rng, "multilabel") + self.dataset = _make_dataset(samples, "multilabel") + self.model = LambdaResNet18ECG(dataset=self.dataset) + self.batch = next(iter(get_dataloader(self.dataset, batch_size=4, shuffle=False))) + + # -- initialisation ------------------------------------------------------- + + def test_initialization(self): + self.assertIsInstance(self.model, LambdaResNet18ECG) + self.assertIn("signal", self.model.feature_keys) + self.assertIn("label", self.model.label_keys) + # backbone has 4 stages + self.assertIsNotNone(self.model.backbone.layer1) + self.assertIsNotNone(self.model.backbone.layer4) + + def test_lambda_layers_present(self): + """Every block in the backbone contains a LambdaConv1d.""" + from pyhealth.models.lambda_resnet import LambdaBottleneck1d, LambdaConv1d + for layer in [self.model.backbone.layer1, + self.model.backbone.layer2, + self.model.backbone.layer3, + self.model.backbone.layer4]: + for block in layer.children(): + self.assertIsInstance(block, LambdaBottleneck1d) + # The lambda layer is embedded in block.conv2 (an nn.Sequential) + has_lambda = any( + isinstance(m, LambdaConv1d) + for m in block.conv2.modules() + ) + self.assertTrue(has_lambda, + "LambdaConv1d not found inside LambdaBottleneck1d") + + def test_backbone_output_dim(self): + x = torch.randn(2, _N_LEADS, _LENGTH) + with torch.no_grad(): + out = self.model.backbone(x) + self.assertEqual(out.shape, (2, 256)) + + # -- forward -------------------------------------------------------------- + + def test_forward_multilabel(self): + with torch.no_grad(): + ret = self.model(**self.batch) + _assert_forward_output(self, ret, batch_size=4, n_classes=_N_LABELS) + self.assertTrue(torch.all(ret["y_prob"] >= 0)) + self.assertTrue(torch.all(ret["y_prob"] <= 1)) + + def test_forward_multiclass(self): + rng = np.random.RandomState(7) + n_classes = 3 + samples = _make_samples(4, rng, "multiclass") + for i, s in enumerate(samples): + s["label"] = i % n_classes + ds = _make_dataset(samples, "multiclass") + model = LambdaResNet18ECG(dataset=ds) + batch = next(iter(get_dataloader(ds, batch_size=4, shuffle=False))) + with torch.no_grad(): + ret = model(**batch) + _assert_forward_output(self, ret, batch_size=4, n_classes=n_classes) + self.assertTrue(torch.allclose(ret["y_prob"].sum(dim=1), + torch.ones(4), atol=1e-5)) + + def test_forward_binary(self): + rng = np.random.RandomState(8) + samples = _make_samples(4, rng, "binary") + for i, s in enumerate(samples): + s["label"] = i % 2 + ds = _make_dataset(samples, "binary") + model = LambdaResNet18ECG(dataset=ds) + batch = next(iter(get_dataloader(ds, batch_size=4, shuffle=False))) + with torch.no_grad(): + ret = model(**batch) + _assert_forward_output(self, ret, batch_size=4, n_classes=1) + + # -- backward ------------------------------------------------------------- + + def test_backward(self): + ret = self.model(**self.batch) + ret["loss"].backward() + has_grad = any( + p.requires_grad and p.grad is not None + for p in self.model.parameters() + ) + self.assertTrue(has_grad, "No parameters received gradients") + + def test_lambda_layer_gradients(self): + """Gradients flow back through LambdaConv1d (embedding parameter).""" + from pyhealth.models.lambda_resnet import LambdaConv1d + ret = self.model(**self.batch) + ret["loss"].backward() + lambda_layers = [ + m for m in self.model.backbone.modules() + if isinstance(m, LambdaConv1d) + ] + self.assertGreater(len(lambda_layers), 0) + for lm in lambda_layers: + self.assertIsNotNone(lm.embedding.grad, + "embedding parameter has no gradient") + + # -- embed ---------------------------------------------------------------- + + def test_embed_flag(self): + batch = dict(self.batch, embed=True) + with torch.no_grad(): + ret = self.model(**batch) + self.assertIn("embed", ret) + self.assertEqual(ret["embed"].shape, (4, 256)) + + # -- hyperparameters ------------------------------------------------------ + + def test_custom_hyperparameters(self): + model = LambdaResNet18ECG( + dataset=self.dataset, + backbone_output_dim=128, + dropout=0.1, + ) + batch = next(iter(get_dataloader(self.dataset, batch_size=2, shuffle=False))) + with torch.no_grad(): + ret = model(**batch) + self.assertIn("loss", ret) + self.assertEqual(ret["y_prob"].shape[1], _N_LABELS) + + # -- sliding window ------------------------------------------------------- + + def test_forward_sliding_window(self): + signal = torch.randn(2, _N_LEADS, 2500) + self.model.eval() + probs = self.model.forward_sliding_window(signal, window_size=_LENGTH) + self.assertEqual(probs.shape, (2, _N_LABELS)) + self.assertTrue(torch.all(probs >= 0)) + self.assertTrue(torch.all(probs <= 1)) + + def test_forward_sliding_window_short_signal(self): + signal = torch.randn(2, _N_LEADS, 500) + self.model.eval() + probs = self.model.forward_sliding_window(signal, window_size=_LENGTH) + self.assertEqual(probs.shape, (2, _N_LABELS)) + + def test_clamp_stability(self): + """Extreme input values are clamped and do not produce NaN/Inf.""" + # The backbone clamps input and inter-stage activations to [-20, 20]. + # Use batch size 4 to match self.batch["label"]. + signal = torch.full((4, _N_LEADS, _LENGTH), fill_value=1e6) + batch = { + "signal": signal, + "label": self.batch["label"], + } + with torch.no_grad(): + ret = self.model(**batch) + self.assertTrue(torch.isfinite(ret["loss"]), + "Loss is not finite for clamped extreme input") + self.assertTrue(torch.all(torch.isfinite(ret["y_prob"]))) + + +# --------------------------------------------------------------------------- +# Cross-model consistency tests +# --------------------------------------------------------------------------- + +class TestECGResNetConsistency(unittest.TestCase): + """Sanity checks that hold across all three model classes.""" + + def _make_model_and_batch(self, model_cls, rng_seed=42): + rng = np.random.RandomState(rng_seed) + samples = _make_samples(4, rng, "multilabel") + ds = _make_dataset(samples, "multilabel") + model = model_cls(dataset=ds) + batch = next(iter(get_dataloader(ds, batch_size=4, shuffle=False))) + return model, batch, ds + + def test_all_models_share_head_architecture(self): + """All three models use the same HeadModule architecture.""" + for cls in [ResNet18ECG, SEResNet50ECG, LambdaResNet18ECG]: + model, _, _ = self._make_model_and_batch(cls) + children = list(model.head.children()) + self.assertIsInstance(children[0], torch.nn.Linear, f"{cls.__name__}: head[0]") + self.assertIsInstance(children[1], torch.nn.ReLU, f"{cls.__name__}: head[1]") + self.assertIsInstance(children[2], torch.nn.BatchNorm1d, f"{cls.__name__}: head[2]") + self.assertIsInstance(children[3], torch.nn.Dropout, f"{cls.__name__}: head[3]") + self.assertIsInstance(children[4], torch.nn.Linear, f"{cls.__name__}: head[4]") + # hidden size is 128 (matching HeadModule in reference code) + self.assertEqual(children[0].out_features, 128, + f"{cls.__name__}: head hidden dim should be 128") + + def test_all_models_produce_finite_output(self): + """Forward pass produces finite loss and probabilities for all models.""" + for cls in [ResNet18ECG, SEResNet50ECG, LambdaResNet18ECG]: + model, batch, _ = self._make_model_and_batch(cls) + with torch.no_grad(): + ret = model(**batch) + self.assertTrue(torch.isfinite(ret["loss"]), + f"{cls.__name__} loss is not finite") + self.assertTrue(torch.all(torch.isfinite(ret["y_prob"])), + f"{cls.__name__} y_prob contains non-finite values") + + def test_all_models_eval_train_switch(self): + """train() / eval() mode switches do not break forward.""" + for cls in [ResNet18ECG, SEResNet50ECG, LambdaResNet18ECG]: + model, batch, _ = self._make_model_and_batch(cls) + model.train() + ret_train = model(**batch) + model.eval() + with torch.no_grad(): + ret_eval = model(**batch) + self.assertIn("loss", ret_train) + self.assertIn("loss", ret_eval) + + def test_all_models_sliding_window_consistent(self): + """forward_sliding_window output shape is consistent with forward.""" + signal = torch.randn(2, _N_LEADS, 2500) + for cls in [ResNet18ECG, SEResNet50ECG, LambdaResNet18ECG]: + model, batch, _ = self._make_model_and_batch(cls) + model.eval() + probs = model.forward_sliding_window(signal, window_size=_LENGTH) + self.assertEqual(probs.shape[0], 2, + f"{cls.__name__}: sliding window batch dim wrong") + self.assertEqual(probs.shape[1], _N_LABELS, + f"{cls.__name__}: sliding window class dim wrong") + + +if __name__ == "__main__": + unittest.main() From ce7a4b41cb08196d935a441ec7463dcb37644637 Mon Sep 17 00:00:00 2001 From: "Kent R. Spillner" Date: Sat, 18 Apr 2026 15:46:17 -0500 Subject: [PATCH 18/39] Add docs for ResNet models for ECG diagnosis Includes docs for ResNet18ECG, LambdaResNet18ECG, and SEResNet50ECG models. Assisted-by: Claude:Claude-4.6-Sonnet --- docs/api/models.rst | 3 ++ .../pyhealth.models.LambdaResNet18ECG.rst | 24 +++++++++++++++ .../models/pyhealth.models.ResNet18ECG.rst | 29 +++++++++++++++++++ .../models/pyhealth.models.SEResNet50ECG.rst | 19 ++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 docs/api/models/pyhealth.models.LambdaResNet18ECG.rst create mode 100644 docs/api/models/pyhealth.models.ResNet18ECG.rst create mode 100644 docs/api/models/pyhealth.models.SEResNet50ECG.rst diff --git a/docs/api/models.rst b/docs/api/models.rst index 7368dec94..5a29bed1e 100644 --- a/docs/api/models.rst +++ b/docs/api/models.rst @@ -187,6 +187,9 @@ API Reference models/pyhealth.models.EHRMamba models/pyhealth.models.JambaEHR models/pyhealth.models.ContraWR + models/pyhealth.models.LambdaResNet18ECG + models/pyhealth.models.ResNet18ECG + models/pyhealth.models.SEResNet50ECG models/pyhealth.models.SparcNet models/pyhealth.models.StageNet models/pyhealth.models.StageAttentionNet diff --git a/docs/api/models/pyhealth.models.LambdaResNet18ECG.rst b/docs/api/models/pyhealth.models.LambdaResNet18ECG.rst new file mode 100644 index 000000000..23714f18e --- /dev/null +++ b/docs/api/models/pyhealth.models.LambdaResNet18ECG.rst @@ -0,0 +1,24 @@ +pyhealth.models.LambdaResNet18ECG +=================================== + +1-D Lambda-ResNet-18 for ECG classification: Nonaka N. & Seita J., In-depth Benchmarking of Deep Neural Network Architectures for ECG Diagnosis. PMLR 149, 2021; lambda layer from Bello I., LambdaNetworks: Modeling Long-Range Interactions Without Attention. ICLR 2021. + +.. autoclass:: pyhealth.models.LambdaConv1d + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: pyhealth.models.LambdaBottleneck1d + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: pyhealth.models.LambdaResNet1d + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: pyhealth.models.LambdaResNet18ECG + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/api/models/pyhealth.models.ResNet18ECG.rst b/docs/api/models/pyhealth.models.ResNet18ECG.rst new file mode 100644 index 000000000..2a5d65e58 --- /dev/null +++ b/docs/api/models/pyhealth.models.ResNet18ECG.rst @@ -0,0 +1,29 @@ +pyhealth.models.ResNet18ECG +=================================== + +1-D ResNet-18 for ECG classification: Nonaka N. & Seita J., In-depth Benchmarking of Deep Neural Network Architectures for ECG Diagnosis. PMLR 149, 2021. + +.. autoclass:: pyhealth.models.BasicBlock1d + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: pyhealth.models.Bottleneck1d + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: pyhealth.models.ResNet1d + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: pyhealth.models.ECGBackboneModel + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: pyhealth.models.ResNet18ECG + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/api/models/pyhealth.models.SEResNet50ECG.rst b/docs/api/models/pyhealth.models.SEResNet50ECG.rst new file mode 100644 index 000000000..077da0dc0 --- /dev/null +++ b/docs/api/models/pyhealth.models.SEResNet50ECG.rst @@ -0,0 +1,19 @@ +pyhealth.models.SEResNet50ECG +=================================== + +1-D SE-ResNet-50 for ECG classification: Nonaka N. & Seita J., In-depth Benchmarking of Deep Neural Network Architectures for ECG Diagnosis. PMLR 149, 2021; SE block from Hu J. et al., Squeeze-and-Excitation Networks. CVPR 2018. + +.. autoclass:: pyhealth.models.SEModule1d + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: pyhealth.models.SEResNetBottleneck1d + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: pyhealth.models.SEResNet50ECG + :members: + :undoc-members: + :show-inheritance: From 096c3384d7326b15f80dfff5bd82e26aa7c54ae9 Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 19 Apr 2026 09:36:47 -0400 Subject: [PATCH 19/39] Speed up tests --- tests/core/test_ptbxl.py | 87 ++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 52 deletions(-) diff --git a/tests/core/test_ptbxl.py b/tests/core/test_ptbxl.py index 2e73d3845..1bedf6cfe 100644 --- a/tests/core/test_ptbxl.py +++ b/tests/core/test_ptbxl.py @@ -43,32 +43,35 @@ class TestPTBXLDataset(unittest.TestCase): ("HR00004", 45, "Female", "164889003", 10), # test ] - def setUp(self): + @classmethod + def setUp(cls): """Create a temporary directory with 4 synthetic .hea/.mat pairs and a matching ptbxl_database.csv""" - self.test_dir = tempfile.mkdtemp() - for record_id, age, sex, dx, _ in self.RECORDS: + cls.test_dir = tempfile.mkdtemp() + for record_id, age, sex, dx, _ in cls.RECORDS: write_hea_file( - Path(self.test_dir) / f"{record_id}.hea", + Path(cls.test_dir) / f"{record_id}.hea", record_id, age, sex, dx ) - write_mat_file(Path(self.test_dir) / f"{record_id}.mat") + write_mat_file(Path(cls.test_dir) / f"{record_id}.mat") write_database_csv( - Path(self.test_dir) / "ptbxl_database.csv", - self.RECORDS + Path(cls.test_dir) / "ptbxl_database.csv", + cls.RECORDS ) + + cls.dataset = PTBXLDataset(root=cls.test_dir) + cls.df = cls.dataset.load_data().compute().set_index("patient_id") - def tearDown(self): - shutil.rmtree(self.test_dir) + @classmethod + def tearDown(cls): + shutil.rmtree(cls.test_dir) def test_instantiation(self): """Test 1 - Dataset can be instantiated""" - dataset = PTBXLDataset(root=self.test_dir) - self.assertIsNotNone(dataset) + self.assertIsNotNone(self.dataset) def test_dataset_name_default(self): """Test 2 - Default dataset name is ptbxl""" - dataset = PTBXLDataset(root=self.test_dir) - self.assertEqual(dataset.dataset_name, "ptbxl") + self.assertEqual(self.dataset.dataset_name, "ptbxl") def test_dataset_name_custom(self): """Test 3 - Can set a custom dataset name""" @@ -77,9 +80,7 @@ def test_dataset_name_custom(self): def test_default_task_returns_task_instance(self): """Test 4 - default_task() returns a PTBXLMultilabelClassification instance""" - ds = PTBXLDataset.__new__(PTBXLDataset) - task = ds.default_task - self.assertIsInstance(task, PTBXLMultilabelClassification) + self.assertIsInstance(self.dataset.default_task, PTBXLMultilabelClassification) def test_classes_attribute(self): """Test 5 - The list of strings CLASSES exists and is not empty""" @@ -89,79 +90,61 @@ def test_classes_attribute(self): def test_load_data_returns_dask_dataframe(self): """Test 6 - load_data() returns a Dask DataFrame""" - dataset = PTBXLDataset(root=self.test_dir) - self.assertIsInstance(dataset.load_data(), dd.DataFrame) + self.assertIsInstance(self.dataset.load_data(), dd.DataFrame) def test_load_data_row_count(self): """Test 7 - load_data() returns one row per .hea file""" - df = PTBXLDataset(root=self.test_dir).load_data().compute() - self.assertEqual(len(df), len(self.RECORDS)) + self.assertEqual(len(self.df), len(self.RECORDS)) def test_load_data_required_columns(self): """Test 8 - load_data() output contains all required BaseDataset columns""" - df = PTBXLDataset(root=self.test_dir).load_data().compute() for col in ["patient_id", "event_type", "timestamp"]: - self.assertIn(col, df.columns, f"Missing required column: {col}") + self.assertIn(col, self.df.reset_index().columns, f"Missing required column: {col}") def test_load_data_attribute_columns(self): """Test 9 - load_data() output contains all ptbxl/ attribute columns""" - df = PTBXLDataset(root=self.test_dir).load_data().compute() for col in ["ptbxl/mat", "ptbxl/age", "ptbxl/sex", "ptbxl/dx_codes", "ptbxl/dx_abbreviations", "ptbxl/split"]: - self.assertIn(col, df.columns, f"Missing attribute column: {col}") + self.assertIn(col, self.df.columns, f"Missing attribute column: {col}") def test_load_data_event_type(self): """Test 10 - All rows have event_type == ptbxl""" - df = PTBXLDataset(root=self.test_dir).load_data().compute() - self.assertTrue((df["event_type"] == "ptbxl").all()) + self.assertTrue((self.df["event_type"] == "ptbxl").all()) def test_age_parsed_correctly(self): """Test 11 - Ages are parsed correctly from .hea files""" - df = PTBXLDataset(root=self.test_dir).load_data().compute() - df = df.set_index("patient_id") - self.assertEqual(df.loc["HR00001", "ptbxl/age"], 56) + self.assertEqual(self.df.loc["HR00001", "ptbxl/age"], 56) def test_sex_parsed_correctly(self): """Test 12 - Sex is parsed correctly from .hea files""" - df = PTBXLDataset(root=self.test_dir).load_data().compute() - df = df.set_index("patient_id") - self.assertEqual(df.loc["HR00001", "ptbxl/sex"], "Female") - self.assertEqual(df.loc["HR00003", "ptbxl/sex"], "Male") + self.assertEqual(self.df.loc["HR00001", "ptbxl/sex"], "Female") + self.assertEqual(self.df.loc["HR00003", "ptbxl/sex"], "Male") def test_dx_codes_parsed_correctly(self): """Test 13 - SNOMED CT codes are parsed correctly from .hea files.""" - df = PTBXLDataset(root=self.test_dir).load_data().compute() - df = df.set_index("patient_id") - self.assertEqual(df.loc["HR00001", "ptbxl/dx_codes"], "251146004,426783006") - self.assertEqual(df.loc["HR00003", "ptbxl/dx_codes"], "426783006") + self.assertEqual(self.df.loc["HR00001", "ptbxl/dx_codes"], "251146004,426783006") + self.assertEqual(self.df.loc["HR00003", "ptbxl/dx_codes"], "426783006") def test_dx_abbreviations_mapped_correctly(self): """Test 14 - SNOMED CT codes are mapped to correct abbreviations""" - df = PTBXLDataset(root=self.test_dir).load_data().compute() - df = df.set_index("patient_id") - self.assertIn("NSR", df.loc["HR00001", "ptbxl/dx_abbreviations"]) - self.assertIn("AF", df.loc["HR00004", "ptbxl/dx_abbreviations"]) + self.assertIn("NSR", self.df.loc["HR00001", "ptbxl/dx_abbreviations"]) + self.assertIn("AF", self.df.loc["HR00004", "ptbxl/dx_abbreviations"]) def test_mat_file_path_correct(self): """Test 15 - .mat file paths point to the correct location""" - df = PTBXLDataset(root=self.test_dir).load_data().compute() - df = df.set_index("patient_id") expected = str(Path(self.test_dir) / "HR00001.mat") - self.assertEqual(df.loc["HR00001", "ptbxl/mat"], expected) + self.assertEqual(self.df.loc["HR00001", "ptbxl/mat"], expected) def test_split_values(self): """Test 16 - Split column only contains train, val, or test""" - df = PTBXLDataset(root=self.test_dir).load_data().compute() - self.assertTrue(df["ptbxl/split"].isin(["train", "val", "test"]).all()) + self.assertTrue(self.df["ptbxl/split"].isin(["train", "val", "test"]).all()) def test_split_from_strat_fold(self): """Test 17 - Splits are correctly assigned from strat_fold values""" - df = PTBXLDataset(root=self.test_dir).load_data().compute() - df = df.set_index("patient_id") - self.assertEqual(df.loc["HR00001", "ptbxl/split"], "train") # fold 1 - self.assertEqual(df.loc["HR00002", "ptbxl/split"], "train") # fold 8 - self.assertEqual(df.loc["HR00003", "ptbxl/split"], "val") # fold 9 - self.assertEqual(df.loc["HR00004", "ptbxl/split"], "test") # fold 10 + self.assertEqual(self.df.loc["HR00001", "ptbxl/split"], "train") # fold 1 + self.assertEqual(self.df.loc["HR00002", "ptbxl/split"], "train") # fold 8 + self.assertEqual(self.df.loc["HR00003", "ptbxl/split"], "val") # fold 9 + self.assertEqual(self.df.loc["HR00004", "ptbxl/split"], "test") # fold 10 def test_unknown_snomed_code_skipped(self): """Test 18 - SNOMED codes not in mapping are skipped without error""" From 6edd09842bb9faf5d160fcd77c06da8eacd3421c Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 19 Apr 2026 09:40:28 -0400 Subject: [PATCH 20/39] Make the .mat file way smaller - should improve performance --- tests/core/test_ptbxl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/test_ptbxl.py b/tests/core/test_ptbxl.py index 1bedf6cfe..4a1e353ed 100644 --- a/tests/core/test_ptbxl.py +++ b/tests/core/test_ptbxl.py @@ -24,7 +24,7 @@ def write_hea_file(path, record_id, age, sex, dx): f.write("#Sx: Unknown\n") def write_mat_file(path): - scipy.io.savemat(str(path), {"val": np.random.randn(12, 5000)}) + scipy.io.savemat(str(path), {"val": np.random.randn(12, 1)}) def write_database_csv(path, records): pd.DataFrame({ From c76f1cfd3db8d8ef628252584792d87ed8b430ea Mon Sep 17 00:00:00 2001 From: AnuragD2 Date: Sun, 19 Apr 2026 02:12:02 -0700 Subject: [PATCH 21/39] Add BiLSTMECG model for 12-lead ECG classification --- pyhealth/models/__init__.py | 1 + pyhealth/models/bilstm_ecg.py | 192 ++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 pyhealth/models/bilstm_ecg.py diff --git a/pyhealth/models/__init__.py b/pyhealth/models/__init__.py index fbc561ec3..ef012bfd1 100644 --- a/pyhealth/models/__init__.py +++ b/pyhealth/models/__init__.py @@ -2,6 +2,7 @@ from .agent import Agent, AgentLayer from .base_model import BaseModel from .biot import BIOT +from .bilstm_ecg import BiLSTMECG from .cnn import CNN, CNNLayer from .concare import ConCare, ConCareLayer from .contrawr import ContraWR, ResBlock2D diff --git a/pyhealth/models/bilstm_ecg.py b/pyhealth/models/bilstm_ecg.py new file mode 100644 index 000000000..a501735af --- /dev/null +++ b/pyhealth/models/bilstm_ecg.py @@ -0,0 +1,192 @@ +"""Bidirectional LSTM for 12-lead ECG multi-label classification. + +This module provides :class:`BiLSTMECG`, a :class:`~pyhealth.models.BaseModel` +subclass implementing the Bidirectional LSTM architecture benchmarked in: + + Nonaka, N. & Seita, J. (2021). *In-depth Benchmarking of Deep Neural + Network Architectures for ECG Diagnosis.* Proceedings of Machine Learning + Research 126:1–19, MLHC 2021. + +The paper's best-performing LSTM variant (``lstm_d1_h64``) uses a **single +bidirectional LSTM layer** with ``hidden_size=64``, producing 128-dimensional +hidden states that are projected to the output head. This implementation +follows the same design but exposes ``hidden_size`` and ``n_layers`` as +constructor arguments so it can be used as a drop-in replacement across the +full ablation grid. + +Mathematical framing +-------------------- +Given an ECG tensor :math:`X \\in \\mathbb{R}^{B \\times C \\times T}` (batch +size :math:`B`, :math:`C=12` leads, :math:`T` time-steps), the model: + +1. Permutes to :math:`(B, T, C)` for sequence-first processing. +2. Passes through a bidirectional LSTM: + + .. math:: + + h_t = \\text{BiLSTM}(x_t, h_{t-1})\\quad h_t \\in \\mathbb{R}^{B \\times 2H} + +3. Takes the **last** time-step output :math:`h_T \\in \\mathbb{R}^{B \\times 2H}`. +4. Projects with a linear head :math:`W \\in \\mathbb{R}^{2H \\times K}` to produce + logits for :math:`K` classes. +5. Optimises with **binary cross-entropy with logits** (multi-label): + + .. math:: + + \\mathcal{L} = -\\frac{1}{K}\\sum_{k=1}^{K} + \\bigl[y_k \\log \\sigma(\\hat{y}_k) + + (1-y_k)\\log(1-\\sigma(\\hat{y}_k))\\bigr] + +Paper alignment +--------------- ++------------+----------------------------+---------------------------+ +| Paper name | Paper setting | Default in this class | ++============+============================+===========================+ +| lstm_d1_h64| 1 layer, hidden=64 | n_layers=1, hidden_size=64| ++------------+----------------------------+---------------------------+ +| lstm_d3_h128| 3 layers, hidden=128 | n_layers=3, hidden_size=128| ++------------+----------------------------+---------------------------+ + +Signal format expected +---------------------- +``feature_keys=["signal"]`` → each batch element is a ``np.ndarray`` of shape +``(12, T)`` loaded by ``SampleSignalDataset`` from a ``.pkl`` file. :math:`T` +is typically 1000 at 100 Hz or 5000 at 500 Hz. + +Author: + CS-598 DLH Project Team — PyHealth contribution +""" + +from typing import List, Optional + +import numpy as np +import torch +import torch.nn as nn + +from pyhealth.models import BaseModel + + +class BiLSTMECG(BaseModel): + """Bidirectional LSTM ECG classifier. + + Extends :class:`~pyhealth.models.BaseModel` so it integrates seamlessly + with :class:`~pyhealth.trainer.Trainer`, :class:`~pyhealth.datasets.PTBXLDataset`, + and :func:`~pyhealth.metrics.multilabel_metrics_fn`. + + Args: + dataset: A PyHealth ``SampleSignalDataset`` (or ``Subset``) that + exposes ``input_info`` with ``"signal"`` → ``{"n_channels": 12}``. + feature_keys (List[str]): Must be ``["signal"]``. + label_key (str): Key in the sample dict that holds the label list. + Use ``"labels"`` to match ``PTBXLMultilabelClassification`` output. + mode (str): ``"multilabel"`` applies ``BCEWithLogitsLoss``; other modes + are passed through to :class:`~pyhealth.models.BaseModel`. + hidden_size (int): LSTM hidden dimension *per direction*. The + bidirectional output is ``2 × hidden_size``. Paper best variant + uses ``hidden_size=64`` (1 layer). Defaults to ``64``. + n_layers (int): Number of stacked LSTM layers. Paper uses ``1``. + Defaults to ``1``. + dropout (float): Dropout probability applied between LSTM layers + when ``n_layers > 1``. Defaults to ``0.2``. + + Examples: + Paper-aligned variant (lstm_d1_h64):: + + >>> from pyhealth.models import BiLSTMECG + >>> model = BiLSTMECG( + ... dataset=sample_dataset, + ... feature_keys=["signal"], + ... label_key="labels", + ... mode="multilabel", + ... hidden_size=64, + ... n_layers=1, + ... ) + + Deeper variant used in ablation grid:: + + >>> model_deep = BiLSTMECG( + ... dataset=sample_dataset, + ... feature_keys=["signal"], + ... label_key="labels", + ... mode="multilabel", + ... hidden_size=128, + ... n_layers=3, + ... ) + """ + + def __init__( + self, + dataset, + feature_keys: List[str], + label_key: str, + mode: str, + hidden_size: int = 64, + n_layers: int = 1, + dropout: float = 0.2, + **kwargs, + ): + super().__init__( + dataset=dataset, + feature_keys=feature_keys, + label_key=label_key, + mode=mode, + ) + + sig_info = self.dataset.input_info["signal"] + in_channels: int = sig_info["n_channels"] # 12 for standard 12-lead ECG + + self.label_tokenizer = self.get_label_tokenizer() + output_size: int = self.get_output_size(self.label_tokenizer) + + # ── Bidirectional LSTM ──────────────────────────────────────────────── + # Input: (B, T, C) after permute + # Output: (B, T, hidden_size * 2) — bidirectional concatenation + self.lstm = nn.LSTM( + input_size=in_channels, + hidden_size=hidden_size, + num_layers=n_layers, + bidirectional=True, + batch_first=True, + dropout=dropout if n_layers > 1 else 0.0, + ) + + # ── Projection head ─────────────────────────────────────────────────── + # Pool over all time-steps: AdaptiveAvgPool1d(1) → (B, hidden_size*2) + self.pool = nn.AdaptiveAvgPool1d(1) + self.fc = nn.Linear(hidden_size * 2, output_size) + + # ------------------------------------------------------------------ + def forward(self, **kwargs) -> dict: # type: ignore[override] + """Forward pass. + + Keyword args are the collated batch dict from the DataLoader. + The key ``self.feature_keys[0]`` (``"signal"``) holds a list of + ``np.ndarray`` of shape ``(12, T)``. + + Returns: + dict with keys ``"loss"``, ``"y_prob"``, ``"y_true"``, + ``"logit"`` — the standard PyHealth model output contract. + """ + # Stack list[(12,T)] → tensor (B, 12, T) + x = torch.tensor( + np.array(kwargs[self.feature_keys[0]]), + device=self.device, + ).float() + + # (B, 12, T) → (B, T, 12) for sequence-first LSTM with batch_first=True + out, _ = self.lstm(x.permute(0, 2, 1)) # (B, T, hidden*2) + # (B, T, hidden*2) → (B, hidden*2, T) → pool → (B, hidden*2) + # Matches paper bi_lstm.py: AdaptiveAvgPool1d over ALL timesteps + pooled = self.pool(out.permute(0, 2, 1)).squeeze(-1) # (B, hidden*2) + logits = self.fc(pooled) # (B, K) + + y_true = self.prepare_labels(kwargs[self.label_key], self.label_tokenizer) + loss = self.get_loss_function()(logits, y_true) + y_prob = self.prepare_y_prob(logits) + + return { + "loss": loss, + "y_prob": y_prob, + "y_true": y_true, + "logit": logits, + } From 8e72652c609b006775be43ae5f1c98e7a1ed7c82 Mon Sep 17 00:00:00 2001 From: AnuragD2 Date: Sun, 19 Apr 2026 02:20:29 -0700 Subject: [PATCH 22/39] docs: add Sphinx .rst entry for BiLSTMECG --- docs/api/models.rst | 1 + docs/api/models/pyhealth.models.BiLSTMECG.rst | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 docs/api/models/pyhealth.models.BiLSTMECG.rst diff --git a/docs/api/models.rst b/docs/api/models.rst index 5a29bed1e..79854c027 100644 --- a/docs/api/models.rst +++ b/docs/api/models.rst @@ -191,6 +191,7 @@ API Reference models/pyhealth.models.ResNet18ECG models/pyhealth.models.SEResNet50ECG models/pyhealth.models.SparcNet + models/pyhealth.models.BiLSTMECG models/pyhealth.models.StageNet models/pyhealth.models.StageAttentionNet models/pyhealth.models.AdaCare diff --git a/docs/api/models/pyhealth.models.BiLSTMECG.rst b/docs/api/models/pyhealth.models.BiLSTMECG.rst new file mode 100644 index 000000000..92acca58b --- /dev/null +++ b/docs/api/models/pyhealth.models.BiLSTMECG.rst @@ -0,0 +1,9 @@ +pyhealth.models.BiLSTMECG +=================================== + +Bidirectional LSTM for 12-lead ECG multi-label classification. + +.. autoclass:: pyhealth.models.BiLSTMECG + :members: + :undoc-members: + :show-inheritance: From bcb983405a35e2f03c56fb69b217f06a85a33c64 Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 19 Apr 2026 11:15:57 -0400 Subject: [PATCH 23/39] Update to the task to point to the right PTBXLDataset attributes --- .../tasks/ptbxl_multilabel_classification.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pyhealth/tasks/ptbxl_multilabel_classification.py b/pyhealth/tasks/ptbxl_multilabel_classification.py index f95c69c83..0c52d98b3 100644 --- a/pyhealth/tasks/ptbxl_multilabel_classification.py +++ b/pyhealth/tasks/ptbxl_multilabel_classification.py @@ -297,7 +297,7 @@ def __call__(self, patient: Patient) -> List[Dict]: Args: patient: A :class:`~pyhealth.data.Patient` object whose events have ``event_type="ptbxl"`` and carry attributes - ``signal_file``, ``scp_codes``, ``age``, and ``sex``. + ``mat_file``, ``scp_codes``, ``age``, and ``sex``. Returns: A list with at most one sample dict @@ -312,24 +312,24 @@ def __call__(self, patient: Patient) -> List[Dict]: for event in events: # ---- 1. Load the .mat signal -------------------------------- - signal_file = getattr(event, "signal_file", None) - if not signal_file: - logger.debug("Skip %s: no signal_file attribute.", event) + mat_file = getattr(event, "ptbxl/mat", None) + if not mat_file: + logger.debug("Skip %s: no *.mat file", event) continue try: from scipy.io import loadmat as _loadmat - mat = _loadmat(signal_file) + mat = _loadmat(mat_file) signal = mat["val"].astype(np.float32) # (12, 5000) @ 500 Hz except Exception as exc: - logger.warning("Cannot load signal from %s: %s", signal_file, exc) + logger.warning("Cannot load signal from %s: %s", mat_file, exc) continue if signal.ndim != 2 or signal.shape[0] != 12: logger.warning( "Unexpected signal shape %s in %s; skipping.", signal.shape, - signal_file, + mat_file, ) continue @@ -340,8 +340,8 @@ def __call__(self, patient: Patient) -> List[Dict]: signal = signal[:, ::5] # shape (12, 1000) # ---- 3. Parse SNOMED-CT codes -------------------------------- - raw_codes: str = str(getattr(event, "scp_codes", "") or "") - codes = [c.strip() for c in raw_codes.split(",") if c.strip()] + dx_codes: str = str(getattr(event, "ptbxl/dx_codes", "") or "") + codes = [c.strip() for c in dx_codes.split(",") if c.strip()] # ---- 4. Map to chosen label space --------------------------- if self.label_type == "superdiagnostic": From a1c29b57ba266e264fd16669e5af186e4f0a41d2 Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 19 Apr 2026 12:19:05 -0400 Subject: [PATCH 24/39] Add TestPTBXLMultilabelClassification to tests --- tests/core/test_ptbxl.py | 91 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 3 deletions(-) diff --git a/tests/core/test_ptbxl.py b/tests/core/test_ptbxl.py index 4a1e353ed..9c4f6f259 100644 --- a/tests/core/test_ptbxl.py +++ b/tests/core/test_ptbxl.py @@ -32,6 +32,29 @@ def write_database_csv(path, records): "strat_fold": [r[4] for r in records], }).to_csv(path, index=False) +@dataclass +class _DummyEvent: + """Event stub for task unit tests""" + signal_file: str + label: str + + # Override __getattr__ with the dummy data + def __getattr__(self, name): + if name == "ptbxl/mat": + return self.mat + if name == "ptbxl/dx_codes": + return self.dx_codes + raise AttributeError(name) + +class _DummyPatient: + """Patient stub for task unit tests""" + def __init__(self, patient_id: str, events: List[_DummyEvent]): + self.patient_id = patient_id + self._events = events + + def get_events(self, event_type=None) -> List[_DummyEvent]: + return self._events + class TestPTBXLDataset(unittest.TestCase): """Test PTBXLDataset with synthetic test data""" @@ -65,7 +88,7 @@ def setUp(cls): def tearDown(cls): shutil.rmtree(cls.test_dir) - def test_instantiation(self): + def test_dataset_instantiation(self): """Test 1 - Dataset can be instantiated""" self.assertIsNotNone(self.dataset) @@ -79,8 +102,13 @@ def test_dataset_name_custom(self): self.assertEqual(dataset.dataset_name, "my_ptbxl") def test_default_task_returns_task_instance(self): - """Test 4 - default_task() returns a PTBXLMultilabelClassification instance""" - self.assertIsInstance(self.dataset.default_task, PTBXLMultilabelClassification) + """Test 4 - default_task() returns a PTBXLMultilabelClassification instance and has correct schema""" + task = self.dataset.default_task + self.assertIsInstance(task, PTBXLMultilabelClassification) + self.assertEqual(task.input_schema, {"signal": "tensor"}) + self.assertEqual(task.output_schema, {"labels": "multilabel"}) + self.assertEqual(task.sampling_rate, 100) + self.assertEqual(task.label_type, "superdiagnostic") def test_classes_attribute(self): """Test 5 - The list of strings CLASSES exists and is not empty""" @@ -203,5 +231,62 @@ def test_missing_csv_raises_error(self): finally: shutil.rmtree(no_csv_dir) +class TestPTBXLMultilabelClassification(unittest.TestCase): + """Test task PTBXLMultilabelClassification with synthetic test data""" + + def test_label_type_diagnostic(self): + """Test 22 - Test creating a new task with label_type of diagnostic""" + task = PTBXLMultilabelClassification(label_type="diagnostic", sampling_rate=500) + self.assertIn("diagnostic", task.task_name.lower()) + self.assertEqual(task.sampling_rate, 500) + self.assertEqual(task.label_type, "diagnostic") + + def test_invalid_sampling_rate_raises_error(self): + """Test 23 - Test that a unhandled sampling_rate raises a ValueError""" + with self.assertRaises(ValueError): + PTBXLMultilabelClassification(sampling_rate=99) + + def test_invalid_sampling_rate_raises_error(self): + """Test 24 - Test that a unhandled label_type raises a ValueError""" + with self.assertRaises(ValueError): + PTBXLMultilabelClassification(label_type="diag") + + def test_superdiagnostic_abbreviations_mapped_correctly(self): + """Test 25 - Test that a valid superdiagnostic abbreviation returns a valid sample""" + task = PTBXLMultilabelClassification(label_type="superdiagnostic", sampling_rate=500) + patient = _DummyPatient("HR00001", [_DummyEvent("test.mat", "164890007")]) + with patch("scipy.io.loadmat", return_value={"val": self.SIGNAL}): + samples = task(patient) + self.assertEqual(len(samples), 1) + self.assertIn("signal", samples[0]) + self.assertIn("labels", samples[0]) + self.assertEqual(samples[0]["signal"].shape, (12, 5000)) + self.assertIn("CD", samples[0]["labels"]) + + def test_signal_decimation(self): + """Test 26 - Test that a sampling rate of 100Hz shrinks the signal shape""" + task = PTBXLMultilabelClassification(sampling_rate=100) + patient = _DummyPatient("HR00001", [_DummyEvent("test.mat", "164890007")]) + with patch("scipy.io.loadmat", return_value={"val": self.SIGNAL}): + samples = task(patient) + self.assertEqual(samples[0]["signal"].shape, (12, 1000)) + + def test_unknown_code_produces_no_samples(self): + """Test 28 - Test records with no mappable SNOMED codes produce no samples""" + task = PTBXLMultilabelClassification() + patient = _DummyPatient("HR00001", [_DummyEvent("test.mat", "999999999")]) + with patch("scipy.io.loadmat", return_value={"val": self.SIGNAL}): + samples = task(patient) + self.assertEqual(len(samples), 0) + + def test_diagnostic_returns_snomed_codes(self): + """Test 29 - Test diagnostic label_type returns SNOMED codes not superclass names""" + task = PTBXLMultilabelClassification(label_type="diagnostic", sampling_rate=500) + patient = _DummyPatient("HR00001", [_DummyEvent("test.mat", "270492004")]) + with patch("scipy.io.loadmat", return_value={"val": self.SIGNAL}): + samples = task(patient) + self.assertEqual(len(samples), 1) + self.assertIn("270492004", samples[0]["labels"]) + if __name__ == "__main__": unittest.main() \ No newline at end of file From 9a4a37184259918a255b91eea3caf26d94836749 Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 19 Apr 2026 12:28:00 -0400 Subject: [PATCH 25/39] Fix minor issues --- tests/core/test_ptbxl.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/tests/core/test_ptbxl.py b/tests/core/test_ptbxl.py index 9c4f6f259..705d3aa46 100644 --- a/tests/core/test_ptbxl.py +++ b/tests/core/test_ptbxl.py @@ -2,6 +2,9 @@ import shutil import unittest from pathlib import Path +from typing import List +from dataclasses import dataclass +from unittest.mock import patch import dask.dataframe as dd import numpy as np @@ -35,8 +38,8 @@ def write_database_csv(path, records): @dataclass class _DummyEvent: """Event stub for task unit tests""" - signal_file: str - label: str + mat: str + dx_codes: str # Override __getattr__ with the dummy data def __getattr__(self, name): @@ -58,7 +61,7 @@ def get_events(self, event_type=None) -> List[_DummyEvent]: class TestPTBXLDataset(unittest.TestCase): """Test PTBXLDataset with synthetic test data""" - # Create records with (record_id, age, sex, dx_codes, strat_fold); at minimum need 1 and 8 for + # Create records with (record_id, age, sex, dx_codes, strat_fold); at minimum need 1, 8, 9, 10 RECORDS = [ ("HR00001", 56, "Female", "251146004,426783006", 1), # train ("HR00002", 37, "Female", "426783006", 8), # train @@ -67,7 +70,7 @@ class TestPTBXLDataset(unittest.TestCase): ] @classmethod - def setUp(cls): + def setUpClass(cls): """Create a temporary directory with 4 synthetic .hea/.mat pairs and a matching ptbxl_database.csv""" cls.test_dir = tempfile.mkdtemp() for record_id, age, sex, dx, _ in cls.RECORDS: @@ -85,7 +88,7 @@ def setUp(cls): cls.df = cls.dataset.load_data().compute().set_index("patient_id") @classmethod - def tearDown(cls): + def tearDownClass(cls): shutil.rmtree(cls.test_dir) def test_dataset_instantiation(self): @@ -234,6 +237,8 @@ def test_missing_csv_raises_error(self): class TestPTBXLMultilabelClassification(unittest.TestCase): """Test task PTBXLMultilabelClassification with synthetic test data""" + SIGNAL = np.random.randn(12, 5000).astype(np.float32) + def test_label_type_diagnostic(self): """Test 22 - Test creating a new task with label_type of diagnostic""" task = PTBXLMultilabelClassification(label_type="diagnostic", sampling_rate=500) @@ -246,7 +251,7 @@ def test_invalid_sampling_rate_raises_error(self): with self.assertRaises(ValueError): PTBXLMultilabelClassification(sampling_rate=99) - def test_invalid_sampling_rate_raises_error(self): + def test_invalid_label_type_raises_error(self): """Test 24 - Test that a unhandled label_type raises a ValueError""" with self.assertRaises(ValueError): PTBXLMultilabelClassification(label_type="diag") @@ -255,7 +260,7 @@ def test_superdiagnostic_abbreviations_mapped_correctly(self): """Test 25 - Test that a valid superdiagnostic abbreviation returns a valid sample""" task = PTBXLMultilabelClassification(label_type="superdiagnostic", sampling_rate=500) patient = _DummyPatient("HR00001", [_DummyEvent("test.mat", "164890007")]) - with patch("scipy.io.loadmat", return_value={"val": self.SIGNAL}): + with patch("yhealth.tasks.ptbxl_multilabel_classification._loadmat", return_value={"val": self.SIGNAL}): samples = task(patient) self.assertEqual(len(samples), 1) self.assertIn("signal", samples[0]) @@ -267,23 +272,23 @@ def test_signal_decimation(self): """Test 26 - Test that a sampling rate of 100Hz shrinks the signal shape""" task = PTBXLMultilabelClassification(sampling_rate=100) patient = _DummyPatient("HR00001", [_DummyEvent("test.mat", "164890007")]) - with patch("scipy.io.loadmat", return_value={"val": self.SIGNAL}): + with patch("pyhealth.tasks.ptbxl_multilabel_classification._loadmat", return_value={"val": self.SIGNAL}): samples = task(patient) self.assertEqual(samples[0]["signal"].shape, (12, 1000)) def test_unknown_code_produces_no_samples(self): - """Test 28 - Test records with no mappable SNOMED codes produce no samples""" + """Test 27 - Test records with no mappable SNOMED codes produce no samples""" task = PTBXLMultilabelClassification() patient = _DummyPatient("HR00001", [_DummyEvent("test.mat", "999999999")]) - with patch("scipy.io.loadmat", return_value={"val": self.SIGNAL}): + with patch("pyhealth.tasks.ptbxl_multilabel_classification._loadmat", return_value={"val": self.SIGNAL}): samples = task(patient) self.assertEqual(len(samples), 0) def test_diagnostic_returns_snomed_codes(self): - """Test 29 - Test diagnostic label_type returns SNOMED codes not superclass names""" + """Test 28 - Test diagnostic label_type returns SNOMED codes not superclass names""" task = PTBXLMultilabelClassification(label_type="diagnostic", sampling_rate=500) patient = _DummyPatient("HR00001", [_DummyEvent("test.mat", "270492004")]) - with patch("scipy.io.loadmat", return_value={"val": self.SIGNAL}): + with patch("pyhealth.tasks.ptbxl_multilabel_classification._loadmat", return_value={"val": self.SIGNAL}): samples = task(patient) self.assertEqual(len(samples), 1) self.assertIn("270492004", samples[0]["labels"]) From b810c3052304c205b1ff5108ed7ab78e9a401baf Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 19 Apr 2026 12:46:38 -0400 Subject: [PATCH 26/39] Resolve scipy issue and resolve inadvertent merge conflict --- tests/core/test_ptbxl.py | 84 ++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/tests/core/test_ptbxl.py b/tests/core/test_ptbxl.py index 705d3aa46..64bd00b09 100644 --- a/tests/core/test_ptbxl.py +++ b/tests/core/test_ptbxl.py @@ -2,9 +2,8 @@ import shutil import unittest from pathlib import Path -from typing import List from dataclasses import dataclass -from unittest.mock import patch +from typing import List import dask.dataframe as dd import numpy as np @@ -61,7 +60,7 @@ def get_events(self, event_type=None) -> List[_DummyEvent]: class TestPTBXLDataset(unittest.TestCase): """Test PTBXLDataset with synthetic test data""" - # Create records with (record_id, age, sex, dx_codes, strat_fold); at minimum need 1, 8, 9, 10 + # Create records with (record_id, age, sex, dx_codes, strat_fold); at minimum need 1, 8, 9, 10 RECORDS = [ ("HR00001", 56, "Female", "251146004,426783006", 1), # train ("HR00002", 37, "Female", "426783006", 8), # train @@ -179,35 +178,31 @@ def test_split_from_strat_fold(self): def test_unknown_snomed_code_skipped(self): """Test 18 - SNOMED codes not in mapping are skipped without error""" - write_hea_file( - Path(self.test_dir) / "HR00099.hea", - "HR00099", 30, "Male", "999999999,426783006" - ) - write_mat_file(Path(self.test_dir) / "HR00099.mat") - pd.DataFrame({ - "ecg_id": [int(r[0].replace("HR", "")) for r in self.RECORDS] + [99], - "strat_fold": [r[4] for r in self.RECORDS] + [1], - }).to_csv(Path(self.test_dir) / "ptbxl_database.csv", index=False) - - df = PTBXLDataset(root=self.test_dir).load_data().compute() - df = df.set_index("patient_id") - self.assertEqual(df.loc["HR00099", "ptbxl/dx_abbreviations"], "NSR") + test_dir = tempfile.mkdtemp() + try: + records = self.RECORDS + [("HR00099", 30, "Male", "999999999,426783006", 1)] + for record_id, age, sex, dx, _ in records: + write_hea_file(Path(test_dir) / f"{record_id}.hea", record_id, age, sex, dx) + write_mat_file(Path(test_dir) / f"{record_id}.mat") + write_database_csv(Path(test_dir) / "ptbxl_database.csv", records) + df = PTBXLDataset(root=test_dir).load_data().compute().set_index("patient_id") + self.assertEqual(df.loc["HR00099", "ptbxl/dx_abbreviations"], "NSR") + finally: + shutil.rmtree(test_dir) def test_invalid_age_handled(self): """Test 19 - Non-integer age values result in None without error""" - write_hea_file( - Path(self.test_dir) / "HR00098.hea", - "HR00098", "NaN", "Female", "426783006" - ) - write_mat_file(Path(self.test_dir) / "HR00098.mat") - pd.DataFrame({ - "ecg_id": [int(r[0].replace("HR", "")) for r in self.RECORDS] + [98], - "strat_fold": [r[4] for r in self.RECORDS] + [1], - }).to_csv(Path(self.test_dir) / "ptbxl_database.csv", index=False) - - df = PTBXLDataset(root=self.test_dir).load_data().compute() - df = df.set_index("patient_id") - self.assertTrue(pd.isna(df.loc["HR00098", "ptbxl/age"])) + test_dir = tempfile.mkdtemp() + try: + records = self.RECORDS + [("HR00098", "NaN", "Male", "426783006", 1)] + for record_id, age, sex, dx, _ in records: + write_hea_file(Path(test_dir) / f"{record_id}.hea", record_id, age, sex, dx) + write_mat_file(Path(test_dir) / f"{record_id}.mat") + write_database_csv(Path(test_dir) / "ptbxl_database.csv", records) + df = PTBXLDataset(root=test_dir).load_data().compute().set_index("patient_id") + self.assertTrue(pd.isna(df.loc["HR00098", "ptbxl/age"])) + finally: + shutil.rmtree(test_dir) def test_no_hea_files_raises_error(self): """Test 20 - FileNotFoundError raised if no .hea files found""" @@ -237,7 +232,16 @@ def test_missing_csv_raises_error(self): class TestPTBXLMultilabelClassification(unittest.TestCase): """Test task PTBXLMultilabelClassification with synthetic test data""" - SIGNAL = np.random.randn(12, 5000).astype(np.float32) + @classmethod + def setUpClass(cls): + """Create a temporary directory with one test .mat file""" + cls.test_dir = tempfile.mkdtemp() + cls.mat_path = str(Path(cls.test_dir) / "test.mat") + scipy.io.savemat(cls.mat_path, {"val": np.random.randn(12, 5000).astype(np.float32)}) + + @classmethod + def tearDownClass(cls): + shutil.rmtree(cls.test_dir) def test_label_type_diagnostic(self): """Test 22 - Test creating a new task with label_type of diagnostic""" @@ -259,9 +263,8 @@ def test_invalid_label_type_raises_error(self): def test_superdiagnostic_abbreviations_mapped_correctly(self): """Test 25 - Test that a valid superdiagnostic abbreviation returns a valid sample""" task = PTBXLMultilabelClassification(label_type="superdiagnostic", sampling_rate=500) - patient = _DummyPatient("HR00001", [_DummyEvent("test.mat", "164890007")]) - with patch("yhealth.tasks.ptbxl_multilabel_classification._loadmat", return_value={"val": self.SIGNAL}): - samples = task(patient) + patient = _DummyPatient("HR00001", [_DummyEvent(self.mat_path, "164890007")]) + samples = task(patient) self.assertEqual(len(samples), 1) self.assertIn("signal", samples[0]) self.assertIn("labels", samples[0]) @@ -271,25 +274,22 @@ def test_superdiagnostic_abbreviations_mapped_correctly(self): def test_signal_decimation(self): """Test 26 - Test that a sampling rate of 100Hz shrinks the signal shape""" task = PTBXLMultilabelClassification(sampling_rate=100) - patient = _DummyPatient("HR00001", [_DummyEvent("test.mat", "164890007")]) - with patch("pyhealth.tasks.ptbxl_multilabel_classification._loadmat", return_value={"val": self.SIGNAL}): - samples = task(patient) + patient = _DummyPatient("HR00001", [_DummyEvent(self.mat_path, "164890007")]) + samples = task(patient) self.assertEqual(samples[0]["signal"].shape, (12, 1000)) def test_unknown_code_produces_no_samples(self): """Test 27 - Test records with no mappable SNOMED codes produce no samples""" task = PTBXLMultilabelClassification() - patient = _DummyPatient("HR00001", [_DummyEvent("test.mat", "999999999")]) - with patch("pyhealth.tasks.ptbxl_multilabel_classification._loadmat", return_value={"val": self.SIGNAL}): - samples = task(patient) + patient = _DummyPatient("HR00001", [_DummyEvent(self.mat_path, "999999999")]) + samples = task(patient) self.assertEqual(len(samples), 0) def test_diagnostic_returns_snomed_codes(self): """Test 28 - Test diagnostic label_type returns SNOMED codes not superclass names""" task = PTBXLMultilabelClassification(label_type="diagnostic", sampling_rate=500) - patient = _DummyPatient("HR00001", [_DummyEvent("test.mat", "270492004")]) - with patch("pyhealth.tasks.ptbxl_multilabel_classification._loadmat", return_value={"val": self.SIGNAL}): - samples = task(patient) + patient = _DummyPatient("HR00001", [_DummyEvent(self.mat_path, "270492004")]) + samples = task(patient) self.assertEqual(len(samples), 1) self.assertIn("270492004", samples[0]["labels"]) From 8d9561055f855e26a9d3d42a6bebc4f93a83825d Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 19 Apr 2026 16:53:22 -0400 Subject: [PATCH 27/39] Adding new comments on PTBXL. --- pyhealth/datasets/ptbxl.py | 57 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/pyhealth/datasets/ptbxl.py b/pyhealth/datasets/ptbxl.py index e87ab01bb..32f41abd0 100644 --- a/pyhealth/datasets/ptbxl.py +++ b/pyhealth/datasets/ptbxl.py @@ -1,3 +1,24 @@ +""" +PyHealth dataset for the PTBXL dataset. + +Data links: + .hea / .mat files: https://www.kaggle.com/datasets/physionet/ptbxl-electrocardiography-database + .csv: https://physionet.org/content/ptb-xl/1.0.1/ptbxl_database.csv + Note that to run this properly the .csv needs to be in the same folder as the dataset + +Dataset paper: + Wagner, P., Strodthoff, N., Bousseljot, R., Samek, W., and Schaeffter, T. (2020) + 'PTB-XL, a large publicly available electrocardiography dataset' (version 1.0.1), PhysioNet. + RRID:SCR_007345. Available at: https://doi.org/10.13026/x4td-x982 + +Dataset paper link: + https://physionet.org/content/ptb-xl/1.0.1/ + +Authors: + Anurag Dixit - anuragd2@illinois.edu + Kent Spillner - kspillne@illinois.edu + John Wells - jtwells2@illinois.edu +""" import logging import pandas as pd import dask.dataframe as dd @@ -10,7 +31,6 @@ logger = logging.getLogger(__name__) """Full list of possible diagnoses for the PTB-XL dataset provided here: https://github.com/physionetchallenges/physionetchallenges.github.io/blob/master/2020/Dx_map.csv - Not all codes are present in the data but they are included for completeness, as referenced in the Data Description section here: https://physionet.org/content/challenge-2020/1.0.2/ """ SNOMED_CT_ABBREVIATION = { @@ -133,6 +153,25 @@ class PTBXLDataset(BaseDataset): PTB-XL is a publically available electrocardiography dataset. Contains 21837 samples from 18885 patients, all approximately 10 seconds in duration. Dataset is available here: https://www.kaggle.com/datasets/physionet/ptbxl-electrocardiography-database + File with train / test splits available here: https://physionet.org/content/ptb-xl/1.0.1/ptbxl_database.csv + + Files in the dataset are in the format HR00001.mat / HR00001.hea. The .hea files contain patient data including age, sex, and diagnosis codes. + The .mat files contain the ECG signal data of shape (12, 5000), mapping to 10 seconds of data sampled at 500Hz for the 12 ECG leads. + The associated .csv file must be in the same directory as the .hea / .mat files. + + Args: + root (str): Root directory of the raw data (.mat files, .hea files, .csv file). + dataset_name (str): Name of the dataset, PTBXL by default. + + Attributes: + root (str): Root directory of the raw data (.mat files, .hea files, .csv file). + dataset_name (str): Name of the dataset, PTBXL by default. + tables (List[str]): Name of the data table(s), PTBXL by default. + CLASSES (List[str]): Constant list of available diagnoses in the dataset as SNOMED CT abbreviations. + default_task (PTBXLMultilabelClassification): Default task for this dataset. + + Examples: + >>> dataset = PTBXLDataset(root="./data") """ CLASSES = list(SNOMED_CT_ABBREVIATION.values()) @@ -153,9 +192,23 @@ def __init__( def load_data(self) -> dd.DataFrame: """Returns a dataframe with each individual row corresponding to each .hea/.mat file combination in the PTB-XL dataset. + Uses the stratified fold assignments from ptbxl_database.csv - 1 through 8 for train, 9 for validation, 10 for test. Returns: - dd.DataFrame: Dataframe with one row per record + dd.DataFrame: Dataframe with one row per record with the following columns: + patient_id (str): .hea file identifier starting with HR + event_type (str): "ptbxl" (only one event type in the dataset) + timestamp (NaT): pd.NaT (no timestamps available in the dataset) + ptbxl/mat (str): Path to the associated .mat file + ptbxl/age (str): Patient age + ptbxl/sex (str): Patient sex + ptbxl/dx_codes (str): Patient SNOMED CT diagnosis codes + ptbxl/dx_abbreviations (str): Patient SNOMED CT diagnosis abbreviations + ptbxl/split": Stratified fold assignment "train" / "test" / "val" + + Raises: + FileNotFoundError: If no .hea files are found in root. + FileNotFoundError: If ptbxl_database.csv is not found in root """ root_path = Path(self.root) files = sorted(root_path.glob("*.hea")) From 7bc71c8fbe64b235f05ff0136be3d0449dcb5c28 Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Sun, 19 Apr 2026 16:58:19 -0400 Subject: [PATCH 28/39] Comments on test_ptbxl.py --- tests/core/test_ptbxl.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/core/test_ptbxl.py b/tests/core/test_ptbxl.py index 64bd00b09..46dd2c2e9 100644 --- a/tests/core/test_ptbxl.py +++ b/tests/core/test_ptbxl.py @@ -1,3 +1,11 @@ +""" +Unit tests for the PTBXLDataset and PTBXLMultilabelClassification classes. + +Authors: + Anurag Dixit - anuragd2@illinois.edu + Kent Spillner - kspillne@illinois.edu + John Wells - jtwells2@illinois.edu +""" import tempfile import shutil import unittest From 061fa0033b387efa44a5c6af88a3d7cc1bac3b1d Mon Sep 17 00:00:00 2001 From: AnuragD2 Date: Sun, 19 Apr 2026 15:11:23 -0700 Subject: [PATCH 29/39] Added PTB-XL SE-ResNet-50 --- .../ptbxl_superdiagnostic_se_resnet.ipynb | 3160 +++++++++++++++++ 1 file changed, 3160 insertions(+) create mode 100644 examples/ptbxl_superdiagnostic_se_resnet.ipynb diff --git a/examples/ptbxl_superdiagnostic_se_resnet.ipynb b/examples/ptbxl_superdiagnostic_se_resnet.ipynb new file mode 100644 index 000000000..caeba1368 --- /dev/null +++ b/examples/ptbxl_superdiagnostic_se_resnet.ipynb @@ -0,0 +1,3160 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e06e22e2", + "metadata": {}, + "source": [ + "# PTB-XL Multi-Label ECG Classification — Ablation Study\n", + "\n", + "**Course:** CS-598 Deep Learning for Healthcare \n", + "**Dataset:** PTB-XL (PhysioNet / CinC Challenge 2020, v1.0.2) \n", + "**Model:** SE-ResNet-50 (Squeeze-Excitation ResNet, Nonaka & Seita 2021)\n", + "\n", + "---\n", + "\n", + "## Background & Motivation\n", + "\n", + "PTB-XL is the largest publicly available clinical 12-lead ECG dataset, containing\n", + "21,837 recordings from 18,885 patients at 500 Hz (≈ 10 s per recording).\n", + "Each recording is annotated with one or more *SNOMED-CT* codes.\n", + "\n", + "We frame ECG diagnosis as **multi-label classification**: given a signal\n", + "$X \\in \\mathbb{R}^{C \\times T}$ ($C=12$ leads, $T$ time-steps), predict a\n", + "binary label vector $y \\in \\{0,1\\}^K$ for $K$ diagnostic classes.\n", + "\n", + "### Mathematical Framing\n", + "\n", + "| Symbol | Meaning |\n", + "|--------|---------|\n", + "| $C = 12$ | ECG leads |\n", + "| $T$ | Time-steps: **1 000** at 100 Hz or **5 000** at 500 Hz |\n", + "| $K$ | Label classes: **5** (superdiagnostic) or **27** (diagnostic) |\n", + "| $f_\\theta$ | SE-ResNet-50 backbone |\n", + "\n", + "**Forward pass:**\n", + "$$\\hat{y} = \\sigma\\!\\left(f_\\theta(X)\\,W^\\top + b\\right) \\in [0,1]^K$$\n", + "\n", + "**Training loss (Binary Cross-Entropy per label):**\n", + "$$\\mathcal{L}_{\\text{BCE}} = -\\frac{1}{K}\\sum_{k=1}^{K}\\left[y_k\\log\\hat{y}_k + (1-y_k)\\log(1-\\hat{y}_k)\\right]$$\n", + "\n", + "**Evaluation — macro-averaged ROC-AUC:**\n", + "$$\\overline{\\text{AUC}} = \\frac{1}{K}\\sum_{k=1}^{K}\\int_0^1 \\text{TPR}_k(t)\\,d\\,\\text{FPR}_k(t)$$\n", + "\n", + "**Evaluation — macro-averaged F1 (threshold = 0.5):**\n", + "$$\\overline{F_1} = \\frac{1}{K}\\sum_{k=1}^{K}\\frac{2\\,\\text{TP}_k}{2\\,\\text{TP}_k + \\text{FP}_k + \\text{FN}_k}$$\n", + "\n", + "---\n", + "\n", + "## Ablation Design\n", + "\n", + "We vary two axes simultaneously (as done in Strodthoff *et al.* 2020):\n", + "\n", + "| Config | `label_type` | `sampling_rate` | $K$ | $T$ |\n", + "|--------|-------------|-----------------|-----|-----|\n", + "| **A** (baseline) | superdiagnostic | 100 Hz | 5 | 1 000 |\n", + "| **B** | superdiagnostic | 500 Hz | 5 | 5 000 |\n", + "| **C** | diagnostic | 100 Hz | 27 | 1 000 |\n", + "| **D** | diagnostic | 500 Hz | 27 | 5 000 |\n", + "\n", + "Holding the model architecture and hyper-parameters **constant** across all\n", + "four configurations isolates the effect of (a) label granularity and (b)\n", + "temporal resolution on downstream performance.\n", + "\n", + "**Hypothesis:**\n", + "* Finer label granularity (27 classes) is a harder task → lower absolute AUC.\n", + "* Higher temporal resolution (500 Hz) provides more information → higher AUC\n", + " at the cost of increased model input size and training time." + ] + }, + { + "cell_type": "markdown", + "id": "89438d66", + "metadata": {}, + "source": [ + "## 0. Environment Setup\n", + "\n", + "Install dependencies if running on a fresh Colab runtime." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "b1dc5e9d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.9.6 (default, Dec 2 2025, 07:27:58) \n", + "[Clang 17.0.0 (clang-1700.6.3.2)]\n", + "PyTorch 2.8.0 | CUDA available: False\n", + "Using device: cpu\n" + ] + } + ], + "source": [ + "# Uncomment the lines below to install on Colab / a fresh environment\n", + "# !pip install pyhealth scipy wfdb --quiet\n", + "\n", + "import sys\n", + "print(f'Python {sys.version}')\n", + "\n", + "import torch\n", + "print(f'PyTorch {torch.__version__} | CUDA available: {torch.cuda.is_available()}')\n", + "\n", + "DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'\n", + "print(f'Using device: {DEVICE}')" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "d88687c4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Repo root on path: /Users/anuragd/CS-598_HealthCareAssignment/DLH598_repo/PyHealth\n" + ] + } + ], + "source": [ + "import sys, pathlib\n", + "# Add the repo root so 'pyhealth' is importable from this notebook\n", + "_REPO_ROOT = str(pathlib.Path(__file__).resolve().parents[1]) if \"__file__\" in dir() else \"/Users/anuragd/CS-598_HealthCareAssignment/DLH598_repo/PyHealth\"\n", + "if _REPO_ROOT not in sys.path:\n", + " sys.path.insert(0, _REPO_ROOT)\n", + "print(f\"Repo root on path: {_REPO_ROOT}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "39342fa9", + "metadata": {}, + "source": [ + "## 1. Dataset Path\n", + "\n", + "Point `PTBXL_ROOT` to the `training/ptb-xl/` sub-directory of the\n", + "PhysioNet Challenge 2020 download (v1.0.2). \n", + "It should contain group sub-directories `g1/`, `g2/`, …, `g22/`, each\n", + "holding pairs of WFDB files (`.hea` header + `.mat` signal matrix).\n", + "\n", + "```\n", + "training/ptb-xl/\n", + " g1/\n", + " HR00001.hea\n", + " HR00001.mat\n", + " ...\n", + " g2/ ...\n", + " ...\n", + " g22/\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "5ce43a37", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PTB-XL root: /Users/anuragd/CS-598_HealthCareAssignment/DLH_Project/classification-of-12-lead-ecgs-the-physionetcomputing-in-cardiology-challenge-2020-1.0.2/training/ptb-xl\n", + "ptbxl_database.csv: /Users/anuragd/CS-598_HealthCareAssignment/DLH_Project/classification-of-12-lead-ecgs-the-physionetcomputing-in-cardiology-challenge-2020-1.0.2/training/ptb-xl/ptbxl_database.csv\n", + "Found 22 group directories\n" + ] + } + ], + "source": [ + "import os\n", + "from pathlib import Path\n", + "\n", + "# -----------------------------------------------------------------------\n", + "# PTB-XL data root — contains g1/…g22/ sub-dirs AND ptbxl_database.csv\n", + "# -----------------------------------------------------------------------\n", + "PTBXL_ROOT = str(\n", + " Path(\"/Users/anuragd/CS-598_HealthCareAssignment/DLH_Project/classification-of-12-lead-ecgs-the-physionetcomputing-in-cardiology-challenge-2020-1.0.2/training/ptb-xl\")\n", + " .resolve()\n", + ")\n", + "\n", + "if not Path(PTBXL_ROOT).exists():\n", + " raise FileNotFoundError(\n", + " f\"PTB-XL root not found: {PTBXL_ROOT}\\n\"\n", + " \"Please set PTBXL_ROOT to the training/ptb-xl/ directory.\"\n", + " )\n", + "\n", + "csv_path = Path(PTBXL_ROOT) / \"ptbxl_database.csv\"\n", + "if not csv_path.exists():\n", + " raise FileNotFoundError(f\"ptbxl_database.csv not found in {PTBXL_ROOT}\")\n", + "\n", + "print(f'PTB-XL root: {PTBXL_ROOT}')\n", + "print(f'ptbxl_database.csv: {csv_path}')\n", + "n_groups = len([d for d in Path(PTBXL_ROOT).iterdir() if d.is_dir() and d.name.startswith('g')])\n", + "print(f'Found {n_groups} group directories')\n" + ] + }, + { + "cell_type": "markdown", + "id": "d27efc8a", + "metadata": {}, + "source": [ + "## 2. Shared Imports" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "dc262078", + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib.ticker as mticker\n", + "from sklearn.metrics import roc_auc_score, f1_score\n", + "\n", + "from pyhealth.datasets import PTBXLDataset, split_by_patient, split_by_sample, get_dataloader\n", + "from pyhealth.tasks import PTBXLMultilabelClassification\n", + "from pyhealth.models import SEResNet50ECG\n", + "from pyhealth.trainer import Trainer\n", + "from pyhealth.metrics import multilabel_metrics_fn\n" + ] + }, + { + "cell_type": "markdown", + "id": "fdef182a", + "metadata": {}, + "source": [ + "## 3. Hyper-parameters\n", + "\n", + "Following the grid-search described in Nonaka & Seita (2021), we fix the\n", + "best-found hyper-parameters for all four ablation runs so that the only\n", + "difference is the task configuration." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "45a40dcb", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Batch size: 64 | LR: 0.01 | Epochs: 5\n", + "Dev mode: True\n" + ] + } + ], + "source": [ + "# Training hyper-parameters (fixed across all ablation configs)\n", + "BATCH_SIZE = 64 # best setting from grid search\n", + "LEARNING_RATE = 0.01 # best setting from Nonaka & Seita (2021) grid search (Table 5a)\n", + "EPOCHS = 5 # increase to 20–30 for full reproduction\n", + "SPLIT = [0.7, 0.1, 0.2] # train / val / test\n", + "MONITOR = 'roc_auc_macro' # PyHealth trainer monitor key\n", + "\n", + "# Use dev=True to cap the dataset at ~1 000 patients for a quick smoke test.\n", + "# Set DEV_MODE=False for the full 21 837-recording experiment.\n", + "DEV_MODE = True\n", + "\n", + "print(f'Batch size: {BATCH_SIZE} | LR: {LEARNING_RATE} | Epochs: {EPOCHS}')\n", + "print(f'Dev mode: {DEV_MODE}')" + ] + }, + { + "cell_type": "markdown", + "id": "18e4a2d1", + "metadata": {}, + "source": [ + "## 4. Load the PTBXLDataset (shared)\n", + "\n", + "The `PTBXLDataset` parses every `.hea` header, extracts patient metadata\n", + "and SNOMED-CT codes, and writes a compact `ptbxl-pyhealth.csv` index\n", + "file on the first run. Subsequent runs load from the parquet cache." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "19663067", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Initializing ptbxl dataset from /Users/anuragd/CS-598_HealthCareAssignment/DLH_Project/classification-of-12-lead-ecgs-the-physionetcomputing-in-cardiology-challenge-2020-1.0.2/training/ptb-xl (dev mode: True)\n", + "No cache_dir provided. Using default cache dir: /Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22\n", + "Found cached event dataframe: /Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/global_event_df.parquet\n", + "Dataset: ptbxl\n", + "Dev mode: True\n", + "Number of patients: 1000\n", + "Number of events: 1000\n" + ] + } + ], + "source": [ + "base_dataset = PTBXLDataset(\n", + " root=PTBXL_ROOT,\n", + " dev=DEV_MODE,\n", + ")\n", + "base_dataset.stats()" + ] + }, + { + "cell_type": "markdown", + "id": "e018207f", + "metadata": {}, + "source": [ + "## 5. Ablation Configurations\n", + "\n", + "Define all four task variants covering the $2 \\times 2$ ablation grid." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "ae5daef7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Ablation configurations:\n", + " A — superdiagnostic / 100 Hz (baseline) → K=5, T=1000\n", + " B — superdiagnostic / 500 Hz → K=5, T=5000\n", + " C — diagnostic (27-class) / 100 Hz → K=27, T=1000\n", + " D — diagnostic (27-class) / 500 Hz → K=27, T=5000\n" + ] + } + ], + "source": [ + "ABLATION_CONFIGS = [\n", + " {\n", + " 'name': 'A — superdiagnostic / 100 Hz (baseline)',\n", + " 'label_type': 'superdiagnostic',\n", + " 'sampling_rate': 100,\n", + " 'n_classes': 5,\n", + " 'T': 1000,\n", + " },\n", + " {\n", + " 'name': 'B — superdiagnostic / 500 Hz',\n", + " 'label_type': 'superdiagnostic',\n", + " 'sampling_rate': 500,\n", + " 'n_classes': 5,\n", + " 'T': 5000,\n", + " },\n", + " {\n", + " 'name': 'C — diagnostic (27-class) / 100 Hz',\n", + " 'label_type': 'diagnostic',\n", + " 'sampling_rate': 100,\n", + " 'n_classes': 27,\n", + " 'T': 1000,\n", + " },\n", + " {\n", + " 'name': 'D — diagnostic (27-class) / 500 Hz',\n", + " 'label_type': 'diagnostic',\n", + " 'sampling_rate': 500,\n", + " 'n_classes': 27,\n", + " 'T': 5000,\n", + " },\n", + "]\n", + "\n", + "print('Ablation configurations:')\n", + "for cfg in ABLATION_CONFIGS:\n", + " print(f\" {cfg['name']} → K={cfg['n_classes']}, T={cfg['T']}\")" + ] + }, + { + "cell_type": "markdown", + "id": "1d48882c", + "metadata": {}, + "source": [ + "## 6. Training Loop\n", + "\n", + "For each configuration we:\n", + "\n", + "1. **Define task** — `PTBXLMultilabelClassification(label_type, sampling_rate)`\n", + "2. **Apply task** — `base_dataset.set_task(task)` → `SampleDataset`\n", + "3. **Split** — 70 % train / 10 % val / 20 % test (by patient to avoid leakage)\n", + "4. **Instantiate SE-ResNet-50** — initialised from the `SampleDataset`\n", + "5. **Train** with `Trainer`, monitoring macro ROC-AUC on the validation set\n", + "6. **Evaluate** on the held-out test set: macro ROC-AUC + macro F1" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "5f46408f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "======================================================================\n", + "Config: A — superdiagnostic / 100 Hz (baseline)\n", + " label_type=superdiagnostic, sampling_rate=100 Hz\n", + " K=5 classes, T=1000 time-steps per lead\n", + "======================================================================\n", + "Setting task PTBXLSuperDiagnostic_100Hz for ptbxl base dataset...\n", + "Task cache paths: task_df=/Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/tasks/PTBXLSuperDiagnostic_100Hz_a435b25e-18d8-5d3b-b000-8457f09e6895/task_df.ld, samples=/Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/tasks/PTBXLSuperDiagnostic_100Hz_a435b25e-18d8-5d3b-b000-8457f09e6895/samples_cdbbc602-34e2-5a41-8643-4c76b08829f6.ld\n", + "Found cached processed samples at /Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/tasks/PTBXLSuperDiagnostic_100Hz_a435b25e-18d8-5d3b-b000-8457f09e6895/samples_cdbbc602-34e2-5a41-8643-4c76b08829f6.ld, skipping processing.\n", + " Total ML samples: 997\n", + " signal shape : (12, 1000)\n", + " labels : tensor([0., 1., 0., 1., 0.])\n", + " Train/Val/Test samples: 697/100/200\n", + " Steps per epoch: 10\n", + "SEResNet50ECG(\n", + " (backbone): ResNet1d(\n", + " (stem): Sequential(\n", + " (0): Conv1d(12, 64, kernel_size=(7,), stride=(2,), padding=(3,), bias=False)\n", + " (1): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU(inplace=True)\n", + " (3): MaxPool1d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)\n", + " )\n", + " (stages): ModuleList(\n", + " (0): Sequential(\n", + " (0): SEResNetBottleneck1d(\n", + " (conv1): Conv1d(64, 64, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn1): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv2): Conv1d(64, 64, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", + " (bn2): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv3): Conv1d(64, 256, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn3): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu): ReLU(inplace=True)\n", + " (se_module): SEModule1d(\n", + " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", + " (fc1): Conv1d(256, 16, kernel_size=(1,), stride=(1,))\n", + " (relu): ReLU(inplace=True)\n", + " (fc2): Conv1d(16, 256, kernel_size=(1,), stride=(1,))\n", + " (sigmoid): Sigmoid()\n", + " )\n", + " (downsample): Sequential(\n", + " (0): Conv1d(64, 256, kernel_size=(1,), stride=(1,), bias=False)\n", + " (1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (1): SEResNetBottleneck1d(\n", + " (conv1): Conv1d(256, 64, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn1): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv2): Conv1d(64, 64, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", + " (bn2): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv3): Conv1d(64, 256, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn3): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu): ReLU(inplace=True)\n", + " (se_module): SEModule1d(\n", + " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", + " (fc1): Conv1d(256, 16, kernel_size=(1,), stride=(1,))\n", + " (relu): ReLU(inplace=True)\n", + " (fc2): Conv1d(16, 256, kernel_size=(1,), stride=(1,))\n", + " (sigmoid): Sigmoid()\n", + " )\n", + " )\n", + " (2): SEResNetBottleneck1d(\n", + " (conv1): Conv1d(256, 64, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn1): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv2): Conv1d(64, 64, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", + " (bn2): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv3): Conv1d(64, 256, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn3): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu): ReLU(inplace=True)\n", + " (se_module): SEModule1d(\n", + " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", + " (fc1): Conv1d(256, 16, kernel_size=(1,), stride=(1,))\n", + " (relu): ReLU(inplace=True)\n", + " (fc2): Conv1d(16, 256, kernel_size=(1,), stride=(1,))\n", + " (sigmoid): Sigmoid()\n", + " )\n", + " )\n", + " )\n", + " (1): Sequential(\n", + " (0): SEResNetBottleneck1d(\n", + " (conv1): Conv1d(256, 128, kernel_size=(1,), stride=(2,), bias=False)\n", + " (bn1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv2): Conv1d(128, 128, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", + " (bn2): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv3): Conv1d(128, 512, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn3): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu): ReLU(inplace=True)\n", + " (se_module): SEModule1d(\n", + " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", + " (fc1): Conv1d(512, 32, kernel_size=(1,), stride=(1,))\n", + " (relu): ReLU(inplace=True)\n", + " (fc2): Conv1d(32, 512, kernel_size=(1,), stride=(1,))\n", + " (sigmoid): Sigmoid()\n", + " )\n", + " (downsample): Sequential(\n", + " (0): Conv1d(256, 512, kernel_size=(1,), stride=(2,), bias=False)\n", + " (1): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (1): SEResNetBottleneck1d(\n", + " (conv1): Conv1d(512, 128, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv2): Conv1d(128, 128, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", + " (bn2): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv3): Conv1d(128, 512, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn3): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu): ReLU(inplace=True)\n", + " (se_module): SEModule1d(\n", + " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", + " (fc1): Conv1d(512, 32, kernel_size=(1,), stride=(1,))\n", + " (relu): ReLU(inplace=True)\n", + " (fc2): Conv1d(32, 512, kernel_size=(1,), stride=(1,))\n", + " (sigmoid): Sigmoid()\n", + " )\n", + " )\n", + " (2): SEResNetBottleneck1d(\n", + " (conv1): Conv1d(512, 128, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv2): Conv1d(128, 128, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", + " (bn2): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv3): Conv1d(128, 512, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn3): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu): ReLU(inplace=True)\n", + " (se_module): SEModule1d(\n", + " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", + " (fc1): Conv1d(512, 32, kernel_size=(1,), stride=(1,))\n", + " (relu): ReLU(inplace=True)\n", + " (fc2): Conv1d(32, 512, kernel_size=(1,), stride=(1,))\n", + " (sigmoid): Sigmoid()\n", + " )\n", + " )\n", + " (3): SEResNetBottleneck1d(\n", + " (conv1): Conv1d(512, 128, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv2): Conv1d(128, 128, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", + " (bn2): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv3): Conv1d(128, 512, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn3): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu): ReLU(inplace=True)\n", + " (se_module): SEModule1d(\n", + " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", + " (fc1): Conv1d(512, 32, kernel_size=(1,), stride=(1,))\n", + " (relu): ReLU(inplace=True)\n", + " (fc2): Conv1d(32, 512, kernel_size=(1,), stride=(1,))\n", + " (sigmoid): Sigmoid()\n", + " )\n", + " )\n", + " )\n", + " (2): Sequential(\n", + " (0): SEResNetBottleneck1d(\n", + " (conv1): Conv1d(512, 256, kernel_size=(1,), stride=(2,), bias=False)\n", + " (bn1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv2): Conv1d(256, 256, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", + " (bn2): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv3): Conv1d(256, 1024, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn3): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu): ReLU(inplace=True)\n", + " (se_module): SEModule1d(\n", + " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", + " (fc1): Conv1d(1024, 64, kernel_size=(1,), stride=(1,))\n", + " (relu): ReLU(inplace=True)\n", + " (fc2): Conv1d(64, 1024, kernel_size=(1,), stride=(1,))\n", + " (sigmoid): Sigmoid()\n", + " )\n", + " (downsample): Sequential(\n", + " (0): Conv1d(512, 1024, kernel_size=(1,), stride=(2,), bias=False)\n", + " (1): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (1): SEResNetBottleneck1d(\n", + " (conv1): Conv1d(1024, 256, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv2): Conv1d(256, 256, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", + " (bn2): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv3): Conv1d(256, 1024, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn3): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu): ReLU(inplace=True)\n", + " (se_module): SEModule1d(\n", + " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", + " (fc1): Conv1d(1024, 64, kernel_size=(1,), stride=(1,))\n", + " (relu): ReLU(inplace=True)\n", + " (fc2): Conv1d(64, 1024, kernel_size=(1,), stride=(1,))\n", + " (sigmoid): Sigmoid()\n", + " )\n", + " )\n", + " (2): SEResNetBottleneck1d(\n", + " (conv1): Conv1d(1024, 256, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv2): Conv1d(256, 256, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", + " (bn2): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv3): Conv1d(256, 1024, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn3): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu): ReLU(inplace=True)\n", + " (se_module): SEModule1d(\n", + " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", + " (fc1): Conv1d(1024, 64, kernel_size=(1,), stride=(1,))\n", + " (relu): ReLU(inplace=True)\n", + " (fc2): Conv1d(64, 1024, kernel_size=(1,), stride=(1,))\n", + " (sigmoid): Sigmoid()\n", + " )\n", + " )\n", + " (3): SEResNetBottleneck1d(\n", + " (conv1): Conv1d(1024, 256, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv2): Conv1d(256, 256, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", + " (bn2): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv3): Conv1d(256, 1024, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn3): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu): ReLU(inplace=True)\n", + " (se_module): SEModule1d(\n", + " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", + " (fc1): Conv1d(1024, 64, kernel_size=(1,), stride=(1,))\n", + " (relu): ReLU(inplace=True)\n", + " (fc2): Conv1d(64, 1024, kernel_size=(1,), stride=(1,))\n", + " (sigmoid): Sigmoid()\n", + " )\n", + " )\n", + " (4): SEResNetBottleneck1d(\n", + " (conv1): Conv1d(1024, 256, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv2): Conv1d(256, 256, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", + " (bn2): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv3): Conv1d(256, 1024, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn3): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu): ReLU(inplace=True)\n", + " (se_module): SEModule1d(\n", + " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", + " (fc1): Conv1d(1024, 64, kernel_size=(1,), stride=(1,))\n", + " (relu): ReLU(inplace=True)\n", + " (fc2): Conv1d(64, 1024, kernel_size=(1,), stride=(1,))\n", + " (sigmoid): Sigmoid()\n", + " )\n", + " )\n", + " (5): SEResNetBottleneck1d(\n", + " (conv1): Conv1d(1024, 256, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv2): Conv1d(256, 256, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", + " (bn2): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv3): Conv1d(256, 1024, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn3): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu): ReLU(inplace=True)\n", + " (se_module): SEModule1d(\n", + " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", + " (fc1): Conv1d(1024, 64, kernel_size=(1,), stride=(1,))\n", + " (relu): ReLU(inplace=True)\n", + " (fc2): Conv1d(64, 1024, kernel_size=(1,), stride=(1,))\n", + " (sigmoid): Sigmoid()\n", + " )\n", + " )\n", + " )\n", + " (3): Sequential(\n", + " (0): SEResNetBottleneck1d(\n", + " (conv1): Conv1d(1024, 512, kernel_size=(1,), stride=(2,), bias=False)\n", + " (bn1): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv2): Conv1d(512, 512, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", + " (bn2): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv3): Conv1d(512, 2048, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn3): BatchNorm1d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu): ReLU(inplace=True)\n", + " (se_module): SEModule1d(\n", + " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", + " (fc1): Conv1d(2048, 128, kernel_size=(1,), stride=(1,))\n", + " (relu): ReLU(inplace=True)\n", + " (fc2): Conv1d(128, 2048, kernel_size=(1,), stride=(1,))\n", + " (sigmoid): Sigmoid()\n", + " )\n", + " (downsample): Sequential(\n", + " (0): Conv1d(1024, 2048, kernel_size=(1,), stride=(2,), bias=False)\n", + " (1): BatchNorm1d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (1): SEResNetBottleneck1d(\n", + " (conv1): Conv1d(2048, 512, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn1): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv2): Conv1d(512, 512, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", + " (bn2): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv3): Conv1d(512, 2048, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn3): BatchNorm1d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu): ReLU(inplace=True)\n", + " (se_module): SEModule1d(\n", + " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", + " (fc1): Conv1d(2048, 128, kernel_size=(1,), stride=(1,))\n", + " (relu): ReLU(inplace=True)\n", + " (fc2): Conv1d(128, 2048, kernel_size=(1,), stride=(1,))\n", + " (sigmoid): Sigmoid()\n", + " )\n", + " )\n", + " (2): SEResNetBottleneck1d(\n", + " (conv1): Conv1d(2048, 512, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn1): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv2): Conv1d(512, 512, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", + " (bn2): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (conv3): Conv1d(512, 2048, kernel_size=(1,), stride=(1,), bias=False)\n", + " (bn3): BatchNorm1d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu): ReLU(inplace=True)\n", + " (se_module): SEModule1d(\n", + " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", + " (fc1): Conv1d(2048, 128, kernel_size=(1,), stride=(1,))\n", + " (relu): ReLU(inplace=True)\n", + " (fc2): Conv1d(128, 2048, kernel_size=(1,), stride=(1,))\n", + " (sigmoid): Sigmoid()\n", + " )\n", + " )\n", + " )\n", + " )\n", + " (gap): AdaptiveAvgPool1d(output_size=1)\n", + " (proj): Linear(in_features=2048, out_features=256, bias=True)\n", + " )\n", + " (head): Sequential(\n", + " (0): Linear(in_features=256, out_features=128, bias=True)\n", + " (1): ReLU(inplace=True)\n", + " (2): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (3): Dropout(p=0.25, inplace=False)\n", + " (4): Linear(in_features=128, out_features=5, bias=True)\n", + " )\n", + ")\n", + "Metrics: ['roc_auc_macro', 'f1_macro']\n", + "Device: cpu\n", + "\n", + "Training:\n", + "Batch size: 64\n", + "Optimizer: \n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 5\n", + "Patience: None\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0 / 5: 100%|██████████| 10/10 [00:58<00:00, 5.86s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-0, step-10 ---\n", + "loss: 0.6510\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.18s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-0, step-10 ---\n", + "roc_auc_macro: 0.5000\n", + "f1_macro: 0.2881\n", + "loss: 1799.7626\n", + "New best roc_auc_macro score (0.5000) at epoch-0, step-10\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Epoch 1 / 5: 100%|██████████| 10/10 [00:57<00:00, 5.77s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-1, step-20 ---\n", + "loss: 0.5082\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.15s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-1, step-20 ---\n", + "roc_auc_macro: 0.5893\n", + "f1_macro: 0.4301\n", + "loss: 11.1620\n", + "New best roc_auc_macro score (0.5893) at epoch-1, step-20\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Epoch 2 / 5: 100%|██████████| 10/10 [00:57<00:00, 5.74s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-2, step-30 ---\n", + "loss: 0.5061\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.15s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-2, step-30 ---\n", + "roc_auc_macro: 0.5173\n", + "f1_macro: 0.3381\n", + "loss: 7.2917\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Epoch 3 / 5: 100%|██████████| 10/10 [00:57<00:00, 5.75s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-3, step-40 ---\n", + "loss: 0.4987\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.16s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-3, step-40 ---\n", + "roc_auc_macro: 0.4391\n", + "f1_macro: 0.3904\n", + "loss: 1.4444\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Epoch 4 / 5: 100%|██████████| 10/10 [00:57<00:00, 5.76s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-4, step-50 ---\n", + "loss: 0.4959\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.20s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-4, step-50 ---\n", + "roc_auc_macro: 0.5381\n", + "f1_macro: 0.4151\n", + "loss: 0.8571\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Training time: 330.4 s\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Evaluation: 100%|██████████| 4/4 [00:16<00:00, 4.02s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Test ROC-AUC (macro): 0.5486\n", + " Test F1 (macro): 0.4428\n", + "\n", + "======================================================================\n", + "Config: B — superdiagnostic / 500 Hz\n", + " label_type=superdiagnostic, sampling_rate=500 Hz\n", + " K=5 classes, T=5000 time-steps per lead\n", + "======================================================================\n", + "Setting task PTBXLSuperDiagnostic_500Hz for ptbxl base dataset...\n", + "Task cache paths: task_df=/Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/tasks/PTBXLSuperDiagnostic_500Hz_a3e4fee9-a5e6-5af9-94ad-7eeb5e82e80a/task_df.ld, samples=/Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/tasks/PTBXLSuperDiagnostic_500Hz_a3e4fee9-a5e6-5af9-94ad-7eeb5e82e80a/samples_cdbbc602-34e2-5a41-8643-4c76b08829f6.ld\n", + "Applying task transformations on data with 1 workers...\n", + "Detected Jupyter notebook environment, setting num_workers to 1\n", + "Single worker mode, processing sequentially\n", + "Worker 0 started processing 1000 patients. (Polars threads: 10)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + " 0%| | 0/1000 [00:00\n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 5\n", + "Patience: None\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0 / 5: 100%|██████████| 10/10 [04:30<00:00, 27.02s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-0, step-10 ---\n", + "loss: 0.6516\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:39<00:00, 19.73s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-0, step-10 ---\n", + "roc_auc_macro: 0.5000\n", + "f1_macro: 0.2682\n", + "loss: 62327.4609\n", + "New best roc_auc_macro score (0.5000) at epoch-0, step-10\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Epoch 1 / 5: 100%|██████████| 10/10 [04:36<00:00, 27.62s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-1, step-20 ---\n", + "loss: 0.5282\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:39<00:00, 19.74s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-1, step-20 ---\n", + "roc_auc_macro: 0.3996\n", + "f1_macro: 0.1906\n", + "loss: 160.0568\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Epoch 2 / 5: 100%|██████████| 10/10 [04:39<00:00, 27.99s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-2, step-30 ---\n", + "loss: 0.5021\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:39<00:00, 19.55s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-2, step-30 ---\n", + "roc_auc_macro: 0.6734\n", + "f1_macro: 0.4020\n", + "loss: 1.1237\n", + "New best roc_auc_macro score (0.6734) at epoch-2, step-30\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Epoch 3 / 5: 100%|██████████| 10/10 [04:34<00:00, 27.48s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-3, step-40 ---\n", + "loss: 0.4587\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:39<00:00, 19.55s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-3, step-40 ---\n", + "roc_auc_macro: 0.6882\n", + "f1_macro: 0.3906\n", + "loss: 0.5006\n", + "New best roc_auc_macro score (0.6882) at epoch-3, step-40\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Epoch 4 / 5: 100%|██████████| 10/10 [04:25<00:00, 26.58s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-4, step-50 ---\n", + "loss: 0.4874\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:38<00:00, 19.38s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-4, step-50 ---\n", + "roc_auc_macro: 0.7684\n", + "f1_macro: 0.4205\n", + "loss: 0.4232\n", + "New best roc_auc_macro score (0.7684) at epoch-4, step-50\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Training time: 1562.8 s\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Evaluation: 100%|██████████| 4/4 [01:14<00:00, 18.60s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Test ROC-AUC (macro): 0.7585\n", + " Test F1 (macro): 0.4163\n", + "\n", + "======================================================================\n", + "Config: C — diagnostic (27-class) / 100 Hz\n", + " label_type=diagnostic, sampling_rate=100 Hz\n", + " K=27 classes, T=1000 time-steps per lead\n", + "======================================================================\n", + "Setting task PTBXLDiagnostic27_100Hz for ptbxl base dataset...\n", + "Task cache paths: task_df=/Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/tasks/PTBXLDiagnostic27_100Hz_c507defc-1543-5249-8da9-3c1bffcf0691/task_df.ld, samples=/Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/tasks/PTBXLDiagnostic27_100Hz_c507defc-1543-5249-8da9-3c1bffcf0691/samples_cdbbc602-34e2-5a41-8643-4c76b08829f6.ld\n", + "Applying task transformations on data with 1 workers...\n", + "Detected Jupyter notebook environment, setting num_workers to 1\n", + "Single worker mode, processing sequentially\n", + "Worker 0 started processing 1000 patients. (Polars threads: 10)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + " 0%| | 0/1000 [00:00\n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 5\n", + "Patience: None\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0 / 5: 100%|██████████| 10/10 [00:59<00:00, 5.90s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-0, step-10 ---\n", + "loss: 0.6324\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.12s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-0, step-10 ---\n", + "roc_auc_macro: nan\n", + "f1_macro: 0.0047\n", + "loss: 4764.1799\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n", + "Epoch 1 / 5: 100%|██████████| 10/10 [00:57<00:00, 5.77s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-1, step-20 ---\n", + "loss: 0.3184\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.13s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-1, step-20 ---\n", + "roc_auc_macro: nan\n", + "f1_macro: 0.0600\n", + "loss: 3.7070\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n", + "Epoch 2 / 5: 100%|██████████| 10/10 [00:57<00:00, 5.71s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-2, step-30 ---\n", + "loss: 0.1647\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.13s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-2, step-30 ---\n", + "roc_auc_macro: nan\n", + "f1_macro: 0.1028\n", + "loss: 1.1566\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n", + "Epoch 3 / 5: 100%|██████████| 10/10 [00:59<00:00, 5.92s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-3, step-40 ---\n", + "loss: 0.1702\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.30s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-3, step-40 ---\n", + "roc_auc_macro: nan\n", + "f1_macro: 0.0440\n", + "loss: 0.7322\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n", + "Epoch 4 / 5: 100%|██████████| 10/10 [00:58<00:00, 5.80s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-4, step-50 ---\n", + "loss: 0.1662\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.16s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-4, step-50 ---\n", + "roc_auc_macro: nan\n", + "f1_macro: 0.0621\n", + "loss: 0.2451\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Training time: 332.8 s\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Evaluation: 100%|██████████| 4/4 [00:15<00:00, 4.00s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Test ROC-AUC (macro): nan\n", + " Test F1 (macro): 0.0660\n", + "\n", + "======================================================================\n", + "Config: D — diagnostic (27-class) / 500 Hz\n", + " label_type=diagnostic, sampling_rate=500 Hz\n", + " K=27 classes, T=5000 time-steps per lead\n", + "======================================================================\n", + "Setting task PTBXLDiagnostic27_500Hz for ptbxl base dataset...\n", + "Task cache paths: task_df=/Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/tasks/PTBXLDiagnostic27_500Hz_0d0c6f8a-c70c-5929-bbf5-ab21dc91f788/task_df.ld, samples=/Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/tasks/PTBXLDiagnostic27_500Hz_0d0c6f8a-c70c-5929-bbf5-ab21dc91f788/samples_cdbbc602-34e2-5a41-8643-4c76b08829f6.ld\n", + "Applying task transformations on data with 1 workers...\n", + "Detected Jupyter notebook environment, setting num_workers to 1\n", + "Single worker mode, processing sequentially\n", + "Worker 0 started processing 1000 patients. (Polars threads: 10)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n", + " 0%| | 0/1000 [00:00\n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 5\n", + "Patience: None\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0 / 5: 100%|██████████| 10/10 [04:30<00:00, 27.08s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-0, step-10 ---\n", + "loss: 0.6280\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:38<00:00, 19.20s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-0, step-10 ---\n", + "roc_auc_macro: nan\n", + "f1_macro: 0.0602\n", + "loss: 14318.4673\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "Epoch 1 / 5: 100%|██████████| 10/10 [04:22<00:00, 26.23s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-1, step-20 ---\n", + "loss: 0.3050\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:38<00:00, 19.17s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-1, step-20 ---\n", + "roc_auc_macro: nan\n", + "f1_macro: 0.0566\n", + "loss: 0.6025\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n", + "Epoch 2 / 5: 100%|██████████| 10/10 [04:20<00:00, 26.08s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-2, step-30 ---\n", + "loss: 0.1670\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:38<00:00, 19.07s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-2, step-30 ---\n", + "roc_auc_macro: nan\n", + "f1_macro: 0.0835\n", + "loss: 1.5772\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n", + "Epoch 3 / 5: 100%|██████████| 10/10 [04:21<00:00, 26.17s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-3, step-40 ---\n", + "loss: 0.1678\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:38<00:00, 19.31s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-3, step-40 ---\n", + "roc_auc_macro: nan\n", + "f1_macro: 0.0840\n", + "loss: 1.3590\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "Epoch 4 / 5: 100%|██████████| 10/10 [04:31<00:00, 27.15s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Train epoch-4, step-50 ---\n", + "loss: 0.1471\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "Evaluation: 100%|██████████| 2/2 [00:38<00:00, 19.38s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-4, step-50 ---\n", + "roc_auc_macro: nan\n", + "f1_macro: 0.1132\n", + "loss: 0.4798\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Training time: 1519.5 s\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Evaluation: 100%|██████████| 4/4 [01:14<00:00, 18.68s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Test ROC-AUC (macro): 0.5914\n", + " Test F1 (macro): 0.1033\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "results = []\n", + "\n", + "for cfg in ABLATION_CONFIGS:\n", + " print('\\n' + '='*70)\n", + " print(f\"Config: {cfg['name']}\")\n", + " print(f\" label_type={cfg['label_type']}, sampling_rate={cfg['sampling_rate']} Hz\")\n", + " print(f\" K={cfg['n_classes']} classes, T={cfg['T']} time-steps per lead\")\n", + " print('='*70)\n", + "\n", + " # ------------------------------------------------------------------\n", + " # 6.1 Task + SampleDataset\n", + " # ------------------------------------------------------------------\n", + " task = PTBXLMultilabelClassification(\n", + " label_type=cfg['label_type'],\n", + " sampling_rate=cfg['sampling_rate'],\n", + " )\n", + " sample_ds = base_dataset.set_task(task)\n", + " print(f' Total ML samples: {len(sample_ds)}')\n", + "\n", + " sample = sample_ds[0]\n", + " print(f' signal shape : {tuple(sample[\"signal\"].shape)}')\n", + " print(f' labels : {sample[\"labels\"]}')\n", + "\n", + " # ------------------------------------------------------------------\n", + " # 6.2 Train / val / test split\n", + " # Note: split_by_sample is used here for compatibility with the current\n", + " # cache (patient_id not stored in sample cache). In PTB-XL dev mode,\n", + " # each patient has exactly one record so this is equivalent to\n", + " # split_by_patient for data-leakage purposes.\n", + " # ------------------------------------------------------------------\n", + " train_ds, val_ds, test_ds = split_by_sample(sample_ds, SPLIT)\n", + " print(f' Train/Val/Test samples: {len(train_ds)}/{len(val_ds)}/{len(test_ds)}')\n", + "\n", + " train_loader = get_dataloader(train_ds, batch_size=BATCH_SIZE, shuffle=True)\n", + " val_loader = get_dataloader(val_ds, batch_size=BATCH_SIZE, shuffle=False)\n", + " test_loader = get_dataloader(test_ds, batch_size=BATCH_SIZE, shuffle=False)\n", + "\n", + " # steps_per_epoch must be set explicitly because litdata's StreamingDataset\n", + " # is an IterableDataset — len(dataloader) returns 0 for iterable dataloaders.\n", + " steps_per_epoch = max(1, len(train_ds) // BATCH_SIZE)\n", + " print(f' Steps per epoch: {steps_per_epoch}')\n", + "\n", + " # ------------------------------------------------------------------\n", + " # 6.3 Model — SE-ResNet-50\n", + " # SE-ResNet augments a ResNet-50 backbone with Squeeze-Excitation\n", + " # channel-attention gates (Hu et al. 2018) inside every bottleneck\n", + " # block. This version uses layer counts [3, 4, 6, 3] and expansion=4,\n", + " # matching the se_resnet1d50 variant in Nonaka & Seita (2021).\n", + " # ------------------------------------------------------------------\n", + " model = SEResNet50ECG(dataset=sample_ds)\n", + "\n", + " # ------------------------------------------------------------------\n", + " # 6.4 Train\n", + " # ------------------------------------------------------------------\n", + " trainer = Trainer(\n", + " model=model,\n", + " device=DEVICE,\n", + " enable_logging=False,\n", + " metrics=['roc_auc_macro', 'f1_macro'],\n", + " )\n", + "\n", + " t0 = time.time()\n", + " trainer.train(\n", + " train_dataloader=train_loader,\n", + " val_dataloader=val_loader,\n", + " optimizer_class=torch.optim.Adam,\n", + " optimizer_params={'lr': LEARNING_RATE},\n", + " epochs=EPOCHS,\n", + " steps_per_epoch=steps_per_epoch,\n", + " monitor=MONITOR,\n", + " )\n", + " elapsed = time.time() - t0\n", + " print(f' Training time: {elapsed:.1f} s')\n", + "\n", + " # ------------------------------------------------------------------\n", + " # 6.5 Evaluate on test set\n", + " # ------------------------------------------------------------------\n", + " test_metrics = trainer.evaluate(test_loader)\n", + " roc_auc = test_metrics.get('roc_auc_macro', float('nan'))\n", + " f1 = test_metrics.get('f1_macro', float('nan'))\n", + "\n", + " print(f' Test ROC-AUC (macro): {roc_auc:.4f}')\n", + " print(f' Test F1 (macro): {f1:.4f}')\n", + "\n", + " results.append({\n", + " 'config': cfg['name'],\n", + " 'label_type': cfg['label_type'],\n", + " 'sampling_rate': cfg['sampling_rate'],\n", + " 'K': cfg['n_classes'],\n", + " 'T': cfg['T'],\n", + " 'roc_auc_macro': roc_auc,\n", + " 'f1_macro': f1,\n", + " 'train_time_s': elapsed,\n", + " })\n" + ] + }, + { + "cell_type": "markdown", + "id": "b2a47542", + "metadata": {}, + "source": [ + "## 7. Results Summary" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "1eb2e622", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " config K T roc_auc_macro f1_macro train_time_s\n", + "A — superdiagnostic / 100 Hz (baseline) 5 1000 0.548581 0.442818 330.432782\n", + " B — superdiagnostic / 500 Hz 5 5000 0.758545 0.416275 1562.846845\n", + " C — diagnostic (27-class) / 100 Hz 27 1000 NaN 0.065967 332.799235\n", + " D — diagnostic (27-class) / 500 Hz 27 5000 0.591420 0.103268 1519.457779\n" + ] + } + ], + "source": [ + "results_df = pd.DataFrame(results)\n", + "display_cols = ['config', 'K', 'T', 'roc_auc_macro', 'f1_macro', 'train_time_s']\n", + "print(results_df[display_cols].to_string(index=False))" + ] + }, + { + "cell_type": "markdown", + "id": "2ba48914", + "metadata": {}, + "source": [ + "## 8. Visualisation — Ablation Results\n", + "\n", + "Bar charts comparing macro ROC-AUC and macro F1 across the four configs." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "88814df1", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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xokTJ9zn6fyW2Dn/m2++CZu+atUqN/3ss89O8J5OnTq514YMGZLgNZ2X6tev787jn332Wdhrw4cPD56HddxF2rVrlyv7qdGwYcPAZZdd5n5/9Jl//PFH1Pn0uUmdh/Ra5Hep9c+dO7fbBzpuo5Ut/d799NNPya7n888/75b/+uuvh00/fPhwwK/fAC3777//Dv4dy29bev52pcQ///wTKF++fKB48eKBxYsXR51n7ty5CfajH15++WVft33EiBGuDGubAT+RXg5kAt0tbtasmavRDU0pTQ+VK1d2/ysNLKV3elUroZqCefPm2b59+1wNWLly5dwdfD+oHVX9+vVdbUzknXXVViTWxko1mErpi6W9m2oavOWqpsNLA/SW51ebbm2TPkNpkIULF3bZBErtVSZDYlT7qdpNpYuqZlkppitXrow67+zZs6158+ZuuVq+arzeeuutNK+3ar1eeeUVu/76611bunPOOcfVNCRFx7JqRlTLo2NctXApPb5VA3jllVe61F0du6oN7dSpU1hTAC9VWVSzGdnsIqk23aoN1X7X/lStlJ7PmTMnwXze+9esWWNt27Z1+1Xbo/0Q2szCo/lUI5kc1Xaptka1zdo3kbTNqam5To/zhGprle6s4y3Ub7/95s4J+g60Xton9957r6tZDaUa83//+9+uttRLlb/gggvcdxNJx6WOB9WCKhshJVKyHvquvM8LPR5SU45V67l3794kU2WVmaPy1bNnz7Dpylg6duyYK4cpORf89ddfweyklFDqs2rjtS9CKatJ5VP7dezYsQnep/donSU0dVq1lo899pj7zvRdRPudUBpxtNr/xOjcpCwiZW+o1lw10Fp2ern//vvdd6N97P2+hdLnKV0/uVpu8c4VkdsdmXGVnrRspWSnB2VA6LfMK3P6HZ01a1aCY0ZNDHRcR1JtrvaBUqqTomNEx8ojjzzifl+iadWqld10001h05QxoN93pf7rvKlz+pIlSxK81/vtXbZsmbVo0cKdl/V716NHD5eO71GZ13EV+bud1G+2+krQeUnL07leTS+UoZJY3yOtW7d2ZTi7NllC9kHQDWQCXUD8/vvvwYujWB08eNBdzOuhC2ClO6o9npap1K+UUtqwgh394PXq1csFNwq407JuydGP4qeffupS1Ty6UNOFe6xtxpOitDS1/ROl0yqNXQ+lWfpJwbUC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343pbYUKFbIFCxaETStcuHDw+dy5c+1///uftWjRwnbu3JmqZW/atMkmT55sixcvtpzipptusuHDh9vMmTOtW7dumb062Q5BNwAAAIC4kjt3bjv//PMTff2xxx6zJ554wj2PDM6T8/zzz1utWrWsUaNGlpWp1vrkyZOWL1++ZOfNkyePde3a1Z555hmC7hiQXg4AAAAAEUF5rFQbfP311ydIaS9atKh999131qxZM1fTfu6557q/Dx8+bL1797ZSpUpZ1apVbcKECWHvXbZsmbVr184qV65sRYoUsYYNG9p//vOfBJ+7e/du69u3r1tGgQIFrGbNmjZ48ODg65dccoldddVV9vLLL9sZZ5zh5lFtvnejwJtWo0YNGzt2rAvIQyldftWqVcH3IOWo6QYAAAAQd44fP56gNlftt9Pi999/t3Xr1tmFF16Y4LVjx45Zly5dbMCAAVahQgW7//777dprr3Xzli9f3t544w2bM2eOe/28886zCy64wL3vr7/+cvP06tXLChYsaF9++aV1797dBcVanhw5csQuu+wy99kjR460evXq2d9//21LliwJW4dvv/3WzTNmzBgX5FerVs0mTpxo/fr1cwG7gvKlS5e6mwQK4h9//PHge9WmW+9Re/cGDRqkaT/FG4JuAAAAAHHlwIEDCdKqVXvcqVOnNC33m2++cf/Xr18/wWtHjx61Rx55xFq3bu3+VtB89dVXW9OmTe3JJ5900xQ4v/nmm+7hBd1qT+0JBAJ28cUX24YNG1zttBd0q3ZdteYKmFWT7vFe96h9utZRwbaXYq4AXJ+h1HG58sor3boqvV415WXKlAm+X9v19ddfp2kfxSOCbgAAAABxRendX3zxRdi0U089Nc3L/eeff1xqemig6tH0yy+/PPh37dq13f8tW7YMq21Xj+mqpfbs2rXL1V6rFnzjxo3BHsRDP2P+/PmuJjo04I5GQbMXcMuaNWts+/btCXpa79ixo40fP96WL18evEkgZcuWdduI1CHoBgAAABBXFAA3btw43Zer9tmqQY+Wpq5AP3/+/MG/veclS5YMm0/TtRyPOjBTDfaIESPsrLPOsuLFi9tzzz1ns2fPDs6zY8cO1+Y7OUprD6WAPtp07+/IntvV5vvQoUPJfg7CEXQDAAAAQDooXbq0a1+toFntr9NKy/nwww9d+rnaXHsiOzlTrff333+f7PIibwZofWXr1q1h07ds2RL2ukftvKPV4iNp9F4OAAAAAOlAPYDL2rVr02V5CuAVYIfWkO/bt8/ef//9sPmUov7zzz+nur211rdcuXKuDXkodeqmz1SHbqHUCZu3jUg5aroBAAAAIIR6DPc6RTt48KD98ccf9tZbb7m/I4cDC6UgNW/evLZixQrXxjqtSpQoYU2aNLGHH37YBcdatp5remjt9G233WbPPvustW3b1rX/Pvvss137b7Vbnzp1aqLLVxvy4cOHu97L1YN6mzZt7KuvvnIdvvXv3z+sVludz6kNuJaP1CHoBgAAAIAQCxcutG7dugX/njt3rnt4PYgnRuNoq+OxTz75JM09oXtee+01u+OOO1xP5AqCFSDv378/bDgvtbVWZ2pDhw61hx56yLXF1njdN998c7LLV9q62qErhV2Be6VKldyQYUOGDAmbb968ea5demjHakiZXIGkjho4e/fudXeT9uzZ4zouAAAAAOKJ2hYrZbpmzZrp0lY5J/vggw/slltuce2iCxcubDmFejgvVqyYzZgxw+LF4WSO+5TGibTpBgAAAIB0ctVVV7nhwKZNm2Y5hQLPjz76yNWkI/UIugEAAAAgnaiH8ClTpuSoWm61D1fbcI0hjtSjTTcAAAAApCN1fqZHTtG8eXP3QGyo6QYAAAAAwCcE3QAAAAAA+ISgGwAAAECKMPAR4kkgnY53gm4AAAAASdI4znLw4MHMXhUgw3jHu3f8x4qO1AAAAAAkKU+ePFayZEnbunWr+1s9c6uXbiCn1nAfPHjQHe867nX8pwVBNwAAAIBkVaxY0f3vBd5ATleyZMngcZ8WBN0AAAAAkqWa7UqVKln58uXt2LFjmb06gK+UUp7WGm4PQTcAAACAFFMgkl7BCBAP6EgNAAAAAACfEHQDAAAAAOATgm4AAAAAAHxC0A0AAAAAgE8IugEAAAAA8AlBNwAAAAAAPiHoBgAAAADAJwTdAAAAAAD4hKAbAAAAAACfEHQDAAAAAOATgm4AAAAAAHxC0A0AAAAAgE8IugEAAAAA8AlBNwAAAAAAPiHoBgAAAADAJwTdAAAAAAD4hKAbAAAAAACfEHQDAAAAAOATgm4AAAAAAHxC0A0AAAAAgE8IugEAAAAA8AlBNwAAAAAAPiHoBgAAAADAJwTdAAAAAAD4hKAbAAAAAACfEHQDAAAAAOATgm4AAAAAAHxC0A0AAAAAgE8IugEAAAAA8AlBNwAAAAAAPiHoBgAAAAAgXoLuyZMnW40aNaxgwYLWtGlTW758eZLzT5gwwc444wwrVKiQVatWzQYMGGCHDx9O0zIBAAAAAMhxQffs2bNt4MCBNnLkSFu5cqU1aNDAWrVqZVu3bo06/2uvvWYPPPCAm//nn3+26dOnu2UMGTIk5mUCAAAAAJBecgUCgYBlEaqFbtKkiU2aNMn9ffLkSVd73bdvXxdcR+rTp48LtufPnx+cds8999jXX39tS5YsiWmZ0ezdu9dKlChhe/bsseLFi6fT1gIAAAAAsquUxolZpqb76NGjtmLFCmvZsmVwWu7cud3fy5Yti/qeCy64wL3HSxf/888/7eOPP7Y2bdrEvEwAAAAAANJLXssitm/fbidOnLAKFSqETdffa9asifqeW265xb2vefPmpgr748ePW69evYLp5bEsU44cOeIeoXcwvFpyPQAAAAAA8e1kCmPDLBN0x2LRokX20EMP2bPPPuvSyH///Xe7++677cEHH7Thw4fHvNzx48fb6NGjE0zftm1bgk7aAMBPx44dc31SvPPOO5YrVy679tpr3fkpb96Ep+/TTjst7G9l+9SqVcsWLFjg/tb58d1337V8+fKF9XvRuHFj93zdunXupqX6v1DnlD169LC77rrL920EAADIjvbt25e9gu6yZctanjx5bMuWLWHT9XfFihWjvkeB9W233eYuDKVevXp24MABu/32223o0KExLVMGDx7sOl8LrelWO/By5crRphtAhho1apQLgn/66Sf3d9u2bV2nkdFuLEae+Bs2bGgdO3a08uXLu781gkPv3r3tqaeeSvBeZQVdfvnl1r59e/vkk09ccx11OqnRIZRVBAAAgHC6tspWQXf+/PmtUaNGrlO0Dh06BKvr9bc6TIvm4MGDro12KAXZonTzWJYpBQoUcI9I+qzIzwMAP7344osuSK5SpYr7WzcUBw0a5Gq/k6K+LlavXm3dunULnrdUU65HtPOYmtz88ssvLsjXubNu3brWvXt3mzZtmnXq1MmnrQMAAMi+UhobZqkIUrXLL7zwgr388suuV3LVyKjmWheN0rlzZ1cL7bn66qvtueees1mzZtnatWvts88+c7U/mu4F38ktEwCyql27dtmGDRtcjbVHz9evX+96yUyKasNb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/eyXX35xaf3Tp0+3p59+2j08GtLtnHPOsYsvvjjBe5s1a+ba3E+aNMm1I//yyy/t3XffdXeRAAAAACDugu6vvvrKxo8f79pV//bbb27awYMHbeXKlcFgDdmbOkKT0Fpt73mxYsUSzH/uuee6Mc01XNv5559vDzzwgM2ePdu99vvvv9uUKVNc4B1NmTJl7IMPPnC90mtYN71X6eWaDgAAAABxE3RrHOxrr73WDds1dOhQe+aZZ+zvv//+v4Xmzu06vwqt3UT2pR7Lq1ataqtWrQpO03O181YbhuSEdn6nztg0Tnrt2rXdOOrt27d3tdh6/vXXX7t5dEwtXbrUduzYYYsXL3Yd4LVo0cKnrQMAAACALBh0Dx8+3D788EN77rnnXCpxaDvcggUL2g033GBz5sxJr/VEJlNt87hx41wArId6Lu/Ro0fUed944w0XSOuYUO/jDz/8sF133XXuNXWaptpuBe16TJs2zdWW67lSzuW7776zI0eOuCHFNNzbokWLrH///hm6vQAAAACQqUOGvf76627M69tvv93VSEaqW7eu6+EaOYNusuh71vcqnTp1siFDhrjn6m1clDYuao+t40IdpmkYNw0fds8997jXChcu7B4epaHnypXL1aR7lDWhdtx6/wUXXGALFiywypUrZ+j2AgAAAECmBt1bt261evXqJfq62vOqbTdyhnz58rnexvWI5AXbni+++CLFy73kkkuCw4V5XnzxRfcAAAAAgLhNL1d73jVr1iT6unqdPv3002NdPAAAAAAA8Rt033LLLfb888/bsmXLgtOUJixqh6t2vZ07d06ftQQAAAAAIJ7Sy9VjuYYL01jLauergFvDhu3cudM2bNhgbdq0cX8DAAAAABCvYq7pzp8/v82dO9e1vT311FOtTp06rsfp+vXr20svveTGWla7bgAAAAAA4lVMNd0aykk13ZdeeqnrxVoPAAAAAACQDjXdhQoVcu25t2zZEsvbAQAAAACICzG36W7UqJH9+OOP6bs2SJNWD36U2auQLc0b3jazVwEAAABADhVzm+4JEybYrFmzbNq0aXb8+PH0XSsAAAAAAOK5prtr166WO3duu+OOO6xfv35WpUoVl3YeSj2a/+9//0uP9QQAAAAAIH6C7tKlS1uZMmXsjDPOSN81AgAAAAAg3oPuRYsWpe+aAAAAAACQw8TcphsAAAAAAPhU0y0nTpywV155xT766CP766+/3LTq1avbVVddZbfeeqvlyZMnLYsHAAAAACA+a7r37NljF154of373/+2Tz/91I4dO+Yen332mXXr1s2aN29ue/fuTd+1BQAAAAAgHoLuoUOH2ooVK2zixIm2bds2W7lypXts3brVJk2aZN9++62bBwAAAACAeBVz0P3uu+/anXfe6R758uULTtfz3r17u8fbb7+dXusJAAAAAED8BN07duxIcriwOnXq2M6dO2NdPAAAAAAA8Rt0n3766fb+++8n+rpeO+2002JdPAAAAAAA8Rt0K61cHai1adPG/b9u3Tr3mDdvnrVt29Z1qNanT5/0XVsAAAAAAOJhyDAF3eo07eGHH3aBdii16x4xYoRr1w0AAAAAQLxK0zjdo0aNcrXZn3/+edg43S1btrSyZcum1zoCAAAAABB/QbcouL7pppvSZ20AAAAAAMhBYm7TrdrtIUOGJPq6xuhesGBBrIsHAAAAACB+g+4HH3zQ/v7770Rf37hxo40dOzbWxQMAAAAAEL9B9w8//GBNmzZN9PUmTZrY999/H+viAQAAAACI36D7yJEjdvTo0SRfP3jwYKyLBwAAAAAgfoPus88+2959992orwUCAXvnnXfszDPPTMu6AQAAAAAQn0F337597csvv7QbbrjBpZofP37cPZRSrmnLli1z8wAAAAAAEK9iHjKsU6dO9scff7gO1VSrnTv3/8XvJ0+etFy5ctmwYcOsS5cu6bmuAAAAAADEzzjdI0eOdMG30sz//PNPN+20006zDh06uP8BAAAAAIhnMaeXexRcDxo0yPr162eVKlVytd8fffSR7d27N33WEAAAAACAeAi6J02aZLVr17bt27eHTf/www+tYcOGNmrUKJsyZYr179/fzj333ATzAfHm2LFj1qdPHytVqpSVLl3a9XOgvg+ScujQITv99NOtZMmSUV/fsmWLW5bKXOhoAZdccomVL1/eihcvbnXq1LGpU6em+/YAAAAA8DHofv/9913NdtmyZYPTFEB0797d8uTJYzNmzHCdqj388MP2119/2bhx41K5OkDOMnbsWFuyZImtXr3afvrpJ1u8eLE99NBDSb5nxIgRVr169URfVxB/zjnnhE3LmzevTZw40TZt2uSyTNTPwvDhw93nAQAAAMgmQbcCh/PPPz9s2sKFC23btm02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jsg41HQjLule0+uvv26XXXaZDRw40Hbs2GFz587N7NUC4lKxYsXs2LFjmb0aQI71xBNPWIUKFVzQHQ0BN5Bx1LRq4sSJdvToUZs2bVpmrw4yCEE34pLSfNatW2e33HKLtWrVysqUKUOKOZBBjh8/7h779u1zbUzffvttu/766zN7tYAcSWXtyy+/tMsvv5xsLiCLOPPMM61KlSou9RzxIW9mrwCQGRRg607jtdde6y5CdMH/n//8x6UAFS1aNLNXD8ixDhw4kODCX+1J1d4UQPpTJteRI0fslFNOyexVARCiWrVqtnnz5sxeDWQQaroRl3f933zzTWvTpo3rUEZU433w4EF79913M3v1gBzfqcw333zjHmrP9vTTT7umHT179szsVQNyNPWgDCBrNXWkXMYParoRdz799FPbtm2bXX311bZ79243TUM4VKpUydWA33bbbZm9ikCOpWFTGjduHPz7wgsvdDfC7rnnHte/wtlnn52p6wfkNGo+pcyu9evXZ/aqAAixYcMGq127dmavBjIINd2IO17bbQ3bUKpUKffQmKXqvVzDOGzdujWzVxGIKxqr2xvGD0D6yps3r7u5NX/+fHeDC0Dm0++dRu244IILMntVkEEIuhFXlEI+Z84c69Chgy1cuDDsod7MdUEye/bszF5NIK78+OOP7v+yZctm9qoAOZKySNR2dNy4cYkOKQYgY2jIsL59+1qBAgWsR48emb06yCCklyOuKOBWZ2n9+vWzSy65JMHrjz76qKsJ18kQQPo7efKkffXVV+65hktZsWKFjR071vXkevHFF2f26gE5kvowue+++9y4watXr7abbrrJ3eRau3atzZgxw/bs2ePmAeDfb56uP3/44QebOnWq/fnnn/bSSy9ZjRo1MnsVkUEIuhFXFFCrB9doAbd06dLF+vfvb3/88YeddtppGb5+QE536NAha9asWTDtVb23durUyUaOHMlwRoCPHnnkEZfKOmnSJPv3v//tRhLQkEUaNnPQoEGZvXpAjv/N0+g4CrI1fJ867q1Tp05mrx4yUK6Aus4DAAAAAADpjjbdAAAAAAD4hKAbAAAAAACfEHQDAAAAAOATgm4AAAAAAHxC0I2YnHfeeTZ58mTLrn799VfLlSuXrV+/3g1bpKFUNFxRkSJF3PTt27dHfd/SpUtdL5SFChWy6tWru95gI/si1N8P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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Figure saved to ptbxl_se_resnet_ablation_results.png\n" + ] + } + ], + "source": [ + "# Short labels for the x-axis\n", + "short_labels = ['A\\n(super/100Hz)', 'B\\n(super/500Hz)', 'C\\n(diag/100Hz)', 'D\\n(diag/500Hz)']\n", + "\n", + "auc_vals = results_df['roc_auc_macro'].tolist()\n", + "f1_vals = results_df['f1_macro'].tolist()\n", + "\n", + "x = np.arange(len(short_labels))\n", + "width = 0.35\n", + "\n", + "fig, ax = plt.subplots(figsize=(10, 5))\n", + "bars_auc = ax.bar(x - width/2, auc_vals, width, label='ROC-AUC (macro)', color='steelblue')\n", + "bars_f1 = ax.bar(x + width/2, f1_vals, width, label='F1 (macro)', color='coral')\n", + "\n", + "ax.set_xticks(x)\n", + "ax.set_xticklabels(short_labels, fontsize=11)\n", + "ax.set_ylim(0, 1.05)\n", + "ax.yaxis.set_major_formatter(mticker.FormatStrFormatter('%.2f'))\n", + "ax.set_ylabel('Score', fontsize=12)\n", + "ax.set_title('PTB-XL Multi-Label Ablation: SE-ResNet-50 (ROC-AUC & F1 by Config)', fontsize=13)\n", + "ax.legend(fontsize=11)\n", + "ax.bar_label(bars_auc, fmt='%.3f', padding=3, fontsize=9)\n", + "ax.bar_label(bars_f1, fmt='%.3f', padding=3, fontsize=9)\n", + "ax.grid(axis='y', alpha=0.3)\n", + "\n", + "plt.tight_layout()\n", + "plt.savefig('ptbxl_se_resnet_ablation_results.png', dpi=150)\n", + "plt.show()\n", + "print('Figure saved to ptbxl_se_resnet_ablation_results.png')" + ] + }, + { + "cell_type": "markdown", + "id": "fea96a63", + "metadata": {}, + "source": [ + "## 9. Analysis & Findings\n", + "\n", + "### Effect of Label Granularity\n", + "\n", + "Comparing configs **A vs C** (both at 100 Hz): moving from the 5-class\n", + "**superdiagnostic** vocabulary to the 27-class **diagnostic** vocabulary\n", + "increases classification difficulty because:\n", + "\n", + "* Rare classes have far fewer positive examples, making gradient updates noisy.\n", + "* The larger output head must learn $K = 27$ independent sigmoid thresholds.\n", + "* Macro averaging penalises poor performance on rare labels equally.\n", + "\n", + "Formally, the expected macro-AUC satisfies\n", + "$$\\overline{\\text{AUC}}_{27} \\leq \\overline{\\text{AUC}}_{5}$$\n", + "when the 27-class problem is strictly harder per class.\n", + "\n", + "### Effect of Sampling Rate\n", + "\n", + "Comparing configs **A vs B** (both superdiagnostic): at 500 Hz ($T = 5000$)\n", + "the model receives 5× more temporal resolution per lead. This allows the\n", + "model to detect high-frequency features (notches, fragmented QRS) that are\n", + "aliased away at 100 Hz. However:\n", + "\n", + "* Input size grows by 5×, substantially increasing memory and training time.\n", + "* SE-ResNet-50's bottleneck blocks use successive strided convolutions to\n", + " progressively downsample, so the effective receptive field scales with $T$;\n", + " the model may not fully exploit the extra resolution within 5 epochs.\n", + "\n", + "### Trade-off\n", + "\n", + "Config **B** (superdiagnostic / 500 Hz) is expected to achieve the highest\n", + "absolute AUC if sufficient epochs are used, while Config **D**\n", + "(diagnostic / 500 Hz) is the most challenging in both accuracy and\n", + "compute cost.\n", + "\n", + "These findings closely mirror the ablation tables in Strodthoff *et al.*\n", + "(2021), where superdiagnostic tasks consistently outperform the fine-grained\n", + "ones and the 500 Hz models narrow the gap only when trained for ≥ 100 epochs." + ] + }, + { + "cell_type": "markdown", + "id": "464a7b21", + "metadata": {}, + "source": [ + "## 10. References\n", + "\n", + "1. Wagner, P. *et al.* (2020). PTB-XL, a large publicly available electrocardiography dataset.\n", + " *Scientific Data* 7, 154. https://doi.org/10.1038/s41597-020-0495-6\n", + "\n", + "2. Reyna, M.A. *et al.* (2020). Will Two Do? Varying Dimensions in Electrocardiography:\n", + " The PhysioNet/Computing in Cardiology Challenge 2020. *CinC 2020*.\n", + "\n", + "3. Strodthoff, N. *et al.* (2021). Deep Learning for ECG Analysis: Benchmarks and Insights\n", + " from PTB-XL. *IEEE JBHI* 25, 1519–1528.\n", + "\n", + "4. Nonaka, N. & Seita, J. (2021). In-depth Benchmarking of Deep Neural Network Architectures\n", + " for ECG Diagnosis. *Machine Learning for Healthcare (MLHC) 2021*.\n", + "\n", + "5. Hu, J. *et al.* (2018). Squeeze-and-Excitation Networks. *CVPR 2018*.\n", + "\n", + "6. Zhao, M. *et al.* (2024). PyHealth: A Deep Learning Toolkit for Healthcare Predictive\n", + " Modeling. *arXiv:2401.06284*." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From d589973bce0de4aa123ed8d8864c27392def94e9 Mon Sep 17 00:00:00 2001 From: AnuragD2 Date: Sun, 19 Apr 2026 22:37:41 -0700 Subject: [PATCH 30/39] Added ResNet18/LambdaResNet18 baselines, fix BiLSTMECG bugs, fix viz QUICK_MODE, run full 2x2 ablation - bilstm_ecg.py: fix super().__init__ kwargs, hardcode in_channels=12, fix forward() label API - ptbxl_multilabel_classification.py: fix getattr namespace (ptbxl/mat->mat, ptbxl/dx_codes->dx_codes), add patient_id to samples - notebook: add ResNet18ECG + LambdaResNet18ECG, QUICK_MODE flag, viz fix, Section 4 markdown correction - ablation chart: ptbxl_model_comparison_ablation.png (4 models x 4 configs, ~1000 patients dev subset, 5 epochs) --- examples/ptbxl_model_comparison_ablation.png | Bin 0 -> 85658 bytes .../ptbxl_superdiagnostic_se_resnet.ipynb | 2978 +++-------------- pyhealth/models/bilstm_ecg.py | 26 +- .../tasks/ptbxl_multilabel_classification.py | 10 +- 4 files changed, 410 insertions(+), 2604 deletions(-) create mode 100644 examples/ptbxl_model_comparison_ablation.png diff --git a/examples/ptbxl_model_comparison_ablation.png b/examples/ptbxl_model_comparison_ablation.png new file mode 100644 index 0000000000000000000000000000000000000000..41c77a1619b11ef34e0955a845bb7eb2152cb7e3 GIT binary patch literal 85658 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 **Note:** The CSV file must already exist in the same directory as the `.hea` / `.mat` files. The path used here is:\n", + "> ```\n", + "> .../training/ptb-xl/ptbxl_database.csv\n", + "> ```" ] }, + { + "cell_type": "markdown", + "id": "382acc8a", + "metadata": {}, + "source": [] + }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 6, "id": "19663067", "metadata": {}, "outputs": [ @@ -323,7 +401,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 13, "id": "ae5daef7", "metadata": {}, "outputs": [ @@ -340,7 +418,7 @@ } ], "source": [ - "ABLATION_CONFIGS = [\n", + "_ALL_CONFIGS = [\n", " {\n", " 'name': 'A — superdiagnostic / 100 Hz (baseline)',\n", " 'label_type': 'superdiagnostic',\n", @@ -371,9 +449,15 @@ " },\n", "]\n", "\n", + "# Apply QUICK_MODE filter — only config A when enabled\n", + "ABLATION_CONFIGS = _ALL_CONFIGS[:1] if QUICK_MODE else _ALL_CONFIGS\n", + "\n", "print('Ablation configurations:')\n", "for cfg in ABLATION_CONFIGS:\n", - " print(f\" {cfg['name']} → K={cfg['n_classes']}, T={cfg['T']}\")" + " print(f\" {cfg['name']} → K={cfg['n_classes']}, T={cfg['T']}\")\n", + "if QUICK_MODE:\n", + " print('\\n[QUICK_MODE] Running config A only (superdiagnostic/100Hz). '\n", + " 'Set QUICK_MODE=False to run all 4 configs.')\n" ] }, { @@ -383,20 +467,20 @@ "source": [ "## 6. Training Loop\n", "\n", - "For each configuration we:\n", + "For each **model × configuration** pair (4 models × 4 configs = 16 runs) we:\n", "\n", "1. **Define task** — `PTBXLMultilabelClassification(label_type, sampling_rate)`\n", "2. **Apply task** — `base_dataset.set_task(task)` → `SampleDataset`\n", - "3. **Split** — 70 % train / 10 % val / 20 % test (by patient to avoid leakage)\n", - "4. **Instantiate SE-ResNet-50** — initialised from the `SampleDataset`\n", + "3. **Split** — 70 % train / 10 % val / 20 % test (by sample; equivalent to by-patient in dev mode)\n", + "4. **Instantiate model** — `ResNet18ECG`, `SEResNet50ECG`, `LambdaResNet18ECG`, or `BiLSTMECG` from the `MODELS` registry\n", "5. **Train** with `Trainer`, monitoring macro ROC-AUC on the validation set\n", "6. **Evaluate** on the held-out test set: macro ROC-AUC + macro F1" ] }, { "cell_type": "code", - "execution_count": 21, - "id": "5f46408f", + "execution_count": null, + "id": "f1c34a69", "metadata": {}, "outputs": [ { @@ -405,6 +489,7 @@ "text": [ "\n", "======================================================================\n", + "Model : ResNet-18\n", "Config: A — superdiagnostic / 100 Hz (baseline)\n", " label_type=superdiagnostic, sampling_rate=100 Hz\n", " K=5 classes, T=1000 time-steps per lead\n", @@ -417,7 +502,7 @@ " labels : tensor([0., 1., 0., 1., 0.])\n", " Train/Val/Test samples: 697/100/200\n", " Steps per epoch: 10\n", - "SEResNet50ECG(\n", + "ResNet18ECG(\n", " (backbone): ResNet1d(\n", " (stem): Sequential(\n", " (0): Conv1d(12, 64, kernel_size=(7,), stride=(2,), padding=(3,), bias=False)\n", @@ -427,288 +512,84 @@ " )\n", " (stages): ModuleList(\n", " (0): Sequential(\n", - " (0): SEResNetBottleneck1d(\n", - " (conv1): Conv1d(64, 64, kernel_size=(1,), stride=(1,), bias=False)\n", + " (0): BasicBlock1d(\n", + " (conv1): Conv1d(64, 64, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", " (bn1): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv2): Conv1d(64, 64, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", - " (bn2): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv3): Conv1d(64, 256, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn3): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " (relu): ReLU(inplace=True)\n", - " (se_module): SEModule1d(\n", - " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", - " (fc1): Conv1d(256, 16, kernel_size=(1,), stride=(1,))\n", - " (relu): ReLU(inplace=True)\n", - " (fc2): Conv1d(16, 256, kernel_size=(1,), stride=(1,))\n", - " (sigmoid): Sigmoid()\n", - " )\n", - " (downsample): Sequential(\n", - " (0): Conv1d(64, 256, kernel_size=(1,), stride=(1,), bias=False)\n", - " (1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (1): SEResNetBottleneck1d(\n", - " (conv1): Conv1d(256, 64, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn1): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " (conv2): Conv1d(64, 64, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", " (bn2): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv3): Conv1d(64, 256, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn3): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (relu): ReLU(inplace=True)\n", - " (se_module): SEModule1d(\n", - " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", - " (fc1): Conv1d(256, 16, kernel_size=(1,), stride=(1,))\n", - " (relu): ReLU(inplace=True)\n", - " (fc2): Conv1d(16, 256, kernel_size=(1,), stride=(1,))\n", - " (sigmoid): Sigmoid()\n", - " )\n", " )\n", - " (2): SEResNetBottleneck1d(\n", - " (conv1): Conv1d(256, 64, kernel_size=(1,), stride=(1,), bias=False)\n", + " (1): BasicBlock1d(\n", + " (conv1): Conv1d(64, 64, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", " (bn1): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu): ReLU(inplace=True)\n", " (conv2): Conv1d(64, 64, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", " (bn2): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv3): Conv1d(64, 256, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn3): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (relu): ReLU(inplace=True)\n", - " (se_module): SEModule1d(\n", - " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", - " (fc1): Conv1d(256, 16, kernel_size=(1,), stride=(1,))\n", - " (relu): ReLU(inplace=True)\n", - " (fc2): Conv1d(16, 256, kernel_size=(1,), stride=(1,))\n", - " (sigmoid): Sigmoid()\n", - " )\n", " )\n", " )\n", " (1): Sequential(\n", - " (0): SEResNetBottleneck1d(\n", - " (conv1): Conv1d(256, 128, kernel_size=(1,), stride=(2,), bias=False)\n", + " (0): BasicBlock1d(\n", + " (conv1): Conv1d(64, 128, kernel_size=(3,), stride=(2,), padding=(1,), bias=False)\n", " (bn1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv2): Conv1d(128, 128, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", - " (bn2): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv3): Conv1d(128, 512, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn3): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " (relu): ReLU(inplace=True)\n", - " (se_module): SEModule1d(\n", - " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", - " (fc1): Conv1d(512, 32, kernel_size=(1,), stride=(1,))\n", - " (relu): ReLU(inplace=True)\n", - " (fc2): Conv1d(32, 512, kernel_size=(1,), stride=(1,))\n", - " (sigmoid): Sigmoid()\n", - " )\n", - " (downsample): Sequential(\n", - " (0): Conv1d(256, 512, kernel_size=(1,), stride=(2,), bias=False)\n", - " (1): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (1): SEResNetBottleneck1d(\n", - " (conv1): Conv1d(512, 128, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " (conv2): Conv1d(128, 128, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", " (bn2): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv3): Conv1d(128, 512, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn3): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (relu): ReLU(inplace=True)\n", - " (se_module): SEModule1d(\n", - " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", - " (fc1): Conv1d(512, 32, kernel_size=(1,), stride=(1,))\n", - " (relu): ReLU(inplace=True)\n", - " (fc2): Conv1d(32, 512, kernel_size=(1,), stride=(1,))\n", - " (sigmoid): Sigmoid()\n", + " (downsample): Sequential(\n", + " (0): Conv1d(64, 128, kernel_size=(1,), stride=(2,), bias=False)\n", + " (1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " )\n", " )\n", - " (2): SEResNetBottleneck1d(\n", - " (conv1): Conv1d(512, 128, kernel_size=(1,), stride=(1,), bias=False)\n", + " (1): BasicBlock1d(\n", + " (conv1): Conv1d(128, 128, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", " (bn1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv2): Conv1d(128, 128, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", - " (bn2): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv3): Conv1d(128, 512, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn3): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " (relu): ReLU(inplace=True)\n", - " (se_module): SEModule1d(\n", - " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", - " (fc1): Conv1d(512, 32, kernel_size=(1,), stride=(1,))\n", - " (relu): ReLU(inplace=True)\n", - " (fc2): Conv1d(32, 512, kernel_size=(1,), stride=(1,))\n", - " (sigmoid): Sigmoid()\n", - " )\n", - " )\n", - " (3): SEResNetBottleneck1d(\n", - " (conv1): Conv1d(512, 128, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " (conv2): Conv1d(128, 128, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", " (bn2): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv3): Conv1d(128, 512, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn3): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (relu): ReLU(inplace=True)\n", - " (se_module): SEModule1d(\n", - " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", - " (fc1): Conv1d(512, 32, kernel_size=(1,), stride=(1,))\n", - " (relu): ReLU(inplace=True)\n", - " (fc2): Conv1d(32, 512, kernel_size=(1,), stride=(1,))\n", - " (sigmoid): Sigmoid()\n", - " )\n", " )\n", " )\n", " (2): Sequential(\n", - " (0): SEResNetBottleneck1d(\n", - " (conv1): Conv1d(512, 256, kernel_size=(1,), stride=(2,), bias=False)\n", - " (bn1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv2): Conv1d(256, 256, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", - " (bn2): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv3): Conv1d(256, 1024, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn3): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (relu): ReLU(inplace=True)\n", - " (se_module): SEModule1d(\n", - " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", - " (fc1): Conv1d(1024, 64, kernel_size=(1,), stride=(1,))\n", - " (relu): ReLU(inplace=True)\n", - " (fc2): Conv1d(64, 1024, kernel_size=(1,), stride=(1,))\n", - " (sigmoid): Sigmoid()\n", - " )\n", - " (downsample): Sequential(\n", - " (0): Conv1d(512, 1024, kernel_size=(1,), stride=(2,), bias=False)\n", - " (1): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (1): SEResNetBottleneck1d(\n", - " (conv1): Conv1d(1024, 256, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv2): Conv1d(256, 256, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", - " (bn2): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv3): Conv1d(256, 1024, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn3): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (relu): ReLU(inplace=True)\n", - " (se_module): SEModule1d(\n", - " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", - " (fc1): Conv1d(1024, 64, kernel_size=(1,), stride=(1,))\n", - " (relu): ReLU(inplace=True)\n", - " (fc2): Conv1d(64, 1024, kernel_size=(1,), stride=(1,))\n", - " (sigmoid): Sigmoid()\n", - " )\n", - " )\n", - " (2): SEResNetBottleneck1d(\n", - " (conv1): Conv1d(1024, 256, kernel_size=(1,), stride=(1,), bias=False)\n", + " (0): BasicBlock1d(\n", + " (conv1): Conv1d(128, 256, kernel_size=(3,), stride=(2,), padding=(1,), bias=False)\n", " (bn1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv2): Conv1d(256, 256, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", - " (bn2): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv3): Conv1d(256, 1024, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn3): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " (relu): ReLU(inplace=True)\n", - " (se_module): SEModule1d(\n", - " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", - " (fc1): Conv1d(1024, 64, kernel_size=(1,), stride=(1,))\n", - " (relu): ReLU(inplace=True)\n", - " (fc2): Conv1d(64, 1024, kernel_size=(1,), stride=(1,))\n", - " (sigmoid): Sigmoid()\n", - " )\n", - " )\n", - " (3): SEResNetBottleneck1d(\n", - " (conv1): Conv1d(1024, 256, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " (conv2): Conv1d(256, 256, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", " (bn2): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv3): Conv1d(256, 1024, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn3): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (relu): ReLU(inplace=True)\n", - " (se_module): SEModule1d(\n", - " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", - " (fc1): Conv1d(1024, 64, kernel_size=(1,), stride=(1,))\n", - " (relu): ReLU(inplace=True)\n", - " (fc2): Conv1d(64, 1024, kernel_size=(1,), stride=(1,))\n", - " (sigmoid): Sigmoid()\n", + " (downsample): Sequential(\n", + " (0): Conv1d(128, 256, kernel_size=(1,), stride=(2,), bias=False)\n", + " (1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " )\n", " )\n", - " (4): SEResNetBottleneck1d(\n", - " (conv1): Conv1d(1024, 256, kernel_size=(1,), stride=(1,), bias=False)\n", + " (1): BasicBlock1d(\n", + " (conv1): Conv1d(256, 256, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", " (bn1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv2): Conv1d(256, 256, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", - " (bn2): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv3): Conv1d(256, 1024, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn3): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " (relu): ReLU(inplace=True)\n", - " (se_module): SEModule1d(\n", - " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", - " (fc1): Conv1d(1024, 64, kernel_size=(1,), stride=(1,))\n", - " (relu): ReLU(inplace=True)\n", - " (fc2): Conv1d(64, 1024, kernel_size=(1,), stride=(1,))\n", - " (sigmoid): Sigmoid()\n", - " )\n", - " )\n", - " (5): SEResNetBottleneck1d(\n", - " (conv1): Conv1d(1024, 256, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " (conv2): Conv1d(256, 256, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", " (bn2): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv3): Conv1d(256, 1024, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn3): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (relu): ReLU(inplace=True)\n", - " (se_module): SEModule1d(\n", - " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", - " (fc1): Conv1d(1024, 64, kernel_size=(1,), stride=(1,))\n", - " (relu): ReLU(inplace=True)\n", - " (fc2): Conv1d(64, 1024, kernel_size=(1,), stride=(1,))\n", - " (sigmoid): Sigmoid()\n", - " )\n", " )\n", " )\n", " (3): Sequential(\n", - " (0): SEResNetBottleneck1d(\n", - " (conv1): Conv1d(1024, 512, kernel_size=(1,), stride=(2,), bias=False)\n", + " (0): BasicBlock1d(\n", + " (conv1): Conv1d(256, 512, kernel_size=(3,), stride=(2,), padding=(1,), bias=False)\n", " (bn1): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (relu): ReLU(inplace=True)\n", " (conv2): Conv1d(512, 512, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", " (bn2): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv3): Conv1d(512, 2048, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn3): BatchNorm1d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (relu): ReLU(inplace=True)\n", - " (se_module): SEModule1d(\n", - " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", - " (fc1): Conv1d(2048, 128, kernel_size=(1,), stride=(1,))\n", - " (relu): ReLU(inplace=True)\n", - " (fc2): Conv1d(128, 2048, kernel_size=(1,), stride=(1,))\n", - " (sigmoid): Sigmoid()\n", - " )\n", " (downsample): Sequential(\n", - " (0): Conv1d(1024, 2048, kernel_size=(1,), stride=(2,), bias=False)\n", - " (1): BatchNorm1d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (0): Conv1d(256, 512, kernel_size=(1,), stride=(2,), bias=False)\n", + " (1): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " )\n", " )\n", - " (1): SEResNetBottleneck1d(\n", - " (conv1): Conv1d(2048, 512, kernel_size=(1,), stride=(1,), bias=False)\n", + " (1): BasicBlock1d(\n", + " (conv1): Conv1d(512, 512, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", " (bn1): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv2): Conv1d(512, 512, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", - " (bn2): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv3): Conv1d(512, 2048, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn3): BatchNorm1d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " (relu): ReLU(inplace=True)\n", - " (se_module): SEModule1d(\n", - " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", - " (fc1): Conv1d(2048, 128, kernel_size=(1,), stride=(1,))\n", - " (relu): ReLU(inplace=True)\n", - " (fc2): Conv1d(128, 2048, kernel_size=(1,), stride=(1,))\n", - " (sigmoid): Sigmoid()\n", - " )\n", - " )\n", - " (2): SEResNetBottleneck1d(\n", - " (conv1): Conv1d(2048, 512, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn1): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " (conv2): Conv1d(512, 512, kernel_size=(3,), stride=(1,), padding=(1,), bias=False)\n", " (bn2): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (conv3): Conv1d(512, 2048, kernel_size=(1,), stride=(1,), bias=False)\n", - " (bn3): BatchNorm1d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (relu): ReLU(inplace=True)\n", - " (se_module): SEModule1d(\n", - " (avg_pool): AdaptiveAvgPool1d(output_size=1)\n", - " (fc1): Conv1d(2048, 128, kernel_size=(1,), stride=(1,))\n", - " (relu): ReLU(inplace=True)\n", - " (fc2): Conv1d(128, 2048, kernel_size=(1,), stride=(1,))\n", - " (sigmoid): Sigmoid()\n", - " )\n", " )\n", " )\n", " )\n", " (gap): AdaptiveAvgPool1d(output_size=1)\n", - " (proj): Linear(in_features=2048, out_features=256, bias=True)\n", + " (proj): Linear(in_features=512, out_features=256, bias=True)\n", " )\n", " (head): Sequential(\n", " (0): Linear(in_features=256, out_features=128, bias=True)\n", @@ -727,7 +608,7 @@ "Optimizer params: {'lr': 0.01}\n", "Weight decay: 0.0\n", "Max grad norm: None\n", - "Val dataloader: \n", + "Val dataloader: \n", "Monitor: roc_auc_macro\n", "Monitor criterion: max\n", "Epochs: 5\n", @@ -739,2330 +620,226 @@ "name": "stderr", "output_type": "stream", "text": [ - "Epoch 0 / 5: 100%|██████████| 10/10 [00:58<00:00, 5.86s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-0, step-10 ---\n", - "loss: 0.6510\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.18s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-0, step-10 ---\n", - "roc_auc_macro: 0.5000\n", - "f1_macro: 0.2881\n", - "loss: 1799.7626\n", - "New best roc_auc_macro score (0.5000) at epoch-0, step-10\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Epoch 1 / 5: 100%|██████████| 10/10 [00:57<00:00, 5.77s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-1, step-20 ---\n", - "loss: 0.5082\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.15s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-1, step-20 ---\n", - "roc_auc_macro: 0.5893\n", - "f1_macro: 0.4301\n", - "loss: 11.1620\n", - "New best roc_auc_macro score (0.5893) at epoch-1, step-20\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Epoch 2 / 5: 100%|██████████| 10/10 [00:57<00:00, 5.74s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-2, step-30 ---\n", - "loss: 0.5061\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.15s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-2, step-30 ---\n", - "roc_auc_macro: 0.5173\n", - "f1_macro: 0.3381\n", - "loss: 7.2917\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Epoch 3 / 5: 100%|██████████| 10/10 [00:57<00:00, 5.75s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-3, step-40 ---\n", - "loss: 0.4987\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.16s/it]" + "Epoch 0 / 5: 0%| | 0/10 [00:00" + ] + }, + "metadata": {}, + "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ - "--- Train epoch-4, step-50 ---\n", - "loss: 0.4959\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.20s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-4, step-50 ---\n", - "roc_auc_macro: 0.5381\n", - "f1_macro: 0.4151\n", - "loss: 0.8571\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " Training time: 330.4 s\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Evaluation: 100%|██████████| 4/4 [00:16<00:00, 4.02s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " Test ROC-AUC (macro): 0.5486\n", - " Test F1 (macro): 0.4428\n", - "\n", - "======================================================================\n", - "Config: B — superdiagnostic / 500 Hz\n", - " label_type=superdiagnostic, sampling_rate=500 Hz\n", - " K=5 classes, T=5000 time-steps per lead\n", - "======================================================================\n", - "Setting task PTBXLSuperDiagnostic_500Hz for ptbxl base dataset...\n", - "Task cache paths: task_df=/Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/tasks/PTBXLSuperDiagnostic_500Hz_a3e4fee9-a5e6-5af9-94ad-7eeb5e82e80a/task_df.ld, samples=/Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/tasks/PTBXLSuperDiagnostic_500Hz_a3e4fee9-a5e6-5af9-94ad-7eeb5e82e80a/samples_cdbbc602-34e2-5a41-8643-4c76b08829f6.ld\n", - "Applying task transformations on data with 1 workers...\n", - "Detected Jupyter notebook environment, setting num_workers to 1\n", - "Single worker mode, processing sequentially\n", - "Worker 0 started processing 1000 patients. (Polars threads: 10)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - " 0%| | 0/1000 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", - "Weight decay: 0.0\n", - "Max grad norm: None\n", - "Val dataloader: \n", - "Monitor: roc_auc_macro\n", - "Monitor criterion: max\n", - "Epochs: 5\n", - "Patience: None\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0 / 5: 100%|██████████| 10/10 [04:30<00:00, 27.02s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-0, step-10 ---\n", - "loss: 0.6516\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:39<00:00, 19.73s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-0, step-10 ---\n", - "roc_auc_macro: 0.5000\n", - "f1_macro: 0.2682\n", - "loss: 62327.4609\n", - "New best roc_auc_macro score (0.5000) at epoch-0, step-10\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Epoch 1 / 5: 100%|██████████| 10/10 [04:36<00:00, 27.62s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-1, step-20 ---\n", - "loss: 0.5282\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:39<00:00, 19.74s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-1, step-20 ---\n", - "roc_auc_macro: 0.3996\n", - "f1_macro: 0.1906\n", - "loss: 160.0568\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Epoch 2 / 5: 100%|██████████| 10/10 [04:39<00:00, 27.99s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-2, step-30 ---\n", - "loss: 0.5021\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:39<00:00, 19.55s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-2, step-30 ---\n", - "roc_auc_macro: 0.6734\n", - "f1_macro: 0.4020\n", - "loss: 1.1237\n", - "New best roc_auc_macro score (0.6734) at epoch-2, step-30\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Epoch 3 / 5: 100%|██████████| 10/10 [04:34<00:00, 27.48s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-3, step-40 ---\n", - "loss: 0.4587\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:39<00:00, 19.55s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-3, step-40 ---\n", - "roc_auc_macro: 0.6882\n", - "f1_macro: 0.3906\n", - "loss: 0.5006\n", - "New best roc_auc_macro score (0.6882) at epoch-3, step-40\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Epoch 4 / 5: 100%|██████████| 10/10 [04:25<00:00, 26.58s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-4, step-50 ---\n", - "loss: 0.4874\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:38<00:00, 19.38s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-4, step-50 ---\n", - "roc_auc_macro: 0.7684\n", - "f1_macro: 0.4205\n", - "loss: 0.4232\n", - "New best roc_auc_macro score (0.7684) at epoch-4, step-50\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " Training time: 1562.8 s\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Evaluation: 100%|██████████| 4/4 [01:14<00:00, 18.60s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " Test ROC-AUC (macro): 0.7585\n", - " Test F1 (macro): 0.4163\n", - "\n", - "======================================================================\n", - "Config: C — diagnostic (27-class) / 100 Hz\n", - " label_type=diagnostic, sampling_rate=100 Hz\n", - " K=27 classes, T=1000 time-steps per lead\n", - "======================================================================\n", - "Setting task PTBXLDiagnostic27_100Hz for ptbxl base dataset...\n", - "Task cache paths: task_df=/Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/tasks/PTBXLDiagnostic27_100Hz_c507defc-1543-5249-8da9-3c1bffcf0691/task_df.ld, samples=/Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/tasks/PTBXLDiagnostic27_100Hz_c507defc-1543-5249-8da9-3c1bffcf0691/samples_cdbbc602-34e2-5a41-8643-4c76b08829f6.ld\n", - "Applying task transformations on data with 1 workers...\n", - "Detected Jupyter notebook environment, setting num_workers to 1\n", - "Single worker mode, processing sequentially\n", - "Worker 0 started processing 1000 patients. (Polars threads: 10)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - " 0%| | 0/1000 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", - "Weight decay: 0.0\n", - "Max grad norm: None\n", - "Val dataloader: \n", - "Monitor: roc_auc_macro\n", - "Monitor criterion: max\n", - "Epochs: 5\n", - "Patience: None\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0 / 5: 100%|██████████| 10/10 [00:59<00:00, 5.90s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-0, step-10 ---\n", - "loss: 0.6324\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.12s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-0, step-10 ---\n", - "roc_auc_macro: nan\n", - "f1_macro: 0.0047\n", - "loss: 4764.1799\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", - " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n", - "Epoch 1 / 5: 100%|██████████| 10/10 [00:57<00:00, 5.77s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-1, step-20 ---\n", - "loss: 0.3184\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.13s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-1, step-20 ---\n", - "roc_auc_macro: nan\n", - "f1_macro: 0.0600\n", - "loss: 3.7070\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", - " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n", - "Epoch 2 / 5: 100%|██████████| 10/10 [00:57<00:00, 5.71s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-2, step-30 ---\n", - "loss: 0.1647\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.13s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-2, step-30 ---\n", - "roc_auc_macro: nan\n", - "f1_macro: 0.1028\n", - "loss: 1.1566\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", - " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n", - "Epoch 3 / 5: 100%|██████████| 10/10 [00:59<00:00, 5.92s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-3, step-40 ---\n", - "loss: 0.1702\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.30s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-3, step-40 ---\n", - "roc_auc_macro: nan\n", - "f1_macro: 0.0440\n", - "loss: 0.7322\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", - " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n", - "Epoch 4 / 5: 100%|██████████| 10/10 [00:58<00:00, 5.80s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-4, step-50 ---\n", - "loss: 0.1662\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:08<00:00, 4.16s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-4, step-50 ---\n", - "roc_auc_macro: nan\n", - "f1_macro: 0.0621\n", - "loss: 0.2451\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", - " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " Training time: 332.8 s\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Evaluation: 100%|██████████| 4/4 [00:15<00:00, 4.00s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " Test ROC-AUC (macro): nan\n", - " Test F1 (macro): 0.0660\n", - "\n", - "======================================================================\n", - "Config: D — diagnostic (27-class) / 500 Hz\n", - " label_type=diagnostic, sampling_rate=500 Hz\n", - " K=27 classes, T=5000 time-steps per lead\n", - "======================================================================\n", - "Setting task PTBXLDiagnostic27_500Hz for ptbxl base dataset...\n", - "Task cache paths: task_df=/Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/tasks/PTBXLDiagnostic27_500Hz_0d0c6f8a-c70c-5929-bbf5-ab21dc91f788/task_df.ld, samples=/Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/tasks/PTBXLDiagnostic27_500Hz_0d0c6f8a-c70c-5929-bbf5-ab21dc91f788/samples_cdbbc602-34e2-5a41-8643-4c76b08829f6.ld\n", - "Applying task transformations on data with 1 workers...\n", - "Detected Jupyter notebook environment, setting num_workers to 1\n", - "Single worker mode, processing sequentially\n", - "Worker 0 started processing 1000 patients. (Polars threads: 10)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", - " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n", - " 0%| | 0/1000 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", - "Weight decay: 0.0\n", - "Max grad norm: None\n", - "Val dataloader: \n", - "Monitor: roc_auc_macro\n", - "Monitor criterion: max\n", - "Epochs: 5\n", - "Patience: None\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0 / 5: 100%|██████████| 10/10 [04:30<00:00, 27.08s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-0, step-10 ---\n", - "loss: 0.6280\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:38<00:00, 19.20s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-0, step-10 ---\n", - "roc_auc_macro: nan\n", - "f1_macro: 0.0602\n", - "loss: 14318.4673\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "Epoch 1 / 5: 100%|██████████| 10/10 [04:22<00:00, 26.23s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-1, step-20 ---\n", - "loss: 0.3050\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:38<00:00, 19.17s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-1, step-20 ---\n", - "roc_auc_macro: nan\n", - "f1_macro: 0.0566\n", - "loss: 0.6025\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", - " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n", - "Epoch 2 / 5: 100%|██████████| 10/10 [04:20<00:00, 26.08s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-2, step-30 ---\n", - "loss: 0.1670\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:38<00:00, 19.07s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-2, step-30 ---\n", - "roc_auc_macro: nan\n", - "f1_macro: 0.0835\n", - "loss: 1.5772\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", - " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n", - "Epoch 3 / 5: 100%|██████████| 10/10 [04:21<00:00, 26.17s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-3, step-40 ---\n", - "loss: 0.1678\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:38<00:00, 19.31s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-3, step-40 ---\n", - "roc_auc_macro: nan\n", - "f1_macro: 0.0840\n", - "loss: 1.3590\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "Epoch 4 / 5: 100%|██████████| 10/10 [04:31<00:00, 27.15s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Train epoch-4, step-50 ---\n", - "loss: 0.1471\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "Evaluation: 100%|██████████| 2/2 [00:38<00:00, 19.38s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Eval epoch-4, step-50 ---\n", - "roc_auc_macro: nan\n", - "f1_macro: 0.1132\n", - "loss: 0.4798\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", - " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " Training time: 1519.5 s\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Evaluation: 100%|██████████| 4/4 [01:14<00:00, 18.68s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " Test ROC-AUC (macro): 0.5914\n", - " Test F1 (macro): 0.1033\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "results = []\n", - "\n", - "for cfg in ABLATION_CONFIGS:\n", - " print('\\n' + '='*70)\n", - " print(f\"Config: {cfg['name']}\")\n", - " print(f\" label_type={cfg['label_type']}, sampling_rate={cfg['sampling_rate']} Hz\")\n", - " print(f\" K={cfg['n_classes']} classes, T={cfg['T']} time-steps per lead\")\n", - " print('='*70)\n", - "\n", - " # ------------------------------------------------------------------\n", - " # 6.1 Task + SampleDataset\n", - " # ------------------------------------------------------------------\n", - " task = PTBXLMultilabelClassification(\n", - " label_type=cfg['label_type'],\n", - " sampling_rate=cfg['sampling_rate'],\n", - " )\n", - " sample_ds = base_dataset.set_task(task)\n", - " print(f' Total ML samples: {len(sample_ds)}')\n", - "\n", - " sample = sample_ds[0]\n", - " print(f' signal shape : {tuple(sample[\"signal\"].shape)}')\n", - " print(f' labels : {sample[\"labels\"]}')\n", - "\n", - " # ------------------------------------------------------------------\n", - " # 6.2 Train / val / test split\n", - " # Note: split_by_sample is used here for compatibility with the current\n", - " # cache (patient_id not stored in sample cache). In PTB-XL dev mode,\n", - " # each patient has exactly one record so this is equivalent to\n", - " # split_by_patient for data-leakage purposes.\n", - " # ------------------------------------------------------------------\n", - " train_ds, val_ds, test_ds = split_by_sample(sample_ds, SPLIT)\n", - " print(f' Train/Val/Test samples: {len(train_ds)}/{len(val_ds)}/{len(test_ds)}')\n", - "\n", - " train_loader = get_dataloader(train_ds, batch_size=BATCH_SIZE, shuffle=True)\n", - " val_loader = get_dataloader(val_ds, batch_size=BATCH_SIZE, shuffle=False)\n", - " test_loader = get_dataloader(test_ds, batch_size=BATCH_SIZE, shuffle=False)\n", - "\n", - " # steps_per_epoch must be set explicitly because litdata's StreamingDataset\n", - " # is an IterableDataset — len(dataloader) returns 0 for iterable dataloaders.\n", - " steps_per_epoch = max(1, len(train_ds) // BATCH_SIZE)\n", - " print(f' Steps per epoch: {steps_per_epoch}')\n", - "\n", - " # ------------------------------------------------------------------\n", - " # 6.3 Model — SE-ResNet-50\n", - " # SE-ResNet augments a ResNet-50 backbone with Squeeze-Excitation\n", - " # channel-attention gates (Hu et al. 2018) inside every bottleneck\n", - " # block. This version uses layer counts [3, 4, 6, 3] and expansion=4,\n", - " # matching the se_resnet1d50 variant in Nonaka & Seita (2021).\n", - " # ------------------------------------------------------------------\n", - " model = SEResNet50ECG(dataset=sample_ds)\n", - "\n", - " # ------------------------------------------------------------------\n", - " # 6.4 Train\n", - " # ------------------------------------------------------------------\n", - " trainer = Trainer(\n", - " model=model,\n", - " device=DEVICE,\n", - " enable_logging=False,\n", - " metrics=['roc_auc_macro', 'f1_macro'],\n", - " )\n", - "\n", - " t0 = time.time()\n", - " trainer.train(\n", - " train_dataloader=train_loader,\n", - " val_dataloader=val_loader,\n", - " optimizer_class=torch.optim.Adam,\n", - " optimizer_params={'lr': LEARNING_RATE},\n", - " epochs=EPOCHS,\n", - " steps_per_epoch=steps_per_epoch,\n", - " monitor=MONITOR,\n", - " )\n", - " elapsed = time.time() - t0\n", - " print(f' Training time: {elapsed:.1f} s')\n", - "\n", - " # ------------------------------------------------------------------\n", - " # 6.5 Evaluate on test set\n", - " # ------------------------------------------------------------------\n", - " test_metrics = trainer.evaluate(test_loader)\n", - " roc_auc = test_metrics.get('roc_auc_macro', float('nan'))\n", - " f1 = test_metrics.get('f1_macro', float('nan'))\n", - "\n", - " print(f' Test ROC-AUC (macro): {roc_auc:.4f}')\n", - " print(f' Test F1 (macro): {f1:.4f}')\n", - "\n", - " results.append({\n", - " 'config': cfg['name'],\n", - " 'label_type': cfg['label_type'],\n", - " 'sampling_rate': cfg['sampling_rate'],\n", - " 'K': cfg['n_classes'],\n", - " 'T': cfg['T'],\n", - " 'roc_auc_macro': roc_auc,\n", - " 'f1_macro': f1,\n", - " 'train_time_s': elapsed,\n", - " })\n" - ] - }, - { - "cell_type": "markdown", - "id": "b2a47542", - "metadata": {}, - "source": [ - "## 7. Results Summary" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "1eb2e622", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " config K T roc_auc_macro f1_macro train_time_s\n", - "A — superdiagnostic / 100 Hz (baseline) 5 1000 0.548581 0.442818 330.432782\n", - " B — superdiagnostic / 500 Hz 5 5000 0.758545 0.416275 1562.846845\n", - " C — diagnostic (27-class) / 100 Hz 27 1000 NaN 0.065967 332.799235\n", - " D — diagnostic (27-class) / 500 Hz 27 5000 0.591420 0.103268 1519.457779\n" - ] - } - ], - "source": [ - "results_df = pd.DataFrame(results)\n", - "display_cols = ['config', 'K', 'T', 'roc_auc_macro', 'f1_macro', 'train_time_s']\n", - "print(results_df[display_cols].to_string(index=False))" - ] - }, - { - "cell_type": "markdown", - "id": "2ba48914", - "metadata": {}, - "source": [ - "## 8. Visualisation — Ablation Results\n", - "\n", - "Bar charts comparing macro ROC-AUC and macro F1 across the four configs." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "88814df1", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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xokTJ9zn6fyW2Dn/m2++CZu+atUqN/3ss89O8J5OnTq514YMGZLgNZ2X6tev787jn332Wdhrw4cPD56HddxF2rVrlyv7qdGwYcPAZZdd5n5/9Jl//PFH1Pn0uUmdh/Ra5Hep9c+dO7fbBzpuo5Ut/d799NNPya7n888/75b/+uuvh00/fPhwwK/fAC3777//Dv4dy29bev52pcQ///wTKF++fKB48eKBxYsXR51n7ty5CfajH15++WVft33EiBGuDGubAT+RXg5kAt0tbtasmavRDU0pTQ+VK1d2/ysNLKV3elUroZqCefPm2b59+1wNWLly5dwdfD+oHVX9+vVdbUzknXXVViTWxko1mErpi6W9m2oavOWqpsNLA/SW51ebbm2TPkNpkIULF3bZBErtVSZDYlT7qdpNpYuqZlkppitXrow67+zZs6158+ZuuVq+arzeeuutNK+3ar1eeeUVu/76611bunPOOcfVNCRFx7JqRlTLo2NctXApPb5VA3jllVe61F0du6oN7dSpU1hTAC9VWVSzGdnsIqk23aoN1X7X/lStlJ7PmTMnwXze+9esWWNt27Z1+1Xbo/0Q2szCo/lUI5kc1Xaptka1zdo3kbTNqam5To/zhGprle6s4y3Ub7/95s4J+g60Xton9957r6tZDaUa83//+9+uttRLlb/gggvcdxNJx6WOB9WCKhshJVKyHvquvM8LPR5SU45V67l3794kU2WVmaPy1bNnz7Dpylg6duyYK4cpORf89ddfweyklFDqs2rjtS9CKatJ5VP7dezYsQnep/donSU0dVq1lo899pj7zvRdRPudUBpxtNr/xOjcpCwiZW+o1lw10Fp2ern//vvdd6N97P2+hdLnKV0/uVpu8c4VkdsdmXGVnrRspWSnB2VA6LfMK3P6HZ01a1aCY0ZNDHRcR1JtrvaBUqqTomNEx8ojjzzifl+iadWqld10001h05QxoN93pf7rvKlz+pIlSxK81/vtXbZsmbVo0cKdl/V716NHD5eO71GZ13EV+bud1G+2+krQeUnL07leTS+UoZJY3yOtW7d2ZTi7NllC9kHQDWQCXUD8/vvvwYujWB08eNBdzOuhC2ClO6o9npap1K+UUtqwgh394PXq1csFNwq407JuydGP4qeffupS1Ty6UNOFe6xtxpOitDS1/ROl0yqNXQ+lWfpJwbUC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343pbYUKFbIFCxaETStcuHDw+dy5c+1///uftWjRwnbu3JmqZW/atMkmT55sixcvtpzipptusuHDh9vMmTOtW7dumb062Q5BNwAAAIC4kjt3bjv//PMTff2xxx6zJ554wj2PDM6T8/zzz1utWrWsUaNGlpWp1vrkyZOWL1++ZOfNkyePde3a1Z555hmC7hiQXg4AAAAAEUF5rFQbfP311ydIaS9atKh999131qxZM1fTfu6557q/Dx8+bL1797ZSpUpZ1apVbcKECWHvXbZsmbVr184qV65sRYoUsYYNG9p//vOfBJ+7e/du69u3r1tGgQIFrGbNmjZ48ODg65dccoldddVV9vLLL9sZZ5zh5lFtvnejwJtWo0YNGzt2rAvIQyldftWqVcH3IOWo6QYAAAAQd44fP56gNlftt9Pi999/t3Xr1tmFF16Y4LVjx45Zly5dbMCAAVahQgW7//777dprr3Xzli9f3t544w2bM2eOe/28886zCy64wL3vr7/+cvP06tXLChYsaF9++aV1797dBcVanhw5csQuu+wy99kjR460evXq2d9//21LliwJW4dvv/3WzTNmzBgX5FerVs0mTpxo/fr1cwG7gvKlS5e6mwQK4h9//PHge9WmW+9Re/cGDRqkaT/FG4JuAAAAAHHlwIEDCdKqVXvcqVOnNC33m2++cf/Xr18/wWtHjx61Rx55xFq3bu3+VtB89dVXW9OmTe3JJ5900xQ4v/nmm+7hBd1qT+0JBAJ28cUX24YNG1zttBd0q3ZdteYKmFWT7vFe96h9utZRwbaXYq4AXJ+h1HG58sor3boqvV415WXKlAm+X9v19ddfp2kfxSOCbgAAAABxRendX3zxRdi0U089Nc3L/eeff1xqemig6tH0yy+/PPh37dq13f8tW7YMq21Xj+mqpfbs2rXL1V6rFnzjxo3BHsRDP2P+/PmuJjo04I5GQbMXcMuaNWts+/btCXpa79ixo40fP96WL18evEkgZcuWdduI1CHoBgAAABBXFAA3btw43Zer9tmqQY+Wpq5AP3/+/MG/veclS5YMm0/TtRyPOjBTDfaIESPsrLPOsuLFi9tzzz1ns2fPDs6zY8cO1+Y7OUprD6WAPtp07+/IntvV5vvQoUPJfg7CEXQDAAAAQDooXbq0a1+toFntr9NKy/nwww9d+rnaXHsiOzlTrff333+f7PIibwZofWXr1q1h07ds2RL2ukftvKPV4iNp9F4OAAAAAOlAPYDL2rVr02V5CuAVYIfWkO/bt8/ef//9sPmUov7zzz+nur211rdcuXKuDXkodeqmz1SHbqHUCZu3jUg5aroBAAAAIIR6DPc6RTt48KD98ccf9tZbb7m/I4cDC6UgNW/evLZixQrXxjqtSpQoYU2aNLGHH37YBcdatp5remjt9G233WbPPvustW3b1rX/Pvvss137b7Vbnzp1aqLLVxvy4cOHu97L1YN6mzZt7KuvvnIdvvXv3z+sVludz6kNuJaP1CHoBgAAAIAQCxcutG7dugX/njt3rnt4PYgnRuNoq+OxTz75JM09oXtee+01u+OOO1xP5AqCFSDv378/bDgvtbVWZ2pDhw61hx56yLXF1njdN998c7LLV9q62qErhV2Be6VKldyQYUOGDAmbb968ea5demjHakiZXIGkjho4e/fudXeT9uzZ4zouAAAAAOKJ2hYrZbpmzZrp0lY5J/vggw/slltuce2iCxcubDmFejgvVqyYzZgxw+LF4WSO+5TGibTpBgAAAIB0ctVVV7nhwKZNm2Y5hQLPjz76yNWkI/UIugEAAAAgnaiH8ClTpuSoWm61D1fbcI0hjtSjTTcAAAAApCN1fqZHTtG8eXP3QGyo6QYAAAAAwCcE3QAAAAAA+ISgGwAAAECKMPAR4kkgnY53gm4AAAAASdI4znLw4MHMXhUgw3jHu3f8x4qO1AAAAAAkKU+ePFayZEnbunWr+1s9c6uXbiCn1nAfPHjQHe867nX8pwVBNwAAAIBkVaxY0f3vBd5ATleyZMngcZ8WBN0AAAAAkqWa7UqVKln58uXt2LFjmb06gK+UUp7WGm4PQTcAAACAFFMgkl7BCBAP6EgNAAAAAACfEHQDAAAAAOATgm4AAAAAAHxC0A0AAAAAgE8IugEAAAAA8AlBNwAAAAAAPiHoBgAAAADAJwTdAAAAAAD4hKAbAAAAAACfEHQDAAAAAOATgm4AAAAAAHxC0A0AAAAAgE8IugEAAAAA8AlBNwAAAAAAPiHoBgAAAADAJwTdAAAAAAD4hKAbAAAAAACfEHQDAAAAAOATgm4AAAAAAHxC0A0AAAAAgE8IugEAAAAA8AlBNwAAAAAAPiHoBgAAAADAJwTdAAAAAAD4hKAbAAAAAACfEHQDAAAAAOATgm4AAAAAAHxC0A0AAAAAgE8IugEAAAAA8AlBNwAAAAAAPiHoBgAAAAAgXoLuyZMnW40aNaxgwYLWtGlTW758eZLzT5gwwc444wwrVKiQVatWzQYMGGCHDx9O0zIBAAAAAMhxQffs2bNt4MCBNnLkSFu5cqU1aNDAWrVqZVu3bo06/2uvvWYPPPCAm//nn3+26dOnu2UMGTIk5mUCAAAAAJBecgUCgYBlEaqFbtKkiU2aNMn9ffLkSVd73bdvXxdcR+rTp48LtufPnx+cds8999jXX39tS5YsiWmZ0ezdu9dKlChhe/bsseLFi6fT1gIAAAAAsquUxolZpqb76NGjtmLFCmvZsmVwWu7cud3fy5Yti/qeCy64wL3HSxf/888/7eOPP7Y2bdrEvEwAAAAAANJLXssitm/fbidOnLAKFSqETdffa9asifqeW265xb2vefPmpgr748ePW69evYLp5bEsU44cOeIeoXcwvFpyPQAAAAAA8e1kCmPDLBN0x2LRokX20EMP2bPPPuvSyH///Xe7++677cEHH7Thw4fHvNzx48fb6NGjE0zftm1bgk7aAMBPx44dc31SvPPOO5YrVy679tpr3fkpb96Ep+/TTjst7G9l+9SqVcsWLFjg/tb58d1337V8+fKF9XvRuHFj93zdunXupqX6v1DnlD169LC77rrL920EAADIjvbt25e9gu6yZctanjx5bMuWLWHT9XfFihWjvkeB9W233eYuDKVevXp24MABu/32223o0KExLVMGDx7sOl8LrelWO/By5crRphtAhho1apQLgn/66Sf3d9u2bV2nkdFuLEae+Bs2bGgdO3a08uXLu781gkPv3r3tqaeeSvBeZQVdfvnl1r59e/vkk09ccx11OqnRIZRVBAAAgHC6tspWQXf+/PmtUaNGrlO0Dh06BKvr9bc6TIvm4MGDro12KAXZonTzWJYpBQoUcI9I+qzIzwMAP7344osuSK5SpYr7WzcUBw0a5Gq/k6K+LlavXm3dunULnrdUU65HtPOYmtz88ssvLsjXubNu3brWvXt3mzZtmnXq1MmnrQMAAMi+UhobZqkIUrXLL7zwgr388suuV3LVyKjmWheN0rlzZ1cL7bn66qvtueees1mzZtnatWvts88+c7U/mu4F38ktEwCyql27dtmGDRtcjbVHz9evX+96yUyKasNb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/eyXX35xaf3Tp0+3p59+2j08GtLtnHPOsYsvvjjBe5s1a+ba3E+aNMm1I//yyy/t3XffdXeRAAAAACDugu6vvvrKxo8f79pV//bbb27awYMHbeXKlcFgDdmbOkKT0Fpt73mxYsUSzH/uuee6Mc01XNv5559vDzzwgM2ePdu99vvvv9uUKVNc4B1NmTJl7IMPPnC90mtYN71X6eWaDgAAAABxE3RrHOxrr73WDds1dOhQe+aZZ+zvv//+v4Xmzu06vwqt3UT2pR7Lq1ataqtWrQpO03O181YbhuSEdn6nztg0Tnrt2rXdOOrt27d3tdh6/vXXX7t5dEwtXbrUduzYYYsXL3Yd4LVo0cKnrQMAAACALBh0Dx8+3D788EN77rnnXCpxaDvcggUL2g033GBz5sxJr/VEJlNt87hx41wArId6Lu/Ro0fUed944w0XSOuYUO/jDz/8sF133XXuNXWaptpuBe16TJs2zdWW67lSzuW7776zI0eOuCHFNNzbokWLrH///hm6vQAAAACQqUOGvf76627M69tvv93VSEaqW7eu6+EaOYNusuh71vcqnTp1siFDhrjn6m1clDYuao+t40IdpmkYNw0fds8997jXChcu7B4epaHnypXL1aR7lDWhdtx6/wUXXGALFiywypUrZ+j2AgAAAECmBt1bt261evXqJfq62vOqbTdyhnz58rnexvWI5AXbni+++CLFy73kkkuCw4V5XnzxRfcAAAAAgLhNL1d73jVr1iT6unqdPv3002NdPAAAAAAA8Rt033LLLfb888/bsmXLgtOUJixqh6t2vZ07d06ftQQAAAAAIJ7Sy9VjuYYL01jLauergFvDhu3cudM2bNhgbdq0cX8DAAAAABCvYq7pzp8/v82dO9e1vT311FOtTp06rsfp+vXr20svveTGWla7bgAAAAAA4lVMNd0aykk13ZdeeqnrxVoPAAAAAACQDjXdhQoVcu25t2zZEsvbAQAAAACICzG36W7UqJH9+OOP6bs2SJNWD36U2auQLc0b3jazVwEAAABADhVzm+4JEybYrFmzbNq0aXb8+PH0XSsAAAAAAOK5prtr166WO3duu+OOO6xfv35WpUoVl3YeSj2a/+9//0uP9QQAAAAAIH6C7tKlS1uZMmXsjDPOSN81AgAAAAAg3oPuRYsWpe+aAAAAAACQw8TcphsAAAAAAPhU0y0nTpywV155xT766CP766+/3LTq1avbVVddZbfeeqvlyZMnLYsHAAAAACA+a7r37NljF154of373/+2Tz/91I4dO+Yen332mXXr1s2aN29ue/fuTd+1BQAAAAAgHoLuoUOH2ooVK2zixIm2bds2W7lypXts3brVJk2aZN9++62bBwAAAACAeBVz0P3uu+/anXfe6R758uULTtfz3r17u8fbb7+dXusJAAAAAED8BN07duxIcriwOnXq2M6dO2NdPAAAAAAA8Rt0n3766fb+++8n+rpeO+2002JdPAAAAAAA8Rt0K61cHai1adPG/b9u3Tr3mDdvnrVt29Z1qNanT5/0XVsAAAAAAOJhyDAF3eo07eGHH3aBdii16x4xYoRr1w0AAAAAQLxK0zjdo0aNcrXZn3/+edg43S1btrSyZcum1zoCAAAAABB/QbcouL7pppvSZ20AAAAAAMhBYm7TrdrtIUOGJPq6xuhesGBBrIsHAAAAACB+g+4HH3zQ/v7770Rf37hxo40dOzbWxQMAAAAAEL9B9w8//GBNmzZN9PUmTZrY999/H+viAQAAAACI36D7yJEjdvTo0SRfP3jwYKyLBwAAAAAgfoPus88+2959992orwUCAXvnnXfszDPPTMu6AQAAAAAQn0F337597csvv7QbbrjBpZofP37cPZRSrmnLli1z8wAAAAAAEK9iHjKsU6dO9scff7gO1VSrnTv3/8XvJ0+etFy5ctmwYcOsS5cu6bmuAAAAAADEzzjdI0eOdMG30sz//PNPN+20006zDh06uP8BAAAAAIhnMaeXexRcDxo0yPr162eVKlVytd8fffSR7d27N33WEAAAAACAeAi6J02aZLVr17bt27eHTf/www+tYcOGNmrUKJsyZYr179/fzj333ATzAfHm2LFj1qdPHytVqpSVLl3a9XOgvg+ScujQITv99NOtZMmSUV/fsmWLW5bKXOhoAZdccomVL1/eihcvbnXq1LGpU6em+/YAAAAA8DHofv/9913NdtmyZYPTFEB0797d8uTJYzNmzHCdqj388MP2119/2bhx41K5OkDOMnbsWFuyZImtXr3afvrpJ1u8eLE99NBDSb5nxIgRVr169URfVxB/zjnnhE3LmzevTZw40TZt2uSyTNTPwvDhw93nAQAAAMgmQbcCh/PPPz9s2sKFC23btm02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jsg41HQjLule0+uvv26XXXaZDRw40Hbs2GFz587N7NUC4lKxYsXs2LFjmb0aQI71xBNPWIUKFVzQHQ0BN5Bx1LRq4sSJdvToUZs2bVpmrw4yCEE34pLSfNatW2e33HKLtWrVysqUKUOKOZBBjh8/7h779u1zbUzffvttu/766zN7tYAcSWXtyy+/tMsvv5xsLiCLOPPMM61KlSou9RzxIW9mrwCQGRRg607jtdde6y5CdMH/n//8x6UAFS1aNLNXD8ixDhw4kODCX+1J1d4UQPpTJteRI0fslFNOyexVARCiWrVqtnnz5sxeDWQQaroRl3f933zzTWvTpo3rUEZU433w4EF79913M3v1gBzfqcw333zjHmrP9vTTT7umHT179szsVQNyNPWgDCBrNXWkXMYParoRdz799FPbtm2bXX311bZ79243TUM4VKpUydWA33bbbZm9ikCOpWFTGjduHPz7wgsvdDfC7rnnHte/wtlnn52p6wfkNGo+pcyu9evXZ/aqAAixYcMGq127dmavBjIINd2IO17bbQ3bUKpUKffQmKXqvVzDOGzdujWzVxGIKxqr2xvGD0D6yps3r7u5NX/+fHeDC0Dm0++dRu244IILMntVkEEIuhFXlEI+Z84c69Chgy1cuDDsod7MdUEye/bszF5NIK78+OOP7v+yZctm9qoAOZKySNR2dNy4cYkOKQYgY2jIsL59+1qBAgWsR48emb06yCCklyOuKOBWZ2n9+vWzSy65JMHrjz76qKsJ18kQQPo7efKkffXVV+65hktZsWKFjR071vXkevHFF2f26gE5kvowue+++9y4watXr7abbrrJ3eRau3atzZgxw/bs2ePmAeDfb56uP3/44QebOnWq/fnnn/bSSy9ZjRo1MnsVkUEIuhFXFFCrB9doAbd06dLF+vfvb3/88YeddtppGb5+QE536NAha9asWTDtVb23durUyUaOHMlwRoCPHnnkEZfKOmnSJPv3v//tRhLQkEUaNnPQoEGZvXpAjv/N0+g4CrI1fJ867q1Tp05mrx4yUK6Aus4DAAAAAADpjjbdAAAAAAD4hKAbAAAAAACfEHQDAAAAAOATgm4AAAAAAHxC0I2YnHfeeTZ58mTLrn799VfLlSuXrV+/3g1bpKFUNFxRkSJF3PTt27dHfd/SpUtdL5SFChWy6tWru95gI/si1N8P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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Figure saved to ptbxl_se_resnet_ablation_results.png\n" + "Figure saved to ptbxl_model_comparison_ablation.png\n" ] } ], "source": [ - "# Short labels for the x-axis\n", - "short_labels = ['A\\n(super/100Hz)', 'B\\n(super/500Hz)', 'C\\n(diag/100Hz)', 'D\\n(diag/500Hz)']\n", - "\n", - "auc_vals = results_df['roc_auc_macro'].tolist()\n", - "f1_vals = results_df['f1_macro'].tolist()\n", - "\n", - "x = np.arange(len(short_labels))\n", - "width = 0.35\n", - "\n", - "fig, ax = plt.subplots(figsize=(10, 5))\n", - "bars_auc = ax.bar(x - width/2, auc_vals, width, label='ROC-AUC (macro)', color='steelblue')\n", - "bars_f1 = ax.bar(x + width/2, f1_vals, width, label='F1 (macro)', color='coral')\n", - "\n", - "ax.set_xticks(x)\n", - "ax.set_xticklabels(short_labels, fontsize=11)\n", - "ax.set_ylim(0, 1.05)\n", - "ax.yaxis.set_major_formatter(mticker.FormatStrFormatter('%.2f'))\n", - "ax.set_ylabel('Score', fontsize=12)\n", - "ax.set_title('PTB-XL Multi-Label Ablation: SE-ResNet-50 (ROC-AUC & F1 by Config)', fontsize=13)\n", - "ax.legend(fontsize=11)\n", - "ax.bar_label(bars_auc, fmt='%.3f', padding=3, fontsize=9)\n", - "ax.bar_label(bars_f1, fmt='%.3f', padding=3, fontsize=9)\n", - "ax.grid(axis='y', alpha=0.3)\n", - "\n", + "_label_map = {\n", + " 'A — superdiagnostic / 100 Hz (baseline)': 'A\\n(super/100Hz)',\n", + " 'B — superdiagnostic / 500 Hz': 'B\\n(super/500Hz)',\n", + " 'C — diagnostic (27-class) / 100 Hz': 'C\\n(diag/100Hz)',\n", + " 'D — diagnostic (27-class) / 500 Hz': 'D\\n(diag/500Hz)',\n", + "}\n", + "configs = [c['name'] for c in ABLATION_CONFIGS]\n", + "short_labels = [_label_map[c] for c in configs]\n", + "model_names = [m['name'] for m in MODELS]\n", + "colors = ['steelblue', 'coral', 'darkorange', 'mediumseagreen']\n", + "\n", + "x = np.arange(len(short_labels))\n", + "n_models = len(model_names)\n", + "width = 0.18\n", + "offsets = np.linspace(-(n_models - 1) * width / 2,\n", + " (n_models - 1) * width / 2,\n", + " n_models)\n", + "\n", + "fig, axes = plt.subplots(1, 2, figsize=(18, 5))\n", + "\n", + "for ax_idx, (metric, metric_label) in enumerate([\n", + " ('roc_auc_macro', 'ROC-AUC (macro)'),\n", + " ('f1_macro', 'F1 (macro)'),\n", + "]):\n", + " ax = axes[ax_idx]\n", + " for m_idx, mname in enumerate(model_names):\n", + " vals = [\n", + " results_df[\n", + " (results_df['model'] == mname) &\n", + " (results_df['config'] == cfg)\n", + " ][metric].values[0]\n", + " for cfg in configs\n", + " ]\n", + " bars = ax.bar(x + offsets[m_idx], vals, width,\n", + " label=mname, color=colors[m_idx])\n", + " ax.bar_label(bars, fmt='%.3f', padding=3, fontsize=7)\n", + "\n", + " ax.set_xticks(x)\n", + " ax.set_xticklabels(short_labels, fontsize=10)\n", + " ax.set_ylim(0, 1.15)\n", + " ax.yaxis.set_major_formatter(mticker.FormatStrFormatter('%.2f'))\n", + " ax.set_ylabel('Score', fontsize=12)\n", + " ax.set_title(f'PTB-XL Ablation — {metric_label}', fontsize=12)\n", + " ax.legend(fontsize=9)\n", + " ax.grid(axis='y', alpha=0.3)\n", + "\n", + "plt.suptitle('ResNet-18 vs SE-ResNet-50 vs Lambda-ResNet-18 vs BiLSTM: Model × Task Ablation on PTB-XL',\n", + " fontsize=12, y=1.02)\n", "plt.tight_layout()\n", - "plt.savefig('ptbxl_se_resnet_ablation_results.png', dpi=150)\n", + "plt.savefig('ptbxl_model_comparison_ablation.png', dpi=150, bbox_inches='tight')\n", "plt.show()\n", - "print('Figure saved to ptbxl_se_resnet_ablation_results.png')" + "print('Figure saved to ptbxl_model_comparison_ablation.png')\n" ] }, { @@ -3094,20 +871,51 @@ "aliased away at 100 Hz. However:\n", "\n", "* Input size grows by 5×, substantially increasing memory and training time.\n", - "* SE-ResNet-50's bottleneck blocks use successive strided convolutions to\n", - " progressively downsample, so the effective receptive field scales with $T$;\n", - " the model may not fully exploit the extra resolution within 5 epochs.\n", - "\n", - "### Trade-off\n", - "\n", - "Config **B** (superdiagnostic / 500 Hz) is expected to achieve the highest\n", - "absolute AUC if sufficient epochs are used, while Config **D**\n", - "(diagnostic / 500 Hz) is the most challenging in both accuracy and\n", - "compute cost.\n", - "\n", - "These findings closely mirror the ablation tables in Strodthoff *et al.*\n", - "(2021), where superdiagnostic tasks consistently outperform the fine-grained\n", - "ones and the 500 Hz models narrow the gap only when trained for ≥ 100 epochs." + "* SE-ResNet-50's strided convolutions progressively downsample, scaling the\n", + " effective receptive field with $T$; BiLSTM processes all time-steps sequentially\n", + " so benefits more directly from longer inputs.\n", + "\n", + "### Effect of Model Architecture\n", + "\n", + "Comparing all four architectures across the same configs:\n", + "\n", + "* **ResNet-18** (control) — a plain 1-D residual network without attention.\n", + " Provides the baseline CNN performance against which attention-augmented\n", + " variants are measured.\n", + "\n", + "* **SE-ResNet-50** — augments bottleneck blocks with Squeeze-Excitation\n", + " channel attention:\n", + " $$\\tilde{x}_c = \\sigma\\!\\left(W_2\\,\\delta\\!\\left(W_1\\,z_c\\right)\\right) \\cdot x_c$$\n", + " where $z_c$ is the global average-pooled channel descriptor and $\\delta$ is ReLU.\n", + " Expected to outperform ResNet-18 by recalibrating channel responses to\n", + " emphasise diagnostically relevant waveform features.\n", + "\n", + "* **Lambda-ResNet-18** — replaces SE modules with Lambda layers that compute\n", + " both *content-based* and *position-based* linear attention without explicit\n", + " softmax:\n", + " $$\\lambda_n = \\sum_m \\text{softmax}(k_m) \\cdot (v_m \\odot e_{n-m})$$\n", + " This captures long-range context more efficiently than convolution while\n", + " remaining computationally lighter than full self-attention.\n", + "\n", + "* **BiLSTM** — a single bidirectional LSTM layer with $H = 64$ hidden units\n", + " ($\\text{lstm\\_d1\\_h64}$ from Nonaka & Seita 2021), processing the full sequence\n", + " left-to-right and right-to-left and taking the last hidden state:\n", + " $$h_T = [\\overrightarrow{h}_T ; \\overleftarrow{h}_1] \\in \\mathbb{R}^{2H}$$\n", + " Captures long-range temporal dependencies but may under-perform CNN variants\n", + " on localised morphological patterns.\n", + "\n", + "### Trade-off Summary\n", + "\n", + "| Factor | Expected ranking |\n", + "|--------|----------------|\n", + "| Fewer classes (5 vs 27) | All models easier; relative ranking preserved |\n", + "| Higher sampling rate (500 Hz) | All models improve; BiLSTM benefits proportionally more |\n", + "| Architecture (no attention) | ResNet-18 < SE-ResNet-50 ≈ Lambda-ResNet-18 |\n", + "| Architecture (CNN vs RNN) | CNN variants expected to outperform BiLSTM on morphological tasks |\n", + "\n", + "These findings closely mirror the comprehensive benchmarks in Strodthoff *et al.*\n", + "(2021) and Nonaka & Seita (2021), where CNN-based models (and attention-augmented\n", + "variants) generally outperform RNN baselines on PTB-XL when trained sufficiently long." ] }, { diff --git a/pyhealth/models/bilstm_ecg.py b/pyhealth/models/bilstm_ecg.py index a501735af..2416bfd02 100644 --- a/pyhealth/models/bilstm_ecg.py +++ b/pyhealth/models/bilstm_ecg.py @@ -125,18 +125,15 @@ def __init__( dropout: float = 0.2, **kwargs, ): - super().__init__( - dataset=dataset, - feature_keys=feature_keys, - label_key=label_key, - mode=mode, - ) + super().__init__(dataset=dataset) + self.feature_key = feature_keys[0] + self.label_key = label_key + self.mode = mode - sig_info = self.dataset.input_info["signal"] - in_channels: int = sig_info["n_channels"] # 12 for standard 12-lead ECG + # PTB-XL always has 12 leads; match the hard-coded default in ResNet18ECG + in_channels: int = 12 - self.label_tokenizer = self.get_label_tokenizer() - output_size: int = self.get_output_size(self.label_tokenizer) + output_size: int = self.get_output_size() # ── Bidirectional LSTM ──────────────────────────────────────────────── # Input: (B, T, C) after permute @@ -167,11 +164,8 @@ def forward(self, **kwargs) -> dict: # type: ignore[override] dict with keys ``"loss"``, ``"y_prob"``, ``"y_true"``, ``"logit"`` — the standard PyHealth model output contract. """ - # Stack list[(12,T)] → tensor (B, 12, T) - x = torch.tensor( - np.array(kwargs[self.feature_keys[0]]), - device=self.device, - ).float() + # Input tensor already collated by the dataloader: (B, 12, T) + x: torch.Tensor = kwargs[self.feature_key].to(self.device) # (B, 12, T) → (B, T, 12) for sequence-first LSTM with batch_first=True out, _ = self.lstm(x.permute(0, 2, 1)) # (B, T, hidden*2) @@ -180,7 +174,7 @@ def forward(self, **kwargs) -> dict: # type: ignore[override] pooled = self.pool(out.permute(0, 2, 1)).squeeze(-1) # (B, hidden*2) logits = self.fc(pooled) # (B, K) - y_true = self.prepare_labels(kwargs[self.label_key], self.label_tokenizer) + y_true = kwargs[self.label_key].to(self.device) loss = self.get_loss_function()(logits, y_true) y_prob = self.prepare_y_prob(logits) diff --git a/pyhealth/tasks/ptbxl_multilabel_classification.py b/pyhealth/tasks/ptbxl_multilabel_classification.py index 0c52d98b3..598df11ae 100644 --- a/pyhealth/tasks/ptbxl_multilabel_classification.py +++ b/pyhealth/tasks/ptbxl_multilabel_classification.py @@ -312,7 +312,7 @@ def __call__(self, patient: Patient) -> List[Dict]: for event in events: # ---- 1. Load the .mat signal -------------------------------- - mat_file = getattr(event, "ptbxl/mat", None) + mat_file = getattr(event, "mat", None) if not mat_file: logger.debug("Skip %s: no *.mat file", event) continue @@ -340,7 +340,7 @@ def __call__(self, patient: Patient) -> List[Dict]: signal = signal[:, ::5] # shape (12, 1000) # ---- 3. Parse SNOMED-CT codes -------------------------------- - dx_codes: str = str(getattr(event, "ptbxl/dx_codes", "") or "") + dx_codes: str = str(getattr(event, "dx_codes", "") or "") codes = [c.strip() for c in dx_codes.split(",") if c.strip()] # ---- 4. Map to chosen label space --------------------------- @@ -359,6 +359,10 @@ def __call__(self, patient: Patient) -> List[Dict]: # No recognised labels → skip (consistent with other tasks). continue - samples.append({"signal": signal, "labels": labels}) + samples.append({ + "patient_id": patient.patient_id, + "signal": signal, + "labels": labels, + }) return samples \ No newline at end of file From 1573aa3b542fff68353a1d5ca1a949453afec6eb Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Mon, 20 Apr 2026 23:36:01 -0400 Subject: [PATCH 31/39] Fixed a bug where we couldn't deal with missing rows in the dataset .csv --- pyhealth/datasets/ptbxl.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pyhealth/datasets/ptbxl.py b/pyhealth/datasets/ptbxl.py index 32f41abd0..e7d524aec 100644 --- a/pyhealth/datasets/ptbxl.py +++ b/pyhealth/datasets/ptbxl.py @@ -228,7 +228,11 @@ def load_data(self) -> dd.DataFrame: age = None sex = None dx = [] - + + ecg_id = int(hea_file.stem.replace("HR", "")) + if ecg_id not in db.index: + continue + with open(hea_file, "r") as f: for line in f: line = line.strip() From 843e257a97a2c70c07094f419f531812de19b46b Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Tue, 21 Apr 2026 00:29:58 -0400 Subject: [PATCH 32/39] Fixing a bug with the attribute names --- pyhealth/tasks/ptbxl_multilabel_classification.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyhealth/tasks/ptbxl_multilabel_classification.py b/pyhealth/tasks/ptbxl_multilabel_classification.py index 0c52d98b3..6b418e922 100644 --- a/pyhealth/tasks/ptbxl_multilabel_classification.py +++ b/pyhealth/tasks/ptbxl_multilabel_classification.py @@ -312,7 +312,7 @@ def __call__(self, patient: Patient) -> List[Dict]: for event in events: # ---- 1. Load the .mat signal -------------------------------- - mat_file = getattr(event, "ptbxl/mat", None) + mat_file = getattr(event, "mat", None) if not mat_file: logger.debug("Skip %s: no *.mat file", event) continue @@ -340,7 +340,7 @@ def __call__(self, patient: Patient) -> List[Dict]: signal = signal[:, ::5] # shape (12, 1000) # ---- 3. Parse SNOMED-CT codes -------------------------------- - dx_codes: str = str(getattr(event, "ptbxl/dx_codes", "") or "") + dx_codes: str = str(getattr(event, "dx_codes", "") or "") codes = [c.strip() for c in dx_codes.split(",") if c.strip()] # ---- 4. Map to chosen label space --------------------------- @@ -359,6 +359,6 @@ def __call__(self, patient: Patient) -> List[Dict]: # No recognised labels → skip (consistent with other tasks). continue - samples.append({"signal": signal, "labels": labels}) + samples.append({"signal": signal, "labels": labels, "split": "split": event.attr_dict.get("split", "train")}) return samples \ No newline at end of file From aa12c02d8084503f5777175c178061701236f351 Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Tue, 21 Apr 2026 00:31:17 -0400 Subject: [PATCH 33/39] Typo --- pyhealth/tasks/ptbxl_multilabel_classification.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyhealth/tasks/ptbxl_multilabel_classification.py b/pyhealth/tasks/ptbxl_multilabel_classification.py index 6b418e922..b7423c4fe 100644 --- a/pyhealth/tasks/ptbxl_multilabel_classification.py +++ b/pyhealth/tasks/ptbxl_multilabel_classification.py @@ -359,6 +359,6 @@ def __call__(self, patient: Patient) -> List[Dict]: # No recognised labels → skip (consistent with other tasks). continue - samples.append({"signal": signal, "labels": labels, "split": "split": event.attr_dict.get("split", "train")}) + samples.append({"signal": signal, "labels": labels, "split": event.attr_dict.get("split", "train")}) return samples \ No newline at end of file From 2684669df367d7c71c2418e595fdad11d2fb4b6e Mon Sep 17 00:00:00 2001 From: AnuragD2 Date: Tue, 21 Apr 2026 02:06:30 -0700 Subject: [PATCH 34/39] Add PTB-XL ablation study: 4 models x 4 configs, 2100 patients, 20 epochs --- examples/ptbxl_ablation_results.csv | 29 + examples/ptbxl_model_comparison_ablation.png | Bin 85658 -> 85995 bytes examples/ptbxl_se_resnet_ablation_results.png | Bin 0 -> 50341 bytes .../ptbxl_superdiagnostic_se_resnet.ipynb | 20317 +++++++++++++++- pyhealth/datasets/ptbxl.py | 22 +- 5 files changed, 20124 insertions(+), 244 deletions(-) create mode 100644 examples/ptbxl_ablation_results.csv create mode 100644 examples/ptbxl_se_resnet_ablation_results.png diff --git a/examples/ptbxl_ablation_results.csv b/examples/ptbxl_ablation_results.csv new file mode 100644 index 000000000..38a0b7383 --- /dev/null +++ b/examples/ptbxl_ablation_results.csv @@ -0,0 +1,29 @@ +model,config,label_type,sampling_rate,K,T,roc_auc_macro,f1_macro,train_time_s +ResNet-18,A -- superdiagnostic / 100 Hz (baseline),superdiagnostic,100,5,1000,0.8822800924422214,0.5773312448880163,245.33349299430847 +ResNet-18,C -- diagnostic (27-class) / 100 Hz,diagnostic,100,27,1000,0.7976528290960323,0.1731836175988993,249.20705914497375 +SE-ResNet-50,A -- superdiagnostic / 100 Hz (baseline),superdiagnostic,100,5,1000,0.8756324212374924,0.6750944493978221,1258.272660970688 +SE-ResNet-50,C -- diagnostic (27-class) / 100 Hz,diagnostic,100,27,1000,0.7179746910029848,0.1256204130133857,1236.1298429965973 +Lambda-ResNet-18,A -- superdiagnostic / 100 Hz (baseline),superdiagnostic,100,5,1000,0.8710290676081464,0.6714607062381933,1764.986752986908 +Lambda-ResNet-18,C -- diagnostic (27-class) / 100 Hz,diagnostic,100,27,1000,0.7194757776214,0.1264442354673686,1827.3542799949648 +BiLSTM,A -- superdiagnostic / 100 Hz (baseline),superdiagnostic,100,5,1000,0.8385706468313311,0.6728857241225712,515.1750118732452 +BiLSTM,C -- diagnostic (27-class) / 100 Hz,diagnostic,100,27,1000,0.8202809196685987,0.2270322400801511,621.5523679256439 +ResNet-18,A -- superdiagnostic / 100 Hz (baseline),superdiagnostic,100,5,1000,0.7629390953402366,0.4981607064856851,33.149600982666016 +ResNet-18,B -- superdiagnostic / 500 Hz,superdiagnostic,500,5,5000,0.8041790533977655,0.5295529580353364,114.70022583007812 +ResNet-18,C -- diagnostic (27-class) / 100 Hz,diagnostic,100,27,1000,0.7437596414651408,0.0829973626692842,33.51596713066101 +ResNet-18,D -- diagnostic (27-class) / 500 Hz,diagnostic,500,27,5000,,0.0567884119790278,112.71233773231506 +SE-ResNet-50,A -- superdiagnostic / 100 Hz (baseline),superdiagnostic,100,5,1000,0.80224405272653,0.3787636008457019,149.36452102661133 +SE-ResNet-50,B -- superdiagnostic / 500 Hz,superdiagnostic,500,5,5000,0.7553646129457944,0.3672557070980227,636.1527981758118 +SE-ResNet-50,C -- diagnostic (27-class) / 100 Hz,diagnostic,100,27,1000,0.7118343780332,0.0924789838720781,144.19747519493103 +SE-ResNet-50,D -- diagnostic (27-class) / 500 Hz,diagnostic,500,27,5000,,0.0573294504553054,634.5040018558502 +Lambda-ResNet-18,A -- superdiagnostic / 100 Hz (baseline),superdiagnostic,100,5,1000,0.7955438206260178,0.3970672129500629,270.942773103714 +Lambda-ResNet-18,B -- superdiagnostic / 500 Hz,superdiagnostic,500,5,5000,0.7723876093621177,0.5211916806450236,1253.451983213425 +Lambda-ResNet-18,C -- diagnostic (27-class) / 100 Hz,diagnostic,100,27,1000,,0.050866630176975,258.62340211868286 +Lambda-ResNet-18,D -- diagnostic (27-class) / 500 Hz,diagnostic,500,27,5000,,0.0817590591398973,1244.6589679718018 +BiLSTM,A -- superdiagnostic / 100 Hz (baseline),superdiagnostic,100,5,1000,0.8135959253589039,0.6451106757639339,77.51535534858704 +BiLSTM,B -- superdiagnostic / 500 Hz,superdiagnostic,500,5,5000,0.8128674499898032,0.5954051718075771,334.53321385383606 +BiLSTM,C -- diagnostic (27-class) / 100 Hz,diagnostic,100,27,1000,0.7833174078893279,0.1443118620835513,78.60738611221313 +BiLSTM,D -- diagnostic (27-class) / 500 Hz,diagnostic,500,27,5000,0.7464315651371636,0.1556990467987535,333.2661380767822 +ResNet-18,B -- superdiagnostic / 500 Hz,superdiagnostic,500,5,5000,0.7910428256059993,0.5280327234424165,80.28677606582642 +ResNet-18,D -- diagnostic (27-class) / 500 Hz,diagnostic,500,27,5000,,0.0446669233731228,74.48672533035278 +BiLSTM,B -- superdiagnostic / 500 Hz,superdiagnostic,500,5,5000,0.793495708090092,0.6315772446563357,262.47332191467285 +BiLSTM,D -- diagnostic (27-class) / 500 Hz,diagnostic,500,27,5000,,0.1162161975668708,261.98120498657227 diff --git a/examples/ptbxl_model_comparison_ablation.png b/examples/ptbxl_model_comparison_ablation.png index 41c77a1619b11ef34e0955a845bb7eb2152cb7e3..ec7be018a159b653efa654a8440a694c9296f900 100644 GIT binary patch literal 85995 zcmeFZg3s00OK&{nQyJL&sygXIA`yDZLUkj7nC=i=e~dUFW&dsnyNHZOjKlKWHiszp6HU1 zQTdXQohQFU364B*{-6W?kn(u?(nHVL#>4BC8-z^bm4}OivxkG-YYtC{o4cK}lc=DS zpx6TrTMrKxcWEIZ$N%vSg3fMlh1fpL+k*e(vWuFrI~f_n4brc3Z!<=8$bc**6!)&19x z-*h4A|Ml~)1U+b4#5bKU*m;CcM`qW8EsP*{u^1;a$hmCR0iS(c| zpQbaz(wCoq`|M0scKV-g7OV}cvWsh^N{B!GcuT``dG<4>YTw3ijz(uV&Ca^}%CA>c zw;tHqj~7EIuiUn3;}o4jKt67w$}<`cM_yzu^@eD^DT)1NZ_z_L|#`;gaIVaYZK zl91j+@do$x+EtBrRP=oU zb+fNq&rYFnQ=v_va{KFp&cy~LHoL8Orz-HH=rGp97nTE##*Nn2Qk^B%)BWH*NkSQO zSNYhtCd#0`*lj!cvt#QjC`yoc*wc`I2^M|b`++2M;z7^qH{n;0LkDexVrsn21)mtT zeO$S|wK(bh2gzT49JCBcGPe;yyqr+Fo_dfpaC5acNs?9CL%7PO|M_0>*dtYMb}9EY zvHQ>CD{$8={B~*zF*CF7gL0mMje&ocV->z@mPps<_oqshpX}5v+-Oz}AZ>+TEQkW! zzk5P0C3NLm5Rrg#SZJM-A6qO_IZrT9G?�{>7c)|61B}`>SBI1C@lnb=Nxvi?7)} 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 literal 0 HcmV?d00001 diff --git a/examples/ptbxl_superdiagnostic_se_resnet.ipynb b/examples/ptbxl_superdiagnostic_se_resnet.ipynb index 047b0f090..307005e1f 100644 --- a/examples/ptbxl_superdiagnostic_se_resnet.ipynb +++ b/examples/ptbxl_superdiagnostic_se_resnet.ipynb @@ -87,7 +87,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 14, "id": "b1dc5e9d", "metadata": {}, "outputs": [ @@ -98,7 +98,8 @@ "Python 3.9.6 (default, Dec 2 2025, 07:27:58) \n", "[Clang 17.0.0 (clang-1700.6.3.2)]\n", "PyTorch 2.8.0 | CUDA available: False\n", - "Using device: cpu\n" + "Apple MPS GPU detected — using Metal backend (~11× faster than CPU)\n", + "Using device: mps\n" ] } ], @@ -112,13 +113,19 @@ "import torch\n", "print(f'PyTorch {torch.__version__} | CUDA available: {torch.cuda.is_available()}')\n", "\n", - "DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'\n", + "if torch.cuda.is_available():\n", + " DEVICE = 'cuda'\n", + "elif torch.backends.mps.is_available():\n", + " DEVICE = 'mps'\n", + " print('Apple MPS GPU detected — using Metal backend (~11× faster than CPU)')\n", + "else:\n", + " DEVICE = 'cpu'\n", "print(f'Using device: {DEVICE}')" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 22, "id": "d88687c4", "metadata": {}, "outputs": [ @@ -165,7 +172,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 23, "id": "5ce43a37", "metadata": {}, "outputs": [ @@ -173,9 +180,9 @@ "name": "stdout", "output_type": "stream", "text": [ - "PTB-XL root: /Users/anuragd/CS-598_HealthCareAssignment/DLH_Project/classification-of-12-lead-ecgs-the-physionetcomputing-in-cardiology-challenge-2020-1.0.2/training/ptb-xl\n", - "ptbxl_database.csv: /Users/anuragd/CS-598_HealthCareAssignment/DLH_Project/classification-of-12-lead-ecgs-the-physionetcomputing-in-cardiology-challenge-2020-1.0.2/training/ptb-xl/ptbxl_database.csv\n", - "Found 22 group directories\n" + "PTB-XL root: /Users/anuragd/CS-598_HealthCareAssignment/DLH_Project/WFDB\n", + "ptbxl_database.csv: /Users/anuragd/CS-598_HealthCareAssignment/DLH_Project/WFDB/ptbxl_database.csv\n", + "Found 0 group directories\n" ] } ], @@ -187,7 +194,7 @@ "# PTB-XL data root — contains g1/…g22/ sub-dirs AND ptbxl_database.csv\n", "# -----------------------------------------------------------------------\n", "PTBXL_ROOT = str(\n", - " Path(\"/Users/anuragd/CS-598_HealthCareAssignment/DLH_Project/classification-of-12-lead-ecgs-the-physionetcomputing-in-cardiology-challenge-2020-1.0.2/training/ptb-xl\")\n", + " Path(\"/Users/anuragd/CS-598_HealthCareAssignment/DLH_Project/WFDB\")\n", " .resolve()\n", ")\n", "\n", @@ -217,19 +224,10 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 24, "id": "0d594aad", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] - } - ], + "outputs": [], "source": [ "import time\n", "import numpy as np\n", @@ -259,7 +257,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 25, "id": "45a40dcb", "metadata": {}, "outputs": [ @@ -267,72 +265,44 @@ "name": "stdout", "output_type": "stream", "text": [ - "Batch size: 64 | LR: 0.01 | Epochs: 5\n", - "Dev mode: True | Quick mode: False\n", + "Batch size: 64 | LR: 0.01 | Epochs: 20\n", + "Dev mode: False | Max patients: 2100 | Run phase: ALL\n", "Models: ['ResNet-18', 'SE-ResNet-50', 'Lambda-ResNet-18', 'BiLSTM']\n" ] } ], "source": [ - "# Training hyper-parameters (fixed across all model × config combinations)\n", - "BATCH_SIZE = 64 # best setting from grid search\n", - "LEARNING_RATE = 0.01 # best setting from Nonaka & Seita (2021) grid search (Table 5a)\n", - "EPOCHS = 5 # increase to 20–30 for full reproduction\n", - "SPLIT = [0.7, 0.1, 0.2] # train / val / test\n", - "MONITOR = 'roc_auc_macro' # PyHealth trainer monitor key\n", + "# Training hyper-parameters (fixed across all model x config combinations)\n", + "BATCH_SIZE = 64\n", + "LEARNING_RATE = 0.01\n", + "EPOCHS = 20\n", + "SPLIT = [0.7, 0.1, 0.2]\n", + "MONITOR = 'roc_auc_macro'\n", "\n", - "# Use dev=True to cap the dataset at ~1 000 patients for a quick smoke test.\n", - "# Set DEV_MODE=False for the full 21 837-recording experiment.\n", - "DEV_MODE = True\n", + "DEV_MODE = False\n", + "RUN_PHASE = 'ALL'\n", + "MAX_PATIENTS = 2100\n", + "QUICK_MODE = False\n", "\n", - "# -----------------------------------------------------------------------\n", - "# QUICK_MODE: when True, only config A (superdiagnostic / 100 Hz) is run\n", - "# for all 4 models → 4 runs instead of 16, ~20–30 min on CPU.\n", - "# This reproduces the 'super.' row from Nonaka & Seita (2021) Fig 5.1\n", - "# and is the recommended setting for smoke-testing relative improvements\n", - "# over ResNet-18. Set to False to run the full 2×2 ablation grid.\n", - "# -----------------------------------------------------------------------\n", - "QUICK_MODE = False\n", - "\n", - "# -----------------------------------------------------------------------\n", - "# Model registry — each entry defines a model class and its constructor\n", - "# kwargs beyond 'dataset'.\n", - "#\n", - "# ResNet18ECG, SEResNet50ECG, LambdaResNet18ECG are ECGBackboneModel\n", - "# subclasses that auto-resolve feature/label keys from the dataset schema.\n", - "# BiLSTMECG requires explicit feature/label keys.\n", - "# -----------------------------------------------------------------------\n", "MODELS = [\n", - " {\n", - " 'name': 'ResNet-18',\n", - " 'cls': ResNet18ECG,\n", - " 'kwargs': {}, # constructor: ResNet18ECG(dataset=...)\n", - " },\n", - " {\n", - " 'name': 'SE-ResNet-50',\n", - " 'cls': SEResNet50ECG,\n", - " 'kwargs': {}, # constructor: SEResNet50ECG(dataset=...)\n", - " },\n", - " {\n", - " 'name': 'Lambda-ResNet-18',\n", - " 'cls': LambdaResNet18ECG,\n", - " 'kwargs': {}, # constructor: LambdaResNet18ECG(dataset=...)\n", - " },\n", + " {'name': 'ResNet-18', 'cls': ResNet18ECG, 'kwargs': {}},\n", + " {'name': 'SE-ResNet-50', 'cls': SEResNet50ECG, 'kwargs': {}},\n", + " {'name': 'Lambda-ResNet-18', 'cls': LambdaResNet18ECG,'kwargs': {}},\n", " {\n", " 'name': 'BiLSTM',\n", " 'cls': BiLSTMECG,\n", - " 'kwargs': { # constructor: BiLSTMECG(dataset=..., feature_keys=..., ...)\n", + " 'kwargs': {\n", " 'feature_keys': ['signal'],\n", " 'label_key': 'labels',\n", " 'mode': 'multilabel',\n", - " 'hidden_size': 64, # lstm_d1_h64 from Nonaka & Seita (2021) Table 3\n", + " 'hidden_size': 64,\n", " 'n_layers': 1,\n", " },\n", " },\n", "]\n", "\n", "print(f'Batch size: {BATCH_SIZE} | LR: {LEARNING_RATE} | Epochs: {EPOCHS}')\n", - "print(f'Dev mode: {DEV_MODE} | Quick mode: {QUICK_MODE}')\n", + "print(f'Dev mode: {DEV_MODE} | Max patients: {MAX_PATIENTS} | Run phase: {RUN_PHASE}')\n", "print(f'Models: {[m[\"name\"] for m in MODELS]}')\n" ] }, @@ -363,7 +333,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 26, "id": "19663067", "metadata": {}, "outputs": [ @@ -371,22 +341,55 @@ "name": "stdout", "output_type": "stream", "text": [ - "Initializing ptbxl dataset from /Users/anuragd/CS-598_HealthCareAssignment/DLH_Project/classification-of-12-lead-ecgs-the-physionetcomputing-in-cardiology-challenge-2020-1.0.2/training/ptb-xl (dev mode: True)\n", - "No cache_dir provided. Using default cache dir: /Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22\n", - "Found cached event dataframe: /Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/global_event_df.parquet\n", + "Initializing ptbxl dataset from /Users/anuragd/CS-598_HealthCareAssignment/DLH_Project/WFDB (dev mode: False)\n", + "No cache_dir provided. Using default cache dir: /Users/anuragd/Library/Caches/pyhealth/e06c1785-82da-5d10-8959-fb69e8e75b53\n", + "Found cached event dataframe: /Users/anuragd/Library/Caches/pyhealth/e06c1785-82da-5d10-8959-fb69e8e75b53/global_event_df.parquet\n", + "Found 21799 unique patient IDs\n", + "Full-dataset patients: 21799\n", + "Capping to 2100 patients.\n", + "Building 2100-patient parquet cache...\n", + "Done.\n", + "Initializing ptbxl dataset from /Users/anuragd/CS-598_HealthCareAssignment/DLH_Project/WFDB (dev mode: False)\n", + "Using provided cache_dir: /Users/anuragd/Library/Caches/pyhealth/ptbxl_cap2100/e06c1785-82da-5d10-8959-fb69e8e75b53\n", + "Found cached event dataframe: /Users/anuragd/Library/Caches/pyhealth/ptbxl_cap2100/e06c1785-82da-5d10-8959-fb69e8e75b53/global_event_df.parquet\n", "Dataset: ptbxl\n", - "Dev mode: True\n", - "Number of patients: 1000\n", - "Number of events: 1000\n" + "Dev mode: False\n", + "Number of patients: 2100\n", + "Number of events: 2100\n" ] } ], "source": [ - "base_dataset = PTBXLDataset(\n", - " root=PTBXL_ROOT,\n", - " dev=DEV_MODE,\n", - ")\n", - "base_dataset.stats()" + "import pathlib, polars as pl\n", + "\n", + "# Reuse the existing full-dataset parquet (e06c1785) — no rebuild, no deletion.\n", + "_bootstrap_ds = PTBXLDataset(root=PTBXL_ROOT, dev=False)\n", + "_full_uuid = _bootstrap_ds.cache_dir.name\n", + "_full_parquet_dir = _bootstrap_ds.cache_dir / 'global_event_df.parquet'\n", + "print(f'Full-dataset patients: {len(_bootstrap_ds.unique_patient_ids)}')\n", + "\n", + "_pids_cap = sorted(_bootstrap_ds.unique_patient_ids)[:MAX_PATIENTS]\n", + "print(f'Capping to {len(_pids_cap)} patients.')\n", + "\n", + "# Build a new independent cache at ~/Library/Caches/pyhealth/ptbxl_cap2100//\n", + "_custom_root = pathlib.Path.home() / 'Library' / 'Caches' / 'pyhealth' / f'ptbxl_cap{MAX_PATIENTS}'\n", + "_custom_parquet_dir = _custom_root / _full_uuid / 'global_event_df.parquet'\n", + "\n", + "if _custom_parquet_dir.is_dir() and any(_custom_parquet_dir.glob('*.parquet')):\n", + " print(f'Custom cache already exists: {_custom_parquet_dir}')\n", + "else:\n", + " print('Building 2100-patient parquet cache...')\n", + " _custom_parquet_dir.mkdir(parents=True, exist_ok=True)\n", + " (\n", + " pl.scan_parquet(_full_parquet_dir)\n", + " .filter(pl.col('patient_id').is_in(_pids_cap))\n", + " .collect()\n", + " .write_parquet(str(_custom_parquet_dir / 'data.parquet'))\n", + " )\n", + " print('Done.')\n", + "\n", + "base_dataset = PTBXLDataset(root=PTBXL_ROOT, dev=False, cache_dir=_custom_root)\n", + "base_dataset.stats()\n" ] }, { @@ -401,7 +404,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 27, "id": "ae5daef7", "metadata": {}, "outputs": [ @@ -409,39 +412,39 @@ "name": "stdout", "output_type": "stream", "text": [ - "Ablation configurations:\n", - " A — superdiagnostic / 100 Hz (baseline) → K=5, T=1000\n", - " B — superdiagnostic / 500 Hz → K=5, T=5000\n", - " C — diagnostic (27-class) / 100 Hz → K=27, T=1000\n", - " D — diagnostic (27-class) / 500 Hz → K=27, T=5000\n" + "Phase [ALL] -- running 4 config(s):\n", + " A -- superdiagnostic / 100 Hz (baseline) -> K=5, T=1000\n", + " B -- superdiagnostic / 500 Hz -> K=5, T=5000\n", + " C -- diagnostic (27-class) / 100 Hz -> K=27, T=1000\n", + " D -- diagnostic (27-class) / 500 Hz -> K=27, T=5000\n" ] } ], "source": [ "_ALL_CONFIGS = [\n", " {\n", - " 'name': 'A — superdiagnostic / 100 Hz (baseline)',\n", + " 'name': 'A -- superdiagnostic / 100 Hz (baseline)',\n", " 'label_type': 'superdiagnostic',\n", " 'sampling_rate': 100,\n", " 'n_classes': 5,\n", " 'T': 1000,\n", " },\n", " {\n", - " 'name': 'B — superdiagnostic / 500 Hz',\n", + " 'name': 'B -- superdiagnostic / 500 Hz',\n", " 'label_type': 'superdiagnostic',\n", " 'sampling_rate': 500,\n", " 'n_classes': 5,\n", " 'T': 5000,\n", " },\n", " {\n", - " 'name': 'C — diagnostic (27-class) / 100 Hz',\n", + " 'name': 'C -- diagnostic (27-class) / 100 Hz',\n", " 'label_type': 'diagnostic',\n", " 'sampling_rate': 100,\n", " 'n_classes': 27,\n", " 'T': 1000,\n", " },\n", " {\n", - " 'name': 'D — diagnostic (27-class) / 500 Hz',\n", + " 'name': 'D -- diagnostic (27-class) / 500 Hz',\n", " 'label_type': 'diagnostic',\n", " 'sampling_rate': 500,\n", " 'n_classes': 27,\n", @@ -449,15 +452,17 @@ " },\n", "]\n", "\n", - "# Apply QUICK_MODE filter — only config A when enabled\n", - "ABLATION_CONFIGS = _ALL_CONFIGS[:1] if QUICK_MODE else _ALL_CONFIGS\n", + "# Filter configs based on RUN_PHASE\n", + "_PHASE_MAP = {\n", + " 'AC': [_ALL_CONFIGS[0], _ALL_CONFIGS[2]], # A + C (100 Hz only)\n", + " 'BD': [_ALL_CONFIGS[1], _ALL_CONFIGS[3]], # B + D (500 Hz only)\n", + " 'ALL': _ALL_CONFIGS,\n", + "}\n", + "ABLATION_CONFIGS = _PHASE_MAP.get(RUN_PHASE, _ALL_CONFIGS)\n", "\n", - "print('Ablation configurations:')\n", + "print(f'Phase [{RUN_PHASE}] -- running {len(ABLATION_CONFIGS)} config(s):')\n", "for cfg in ABLATION_CONFIGS:\n", - " print(f\" {cfg['name']} → K={cfg['n_classes']}, T={cfg['T']}\")\n", - "if QUICK_MODE:\n", - " print('\\n[QUICK_MODE] Running config A only (superdiagnostic/100Hz). '\n", - " 'Set QUICK_MODE=False to run all 4 configs.')\n" + " print(f\" {cfg['name']} -> K={cfg['n_classes']}, T={cfg['T']}\")\n" ] }, { @@ -479,7 +484,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 28, "id": "f1c34a69", "metadata": {}, "outputs": [ @@ -490,18 +495,99 @@ "\n", "======================================================================\n", "Model : ResNet-18\n", - "Config: A — superdiagnostic / 100 Hz (baseline)\n", + "Config: A -- superdiagnostic / 100 Hz (baseline)\n", " label_type=superdiagnostic, sampling_rate=100 Hz\n", " K=5 classes, T=1000 time-steps per lead\n", "======================================================================\n", "Setting task PTBXLSuperDiagnostic_100Hz for ptbxl base dataset...\n", - "Task cache paths: task_df=/Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/tasks/PTBXLSuperDiagnostic_100Hz_a435b25e-18d8-5d3b-b000-8457f09e6895/task_df.ld, samples=/Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/tasks/PTBXLSuperDiagnostic_100Hz_a435b25e-18d8-5d3b-b000-8457f09e6895/samples_cdbbc602-34e2-5a41-8643-4c76b08829f6.ld\n", - "Found cached processed samples at /Users/anuragd/Library/Caches/pyhealth/da5d24ca-1872-550a-8963-ad9ebf8dea22/tasks/PTBXLSuperDiagnostic_100Hz_a435b25e-18d8-5d3b-b000-8457f09e6895/samples_cdbbc602-34e2-5a41-8643-4c76b08829f6.ld, skipping processing.\n", - " Total ML samples: 997\n", + "Task cache paths: task_df=/Users/anuragd/Library/Caches/pyhealth/ptbxl_cap2100/e06c1785-82da-5d10-8959-fb69e8e75b53/tasks/PTBXLSuperDiagnostic_100Hz_a435b25e-18d8-5d3b-b000-8457f09e6895/task_df.ld, samples=/Users/anuragd/Library/Caches/pyhealth/ptbxl_cap2100/e06c1785-82da-5d10-8959-fb69e8e75b53/tasks/PTBXLSuperDiagnostic_100Hz_a435b25e-18d8-5d3b-b000-8457f09e6895/samples_cdbbc602-34e2-5a41-8643-4c76b08829f6.ld\n", + "Applying task transformations on data with 1 workers...\n", + "Detected Jupyter notebook environment, setting num_workers to 1\n", + "Single worker mode, processing sequentially\n", + "Worker 0 started processing 2100 patients. (Polars threads: 10)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/2100 [00:00\n", "Optimizer params: {'lr': 0.01}\n", - "Weight decay: 0.0\n", + "Weight decay: 0.0001\n", "Max grad norm: None\n", - "Val dataloader: \n", + "Val dataloader: \n", "Monitor: roc_auc_macro\n", "Monitor criterion: max\n", - "Epochs: 5\n", + "Epochs: 20\n", "Patience: None\n", "\n" ] }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "440fd14663964608a2c8c5e820cb2ffb", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 20\n", + "Patience: None\n", + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "94f804cf3abc4675869e70555fc4edd6", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 20\n", + "Patience: None\n", + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1482792f487742daaa53c39f7ac27411", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 20\n", + "Patience: None\n", + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ac15e47ec55b48aeb7acf878c444000f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 20\n", + "Patience: None\n", + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "060cbccc589b48c9940e3470b8112dee", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 20\n", + "Patience: None\n", + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "56a00d65a7344edbbe614c3bfefa0c8d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 20\n", + "Patience: None\n", + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "18f9bdd6156046d7be393948f969336f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 20\n", + "Patience: None\n", + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "95bf778080a6467ea5e5dd3eec595016", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 20\n", + "Patience: None\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "962e9fde512f489ca329ae87b2ae90a2", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 20\n", + "Patience: None\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "7aefc7f1bbaa440ab002d8050058f6ca", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 20\n", + "Patience: None\n", + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "db05ddd056ca446cbceeae9b3ef51737", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 20\n", + "Patience: None\n", + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "66baaec8d0b348a1ae3913d1b20ca711", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 20\n", + "Patience: None\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f4f0ecdd39d943d7bb28e8edf39bd1e6", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 20\n", + "Patience: None\n", + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "071d499d11b94775aaf8a3d84993945a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 20\n", + "Patience: None\n", + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "df836fc2560f4e9f90d0fd811a451d45", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.01}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 20\n", + "Patience: None\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "534a0b5918fc4f269a15ca5e239da5d4", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 20: 0%| | 0/22 [00:00 examples/ptbxl_results_phase_ALL.csv\n", + " model config roc_auc_macro f1_macro train_time_s\n", + " ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 0.762939 0.498161 33.149601\n", + " ResNet-18 B -- superdiagnostic / 500 Hz 0.804179 0.529553 114.700226\n", + " ResNet-18 C -- diagnostic (27-class) / 100 Hz 0.743760 0.082997 33.515967\n", + " ResNet-18 D -- diagnostic (27-class) / 500 Hz NaN 0.056788 112.712338\n", + " SE-ResNet-50 A -- superdiagnostic / 100 Hz (baseline) 0.802244 0.378764 149.364521\n", + " SE-ResNet-50 B -- superdiagnostic / 500 Hz 0.755365 0.367256 636.152798\n", + " SE-ResNet-50 C -- diagnostic (27-class) / 100 Hz 0.711834 0.092479 144.197475\n", + " SE-ResNet-50 D -- diagnostic (27-class) / 500 Hz NaN 0.057329 634.504002\n", + "Lambda-ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 0.795544 0.397067 270.942773\n", + "Lambda-ResNet-18 B -- superdiagnostic / 500 Hz 0.772388 0.521192 1253.451983\n", + "Lambda-ResNet-18 C -- diagnostic (27-class) / 100 Hz NaN 0.050867 258.623402\n", + "Lambda-ResNet-18 D -- diagnostic (27-class) / 500 Hz NaN 0.081759 1244.658968\n", + " BiLSTM A -- superdiagnostic / 100 Hz (baseline) 0.813596 0.645111 77.515355\n", + " BiLSTM B -- superdiagnostic / 500 Hz 0.812867 0.595405 334.533214\n", + " BiLSTM C -- diagnostic (27-class) / 100 Hz 0.783317 0.144312 78.607386\n", + " BiLSTM D -- diagnostic (27-class) / 500 Hz 0.746432 0.155699 333.266138\n" + ] + } + ], + "source": [ + "# -- Stash results for this phase to CSV -----------------------------------\n", + "# Saves after each phase so results survive kernel restarts / interrupts.\n", + "# Phase 1 (AC) -> examples/ptbxl_results_phase_AC.csv\n", + "# Phase 2 (BD) -> examples/ptbxl_results_phase_BD.csv\n", + "import pathlib, pandas as pd\n", + "\n", + "_csv_dir = pathlib.Path('examples')\n", + "_csv_dir.mkdir(exist_ok=True)\n", + "_phase_csv = _csv_dir / f'ptbxl_results_phase_{RUN_PHASE}.csv'\n", + "\n", + "_phase_df = pd.DataFrame(results)\n", + "_phase_df.to_csv(_phase_csv, index=False)\n", + "print(f'Stashed {len(_phase_df)} results -> {_phase_csv}')\n", + "print(_phase_df[['model', 'config', 'roc_auc_macro', 'f1_macro', 'train_time_s']].to_string(index=False))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "stash001", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Stashed 16 results → examples/ptbxl_results_phase_ALL.csv\n", + " model config roc_auc_macro f1_macro train_time_s\n", + " ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 0.762939 0.498161 33.149601\n", + " ResNet-18 B -- superdiagnostic / 500 Hz 0.804179 0.529553 114.700226\n", + " ResNet-18 C -- diagnostic (27-class) / 100 Hz 0.743760 0.082997 33.515967\n", + " ResNet-18 D -- diagnostic (27-class) / 500 Hz NaN 0.056788 112.712338\n", + " SE-ResNet-50 A -- superdiagnostic / 100 Hz (baseline) 0.802244 0.378764 149.364521\n", + " SE-ResNet-50 B -- superdiagnostic / 500 Hz 0.755365 0.367256 636.152798\n", + " SE-ResNet-50 C -- diagnostic (27-class) / 100 Hz 0.711834 0.092479 144.197475\n", + " SE-ResNet-50 D -- diagnostic (27-class) / 500 Hz NaN 0.057329 634.504002\n", + "Lambda-ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 0.795544 0.397067 270.942773\n", + "Lambda-ResNet-18 B -- superdiagnostic / 500 Hz 0.772388 0.521192 1253.451983\n", + "Lambda-ResNet-18 C -- diagnostic (27-class) / 100 Hz NaN 0.050867 258.623402\n", + "Lambda-ResNet-18 D -- diagnostic (27-class) / 500 Hz NaN 0.081759 1244.658968\n", + " BiLSTM A -- superdiagnostic / 100 Hz (baseline) 0.813596 0.645111 77.515355\n", + " BiLSTM B -- superdiagnostic / 500 Hz 0.812867 0.595405 334.533214\n", + " BiLSTM C -- diagnostic (27-class) / 100 Hz 0.783317 0.144312 78.607386\n", + " BiLSTM D -- diagnostic (27-class) / 500 Hz 0.746432 0.155699 333.266138\n" + ] + } + ], + "source": [ + "# ── Stash results for this phase to CSV ──────────────────────────────\n", + "# Saves after each phase so results survive kernel restarts / interrupts.\n", + "# Phase 1 (AC) → ptbxl_results_phase_AC.csv\n", + "# Phase 2 (BD) → ptbxl_results_phase_BD.csv\n", + "import pathlib, pandas as pd\n", + "\n", + "_csv_dir = pathlib.Path('examples')\n", + "_csv_dir.mkdir(exist_ok=True)\n", + "_phase_csv = _csv_dir / f'ptbxl_results_phase_{RUN_PHASE}.csv'\n", + "\n", + "_phase_df = pd.DataFrame(results)\n", + "_phase_df.to_csv(_phase_csv, index=False)\n", + "print(f'Stashed {len(_phase_df)} results → {_phase_csv}')\n", + "print(_phase_df[['model', 'config', 'roc_auc_macro', 'f1_macro', 'train_time_s']].to_string(index=False))\n" + ] + }, + { + "cell_type": "markdown", + "id": "b2a47542", + "metadata": {}, + "source": [ + "## 7. Results Summary" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "1eb2e622", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loaded 28 results from ['ptbxl_results_phase_AC.csv', 'ptbxl_results_phase_ALL.csv', 'ptbxl_results_phase_BD.csv']\n", + "\n", + " model config K T roc_auc_macro f1_macro train_time_s\n", + " ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 5 1000 0.882280 0.577331 245.333493\n", + " ResNet-18 C -- diagnostic (27-class) / 100 Hz 27 1000 0.797653 0.173184 249.207059\n", + " SE-ResNet-50 A -- superdiagnostic / 100 Hz (baseline) 5 1000 0.875632 0.675094 1258.272661\n", + " SE-ResNet-50 C -- diagnostic (27-class) / 100 Hz 27 1000 0.717975 0.125620 1236.129843\n", + "Lambda-ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 5 1000 0.871029 0.671461 1764.986753\n", + "Lambda-ResNet-18 C -- diagnostic (27-class) / 100 Hz 27 1000 0.719476 0.126444 1827.354280\n", + " BiLSTM A -- superdiagnostic / 100 Hz (baseline) 5 1000 0.838571 0.672886 515.175012\n", + " BiLSTM C -- diagnostic (27-class) / 100 Hz 27 1000 0.820281 0.227032 621.552368\n", + " ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 5 1000 0.762939 0.498161 33.149601\n", + " ResNet-18 B -- superdiagnostic / 500 Hz 5 5000 0.804179 0.529553 114.700226\n", + " ResNet-18 C -- diagnostic (27-class) / 100 Hz 27 1000 0.743760 0.082997 33.515967\n", + " ResNet-18 D -- diagnostic (27-class) / 500 Hz 27 5000 NaN 0.056788 112.712338\n", + " SE-ResNet-50 A -- superdiagnostic / 100 Hz (baseline) 5 1000 0.802244 0.378764 149.364521\n", + " SE-ResNet-50 B -- superdiagnostic / 500 Hz 5 5000 0.755365 0.367256 636.152798\n", + " SE-ResNet-50 C -- diagnostic (27-class) / 100 Hz 27 1000 0.711834 0.092479 144.197475\n", + " SE-ResNet-50 D -- diagnostic (27-class) / 500 Hz 27 5000 NaN 0.057329 634.504002\n", + "Lambda-ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 5 1000 0.795544 0.397067 270.942773\n", + "Lambda-ResNet-18 B -- superdiagnostic / 500 Hz 5 5000 0.772388 0.521192 1253.451983\n", + "Lambda-ResNet-18 C -- diagnostic (27-class) / 100 Hz 27 1000 NaN 0.050867 258.623402\n", + "Lambda-ResNet-18 D -- diagnostic (27-class) / 500 Hz 27 5000 NaN 0.081759 1244.658968\n", + " BiLSTM A -- superdiagnostic / 100 Hz (baseline) 5 1000 0.813596 0.645111 77.515355\n", + " BiLSTM B -- superdiagnostic / 500 Hz 5 5000 0.812867 0.595405 334.533214\n", + " BiLSTM C -- diagnostic (27-class) / 100 Hz 27 1000 0.783317 0.144312 78.607386\n", + " BiLSTM D -- diagnostic (27-class) / 500 Hz 27 5000 0.746432 0.155699 333.266138\n", + " ResNet-18 B -- superdiagnostic / 500 Hz 5 5000 0.791043 0.528033 80.286776\n", + " ResNet-18 D -- diagnostic (27-class) / 500 Hz 27 5000 NaN 0.044667 74.486725\n", + " BiLSTM B -- superdiagnostic / 500 Hz 5 5000 0.793496 0.631577 262.473322\n", + " BiLSTM D -- diagnostic (27-class) / 500 Hz 27 5000 NaN 0.116216 261.981205\n", + "\n", + "Combined results saved -> examples/ptbxl_ablation_results.csv\n" + ] + } + ], + "source": [ + "# -- Combine results from all completed phases ----------------------------\n", + "import pathlib, pandas as pd\n", + "\n", + "_csv_dir = pathlib.Path('examples')\n", + "_phase_files = sorted(_csv_dir.glob('ptbxl_results_phase_*.csv'))\n", + "\n", + "if _phase_files:\n", + " results_df = pd.concat([pd.read_csv(f) for f in _phase_files], ignore_index=True)\n", + " print(f'Loaded {len(results_df)} results from {[f.name for f in _phase_files]}')\n", + "else:\n", + " # Fall back to in-memory results if no CSVs saved yet\n", + " results_df = pd.DataFrame(results)\n", + " print(f'Using in-memory results ({len(results_df)} rows)')\n", + "\n", + "display_cols = ['model', 'config', 'K', 'T', 'roc_auc_macro', 'f1_macro', 'train_time_s']\n", + "print()\n", + "print(results_df[display_cols].to_string(index=False))\n", + "\n", + "# Also save the combined CSV for git / report\n", + "_combined_csv = _csv_dir / 'ptbxl_ablation_results.csv'\n", + "results_df.to_csv(_combined_csv, index=False)\n", + "print(f'\\nCombined results saved -> {_combined_csv}')\n" ] }, { @@ -765,13 +20606,13 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 36, "id": "88814df1", "metadata": {}, "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABv4AAAIDCAYAAADMsGn8AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8ekN5oAAAACXBIWXMAAA9hAAAPYQGoP6dpAACZSElEQVR4nOzdB5hU1fk44EORoiBGLICioCLYsSd2EyMq9lhij7HEbuxdrFFjI8YaEY1RE3vvNbFg771hRQUrWACB+T/f+T2z/9nK7rLLLrPv+zwDO3fu3Dm3znz3O6VdoVAoJAAAAAAAAGCW1r6lCwAAAAAAAADMOIk/AAAAAAAAKAMSfwAAAAAAAFAGJP4AAAAAAACgDEj8AQAAAAAAQBmQ+AMAAAAAAIAyIPEHAAAAAAAAZUDiDwAAAAAAAMqAxB8AAAAAAACUAYk/AACgWayzzjpp6aWXbvT7H3nkkdSuXbv8P8yoOJZOOOGEli5G2fvDH/6Q+vXr1+hrRjzKURx7cQx++eWXzfo5H3zwQf6cs846q95lakqu2wAALU/iDwDKyBVXXJFvthQfHTt2TAsssEC+Cffpp5822+cWbxzNP//86ccff6z2etwA3HjjjRu17AsvvDCvV319//33adiwYWmDDTZIc889dy5XXe+/7rrr0i9/+cs011xzpZ49e6a111473Xnnnak1GDduXDrwwAPToEGDUteuXdN8882XVllllXTEEUfk9SyK/Vu630sfXbp0me7nOG7+/43Kmh5PPvlktfmfeOKJtMYaa6TZZ5899erVKx1wwAGV9klLKa7HDTfc0NJFabWqni+dO3dOiy++eDr++OPTxIkTm+1zI5kRn7fJJpvM0I36quLciXOpITfZ33rrrXTQQQel1VZbLV8j4rOjDDWJbXLaaaelJZdcMh/vcW3Yeuut02uvvZZaWtVrVzziOrnuuuumu+++u97vf/bZZ2foWlzX9aPqo2q5H3vssWqfVygUUt++ffPrjb0GhuJn7L777jW+fswxx1TM09zJqNaopuOnpkdjk5jNJb7folyrrrpqi5ejId+z5SSOiarXnTXXXDPdfPPNjTq2ir+Hio/27dun3r175/O/pt8gVY0YMSK/75///Ge110aNGpWXd+ihhzZZpRwAoPXr2NIFAACa3kknnZT69++fb9jGDYO4ARE3F1999dV6JYIaa+zYsemiiy5KhxxySJPeWJpnnnnyzfr6iJuXsf4LLbRQWm655eq8Gf73v/89J2yGDh2aTj/99Ly9YlvFjZYbb7wxbbnllqmlfP3112mllVZK48ePT3/84x/zDeevvvoqvfzyy3kb77333qlbt24V80fyIm78VNWhQ4d6f2ZbPm6K4nhYeeWVK01bbLHFKj1/8cUX029+85u0xBJLpHPOOSd98sknOWHzzjvv1CvZQMsrPV++++67dOutt6aTTz45vffee+nqq69u1s++44470nPPPZdWXHHFJlleJP5OPPHE/Hd9W0rFjeDzzjsvJ/PiOI5jujY77LBDuu2229Iee+yRVlhhhTRmzJh0wQUXpF/96lfplVdeSQsvvHBqacVrVyTMvvjii3zt2mijjdLtt99eKXH2008/5YoNTX0tjm34r3/9q9L7jjrqqHyNjuRabeK6es011+RKBKX++9//5utKHKczKj4jvs/imtipU6dKr/373//Orzdnwrs1W2uttartt0iSRlJ3zz33rJhW+l3bGsQ1KhJGTz/9dHr33XerfUfNLLV9z8Z2jXOt6vFWbgYPHlzxuyWui5dcckn+3RjXhfXXX79Rx1a8N6ZNmzYtffzxx+nSSy/N2zP2dXxebXbbbbec9IvkXlzzoiJb+Pnnn/PnRUWC4vcEANA2SPwBQBnacMMN843K4o2GuDFzxhln5Ju322yzTbN9btyUOPPMM9M+++yTW0W0hKgh/dlnn+VWWNGKo2oSp2riL16Pm8PFlhhxYzdatMQNlJZM/F122WXpo48+So8//nhulVMqbkBXvaEWN7N33HHHGfrMtnzcFEWN/a222qrOeY4++uj0i1/8IieV55xzzjwtbsJGYuS+++7LN/xo3aqeL3HsxXkWiZBI5kYr1OYQFRImTJiQb8DGedVSNt100/Ttt9+m7t2756R1bYm/aPF700035ZvJcY6Wnie//vWv82vRcrCllV67ijfBYx/G/ixN/DWmAkN9rsWx3KrX36hMEtfQuq7LkZy8/vrrcxK2NCEZycBIDDdFK7xo/R7HWlRK2GyzzSq1Wh49enT63e9+lxODs6Jo2bzeeuvlFvtVff755/k3QF0tJhdZZJH8KLXXXnvlaTP6fdpcYp/Fvotz709/+lNOAkYvB61JtC5rzspCrUX8Viw9TnbeeeechD333HMrjqOG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j3nmmafwxBNPTHfZ9957b57/9ttvrzR9o402KiyyyCIVzzfbbLO8Dg31+eef5/0WnzFo0KDCXnvtVbjmmmsK3377bbV5d9lll0rrUPoYMmTIdD/LcVMoPP7444Xf/e53hcsuu6xw6623Fk477bRCz549C126dCk8//zzFfNdf/31+bP+97//VVvG1ltvXejVq1edn3PJJZfk97/yyiuVpi+55JKFX//61w3afjV5+OGH8/KjnHX58ccfKz2fPHlyYemll65UhhDLimNg9OjR1dYh1nX8+PEV04866qg8vXTe2Acx7eyzz66YNmnSpMLgwYML8803X/7cMHz48DzfddddVzHfDz/8UFhsscXy9Fiv2soeYn+1a9eu8OGHH053G8X5Msccc+TjLh7vvvtu4ayzzsrvj20Qx3OYMGFCYa655irsscce1c7NHj16VEz/5ptvchnPPPPMOj83tkXxWnDiiSfm9zz33HP5eWyzqss4+eSTcznffvvtSss58sgjCx06dCh89NFH+XmsQ7w3zqfGiM+sut9KPfXUU4VFF1200nVlxRVXLHz22WfTXXYcE7PNNlvh66+/rrT/Y7v+8Y9/rJgW23PfffdtcNmL166qjzhmr7jiimrzV91OxffHd0VTXItL1XUNKv3c888/v9C9e/eK4zquI+uuu26N18DS631t+6vq+sZ2je3fqVOnwr/+9a88/c4778zH+wcffFDpOhzinIxzM86Fn376qWJZd9xxR57v+OOPr5gW53Hv3r0rbYv77rsvzxdlb8y1O7ZZQ67dt912W6Fjx46FbbfdNn8vbbjhhvm6/cgjjxQaI8652MZFscypU6dWmie2fRxjJ510UoO+60u39RtvvFHo06dPYeWVV650ftTl2Wefze+///778/O4Vi244IKFAw88sFr5Yr6uXbsWPvnkk0rnckw/6KCDqpWpVE3X2PgtUfrbpq5jvPg9VLxuN+SYKh7fpds2LL/88vm6U5exY8fm43z99devtM/iHItljhw5stp305VXXlnp2hTfa/FbYHri+I7PKX6PvPTSS4Xf//73eZn7779/vY6tUsX9UPUR18o4Txqi+Ps0HvHdOr3vIwCgPGnxBwBlKMa8iZYgUUs7asJHi6yo+R61s4s1ih966KFcgzhapRRbh0WXY1HLOmqQF1sTRJdp0YIiptVH1F6PVkp1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AQAAAAAAQBmQ+AMAAAAAAIAyIPEHAAAAAAAAZUDiDwAAAAAAAMqAxB8AAAAAAACUAYk/AAAAAAAAKAMSfwAAAAAAAFAGJP4AAAAAAACgDEj8AQAAAAAAQBmQ+AMAAAAAAIAyIPEHAAAAAAAAZUDiDwAAAAAAAMqAxB8AAAAAAACUAYk/AAAAAAAAKAMSfwAAAAAAAFAGJP4AAAAAAACgDEj8AQAAAAAAQBmQ+AMAAAAAAIAyIPEHwAw74YQTUrt27dKXX3453Xn79euX/vCHPzTp58fyYrlQk+uuuy7NPffc6fvvv0/l6pe//GU6/PDDW7oYAADQJMSYs66Iu+abb7509dVXp3J18cUXp4UWWihNmjSppYsCUCOJP4A6XHHFFTnYKD66dOmSFl988bTffvulL774Is8TwUDpPLU9Ylmh6vQ55pgjLbnkkumUU05JP/74Y53lmThxYlpsscXSoEGD0uTJk6u9vuGGG6YePXqkMWPG5OePPPJI/owbbrhhhrbDKquskpdz0UUXpZYS6xTB34svvpjKXXG/FR8dOnTIgdNWW22V3njjjVrfd8cdd6QNNtgg9ezZs+JYPfTQQ9NXX31V52dtueWWqVevXqlTp075czbZZJN00003NajM22yzTS7rEUccUee59Oyzz9b4+sYbb1xjYB3H/LnnnptWXXXVfGyXnoNvv/32dMs1derUNGzYsLT//vunbt26pXIV2/2CCy5In3/+eUsXBQCAOogx/48Ys2VjzNLH73//+4r5nn766bTPPvukFVdcMc0222z59Yb629/+lrp3715pueUmEsNxvlxyySUtXRSAGnWseTIApU466aTUv3//HBQ99thjOTi566670quvvpqGDx9eqSVRTP/3v/+dkxXzzDNPxfTVVlut4u/f/va3aeedd85/x3sfffTRdNxxx6WXXnopXX/99bWWI4LC+Oz1118/nXbaaTmhUfSf//wn3XPPPenvf/976tOnT5Ot+zvvvJOeeeaZHHxGjb299947tVRQduKJJ+ZyDB48uNJrl156aZo2bVoqNwcccEBaeeWV088//5xefvnlXKswArY47iJRVyoSfGeffXZabrnlchIoWrg9//zz6fzzz8/HxoMPPpgGDhxY6T1x/MSxPWDAgPSnP/0pLbzwwjlJGMfw7373u7y/t99+++mWc/z48en222/P+yaO/dNPP71RAWJVUbs3EpnPPfdcTgxGWSJ599Zbb+V1+sc//lHjzYlSUa6Yf88990zlbLPNNktzzjlnuvDCC/M+BQCgdRNjijFbMsYsVVoBM461ESNGpGWXXTYtssgi9apsWSpi10j8HXTQQbkCa7mK82aXXXZJ55xzTq5k2hTxL0CTKgBQq8svv7wQl8pnnnmm0vSDDz44T7/mmmuqvefMM8/Mr40ePbrGZcZr++67b7XpW221VaF9+/aFn376abrl2n777QudO3cuvPXWW/n5N998U+jVq1dh5ZVXLkydOrVivocffjh/3vXXX19orOOPP74w33zzFW688cZCu3btalyvYcOG5c8ZN27cdJe38MILF3bZZZcGlyP2QXxG7JNyV9t+u+iii/L0M844o9L0OA5j+rbbbluYMmVKpdeeeuqpwuyzz15YZpllCj///HPF9Fh2vCeOu8mTJ1crwz333FO4/fbb61XekSNHFmabbbbCQw89lJf5yCOP1PtcKho6dGg+NqpOi3PihhtuqDb/xIkTC4cccsh0y7bpppsW1lhjjcKs5vvvv2/we/bbb7+8DadNm9YsZQIAYMaJMcWYLaG+++3zzz8v/Pjjj/nvOKYaeuv4pptuyu959913C7OSiJUnTZrUoPc8++yzeV0ffPDBZisXQGPp6hOgEX7961/n/0ePHt1ky4wWXFFLrGPH6TfGjpqes88+e9prr73y8yOPPDKNGzcudzPRvn3TXtqvueaa3MVktLiKLl7ieV0ttKLLx2h5FN1NHnjggbkGa12+/vrr3FptmWWWya254r3RnUzUTC2KVm7FWom77rprta5tahp/4YcffkiHHHJI6tu3b+rcuXNu7XbWWWdF1FJpvlhOdKtzyy23pKWXXjrPu9RSS+Wara3Nmmuumf9/7733Kk2PWqq/+MUvcgu4qrUqowudaAH4yiuvVOqOJ2r/RqvAkSNH5i5cqhoyZEje5/URtXSjhvG6666bllhiiSYZy+Gpp55Kd955Z9ptt91y68OqYj/F/qxLHHuxH9dbb71qrxX3e9R+jm6Qunbtmn71q1/l7RTiXIouj6Im5zrrrJM++OCDSu+PGtRbb711HtchyhLHWdRq/emnn6p91ptvvpnPi3nnnTd/ThyLxxxzTLXxS15//fXcqjH25RprrJFfmzJlSjr55JPToosumj8njvOjjz66xrEkYh98+OGHbaKrIgCAciPGrJkYc+aaf/75c8zSWLHOsd0ifikV2zP2xUcffZT3e/y9wAIL5OEKQsRhcQ5EN7XRE03VY6I++7Qojo+IsaIL3YjnevfunYe3KMbREdvFPop9F61ri7FWxGPhoYceyrF3lGWuuebKvavUNORGdIcaMfWtt97a6O0F0Fwk/gAaofiDMQKPxogfohHAxCNu1MeP2n/+85/5pn99grIYhy26U3z44YdztxKR8IkuO5ZffvnUlCL58u6776btttsuj/8WP5brSupEQBbrFl3EbLTRRum8886bbheL77//fg4O4sd/dJNx2GGH5R/9a6+9dsU4EpFMKnZfGMv717/+lR9rrbVWjcuMwGvTTTfNwWt0FRnLjaAsln3wwQdXmz+61olxDGIMgr/+9a95HSLZVNfYeC2hmHyKxFBpNznRlWWxq8eaFLv8iTEAi++JZNTmm2+ex16YEbGP4jiMYyTE/5FgnF4XnNNz22235f932mmnRi8jugiNcqywwgo1vh7Juwjco4uWCAwjmIvjMILPOHbjmIhjZtSoUemPf/xjpfdGwjDGS4luiaLro0iUxv/FbV0UXbTG+IQRPO6xxx6525vY7tEFaVWRSIxl/uUvf8nzht133z0df/zxeR3ieI7zIs6vmsbLiMAzPP74443eZgAAtAwxZs3EmE1rwoQJFcdJ8dGUXZo+8cQTtcZfMf56JOsicRrbJBKEkSCNZGts05VWWimdccYZOUaNuKo0CV6ffVr8jJgnKsdGfBTDYUSy+Lvvvsvd6Ja6/PLLcwwX+z/miyTeAw88kGO7sWPH5hgx9m2s0+qrr16tMmiIdRV/Aa1So9sKArShblgeeOCB3MXIxx9/XPjPf/5T6NmzZ6Fr166FTz75pFHdsNT02HzzzXP3hfUV3fmtvvrq+b19+/YtTJgwodo8M9oNS3QdGMsudh1433335eW98MILNXbDEt0qltpnn33y9JdeeqnWblhinUu7jgmx7aKbmZNOOqle3bDE8kq7ibzlllvyvKecckq1rm6iK5nSbkdivk6dOlWaFuWN6X//+98LLaG436ILzTjuxowZk7veXGyxxXL5n3766Wrreu6559a5zDnnnLOwwgor5L9vvfXWer2nPs4666x8LowfPz4/f/vtt/Oyb7755hnq6nOLLbbI80cXQ401YsSIvIxXXnml2msxPY6x0vP0kksuydOjS6Pi+oSjjjqq2jld7P6m1GmnnZb3z4cfflgxba211ip079690rRQ2h1n8fzZbrvtKs3z4osv5um77757pemHHnponh5dq1YVx/Lee+9d53YBAKDliDHFmC2huN9qetR2XDW0q8/oLjO2RU1DMsT2jGX95S9/qZgWsV4c8/GeOAeK3nzzzTxvHAMN3acRQ8d7zznnnGplKB5z8b6YJ2LksWPHVppn8ODBuRvar776qtK+iy5zd95552rL3HPPPfM6ALQ2WvwB1EN0FRjd9EXNtKixF11L3HzzzblrisaI1ln3339/fkS3EEcddVTu9iNqY1btJqQ20TVF1EgL0UVhlKkpRReD1157bdp2220rBqqOrjeiJmhtNTL33XffSs+jpmhxgPDaRJcaxa5jonZe1ICMdYnak88//3yjyh6fF11eRg3VUtG6K7bv3XffXW3/lnZFEgOZR+u5qFXYkqKVWRx3ffr0yTUgo5Zi1EItHYw9amyG6bXci9fHjx+f/y7+P6Ot/UIcC0OHDq1Y1oABA3LNyhnt7rMpylisTVvaQrLUb37zm0rd90TLvBA1cUs/tzi99Hgo7f4muvyJmrKrrbZaPr5eeOGFPD26Rvrf//6X92N0CVqqpsHfi90qFRXPm6o1iOM4DtEValWxrlEWAABaNzGmGLMlRG8ixeOk+IguYZtCdMcZ26K2+KvYo0lRdKMZ+yS61IyWnUUxLV4r3Vb13ac33nhjmmeeeSqOk7pisIj74hws+uyzz/KwCdEtafE8KO67GFahpmMu1jWGe4ieWwBak+m39Qcgd/0X/cNHFynR5338uJyRcQ4WXHDBSuOORZch0aVL9Fkf3TFusskm6fvvv8+PoggySn+U3nTTTbm7wBgzILodjC4yimPANYX77rsvJy5ijLjoiqUoxnH797//nbvgqLoNIulTKgKdmKemLjGKoluR6P7wwgsvzF15xI/4osZ2cxNd20SyrGrSKLpzKb5eqmpSpvgD/ptvvqnzcz7//PPUWPUJriIoi30ax0HcBPjPf/5TbZsX17GYAKxNvB4BdSh2CTq999S2njEORyS+omvMSHJFNyylx0iMiRfnTCTvaut+tCalgVhpGSPomxG13eiout9jvULcfKlpeunxEGNTxP6JLkmrHieRoA3FQDXO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4Re/+EVhv/32K/z000+V3rvwwgsXdtlll4rnEydOLBxyyCGF3r17F7p27VpYffXVC6NGjSqsvfba+VHq1ltvLSy55JK5fPF5sX9CLC+WW2rChAmFgw46qNCnT5/CbLPNVhgwYEDeN9OmTas0Xyxn3333rbaOVcs5MxX32/XXX1/j6+uss05hzjnnLHz77beVpt9yyy2F3/72t3nbd+7cubDYYovlbTtu3LhaP+vBBx8sbLbZZoX55psvb9d55523sMkmm+RtXZvJkycXevbsWVhzzTXrXI/+/fsXll9++UrT7r777sK6666byx/7JeY5+OCDC998802tx1oc1yuvvHKhW7duhU6dOuV9uf/++xfefffdwvTEudCuXbvCRx99NN39HudsTI/jZHr7I4719dZbL5dpnnnmKeyxxx6Fl156qdJxWfTqq6/mc2WuueYqdOnSpTBw4MDCcccdV/F68fypaT/9/PPPhRNPPDFvp9heffv2LRx11FH5vCk1derUfA4de+yx090mAAC0HDFmzcSYLRtjVp2vpkfVbVeTSZMm5fjo5JNPrjQ91nuOOeaoNn8sc6mllqpxWw0dOrRR+zSOpWOOOaYihurVq1c+ht577706476iBx54IC8/Pifi1oiP4zis6ogjjigstNBC1fY/QGvQLv5pmZQjAEDzihrMUUt5m222yd0UlavoLjVqPr/33nu5ixoAAICWEHHX5ZdfnntqKR2GopxMmjQpjzMZ418eeOCBLV0cgGraV58EAFAeItCMLmFibJDvv/8+lasYxzHGZJH0AwAAWlKMrRexVwzPUK4isRlDaey1114tXRSAGmnxBwAAAAAAAGVAiz8AAAAAAAAoAxJ/AAAAAAAAUAYk/gAAAAAAAKAMSPwBAAAAAABAGejY0gWYFUybNi2NGTMmde/ePbVr166liwMAAFBNoVBIEyZMSH369Ent26vj2RBiPgAAoFziPYm/eogAsG/fvi1dDAAAgOn6+OOP04ILLtjSxZiliPkAAIByifck/uohan0WN+icc87Z0sUBAACoZvz48Tl5VYxfqD8xHwAAUC7xnsRfPRS7eokAUBAIAAC0ZrqqbDgxHwAAUC7xnoEfAAAAAAAAoAxI/AEAAAAAAEAZkPgDAAAAAACAMmCMPwAAmI5CoZCmTJmSpk6d2tJFoY2bbbbZUocOHVq6GAAAUFYi1vv5559buhi0cR06dEgdO3ac4XHbJf4AAKAOkydPTp999ln68ccfW7ookAPABRdcMHXr1q2liwIAAGXh+++/T5988kmu8AktbfbZZ0+9e/dOnTp1avQyJP4AAKAW06ZNS6NHj8617vr06ZN/eM9ozTtorLgRMW7cuHxTYsCAAVr+AQBAE7T0i9/XkWyZd955xXu0aLwXFY8j5ov7EBHztW/fuNH6JP4AAKAW8aM7kn99+/bNgSC0tLgZ8cEHH+RuiCT+AABgxsTv6ki4xO/srl27tnRxaOO6du2ah3f48MMP8/2ILl26NGo5jUsXAgBAG9LYWnbQ1NRABgCApud3NuV0/8EdDAAAAAAAACgDEn8AAAAAAABQBozxBwAADTTk5Dub/TPuPW5og+ZfZ5110qhRo/J4AJ06dUrLLLNMOvvss9NKK63U6DI88sgjad11102/+93v0g033FAx/c9//nP69ttv0xVXXFGvZWy++eZ5/rqcf/75eXmvvPJK2nDDDdMtt9xS6fXXX3897b///un5559PnTt3TptuumkaPny4sRcBAICyj/nEezSEFn8AAFAmzjjjjPT999+nzz//PK266qppyy23nOFlRtB17733pqeffjo1pz59+qRjjz027bHHHjW+vv3226eBAwemL774IgeLL730Ujr55JObtUwAAACthXiP+pL4AwCAMhM1QHfZZZf08ccfp3HjxqVCoZDOO++8NGjQoDTXXHPl2qJvvPFGxfznnHNOWmihhVL37t1Tv3790ogRIype69KlSzrooIPSkUceWevnjR07Nu2www6pd+/eOaCLGqKTJk1KX331Va7N+d1336Vu3brlx6OPPlrjMiJojZqi88wzT42vv//++2nHHXfM6zbvvPPmGqAREAIAALQl4j2mR+IPAADKzE8//ZQuu+yyHFT94he/SBdddFF+fvvtt6cvv/wyB12bbLJJmjx5cnr77bdzzcv77rsvTZgwIT311FNplVVWqbS8Qw89NAddURO0qggyIyjr1atXeu+99ypqZ55yyimpZ8+e6e677049evTINVPjseaaazZqnaIMV155ZV63qOF6880353UAAABoS8R7TI/EHwAAlImjjjoq1/CcY4450jXXXJNuuumm1LFjx3TBBRekk046KQ0YMCA/P+CAA3JAFUFfhw4dcjD32muv5Wnzzz9/WnbZZSstd84558zBYiw/5i317LPPpnfeeSedeeaZefyFCP6OPvro/PlNKWqSPvbYY7mWatQ07du3b/rjH//YpJ8BAADQWon3qC+JPwAAKBOnnXZaHlQ9unxZYIEF0ssvv5ynf/DBB7nblAgSi49vvvkmffLJJ2nRRRdN//znP/Ng6xEErr/++unFF1+stuy99947v+c///lPpemx7PjMueeeu2LZW221VR6boTZLLbVURVcwV1999XTXKz53vfXWy+NB/Pjjj+nrr7/OwW6sEwAAQFsg3qO+OtZ7TgAAYJYQQeCll16a1lprrbTFFlvk2pLDhw9PG2ywQY3zb7PNNvkRNUCPP/74tNNOO1UbTyHGWojB1Y877rg0ZMiQiumx7Pnmmy999tlnNS67ffvqdQ2jtmlDRJcyUbaoudquXbtclj/96U+5VigAAEBbIt5jerT4AwCAMrTCCivkQd3/8pe/pH333TcHeG+99VZ+bfz48enWW2/NYzzEtPvvvz8HWhFgRa3M6B6mJttvv32ueXnttddWTFt55ZVzMBhdw8TyomuYDz/8MI/1EKJWaUyPAeHrMmXKlDRx4sT8/7Rp0/LfMSZFiEHqo1wXXnhhfj2WF4Hu8ssv34RbDAAAYNYg3qMuEn8AAFCmjjnmmDRixIi0+eabpz/84Q95kPcYv2GJJZaoGJMhgq2o1RkBW4zX8NBDD6Urrrii1tqcp59+evrqq68qpsWYEXfccUf69NNP83JjYPehQ4emd999N78+cODAtNtuu6Ull1wydwsT4zbUJAaH79q1azr11FPzoPTxd3RDEyIIjGn//ve/8wD2/fr1y93NRJc1AAAAbZF4j9q0K1QdrZFqIkMeB/R3332XTxwAANqGqIU4evTo1L9//9SlS5eWLg7UeUyKWxrPtgMAaJvEfMwqx2RDYhYt/gAAAAAAAKAMSPwBAAAAAABAGZD4AwAAAAAAgDIg8QcAAAAAAABlQOIPAAAAAAAAyoDEHwAAAAAAAJQBiT8AAAAAAAAoAxJ/AAAAAAAAUAYk/gAAAAAAAKAMdGzpAgAAwCznhC1mwmfc3KDZ33rrrXTooYemUaNGpcmTJ6c+ffqkXXfdNR1xxBFpnXXWydNnm222ivm7dOmSvvzyyxqXdcUVV6Tddtstde3aNbVr1y7NP//8ad99900HHXTQDK9Wv3798ue+9957ebnhxRdfTMsvv3wqFAr1Xsbw4cPT5ptvXus8jzzySFp33XXTHHPMUTHtD3/4Qzr//PMrnt9yyy3psMMOS59++mlaYYUV0ogRI9KgQYNmaP0AAIAyIOZrNDFfy9PiDwAAysDQoUPTcsstlz766KP0zTffpBtvvDEtssgiFa+fccYZ6fvvv6941BYAFi2zzDJ5vgkTJqQrr7wyHXPMMemhhx5qkrJGAHrSSSel5tajR49K61waAEbQvMMOO6Rzzz03ff311+nXv/512myzzdKUKVOavVwAAAANJearTsxXM4k/AACYxRVrU/7pT39Ks88+e+rQoUNaaqml0tZbb90ky19ttdXy8p577rmKac8//3yuXTn33HOnxRZbLF166aWVXvvlL3+Z5pxzzjTPPPOkTTbZpNLyDj/88HT55ZfnMtckaoGed955uSbmXHPNlWuvvvHGG/m1WKcIdLfbbrvUrVu3tNdeezVqna666qpc/o033jgHpccdd1waO3ZsevTRRxu1PAAAgOYi5mu4q9pwzCfxBwAAs7iePXumgQMH5m5errvuuvThhx822bIjIPvf//6XXn311bT44ovnaZ9//nn67W9/m/bee+80bty43H3KsGHD0oMPPphf32+//XLg9+233+YuVaJrlVKxnJ122ikde+yxNX7mRRddlC677LJ0++235wB3yy23zMuL7myuv/76tNBCC6V///vfuUbnxRdfXGvZ4/Xo/mbBBRfMNT2jLEUvv/xyGjx4cMXz6BJnySWXzNMBAABaEzFfzcR8NZP4AwCAWVyMyRDjG0S3LyeeeGLu7iUCmvvvv79inqOOOirXpCw+IoiryyuvvJLni5qRa6+9djrkkEPSpptuml/717/+ldZaa620zTbb5JqmSy+9dA5Ar7nmmoqAKgLRMWPGpM6dO+d5qzrhhBNykPfCCy9Ue+2CCy7I3cIMGDAgdezYMR1wwAHpp59+Sk899VS9t0nUHI1xJD7++OP07LPP5mA2Aslp06ZVBIixfqXieXRzAwAA0JqI+aoT89VO4g8AAMpAr1690tlnn51ee+21XCNzww03TFtssUUeyyCcdtppuTZm8VEMEP/yl7/k7lPiEe8pHe8h5ougKLpEibEeimMhfPDBB+muu+6qFFRGNy2fffZZfn3kyJFp4sSJacUVV8zBWOk4C0W9e/fO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3HHHHBjFsiNh8/DDD9d7XSLJs8suu6SPP/44j8UWYtDz8847Lw0aNCiXJ2qMxu/+0vVfaKGF8vr369cvjRgxolJXKRE7HHnkkbV+5tixY9MOO+yQevfuncsdNUQnTZqUx1uI2pyx7rGu8Xj00UdrXEZsh6gpOs8889T4esQ0sV1i/aKryagBGgFhORLzAQBA0xHvifdafeLv2WefTcsvv3x+FAO4+Pv444/Pz+OALAaEoX///unOO+/MNT6XW2653AQ0duyQIUMq5okD86yzzsrLiH5pI6i85557qg3+DgAA5WjhhRdODz30UB7DLH4rH3bYYenxxx+vNE8EYxF4RE3Iv/3tb2mbbbbJXY9EsBXByV/+8pdK8//rX//K74lA8ptvvqnobib+jiBmv/32y92hROLlqquuqvTe3/zmNzlQi0Dq97//fdpqq63qPUj5Tz/9lC677LIcUP3iF7/I0y666KI87fbbb09ffvllDro22WSTNHny5PT222/nmpf33Xdf/oynnnoqrbLKKpWWeeihh+Z1j5qgVUWQGesTXUa+9957FbUzTznllDzewt1335169OiRk1nxWHPNNVNjRBmuvPLKvH4RdN988815HcqRmA8AAJqOeE+81+oTf5GtjY1d9VHsbzX+j35Vq77nhRdeyFnY2Dl/+MMfqi03DsQPP/wwzxM7P5qLAgBAWxBjGvTt2zfX9IwxDiJhUvU3dQRLc8wxR1pyySVzcmWNNdZISy21VO4aZYsttshjEZSKWqJRGzJqXEYXJddcc03uXvGOO+7I0//0pz/lAckjmPn1r39d6b0RHEbwFGM4RFAa73v55ZfrXIdoBRafFWWMz7rpppvy8sMFF1yQTjrppFzLNaYdcMABOaCK3/0x9lvEE6+99lqeFomgZZddttKyoyZqrH98RsxbNUn1zjvvpDPPPDOPvxDB39FHH53L0JSiJuljjz2Wa6lGTdPYX3/84x9TORLzAQBA0xHvifdafeIPAABoWldffXVaYYUV0txzz52DqbvuuivXlCxV2jIqAp6qz6N2Y9VapaV/R23LqC06ZsyYSq9VnTeCvmOOOSYHbRGARXmi65RieSL4LHajEuUuOu2003KN0ujyZYEFFqgUOEYt1Og2JZZVfERN1E8++SR3dRPdP0Z3N7FO66+/fkUXk6Wia8h4z3/+859K02PZ8bnFbRePqLEaYzPUprZ1qE187nrrrZfHg/jxxx/T119/nQPeWCcAAIC6iPfEezX5v7QpAABQdqLLxBgjIbo9jFZTUUMyxh6oWtOxoaJlVbFFVXxGcayCqP0Zr1Utw3zzzZf/jpqT8YhuViIYjFqp0YVLsTxRU7MuEQTGWG5rrbVWrpkanxe1JaObmg022KDG90Q3NvGIGqDRFeROO+1UbTyFKH/UZD3uuOMqdSEZy46yR/eTNWnfvno9yumtQ1XRgi3KFjVXY3tEWaIGbdQKBQAAqI14T7xXGy3+AACgTEyZMiVNnDix4hG1CyPIimAmgpao/RnjH8yo6AolantG7cgIrmLshlj+0KFD06effpqDtShLjM0W400UxbgTEejEmA1RazS6bKnveA9FUZs1gtriOBT77rtvLsNbb71V8Rm33nprXm5Mi7HhItCKz41amcUuY6rafvvtc83La6+9tmLayiuvnIPB6BomlhfbMgLdGOshRK3SmB4Dwtdnv8T/UQs2/o71DzFIfZTrwgsvzK/H8mL7FcfAAwAACOI98V59SfwBAECZiDEUunbtWvHYbLPNclcrMe5CjFcQQU4MXj6joluSGD8iunWJcQpigPgQXaREEBbPo5uUGFx+hx12qHhf1EaNrlHifYssskgu44ILLtjgz491imVHVzAxtluM+RaDvEd3MksssUTFmAwRbEWtzgjYYv0jKC2OJVdVBLKnn356HoS+KMaMiHEsIriN5cZYFRHsvvvuu/n1gQMHpt122y2PlRHrG+M21CQGh491PfXUU/Og9PF3dEMTIgiMaf/+979zgNyvX78cYEeXNQAAAEXiPfFefbUrzGi7zzYgssix06M/2ji4AABoG6Km3ujRo1P//v1Tly5dWro4UOcxKW5pPNsOAKBtEvMxqxyTDYlZtPgDAAAAAACAMiDxBwAAAAAAAGVA4g8AAAAAAADKgMQfAAAAAAAAlAGJPwAAAAAAACgDEn8AAAAAAABQBiT+AAAAAAAAoAxI/AEAAAAAAEAZkPgDAIAy9+ijj6YFF1ywpYsBAABAExPvUZXEHwAAlIF11lknde7cOXXr1i117949LbXUUun666/Pr6255prpk08+qZj3D3/4Q/rzn/9c43I+++yztP3226devXrl5SyyyCLpoIMOyq/FMmP58ZhtttlSp06dKp7Ha6Ffv36pXbt26Z133qm03H333TdPHz58eDNuBQAAgPIj3qMhOjZobgAAIK15x8HN/hmPbnxOg99zxhln5ACvUCiku+66K22xxRZplVVWSQsvvHC9l7HTTjulhRZaKL355ptpzjnnTKNHj06PPfZYfu21116rFEzONddcNQZ2AwcOTFdccUU69dRT8/NJkyal6667Lg0YMKDB6wQAADCztcaYT7xHfWnxBwAAZSZqWg4dOjQHam+99VZ65JFH8t/18eSTT6Zdd901z9++ffu06KKLpl122aVBnx9B4pVXXpmmTZuWn99yyy1p5ZVXTn369GnU+gAAAPB/xHtMj8QfAACUmQjAbr311vTTTz+lwYMHN+i9q6++eq5FGoHc22+/3ajPHzRoUOrbt2+677778vORI0fm4BIAAIAZI95jeiT+AACgTBx11FG55uYcc8yRttxyy3Tsscem+eabr0HLiHEiNtlkk9ylS4zjEN3GXHPNNQ0uSwR+l19+eR5r4oUXXkibbrppg5cBAADA/xHvUV8SfwAAUCZOO+209O233+aan9Hlyz//+c90ySWXNGgZMc7DCSeckJ5//vn0zTffpAMOOCDtvPPO6Y033mjQcrbddtt0//33p3PPPTf/HQPRAwAA0DjiPepL4g8AAMrQYostljbaaKN0xx13NHoZ3bp1S4ccckjq0aNHev311xscUMa4ExEI6vYFAACg6Yj3qIvEHwAAlKEPPvgg3XXXXWmZZZap8fWpU6emiRMnVnqEww47LL344otp8uTJ+TFixIj0ww8/pBVXXLHBZTjjjDPSQw89lFZYYYUZXh8AAAD+j3iPukj8AQBAmTjiiCNyrc14rLHGGmm99dZLxx9/fI3znn/++alr166VHmHSpEnp97//ferZs2fq1atXHrchBo7v169fg8vTp0+ftM4668zwegEAALR14j3qq12hUCjUe+42avz48bm563fffZebsAIA0DZErcjRo0en/v37py5durR0caDOY1Lc0ni2HQBA2yTmY1Y5JhsSs2jxBwAAAAAAAGVA4g8AAAAAAADKgMQfAAAAAAAAlAGJPwAAAAAAACgDEn8AAAAAAABQBiT+AABgOqZNm9bSRYCsUCi0dBEAAKDs+J1NOd1/6NgkJQEAgDLUqVOn1L59+zRmzJg077zz5uft2rVr6WLRhm9GjBs3Lh+Ds802W0sXBwAAZnnxuzp+X8fv7Ij5xHu0ZLw3efLkfCzGfYi4/9BYEn8AAFCL+LHdv3//9Nlnn+XkH7S0uBGx4IILpg4dOrR0UQAAYJYXv6vj9/Unn3ySPvjgg5YuDqTZZ589LbTQQvl+RGNJ/AEAQB2ill386J4yZUqaOnVqSxeHNi5qJEv6AQBA0+nWrVsaMGBA+vnnn1u6KLRxHTp0SB07dpzhlqcSfwAAMB3FrhV1rwgAAFCeCRcV7CgXjW8rCAAAAAAAALQaEn8AAAAAAABQBiT+AAAAAAAAoAxI/AEAAAAAAEAZkPgDAAAAAACAMiDxBwAAAAAAAGVA4g8AAAAAAADKgMQfAAAAAAAAlAGJPwAAAAAAACgDEn8AAAAAAABQBiT+AAAAAAAAoAxI/AEAAAAAAEAZkPgDAIBW6I477kgDBw5MAwYMSCNGjKj02oQJE9LgwYMrHj169EjDhw/Pr73wwgtp1VVXTUsvvXTafvvt088//1zxvrPOOisvc8kll0znnnvuTF8nAAAAoHl1bOblAwAADTRlypR08MEHp4cffjgn9VZcccW0xRZbpJ49e+bXu3fvnl588cX8d6FQSP369UubbbZZfr777runCy+8MCf/Tj311HT55ZenPffcMz3wwAN5ea+++mqabbbZ0tixY1t0HQEAAICmp8UfAAC0Mk8//XRaaqml0gILLJC6deuWNtxww3TffffVOO+oUaNSr169Uv/+/fPzjz76KCf9wq9//et000035b8vueSSdNRRR+WkX5hvvvlm2voAAAAAM4fEHwAAtDJjxozJSb+i+PvTTz+tcd7rrrsubbvtthXPF1100XTvvffmv2+++eaK973zzju51d8qq6yS1l9//fT22283+3oAAAAAM5fEHwAAzKKim88bb7wxbbPNNhXTRo4cmc4888y00korpc6dO6cOHTpUdB/6ww8/5NaEhxxySNp1111bsOQAAABAc5D4AwCAVqZPnz6VWvjF3zGtqsceeywtvPDCacEFF6yYtuSSS+aWfc8++2waMmRIGjBgQEWrwS233DL/HdO1+AMAAIDyI/EHAACtTHTH+eqrr+aE3/fff5/uvvvunKybXjefYdy4cRUt/M4444y055575uebbrppeuSRR/Lf0eovEoYAAABAeZH4AwCAVqZjx47p7LPPTuuuu24aPHhw7pqzZ8+eaaONNsrj/4Vp06blMfy22mqrSu+98sor08CBA3PLvzXXXDP99re/zdN333339OKLL6all1467b///unSSy9tkXUDAAAA2lDi74ILLkj9+vVLXbp0SauuumqujVyX4cOH5xsbXbt2TX379k0HHXRQmjhx4gwtEwAAWlq00IvuON99992KVnt33XVXRZef7du3T5988knq3bt3pfdFkvCtt97K7z388MMrpsd4f9dee21uSfjUU0+l5ZdffiavEfwfMR8AAEAbSfzFjYiDDz44DRs2LD3//PNpueWWy10ajR07tsb5r7nmmnTkkUfm+d9444102WWX5WUcffTRjV4mbccdd9yRbyDEuDcjRoyo9NqECRNy7frio0ePHvmGQ7F2/WGHHZYWX3zxtMQSS+QutsLHH3+c1l577Tz/iiuumJ555pkWWS8AAGitxHwAAADNq12hUCikViJqZq688srp/PPPr0iwRI3O6Ioogr2q9ttvvxz8Pfjgg5VqOEcN5scee6xRy6zJ+PHjc+Lnu+++S3POOWcTrS0tKca8ie6vHn744bxvI1H3xBNP5C60qopTJGoPx5g4/fv3z0nCSOpdcskl+bWvvvoqzTPPPPmYiq6z/vSnP6V77rknnXvuuenee+9tkfUDAKDtmRXiFjEfAABAwzUkZumYWonJkyen5557Lh111FEV06L7ovXWWy+NGjWqxvesttpq6aqrrsrduKyyyirp/fffz90f7bTTTo1eZpg0aVJ+lG7QYgAZD2Z9Tz75ZE78FbvG2mCDDXKybrvttqs2byQEe/XqlRZeeOG8/yPhd+ONN1YcC3PPPXfF33GsxN/ffPNNfo/jBQCAmaW1//YU8wEAADROQ+KUVpP4+/LLL9PUqVPT/PPPX2l6PH/zzTdrfM/222+f37fGGmvkllfRimuvvfaq6PalMcsMp512WjrxxBOrTR83bly1sSSYNcX+j4RdsfufyJTHODg1dQf0z3/+M2244YYVr3344YfpvPPOS/fdd19acMEF0+mnn57mm2++tMcee6Rtt902t/SLk/D222/XvRAAADNNdFffmon5AAAAmj/eazWJv8aIrhf/8pe/pAsvvDB37/Luu++mAw88MJ188snpuOOOa/Ryo7ZojBFRWvszuoqZd955dftSJmI/du3aNSfsQrdu3VK7du0qnhfFzYVoCfj4449XvPbjjz/mrj9ffPHF3PovEn9RC/nqq6/OXRHtvffe6c4778zHUSQHAQBgZujSpUsqN2I+AACA1KB4r9Uk/mKMtA4dOqQvvvii0vR4Hl0m1iQCvejiZffdd8/Pl1lmmfTDDz+kPffcMx1zzDGNWmbo3LlzflQVXcbEg1lftNQbM2ZMxf6Mv6ProKr799FHH81dfC600EIV0xZYYIH0u9/9Ls+71VZb5bFE4u+RI0fmGxPx9yabbJJ23XVXxwsAbdqad/z/m+pQjh7d+JzUmrT2355iPgAAgMZpSJzSaiKaTp06pRVXXLHSoO3RXWI8/9WvflXje6LlVdWVjaCv2FKrMcukbYgk36uvvpo+/fTT9P3336e77747DRkypNp81113Xe6+s9Smm26aE3wh/l9iiSXy31FDuHisxRiC8RwAAPg/Yj4AAIDm12pa/IXoamWXXXZJK620Uk7MDB8+PNfmjJZTYeedd86trWI8hhCtqs4555y0/PLLV3T7EjVCY3oxGJzeMmmbOnbsmM4+++y07rrr5hsDhx9+eOrZs2faaKON0ogRI1KfPn3y9Jtvvjk988wz1boF2m677fJxGOMEXnHFFXn6WWedlcf5i+lxA+If//hHC60dAAC0TmI+AACANpT4i5ZVMZj68ccfnz7//PM0ePDgPL5acaD2jz76qFJtz2OPPTaPyxb/R8utGI8hAsBTTz213suk7YqWe/Eoddddd1X8HcfaJ598Uu19key79957q01feuml06hRo5qptAAAMOsT8wEAADSvdoXoH4U6xUDvPXr0SN99952B3gEA6skYf5S71jbGn7il8Ww7AACgXGKWVjPGHwAAAAAAANB4En8AAAAAAABQBiT+AAAAAAAAoAxI/AEAAAAAAEAZ6NjSBaDxhpx8Z0sXAZrVvccNbekiAAAAAADALEOLPwAAAAAAACgDEn8AAAAAAABQBiT+AAAAAAAAoAxI/AEAAAAAAEAZkPgDKFN33HFHGjhwYBowYEAaMWJEpdcmTJiQBg8eXPHo0aNHGj58eH7tyy+/TOuuu25+35ZbbpkmTpyYp3/88cdp7bXXzvOvuOKK6ZlnnmmR9QIAAAAAoGYSfwBlaMqUKenggw9ODz30UHrhhRfSmWeemb766quK17t3755efPHF/IjX55prrrTZZpvl104//fT0u9/9Lr3zzjtpkUUWqUga/vWvf03bb799fs+pp56ajj322BZbPwAAAAAAqpP4AyhDTz/9dFpqqaXSAgsskLp165Y23HDDdN9999U476hRo1KvXr1S//798/Pbbrst7bTTTvnvHXfcMd1+++3573bt2uWWguG7775LvXv3nmnrAwAAAADA9HWsxzwAzGLGjBmTk35F8fenn35a47zXXXdd2nbbbSueR1Ivuv6s+r6jjz46/fa3v81dgk6bNi0nDAEAAAAAaD20+ANowwqFQrrxxhvTNttsM915r7nmmrT33nunTz75JF166aVpt912myllhMaMYxmie9vownbQoEFpySWXTO+9916efv/99+exKqNV7EEHHVQx/wknnJAWXHDBirEvH3300Zm6PgAAAAAwoyT+AMpQnz59KrXwi79jWlWPPfZYWnjhhXOyoyha+0Wrv6rvu+yyy9LWW2+d/x46dGge6w9a6ziW4cADD8ytWd9888307LPP5i5to7Xq7rvvnm655Zb02muvpe+//75SN7hHHnlkxfiXa665ZgusGQAAAAA0nsQfQBlaZZVV0quvvpoTd5HYuPvuu9OQIUOm281n2HjjjdO//vWv/PdVV12VNtlkk/x3375904MPPpj/fvLJJ/NzaK3jWEbyOpJ922+/fX4+++yzpznmmCN9+eWXef5+/frl6b/+9a/TTTfd1GLrAQAAAABNSeIPoAx17NgxnX322WndddfNXRYecsghqWfPnmmjjTbK4/+FaPl08803p6222qrSe4866qh0/fXXp8UWWyy9++67uXVUOOuss9Lf/va3tNxyy6X9998//eMf/2iRdYP6jGM5evToNM8886QddtghLb/88rlLz2glOO+886YffvghvfLKK2nq1Knptttuq/S+c845Jy277LK5W9tImgMAAADArETiD6BMbbrppuntt9/Oybs999wzT7vrrrsquu5s3759Hq+vd+/eld4XiZH//ve/+X3RHWLXrl3z9KWXXjqNGjUqvfTSS+mZZ55JK6+8cgusFdRPJPmiVeBhhx2WnnvuuTRu3Lh0+eWXp3bt2uWWrHvttVdabbXVcsKwQ4cO+T2R7HvnnXdy16HRQvDEE09s6dUAAAAAgAaR+AMAym4cy0jo9e/fP7d4jST3ZpttVjEu5RprrJEef/zx9NRTT+XXBwwYkKfPP//8OQkYjz/+8Y85wQ0AAAAAsxKJPwCg7MaxjJas8803X+7yMzzyyCNpiSWWyH+PHTs2/x/v+/vf/5522223/Pyzzz6reP+tt96axxAEAAAAgFlJx5YuAADAjIxjGeNVHn744RXjWI4YMSK3/jv33HPT7373u/Tzzz/nln177LFHfu9pp52W7rnnnvz30UcfnQYNGpT/jmVEq8DoDnTxxRc3jiUAAAAAs5x2hUKh0NKFaO3Gjx+fevTokb777rs055xzptZiyMl3tnQRoFnde9zQli4CADNgzTsObukiQLN6dONzUmvSWuOWWYFtBwAAlEvMoqtPAAAAAAAAKAO6+gRarxO2aOkSQPM64eaWLgEAAAAAUEa0+AMAAAAAAIAyIPEHAAAAAAAAZUDiDwAAAAAAAMqAxB8AAAAAAACUgY4tXQAAaLPObtfSJYDmNfCgli4BAAAAQJuixR8AAAAAAACUAYk/AAAAAAAAKAMSfwAAAAAAAFAGJP4AAAAAAACgDEj8AQAAAAAAQBmQ+AMAAAAAAIAyIPEHAAAAAAAAZUDiDwAAAAAAAMqAxB8AAAAAAACUAYk/AAAAAAAAKAMSfwAAAAAAAFAGJP4AAAAAAACgDEj8AQAAAAAAQBmQ+AMAAAAAAIAyIPEHAAAAAAAAZUDiDwAAAAAAAMqAxB8AAAAAAACUAYk/AAAAAAAAKAMdZ3QBTz75ZHr44YfT2LFj0z777JMGDBiQfvzxx/Tmm2+mxRdfPHXr1q1pSgoAAMBMJ+YDAABoAy3+Jk+enLbccsu0+uqrp2OOOSadd9556eOPP/6/hbZvn9Zff/30t7/9rSnLCgAAwEwi5gMAAGhDib/jjjsu3XHHHemiiy5Kb731VioUChWvdenSJW299dbp1ltvbapyAgAAMBOJ+QAAANpQ4u/f//532nvvvdOee+6Z5p577mqvL7HEEun999+f0fIBAADQAsR8AAAAbSjxF+M7LLPMMrW+3qFDhzzuAwAAALMeMR8AAEAbSvz17ds3D+Zem8cffzwttthijV08AAAALUjMBwAA0IYSf9tvv3265JJL0qhRoyqmtWvXLv9/6aWXpuuuuy7tvPPOTVNKAAAAZioxH7O6GKNy4MCBacCAAWnEiBHVXl9nnXXSoEGD0uDBg/Pjp59+ytO33XbbimkLLLBA2nzzzastN86FV199daatCwAANHvi75hjjkmrrbZaWmuttdK6666bf/QedNBBaaGFFkp/+tOf0gYbbJCfN9QFF1yQ+vXrlweLX3XVVdPTTz9d5/zffvtt2nfffVPv3r1T586d0+KLL57uuuuuGVomAABAWyfmY1Y2ZcqUdPDBB6eHHnoovfDCC+nMM89MX331VbX5brjhhvTiiy/mR9euXfO0a6+9tmJaHPulib+JEyemc889N62yyiozdX0AgPLS1BWUrrrqqtxN/7LLLpvWW2+99Mknn8z0daIMEn+dOnVK99xzT7r88svTIosskg/CSZMm5QPriiuuSLfffnse86Eh4sd1/DAfNmxYev7559Nyyy2XhgwZkseWqMnkyZPTb3/72/TBBx/kH+tvvfVWrnkaB3xjlwkAAICYj1lbJH+XWmqpfKx069Ytbbjhhum+++5r0DLieL/33nsrJf7++te/pr333rsiSQgA0BoqKC266KLp0UcfTS+//HLaZptt0tFHHz3T14tZPPEX2eU4MCMrveOOO6Zbbrklvfbaa+mNN97I06K7l2IXMA1xzjnnpD322CPtuuuuackll0wXX3xxmn322dPIkSNrnD+mf/311/nzV1999VzDc+21186BXmOXCQAA0NaJ+ZjVjRkzplKCOP7+9NNPa+zSdvnll8/HUVV33313+tWvfpXmmmuu/DwS0E8++WTaaqutmrn0AEA5a44KSqW/WVZeeeUaf/fQdjQq8RfZ5Rjr4YsvvmiygkRNzueeey43Q60oXPv2+XnpmBKlbrvttnxAR7cv888/f1p66aXTX/7ylzR16tRGLxMAAKCtE/PRFlx99dW5VvwjjzySbr311nTnnXdWej3GsYzutIoOPfTQdPrpp7dASQGActIcFZRKRe8c66+/fjOUnFlFx8a+ccUVV2zSgay//PLLHLxFMFcqnr/55ps1vuf999/PzWF32GGHPMbDu+++m/bZZ5/0888/525eGrPMYrY8HkXjx4/P/0+bNi0/Wot2qdDSRYBmNS01vBY5tJEet2GW0M5PFcpca4oNmqM8Yj5mZb169cpj2xT3Z/wdtd9L92+MGxnPu3fvnrbeeutc+z5q3Bdbvd5///3poosuqnhPdCW76aab5r8///zz3KVs1LSPlqYAAPUVvy0KhULFb4z4uzi96F//+ldOCH733Xe5VV+MBTh06NCK16PLz/j9UvW3a/SUERXg/vvf//pdW2Yasj8bnfgbPnx42mijjXKNyz/84Q+pY8dGL2qGVnS++eZL//jHP/LYEhGYRmY8+sSNILCxTjvttHTiiSdWmz5u3Lg8kHdrsVB3d9Mob2On/f+aL1CWuq7Y0iWAZtU/9WzpIkCzam1jyE2YMKFJlyfmY1YW3cJGa76XXnopzTnnnLk135/+9KeK8zbG1okbaT179swtR6PFX4yHU3w9urRdZZVVcgIwHuGJJ56oWP6WW26ZW5/OM888re5aAAC0/t41ooJb8TfEO++8k1v2lf6mmG222Sqeb7DBBjmRF5WYQvw2ia5BTz755ErviXH/Dj/88Dw2YPzOobw0JN5rdOQWgV90oRI/nA844ICcfa46uHWM+RA/susjfixHIFe1K5l4HjX1ahK18+IEKB1Qfokllsg17+KHe2OWGY466qg8nkVp7c++ffumeeedNwcMrcVHE7SGorzNN1Vf1JS5wnMtXQJoVqPTGi1dBGhWkZBqTbp06dKkyxPzMauLbrGiq85IIEc3nYMGDco15S+99NLUo0ePtNlmm+XWo9FqdOONN05//OMfK8aujJtpMb5lbed5p06d0txzz93qrgMAQOsXvQbEb9H4HRK/Sf73v//lCkVRIalYQenbb7/Nv3XjN+9jjz2Wx9gu/u648cYb87jXiyyySMUyYyzi+M0eSb9ll122xdaN1hHvNTrxFz9w40AcOHBgagrxozlqbz744IMVA1LGj/N4vt9++9X4nhjc/ZprrsnzRUAa3n777RwcxvJCQ5cZOnfunB9VxWcUP6c1KOgGkTLXXne2lD1dLlDeCn6qUOZaU2zQHOUR8zGri2OieFyUjodTFOND1ibG96tLjAsIANAY8Tv27LPPTr/5zW/yb9dopRcV0KK3jREjRuRkYHQ/XqygtMkmm+SeCYoVlCK5F5WbSn+3RuLwq6++ypX3Qv/+/dPNN9/cYutI02tInNLoxF9z/MiNLPcuu+ySVlpppdylRnQt88MPP6Rdd901vx5Z7ahlGt2yhL333judf/756cADD0z7779/bhIbB3hktuu7TAAAAKoT8wEAQPOIcYOLYwcXxZjW9amgFOP7VRUJw3hAmPmDNNQhstQxpsLxxx+fu24ZPHhwuueeeyoGav/oo48qZTWjK5YYSPuggw7KzVcjQIyA8Igjjqj3MgEAAJg5xHwAAADNq12hUGh0X3rRzPSqq67Kg2R/+OGHedrCCy+c+8bfYYcdKo3DMCuL8R6ieW0MiNmaxnsYcvKdLV0EaFb3TlVLhTLX/ZaWLgE0qzUHHtTSRYBm9ejG56Ryj1vEfAAAALNWzNLowQti4THeQgx+HYNeR3+z8bj//vtzlyprrLFGLggAAACzHjEfAADArKfRib9jjjkm9zP797//PXer8vzzz+fH2LFj8xgMzz77bJ4HAACAWY+YDwAAoA2N8XfzzTenffbZJz9KzTbbbHkA9jfeeCPdcMMNOUgEAABg1iLma3mGd6Cc3Xvc0JYuAgBAWWp04u+rr75KAwcOrPX1QYMGpa+//rqxiwcAAKAFifkAAKjNmncc3NJFgDY1pvtM6epzscUWS7fddlutr8driy66aGMXDwAAQAsS8wEAALShxF909xIDvG+00Ub5/w8++CA/7r333jR06NA84Pt+++3XtKUFAABgphDzAQAAtKGuPiMIjEHdTz/99Bz4VR3z4fjjj8/jPgAAADDrEfMBAAC0ocRfOOGEE3INzwceeCB9+OGHedrCCy+c1ltvvTTPPPM0VRkBAABoAWI+AACANpT4CxHs/f73v2+a0gAAANCqiPkAAADawBh/UePz6KOPrvX1Y445Jj300EONXTwAAAAtSMwHAADQhhJ/J598cvr4449rff3TTz9Np5xySmMXDwAAQAsS8wEAALShxN8rr7ySVl111VpfX3nlldPLL7/c2MUDAADQgsR8AAAAbSjxN2nSpDR58uQ6X//xxx8bu3gAAABakJgPAACgDSX+ll566XTzzTfX+FqhUEg33XRTWnLJJWekbAAAALQQMR8AAEAbSvztv//+6fHHH09bb7117gJmypQp+RFdvcS0UaNG5XkAAACY9Yj5AAAAZj0dG/vGHXfcMb333nt5wPeo6dm+/f/lEKdNm5batWuXjj322LTLLrs0ZVkBAACYScR8AAAAbSjxF4YNG5aDwej+5f3338/TFl100bT55pvn/wEAAJh1ifkAAADaSFefRRHsHXrooemAAw5IvXv3zjVC77zzzjR+/PimKSEAAAAtRswHAABQpi3+zj///HTeeeelJ554Is0zzzwV0++444601VZbpZ9//jkP8h5ivieffLLSfAAAALReYj4AAIA21OLvtttuy7U9SwO7GNx9t912Sx06dEgjR47Mg76ffvrp6cMPP0ynnnpqc5QZAACAZiDmAwAAaEOJv9dffz398pe/rDTt4YcfTuPGjUsHHXRQHth9qaWWSocffnjaZptt0l133dXU5QUAAKCZiPkAAADaUOLvq6++Sn379q007cEHH0zt2rVLW2yxRaXpq6++evroo4+appQAAAA0OzEfAABAG0r8zT///Onzzz+vNO3RRx9Ns88+e1puueUqTe/UqVN+AAAAMGsQ8wEAALShxN9KK62U/vnPf6YJEybk56+99lp6+umn05AhQ1LHjh0rzfvmm2+mBRdcsGlLCwAAQLMR8wEAAMzaKkdu0zFs2LC08sorpwEDBuRxHZ577rnc5ctRRx1Vbd6bb745/frXv27KsgIAANCMxHwAAABtqMXfMssskx566KG04oorpjFjxuRB32Mw93he6pFHHsldwWy99dZNXV4AAACaiZgPAACgDbX4C6uttlq6884765xnnXXWSa+88sqMlAsAAIAWIOYDAABoIy3+AAAAAAAAgNZJ4g8AAAAAAADKgMQfAAAAAAAAlAGJPwAAAAAAACgDEn8AAAAAAABQBiT+AAAAAAAAoAxI/AEAAAAAAEAZkPgDAAAAAACAMiDxBwAAAAAAAGVA4g8AAAAAAADKgMQfAAAAAAAAlAGJPwAAAAAAACgDEn8AAAAAAABQBiT+AAAAAAAAoAxI/AEAAAAAAEAZkPgDAAAAAACAMiDxBwAAAAAAAGVA4g8AAAAAAADKgMQfAAAAAAAAlAGJPwAAAAAAACgDEn8AAAAAAABQBiT+AAAAAAAAoAxI/AEAAAAAAEAZkPgDAAAAAACAMiDxBwAAAAAAAGVA4g8AAAAAAADKgMQfAAAAAAAAlIFWmfi74IILUr9+/VKXLl3Sqquump5++ul6ve8///lPateuXdp8880rTS8UCun4449PvXv3Tl27dk3rrbdeeuedd5qp9AAAANRGvAcAANCGEn/XXnttOvjgg9OwYcPS888/n5Zbbrk0ZMiQNHbs2Drf98EHH6RDDz00rbnmmtVe++tf/5rOO++8dPHFF6ennnoqzTHHHHmZEydObMY1AQAAoJR4DwAAoI0l/s4555y0xx57pF133TUtueSSOXibffbZ08iRI2t9z9SpU9MOO+yQTjzxxLTIIotUq/05fPjwdOyxx6bNNtssLbvssunKK69MY8aMSbfccstMWCMAAACCeA8AAKANJf4mT56cnnvuudw1S1H79u3z81GjRtX6vpNOOinNN998abfddqv22ujRo9Pnn39eaZk9evTIXcrUtUwAAACajngPAACg+XWcCZ9Rb19++WWuzTn//PNXmh7P33zzzRrf89hjj6XLLrssvfjiizW+HkFgcRlVl1l8rapJkyblR9H48ePz/9OmTcuP1qJdKrR0EaBZTUvtWroI0Jbq30CTa+enCmWuNcUGrbE8rTXeC2I+aHmt6VwDoOHEe5S7aa3st0pDytOqEn8NNWHChLTTTjulSy+9NM0zzzxNttzTTjstdyNT1bhx41rVOBELdXd1pbyNnbZASxcBmlfXFVu6BNCs+qeeLV0EaFbTG5euJeKjctJc8V4Q80HLa23XUKD1uf/++/P3ddzs3nfffXPX36U233zz/Hvh559/zn/HOMLhwAMPTE8++WTq3r17fj5ixIjUr1+//PdFF12Urr766tzrQCzvT3/6UwusWXkQ71Huxs7C8V6rSvxFMNehQ4f0xRdfVJoez3v16lVt/vfeey8P8r7JJptUy3p27NgxvfXWWxXvi2X07t270jIHDx5cYzmOOuqoii+KYu3Pvn37pnnnnTfNOeecqbX4aILWUJS3+aZ+2tJFgOZVeK6lSwDNanRao6WLAM0qup9sTbp06ZJas9YS7wUxH7S81nYNBVqXKVOmpJNPPjk9/PDDuRvvlVdeOe28886pZ8//n2y655578vd2zLvWWmulbbfdNi2//PL5N9Hf//73tPHGG1da5gMPPJCeeeaZ9Nprr6XZZpst39R3LWq80emrli4CNKv5ZuF4r1Ul/jp16pRWXHHF9OCDD+ZaGsXALp7vt99+1eYfNGhQeuWVVypNi0HdI/P5t7/9LQducRGPYDCWUQz8Iqh76qmn0t57711jOTp37pwfVUVNkHi0FgXdIFLm2uvaiLLXuroMgKZW8FOFMteaYoPWWJ7WGu8FMR+0vNZ0rgGtz7PPPpuWWmqp/H0fNtxww5y422677SrmmWuuufL/0ZV4tPqLCkZxbWnXrl2N3+nRi8DRRx9d8RugpopH1J94j3LXvpX9VmlIeVpV4i9ErctddtklrbTSSmmVVVZJw4cPTz/88EPadddd8+tRs2OBBRbIXbNEhnPppZeu9P7iBb90+p///Od0yimnpAEDBqT+/fun4447LvXp06ci2AQAAKD5ifcAgPoYM2ZM/k1QFH9/+mn1nqFWW221XFFon332qdTa/9BDD03HHHNM2mijjfLvhEgKvvPOOzl5GL9H4jfF+eefnxZffPGZtk4AM0urS/xFk+wYV+H444/Pg7HHBTuabRcHa//oo48anGk9/PDDczC55557pm+//TatscYaeZmtvSscAACAciLeAwCa0hNPPJF7A9hqq63Sq6++misHRQWiaM03adKkXOHo4osvzmMERpeg8Zvh6aefTvfee2+uePT444+39CoAlH/iL0Q3LzV19RIeeeSROt97xRVXVJsWzbtPOumk/AAAAKDliPcAgOmJ1vulLfzi7+gtoCbdu3dPv/nNb3LFn0j8Fcf9jUpA0ZvA9ddfX9FqcMstt8x/DxkyJO24444zZV0AZrbW1UkpAAAAAABtWiT5ogVfJPy+//77dPfdd+dkXdF3332XexEI0bIvWvDF+MDhs88+qxhL+LbbbstjBYZNN920opJRtPpbeOGFW2DNANpoiz8AAAAAANqmjh07prPPPjutu+66OYEXXXv37Nkzj9k3YsSI9PPPP6ff/e53afLkyfn1bbbZJm288cb5vTvssEP68ssv8/Rf/vKX6YADDsjTd99999wCMFoFzjHHHOnSSy9t4bUEaB4SfwAAAAAAtCrRQi8epe66666Kv5999tka3/fQQw/VOL1z587p2muvbeJSArQ+uvoEAAAAAACAMiDxBwAAAAAAAGVA4g8AAAAAAADKgMQfAAAAAAAAlAGJPwAAAAAAACgDHVu6AAAAAAAAZeXsdi1dAmheAw9q6RIAtdDiDwAAAAAAAMqAxB8AAAAAzGLuuOOONHDgwDRgwIA0YsSIaq+vtdZaabnllktLLrlkOumkkyqmv/fee2mllVZKiy22WNprr71SoVDI07fddts0ePDg/FhggQXS5ptvPlPXBwBoGhJ/AAAAADALmTJlSjr44IPTQw89lF544YV05plnpq+++qpaYvCll15KL7/8crrrrrvyfOGII45IJ5xwQnr33XfTl19+me688848/dprr00vvvhifqy77roSfwAwi5L4AwAAAIBZyNNPP52WWmqp3DKvW7duacMNN0z33XdfpXnmnHPO/P/PP/+cH+3atcut+5544ok0dOjQ/NqOO+6Ybr/99krvmzRpUrr33nsl/gBgFiXxBwAAAACzkDFjxuSkX1H8/emnn1abb7XVVkvzzTdfWm+99XIXntEqcO65585JwNred/fdd6df/epXaa655poJawIANDWJPwAAAAAoQ9G6L5KE0X3nq6++Wq/3XHfddXm8PwBg1iTxBwAAAACzkD59+lRqqRd/x7SadO/ePf3mN79J99xzT+rZs2f6+uuvc5efNb3vp59+Svfff3/adNNNZ8JaAADNQeIPAAAAAGYhq6yySm7BF4m777//PnfPOWTIkIrXv/vuuzRu3LhKY/YNGjQod/H5y1/+Mt155535tauvvjptsskmFe+766670lprrZWThQDArEniDwAAAABmIR07dkxnn312WnfddfPYfYccckhuzbfRRhvlrj2//fbbtOGGG6Zll102rbjiimnttddOG2+8cX7vGWeckYYNG5YWXXTR9Itf/CINHTq0Ujef22yzTQuuGQAwozrO8BIAAAAAgJkquuOs2iVntNgrevbZZ2t834ABA9Jzzz1X42vXXnttE5cSAJjZtPgDAAAAAACAMiDxBwAAAAAAAGVA4g8AAAAAAADKgMQfAAAAAAAAlAGJPwAAAAAAACgDHVu6AAAAAAC0MSds0dIlgObVvaULAEBbpcUfAAAAAAAAlAGJPwAAAAAAACgDEn8AAAAAAABQBiT+AAAAAAAAoAxI/AEAAAAAAEAZkPgDAAAAAACAMiDxBwAAAAAAAGVA4g8AAAAAAADKgMQfAAAAAAAAlAGJPwAAAAAAACgDEn8AAAAAAABQBiT+AAAAAAAAoAxI/AEAAAAAAEAZkPgDAAAAAACAMiDxBwAAAAAAAGVA4g8AAAAAAADKgMQfAAAAAAAAlAGJPwAAAAAAACgDEn8AAAAAAABQBiT+AAAAAAAAoAxI/AEAAAAAAEAZkPgDAAAAAACAMiDxBwAAAAAAAGVA4g8AAAAAAADKgMQfAAAAAAAAlAGJPwAAAAAAACgDEn8AAAAAAABQBiT+AAAAAAAAoAxI/AEAAAAAAEAZaJWJvwsuuCD169cvdenSJa266qrp6aefrnXeSy+9NK255prpF7/4RX6st9561eYvFArp+OOPT717905du3bN87zzzjszYU0AAAAoJd4DAABoQ4m/a6+9Nh188MFp2LBh6fnnn0/LLbdcGjJkSBo7dmyN8z/yyCNpu+22Sw8//HAaNWpU6tu3b1p//fXTp59+WjHPX//613Teeeeliy++OD311FNpjjnmyMucOHHiTFwzAACAtk28BwAA0MYSf+ecc07aY4890q677pqWXHLJHLzNPvvsaeTIkTXOf/XVV6d99tknDR48OA0aNCiNGDEiTZs2LT344IMVtT+HDx+ejj322LTZZpulZZddNl155ZVpzJgx6ZZbbpnJawcAANB2ifcAAADaUOJv8uTJ6bnnnstdsxS1b98+P4/anfXx448/pp9//jnNPffc+fno0aPT559/XmmZPXr0yF3K1HeZAAAAzBjxHgAAQPPrmFqRL7/8Mk2dOjXNP//8labH8zfffLNeyzjiiCNSnz59KgK/CAKLy6i6zOJrVU2aNCk/isaPH5//j5ql8Wgt2qVCSxcBmtW01K6liwBtqf4NNLl2fqpQ5lpTbNAay9Na470g5oOWJ96j/In3KG/iPcrdtFYUFzS0PK0q8TejTj/99PSf//wnjwMRA8U31mmnnZZOPPHEatPHjRvXqsaJWKi7qyvlbey0BVq6CNC8uq7Y0iWAZtU/9WzpIkCzqm1cupYyYcKEVM6aKt4LYj5oeeI9yp54jzIn3qPcjZ2F471WlfibZ555UocOHdIXX3xRaXo879WrV53vPeuss3Ig+MADD+RxHYqK74tl9O7du9IyY5yImhx11FF5wPnS2p8xiPy8886b5pxzztRafDRB7TjK23xTP23pIkDzKjzX0iWAZjU6rdHSRYBmNd9886XWZEaTYW0l3gtiPmh54j3KnniPMifeo9zNNwvHe60q8depU6e04oor5oHaN9988zytOHD7fvvtV+v7/vrXv6ZTTz013XvvvWmllVaq9Fr//v1zMBjLKAZ+EdQ99dRTae+9965xeZ07d86PqmL8iXi0FgXdYlDm2uvaiLLXuroMgKZW8FOFMteaYoPWWJ7WGu8FMR+0PPEe5U+8R3kT71Hu2reiuKCh5WlVib8QtS532WWXHNCtssoqafjw4emHH35Iu+66a3595513TgsssEDumiWcccYZ6fjjj0/XXHNN6tevX8U4Dt26dcuPdu3apT//+c/plFNOSQMGDMiB4XHHHZfHhSgGmwAAADQ/8R4AAEDzanWJv2233TaPqxDBXQR1UWvznnvuqRis/aOPPqqU2bzooovS5MmT01ZbbVVpOcOGDUsnnHBC/vvwww/PweSee+6Zvv3227TGGmvkZbb2rnAAAADKiXgPAACgebUrFAr6VpiO6CqmR48e6bvvvmtV4z0MOfnOli4CNKt7p45o6SJA8+p+S0uXAJrVmgMPaukiQLN6dONzUmvSWuOWWUFr3XZiPsqZeI+yJ96jzIn3KHePzsLxXuvqpBQAAAAAAABoFIk/AAAAAAAAKAMSfwAAAAAAAFAGJP4AAAAAAACgDEj8AQAAAAAAQBmQ+AMAAAAAAIAyIPEHAAAAAAAAZUDiDwAAAAAAAMqAxB8AAAAAAACUAYk/AAAAAAAAKAMSfwAAAAAAAFAGJP4AAAAAAACgDEj8AQAAAAAAQBmQ+AMAAAAAAIAyIPEHAAAAAAAAZUDiDwAAAAAAAMqAxB8AAAAAAACUAYk/AAAAAAAAKAMSfwAAAAAAAFAGJP4AAAAAAACgDEj8AQAAAAAAQBmQ+AMAAAAAAIAyIPEHAAAAAAAAZUDiDwAAAAAAAMqAxB8AAAAAAACUAYk/AAAAAAAAKAMSfwAAAAAAAFAGJP4AAAAAAACgDEj8AQAAAAAAQBmQ+AMAAAAAAIAyIPEHAAAAAAAAZUDiDwAAAAAAAMqAxB8AAAAAAACUAYk/AAAAAAAAKAMSfwAAAAAAAFAGJP4AAAAAAACgDEj8AQAAAAAAQBmQ+AMAAAAAAIAyIPEHAAAAAAAAZUDiDwAAAAAAAMqAxB8AAAAAAACUAYk/AAAAAAAAKAMSfwAAAAAAAFAGJP4AAAAAAACgDEj8AQAAAAAAQBmQ+AMAAAAAAIAyIPEHAAAAAAAAZUDiDwAAAAAAAMqAxB8AAAAAAACUAYk/AAAAAAAAKAMSfwAAAAAAAFAGJP4AAAAAAACgDEj8AQAAAAAAQBmQ+AMAAAAAAIAyIPEHAAAAAAAAZaBVJv4uuOCC1K9fv9SlS5e06qqrpqeffrrO+a+//vo0aNCgPP8yyyyT7rrrrkqvFwqFdPzxx6fevXunrl27pvXWWy+98847zbwWAAAAVCXeAwAAaEOJv2uvvTYdfPDBadiwYen5559Pyy23XBoyZEgaO3ZsjfM/8cQTabvttku77bZbeuGFF9Lmm2+eH6+++mrFPH/961/Teeedly6++OL01FNPpTnmmCMvc+LEiTNxzQAAANo28R4AAEAbS/ydc845aY899ki77rprWnLJJXPwNvvss6eRI0fWOP/f/va3tMEGG6TDDjssLbHEEunkk09OK6ywQjr//PMran8OHz48HXvssWmzzTZLyy67bLryyivTmDFj0i233DKT1w4AAKDtEu8BAAA0r46pFZk8eXJ67rnn0lFHHVUxrX379rmrllGjRtX4npgeNUZLRe3OYpA3evTo9Pnnn+dlFPXo0SN3KRPv/f3vf19tmZMmTcqPou+++y7//+2336Zp06al1mLqxB9augjQrL6dNqWliwDNa7Z2LV0CaFZTf/j/v6egHEV80JqMHz++IhnWGrWWeC+I+aDlifcoe+I9ypx4j3L37Swc77WqxN+XX36Zpk6dmuaff/5K0+P5m2++WeN7Isiraf6YXny9OK22eao67bTT0oknnlht+sILL9zANQJmxC9augAAzKALW7oA0Kx+0UqP8QkTJuTkV2vTWuK9IOaDlifeA5jVtc7fwtBUZuV4r1Ul/lqLqIFaWqs0anx+/fXXqWfPnqldO7V1AGiaWjp9+/ZNH3/8cZpzzjlbujgAlIGo+RlBYJ8+fVq6KK2emA+A5iTeA6Al471WlfibZ555UocOHdIXX3xRaXo879WrV43viel1zV/8P6b17t270jyDBw+ucZmdO3fOj1JzzTVXI9cKAGoXQaBAEICm0hpb+rW2eC+I+QCYGcR7ALREvNc+tSKdOnVKK664YnrwwQcr1byM57/61a9qfE9ML50/3H///RXz9+/fPweDpfNErZunnnqq1mUCAADQtMR7AAAAza9VtfgL0d3KLrvsklZaaaW0yiqrpOHDh6cffvgh7brrrvn1nXfeOS2wwAJ5TIZw4IEHprXXXjudffbZaejQoek///lPevbZZ9M//vGP/Hp00/LnP/85nXLKKWnAgAE5MDzuuONyc8jNN9+8RdcVAACgLRHvAQAAtLHE37bbbpvGjRuXjj/++DwYe3TPcs8991QM1v7RRx+l9u3/f0PF1VZbLV1zzTXp2GOPTUcffXQO9m655Za09NJLV8xz+OGH52Byzz33TN9++21aY4018jK7dOnSIusIANG92LBhw6p1MwYA5Uy8B0BbIN4DoCW1K8SIgAAAAAAAAMAsrVWN8QcAAAAAAAA0jsQfAAAAAAAAlAGJPwAAAAAAACgDEn8AAAAAAABQBiT+AGAmGzVqVOrQoUMaOnRoSxcFAACAJibmA6AltSsUCoUWLQEAtDG777576tatW7rsssvSW2+9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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAABv4AAAIDCAYAAADMsGn8AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8ekN5oAAAACXBIWXMAAA9hAAAPYQGoP6dpAADGu0lEQVR4nOzdCbxM9f/H8Y99X8qSfSuRXbY2opSQrYWSdippQ4mytEl+IknahFSKZClLlOWXSsgSClGWJGtlyc78H+/v73HmPzN37upeM3fu6/l4HO6cOXPm7HPO9/P9fr6ZfD6fzwAAAAAAAAAAAACka5kjvQAAAAAAAAAAAAAAzhyBPwAAAAAAAAAAACAGEPgDAAAAAAAAAAAAYgCBPwAAAAAAAAAAACAGEPgDAAAAAAAAAAAAYgCBPwAAAAAAAAAAACAGEPgDAAAAAAAAAAAAYgCBPwAAAAAAAAAAACAGEPgDAAAAAAAAAAAAYgCBPwAAAABponHjxlatWrUUf37hwoWWKVMm9z9wpnQsPfPMM5FejJh31113Wbly5VJ8zdAQi3Ts6Rjcu3dvmn7Pli1b3Pe8/PLLSV6m1MR1GwAAIPII/AEAEEPGjRvnClu8IWvWrFayZElXCPfHH3+k2fd6BUfnnXeeHT58OM77KgC8/vrrUzTvUaNGufVKqkOHDtmAAQPsuuuus3PPPdctV0KfnzRpkl1yySVWsGBBK1SokF155ZU2c+ZMiwZ79uyxRx991CpXrmy5cuWyokWLWv369e3JJ5906+nR/g3c74FDzpw5E/0ejpv/L6gMN3z//fdxpv/uu+/siiuusNy5c1uxYsXskUceCdonkeKtx+TJkyO9KFEr9HzJkSOHXXjhhda/f387evRomn2vghn6vlatWp1RQX0onTs6l5JTyL5hwwbr3r27XXbZZe4aoe/WMoSjbTJo0CCrUqWKO951bbj55pvtp59+skgLvXZp0HWySZMmNnv27CR//ocffjija3FC14/QIXS5v/nmmzjf5/P5rHTp0u79lF4DxfuOzp07h33/6aef9k+T1sGoaBTu+Ak3pDSImVb0+6blatCgQcSXIzm/s7FEx0Todadhw4Y2derUFB1b3v2QN2TOnNmKFy/uzv9w9yChRo8e7T733nvvxXlv8eLFbn6PP/54qlXKAQAA0S9rpBcAAACkvueee87Kly/vCmxVYKACCBUurl27NkmBoJTavXu3vfHGG9azZ89ULVgqXLiwK6xPChVeav3LlCljNWvWTLAw/LXXXnMBm5YtW9pLL73ktpe2lQpaPv30U7vhhhssUv766y+rW7euHThwwO655x5X4Lxv3z5bvXq128Zdu3a1vHnz+qdX8EIFP6GyZMmS5O/MyMeNR8dDvXr1gsZdcMEFQa9XrVplV199tV100UU2bNgw2759uwvYbNy4MUnBBkRe4Pmyf/9+mz59uj3//PP266+/2ocffpim3z1jxgxbvny51alTJ1Xmp8Dfs88+6/5OakspFQSPGDHCBfN0HOuYjs9tt91mn332mXXp0sUuvvhi27Fjh73++ut26aWX2po1a6xs2bIWad61SwGzXbt2uWtXixYt7PPPPw8KnB05csRVbEjta7G24fvvvx/0uT59+rhrtIJr8dF1dcKECa4SQaD//ve/7rqi4/RM6Tv0e6ZrYvbs2YPe++ijj9z7aRnwjmaNGjWKs98UJFVQ97777vOPC/ytjQa6RilgtHTpUtu0aVOc36izJb7fWW1XnWuhx1usqVWrlv++RdfFt956y9036rpw7bXXpujY0mc17vTp0/b777/bO++847an9rW+Lz733nuvC/opuKdrniqyyYkTJ9z3qSKB9zsBAAAyBgJ/AADEoObNm7uCSq+gQQUzgwcPdoW37du3T7PvVaHEkCFD7MEHH3StIiJBNaT//PNP1wpLrThCgzihgT+9r8JhryWGCnbVokUFKJEM/L377ru2bds2+/bbb12rnEAqgA4tUFNhdqdOnc7oOzPyceNRjf2bbropwWmeeuopO+ecc1xQOX/+/G6cCmEVGJk7d64r8EN0Cz1fdOzpPFMgRMFctUJNC6qQcPDgQVcAq/MqUlq3bm3//POP5cuXzwWt4wv8qcXvlClTXGGyztHA8+Sqq65y76nlYKQFXru8QnDtQ+3PwMBfSiowJOVarPmGXn9VmUTX0ISuywpOfvLJJy4IGxiQVDBQgeHUaIWn1u861lQpoU2bNkGtljdv3mw33nijCwymR2rZ3LRpU9diP9TOnTvdPUBCLSYrVKjghkAPPPCAG3emv6dpRftM+07n3v333++CgMpyEE3UuiwtKwtFC90rBh4nd9xxhwvCvvLKK/7jKLnHlu4/dN3wtG3b1rXM03UiocCf7mEVeNQ0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zpy+FStW+Kf75JNP3Hd9/fXXceZx8803+4oVK5bg97z11lvu82vWrAkaX6VKFd9VV12VrO0XzoIFC9z8tZwJOXz4cNDr48eP+6pVqxa0DKJ56RjYvHlznHXQuh44cMA/vk+fPm584LTaBxo3dOhQ/7hjx475atWq5StatKj7Xhk+fLibbtKkSf7p/v33X98FF1zgxmu94lt20f7KlCmTb+vWrYluI50vefLkccedhk2bNvlefvll93ltAx3PcvDgQV/BggV9Xbp0iXNuFihQwD/+77//dss4ZMiQBL9X28K7Fjz77LPuM8uXL3evtc1C5/H888+75fzll1+C5tO7d29flixZfNu2bXOvtQ76rM6nlNB3hu63QEuWLPGdf/75QdeVOnXq+P78889E561jIlu2bL6//voraP9ru95zzz3+cdqe3bp1S/aye9eu0EHH7Lhx4+JMH7qdvM/rtyI1rsWBEroGBX7vyJEjffny5fMf17qONGnSJOw1MPB6H9/+Cl1fbVdt/+zZs/vef/99N37mzJnueN+yZUvQdVh0Turc1Llw5MgR/7xmzJjhpuvfv79/nM7j4sWLB22LuXPnuum07Cm5dmubJefa/dlnn/myZs3q69Chg/tdat68ubtuL1y40JcSOue0jT2a56lTp4Km0bbXMfbcc88l67c+cFuvW7fOV6JECV+9evWCzo+E/PDDD+7zX375pXuta1WpUqV8jz76aJzl03S5cuXybd++Pehc1vju3bvHWaZA4a6xupcIvLdJ6Bj3foe863Zyjinv+A7ctlK7dm133UnI7t273XF+7bXXBu0znWOa55gxY+L8No0fPz7o2qTfNd0LJEbHt77H+x358ccffbfccoub58MPP5ykYyuQtx9CB10rdZ4kh3d/qkG/rYn9HgEAgNhEiz8AAGKQ+rxRSxDV0lZNeLXIUs131c72ahTPnz/f1SBWqxSvdZhSjqmWtWqQe60JlDJNLSg0LilUe12tlBJqvaWWBgUKFHCp3AJbp6lFkFrWLFiwwM4GpXaqVKmSa8mkZVILJKWwU236TZs2JfhZ1ZpXqiq1XAmsPa6UbR06dPCP0/ZTK5n4auTHRzX/lUpMNbY1X6VGVOsMpd56/vnnXUrCQKoRru8OHdTPVFJl5ONGaSiVXk8t1tT/mVozfv/9964li1pKeLxlUwqxUNoHibVY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79mzZ0+i8ytbtqzvzjvvTPZyaB/oO7RPYl18++2NN95w4wcPHhw0Xsehxnfo0MF38uTJoPeWLFniy507t6969eq+EydO+Mdr3vqMjrvjx4/HWYYvvvjC9/nnnydpeceMGePLli2bb/78+W6eCxcuTPK55GnZsqU7NkLH6ZyYPHlynOmPHj3q69mzZ6LL1rp1a98VV1zhS28OHTqU7M889NBDbhuePn06TZYJAAAAZ45nTJ4xIyGp+23nzp2+w4cPu791TCW36HjKlCnuM5s2bfKlJ3pWPnbsWLI+88MPP7h1nTdvXpotFwCkFKk+ASAFrrrqKvf/5s2bU22easGlWmJZsybeGFs1PXPnzm0PPPCAe927d2/bs2ePSzOROXPqXtonTJjgUkyqxZVSvOh1Qi20lPJRLY+UbvLRRx91NVgT8tdff7nWatWrV3etufRZpZNRzVSPWrl5tRLvvvvuOKltwvW/8O+//1rPnj2tdOnSliNHDtfa7eWXX9ZTS9B0mo/S6kybNs2qVavmpq1ataqr2RptGjZs6P7/9ddfg8arluo555zjWsCF1qpUCh21AFyzZk1QOh7V/lWrwDFjxrgULqGaNWvm9nlSqJauahg3adLELrroolTpy2HJkiU2c+ZMu/fee13rw1DaT9qfCdGxp/3YtGnTOO95+121n5UGKVeuXHbppZe67SQ6l5TySDU5GzdubFu2bAn6vGpQ33zzza5fBy2LjjPVaj1y5Eic71q/fr07L4oUKeK+R8fi008/Haf/kp9//tm1atS+vOKKK9x7J0+etOeff97OP/989z06zp966qmwfUloH2zdujVDpCoCAACINTxjhscz5tl13nnnuWeWlNI6a7vp+SWQtqf2xbZt29x+198lS5Z03RWInsN0DihNrTLRhB4TSdmnHh0fesZSCl09zxUvXtx1b+E9R+vZTvtI+06ta71nLT2Pyfz5892zt5alYMGCLrtKuC43lA5Vz9TTp09P8fYCgLRC4A8AUsC7YdSDR0roRlQPMBpUUK+b2vfee88V+ifloUz9sCmd4oIFC1xaCQV8lLKjdu3alpoUfNm0aZPdeuutrv833SwnFNTRA5nWTSliWrRoYSNGjEg0xeJvv/3mHg508680GU888YS76b/yyiv9/UgomOSlL9T83n//fTc0atQo7Dz14NW6dWv38KpUkZqv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tq2bPiEjR45081uzZo01b97cpk2bFvT+zz//bA8//LCtWLHCcuTIYa1bt7bhw4fT9yIAAAAAAECMlXVRzoXkoMUfACBmDR482A4dOmQ7d+60Bg0a2A033HDG89TNx5w5c2zp0qWWlkqUKGF9+/a1Ll26hH2/Y8eOVqlSJdu1a5e7afrxxx/t+eefT9NlAgAAAAAAQGRQzoWkIvAHAIh5qgl155132u+//2579uwxn89nI0aMsMqVK1vBggVdral169b5px82bJiVKVPG8uXLZ+XKlbPRo0f738uZM6d1797devfuHe/37d6922677TYrXry4u7FRTaljx47Zvn37XK2m/fv3W968ed2waNGisPPQzZtqTBUuXDjs+7/99pt16tTJrVuRIkVcTSjdGAEAAAAAACB2Uc6FxBD4AwDEvCNHjti7777rbi7OOecce+ONN9zrzz//3Pbu3etuPlq1amXHjx+3X375xdVAmjt3rh08eNCWLFli9evXD5rf448/7m4+VCMqlG62dHNSrFgx+/XXX/21lF544QUrVKiQzZ492woUKOBqaGlo2LBhitZJyzB+/Hi3bqrpNXXqVLcOAAAAAAAAiF2UcyExBP4AADGrT58+rqZTnjx5bMKECTZlyhTLmjWrvf766/bcc89ZxYoV3etHHnnE3Vjo5idLlizupuann35y48477zyrUaNG0Hzz58/vbpo0f00b6IcffrCNGzfakCFDXB5y3QQ99dRT7vtTk2pUffPNN662lmpclS5d2u65555U/Q4AAAAAAABEB8q5kFQE/gAAMWvQoEGuc2GlPihZsqStXr3ajd+yZYtLH6CbJW/4+++/bfv27Xb++efbe++95zod1s3Qtddea6tWrYoz765du7rPfPzxx0HjNW9957nnnuuf90033eRylMenatWq/pQIH374YaLrpe9t2rSpy4t++PBh++uvv9xNn9YJAAAAAAAAsYdyLiRV1iRPCQBAOqWboXfeeccaNWpk7dq1c7WGhg8fbtddd13Y6du3b+8G1YTq37+/3X777XHyiivnuDoZ7tevnzVr1sw/XvMuWrSo/fnnn2HnnTlz3Do3qnWVHEqtoGVTDa5MmTK5Zbn//vtd7SgAAAAAAADELsq5kBha/AEAMoSLL77YdW784osvWrdu3dyNzoYNG9x7Bw4csOnTp7tc5xr35Zdf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02s9qOXKlcuyZMnihnDT6LPxyZEjhxtCaWNrAICMStdA9cfjDX5DA/5OKz2Tl26hcePGrs8gLxVYWoqzPRKZNvD/5NJ6KXWZavxkz57dqlevbkOHDrW6devamVi4cKE1adLEbrzxRps8ebJ/vLaf+lEaN25ckuahba7pE6I+mDS/NWvWWPPmzW3atGlB7//888+uXybdaOr3uHXr1jZ8+HDLnTt3vNue32gA+N/vdKzhmQ8AAABpKb2UdVHOlTHKuRIq60rOc0q6fqK59NJLbd68eUHjvvzySzdedKDUqVMnaBo1kdRrbxoAANKbwYMH26FDh1z6swYNGtgNN9yQKvPVzcecOXNs6dKllpZKlChhffv2tS5duoR9v2PHjlapUiVXaKubph9//NGef/75NF0mAEB04pkPAAAAiG2Uc6W+qAr8aeeuWrXKDbJ582b397Zt2/zpWO644w7/9A888ID99ttv1qtXL9d/w6hRo2zSpEnWvXt3/zRK3/LOO+/Ye++9Z+vWrbOuXbvav//+a3fffXcE1hAAEGnDhg2zihUrWr58+ez88893tXI8W7ZscTVqxowZYxUqVLC8efO635g///zTrrnmGte64Morr3Q3IoH++9//uh/wggULWocOHVyKA8/XX3/taitpXrpxCc3H3alTJ3eDoHmr4HLBggVJXhcVdt555532+++/uz6JRJ3/jhgxwipXruyWRzWn9PsXuP5lypRx61+uXDkbPXp0UMoA/Yb27t073u/cvXu33XbbbVa8eHG33KopdezYMZd3XLWatO5aVw2LFi0KOw9tB9WYKly4cNj39duu7aL1U8o11YTSjREAIP3jmQ8AAABIPZRzUc4V9YG/H374wWrXru0G7wFOf/fv39+91gHpPRBK+fLlbebMma7GZ82aNV0TUO3YZs2a+afRgfnyyy+7eSgvrR4qv/jiizidvwMAMoayZcva/PnzXV8++s144okn7Ntvvw2aRjcl+gFWjaBXX33V2rdv75rg66ZDP9Ivvvhi0PTvv/+++4xuqP7++29/2gX9rR/zhx56yKUFUAHkBx98EPTZq6++2t2w6IbilltusZtuuinJnfUeOXLE3n33XXdjcc4557hxb7zxhhv3+eef2969e93NR6tWrez48eP2yy+/uBpIc+fOdd+xZMkSq1+/ftA8H3/8cbfuqhEVSjdb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2L59+/zvqxlwz549XS2AtWvXunFTpkxx/+uE//jjj+3HH3+0iy++2N566y03vlmzZq6Z7+rVq12QUBcFAEgP+vTp42ow5cmTx2644Qbr27evFS1aNFnzUL70Vq1audQGymeu9AkTJkxI9rLoBmjs2LEu57quz61bt072PAAAAAAAAJAxUc6FpCLwF2OWLl3qTtiSJUu6TjebN2/uj7wHBv8OHz5sp06dsn///deKFy/ubyJ88OBB9/eBAwf846+55hrLmjWre79u3br2xx9/RGDNACD5Bg0aZP/884+rAaXUB++9956/UkNSKd/5M888YytWrLC///7bHnnkEbvjjjts3bp1yZpPhw4d7Msvv7RXXnnF/a0OmQEAAAAAAICkoJwLSUXgL8bs2LHDBf08+jswUKfg3ciRI12aT+XczZcvnzVu3Ni998Ybb9h1113nxq9Zs8Z19BnamlDRf6UBTeu0ow0bNnTNlDUUKVLEtUb0vPzyy26eVapUcRcWAEiKCy64wFq0aOGuSymlChVqNV2gQAH7+eefk31jpfzrum6R/gAAAAAAAAApRTkXEkLgL4M5ceKEvf322y6wpyChWv998MEH7j2dpIrSa/yll17qahAEeuKJJ+ySSy6xBg0apHna0UWLFtmqVavcoCBf27Zt3fivvvrKFixY4KbXxei2225L1vqndiBS89O06gjVWwcA0WnLli02a9Ysd46Ho1bQR48eDRq8a5+uRcePH3eDrh1qLV2nTp1kL8PgwYPdNVHplAEAAAAAAICUoJwLCSHwF2PUWi+whZ/+1jiPTmql7SxTpoxlyZLF5QL+7rvvbM+ePa45b+3atd10N998sxvvGTVqlHs/OS3sziTtaODyb9682Ro1auReq+mychlny5bNvU5ODuO0CETq78mTJ/uXD0B0efLJJ931R8MVV1xhTZs2tf79+4edVq2hc+XKFTSIKgCoIkChQoWsWLFiLn+5OlAuV65cspdH12OvlTUAAAAAAACQVJRzIamyJnlKpAv169d3QSsFzNREd/bs2davXz//+wrCrV692uXvPeecc2zevHl20UUXub8V/FOQrXz58m68gloyc+ZMF/lXQExBw7RIO5ozZ067+uqr41wo1NnojTfe6FrUycaNG12rPwXw1JGpPn/hhRcmOxApXiDy1ltvjROI1LyTEohUy0EAZouu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1D3SSwAAyIDps3/77TfbtGmTXXTRRe7133//bTVq1LDVq1e7VoM33HCDG68W+Z06dTpr6wMAAAAAwJki1ScAAACADJU+u3r16i5N9pYtW9xwzjnnuKCfqGKO18+cWv0pYAgAAAAAQHpBiz8AAAAAGS59dnw6d+5sd9xxh1WrVs3y5Mlj77zzTpqvBwAAAAAAqYUWfwAAAPGYMWOGVapUySpWrOiCCIEOHjxotWrV8g8FChSw4cOHu/c0rT6j/jjVCsmjlkWNGzd2rY2aN29u+/fvP+vrBMQatdD75ZdfXOrO++67z58+W0G/wPTZxYsXj3cee/fu9f+dI0cOmzhxomtJuGTJEqtdu/ZZWAsAAAAAAFIHgT8AABCzgbbff//drrzySjd9nTp1ktziR06ePGk9evSw+fPn28qVK23IkCG2b98+//v58uWzVatWuUHvFyxY0Nq0aePea9Cggc2dOzdOisCePXta165dbc2aNa7fsMGDB6d4WwEAAAAAAAChCPwBAICokBaBtv/85z/WsWNH95mBAwda3759k7w86turatWqVrJkScubN69roafvCGfx4sVWrFgxK1++vHutFn3e34HWrVtnV111lftb/0+ZMiXJywMAAAAAAAAkhj7+ACCtPdPOoka+SC8AkLRAm3iBtltvvTVJgbZw1AJQLQVFaTUTSvUXaseOHf5lEf39xx9/hJ120qRJ1qFDh0TnWaNGDRfsu//++93/8c0PAAAAAAAASAkCfwAAICqkRaDtqaeesmuuucalBD19+rQLGKY2n89nn376aZLmPXToUHvwwQftrbfespYtW1qePHlSfXkAAAAAAACQcZHqEwAApCteoK19+/aJTjthwgTXp9727dvtnXfesXvvvTfJ31OiRImgwKP+1rhQ33zzjUsxWqpUqUTnqWDm9OnTbcWKFa7VX+nSpZO8PAAAAAAAAEBiaPEHAACiQrhAW/369c8o0Pbuu+/awoUL3d9qYXfnnXcmeXn03WvXrnXLUaBAAZs9e7b169cvxa0PZe/evVaoUCGXglR9Dt53331JXh4AZg1n9LBosuj6YZFeBAAAAAAAgtDiDwAARIXAQNuhQ4dcoK1Zs2ZnFGhTi7p58+a5v7///vtktbDLmjWrS83ZpEkTq1WrlvXs2dMF7Vq0aOHSkorSh06dOtVuuummoM8qlacCk2pp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VVfZlClT0nhpAQDIYIG/fv36uab5b7zxhm3YsMF8Pp//vZw5c9rNN99s06dPT63lBAAAAACcRTzzAQBS244dO1zQz6O///jjjzjTKZVn7dq1bdiwYf5x559/vs2ZM8f9PXXq1KDPffTRR1ajRg33uV27dqX5egAAEJOBP/2gdu3a1e677z4799xz47x/0UUX2W+//XamywcAAAAAiACe+QAAkfDhhx/a6tWrbeHCha6CycyZM934MWPGuNSgdevWtRw5cliWLFnc+FatWrnfI32mfv369tBDD0V4DQAASKeBv927d1v16tXjfV8/vur3AQAAAACQ/vDMBwBIbSVKlAhqqae/NS6Q1yKwQIEC1r59e1u2bJl7XaVKFfvqq6/shx9+sGbNmlnFihXd+EKFCrlAoHTp0sU/PQAAGVWKA3+lS5e29evXx/v+t99+axdccEFKZw8AAAAAiCCe+aKT0q9WqlTJFXiPHj06zvuNGze2ypUrW61atdxw5MgRN75Dhw7+cSpUb9u2rRuv1jPeeM23YMGCZ32dAGQcapG3du1aF/A7dOiQzZ492wXxPCdPnrS9e/f6+5rV++oTUPbs2eOfZvDgwa5FuuzcudP/+WnTpvmnBwAgo8qa0g8qZ7bybN9444124YUXunGZMmVy/7/zzjs2adIke+mll1JvSQEAAAAAZw3PfNFHhd09evSwBQsWuJYwderUsXbt2rnWLoEmT55s1apVCxo3ceJE/9+dOnWypk2bur+feOIJN4gCid98881ZWRcAGVPWrFlt6NCh1qRJEzt9+rT16tXLXcNatGjhrkG6tikQeOLECTt16pRL43nTTTe5z44fP97efvtt1+ds586d7ZprrnHjhw8f7ipFqCX6eeedZ2+99VaE1xIAgHTa4u/pp5+2yy67zBo1auR+rPUA2L17dytTpozdf//9dt1117nXyfX6669buXLlXGfxDRo0sKVLlyY4/T///GPdunWz4sWLu2b9eiCdNWvWGc0TAAAAADI6nvmij9ZLLVnUYi9v3rzWvHlzmzt3brLmcezYMZszZ46/xV8gBXPVMhAA0lLr1q3tl19+sU2bNvlb7em6rpSfefLkseXLl7v++n766SdXwcSrdNKzZ0/bsGGD+6wChh5No1aEP/74o7smli9fPmLrBgBAug78Zc+e3b744gsbO3asVahQwaUS0QNEjRo1bNy4cfb555/7O9lNKtVAVO3FAQMG2IoVK6xmzZqulo/6lghHTf5Vu2fLli2uRqN+/FXz1MsFnpJ5AgAAAAB45otGO3bsCFp3/R3YV1Zga83atWu7FpuhlDbv0ksvjZPSU6n1VGjutQQEAAAAkIFSfaqPANX+VK1PpQjRkBr0UKJOeO+++273+s0337SZM2famDFjrHfv3nGm1/i//vrLvvvuO8uWLZsbp1qeZzJPAAAAAMjoeOZLvz788EMXENy/f79rVaN++1q2bJloq74pU6a46b3tDAAAACADtfjLlSuXy5e9a9euVFsQ1eRUU/7A2oWZM2d2rxcvXhz2M5999pmrqai0L8rhrT4MXnzxRZcDPKXzBAAAAICMjme+6KQ0eIEt/PS3xgXyWgSqn6z27dvbsmXLggK6X375pQvwhVLLSdJ8AgAAABm0xZ+oE3Hlz04tSiuihzc9zAXS6/Xr14f9zG+//Wbz58+32267zeUCV27wBx980HUArDQvKZmnKH2NBs+BAwfc/+p0WEO0yGQ+iyan7X8512Mgk22qyxRduyqqjuO0wLmRPs4L4dzIuOdGdJ0XwrmRkc8NpB+cG2d3eXjmiz5169Z1++T33393gT2l7VTLTG+ZT5486fpELFy4sAuKapvdcccd/vdnzJhhDRs2dH1oBa6n0qKuW7fOrrzyygTWn9/uhHB9AgAAQLTc36U48Dd8+HBr0aKFq3F51113WdasKZ7VGa1o0aJF7e2333Z9S+jBVDUehwwZ4h4CU2rQoEH27LPPxhm/Z88eO3r0qEWLMvmi68li9+n/72siKuSqY9GivBWyaBKL/Z0E4txIH+eFcG5k3HMjqs4L4dzI0OcG0g/OjYQdPHgwVefHM1906tu3rzVu3NhtGwVBFfhUP4hDhw61/PnzW9u2bV0AUOOvvfZaa9Sokf9Y/eCDD9w+DT1233vvPTePffv2xf/F+fntTgjXJwAAAETL816Kn9z04KcUKvfff7898sgjLp2I0sEEypQpk+scPClUI1EPcqGpZPS6WLFiYT9TvHhx1/9AYIfyF110ke3cudPVbkzJPKVPnz6uc/jA2p+lS5e2IkWKuAepaLHtYHTVuCx6Km6n8hHlW27RYrNdYdFEhSexjHMjfZwXwrmRcc+NqDovhHMjQ58bSD82WwJBiQiItnMjZ86cqTo/nvmi0+233+6GQErf6Vm1alW8n506dWrY8U888UTiX3yA3+6E8NsNhNd45uMWLRa2fDnSiwAAwFl53ktx4O/cc8+1QoUKuY7CU0P27Nld7c158+a5GoqiGox6/dBDD4X9zOWXX24TJkxw0+mBVH755Rf3cKj5SXLnKTly5HBDKH2H9z3RwBdlqVYyR1EKuf+JntQmvujaVVF1HKcFzo30cV4I50bGPTei67wQzo2MfG4g/eDcOLvLwzMfgvHbnRCuT0D0nxucFwCA9Cw5v2MpDvwtXLjQUptqXN55552u34L69eu71DL//vuv3X333e599U2gWqZKyyJdu3a1kSNH2qOPPmoPP/ywbdy40XX0rtqoSZ0nAAAAACAunvkAAAAAIP05+500JKBDhw6uT4X+/fu71C21atWyL774wt9R+7Zt24KimkrFMmfOHOvevbvVqFHDPSDqgfDJJ59M8jwBAAAAAGcHz3wAAAAAEMWBP3UWrs7BZ86caVu3bnXjypYta9dff73ddtttQf0wJJXSscSXkiVcjdNLL73Uvv/++xTPEwAAAAAQHs98AAAAAJC+pDi59f79+11/C/fcc4/NnTvXTpw44QZ1Kq6UKldccYXrIB0AAAAAkP7wzAcAAAAAGajF39NPP23Lly+31157zbp06WLZsmVz4/UgOHr0aNfngqbR+wAAAACA9IVnvshr9vxMixZzIr0AAAAAANK2xd/UqVPtwQcfdIP3ACj6Wx2wa/j0009TOnsAAAAAQATxzAcgPZkxY4ZVqlTJKlas6ConhGrcuLFVrlzZ9QOq4ciRI258z549XR+iGm6++WY7fPiwG3/06FG74YYb3PyaNGlie/fuPevrBAAAcFYDf/v27XM3VPHRzdRff/2V0tkDAAAAACKIZz4A6cXJkyetR48eNn/+fFu5cqUNGTLEXcNCTZ482VatWuWGXLlyuXEDBgyw1atXu6FMmTL21ltvufEKHlaoUME2btxoN954o7300ktnfb0AAADOauDvggsusM8++yze9/Xe+eefn9LZAwAAAAAiiGc+AOnF0qVLrWrVqlayZEnLmzevNW/e3PVNmhT58+d3//t8PtfKL1OmTP5r3O233+7+7tSpk33++edpuAYAAABREPhTuhfdRLVo0cL9v2XLFjfMmTPHWrZs6Tp8f+ihh1JxUQEAAAAAZwvPfADS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Vq2SJUuWyJdffim9evXK8s9xww03mL89derULN83nBcrWlnUHnHZITExUU6fPi1NmjSRy5cvm7/1zDPPmMppIAcPHpQ2bdqY2NEf+vRHvZ49e8rKlSszdQ3JKj169DBxcOnSpSzfN3KWk2PDpgmh93WiTp06nnXr16+XLl26mPJv377dJGz6SEpKytR1JKto0kjdCQjPei35HrJLqMeGIudDuNdryfkQTE6ODRs5XxiwgDQkJCRYRYsW9bw+deqU9dBDD1k33HCDlS9fPqtixYrWzJkzzbp169ZZejidPn3as/327dvNsoMHD5rXs2bNsgoVKmR9/PHH5r1RUVFWixYtrB9//NHn7y5atMiqVauWWV++fHlr5MiR1sWLFz3rdZ9vv/221a5dO+vaa6+1RowY4Vl35513WlOnTk31WcqWLWtNnDgx1fLY2Fird+/enteXL1+2SpYsaY0bN868PnPmjJUnTx7zXdh2795tyrBhwwbzWj+fvtbP669JkyZW3759fb4j/0e3bt0823/xxRdW3rx5rd9//z2d/xkEU6jHhf330pKSkmKVKFHCmjBhgmeZxoH+3Xnz5pnX3333nfl733zzjWeb5cuXWxEREdaRI0fS/OyB4lHLEyguvMs/Z84cq3Tp0lf4n4HTY0UtXbrUqlSpkomVpk2bev7/7ePE//jcv3+/1b59e6tYsWLWddddZ9WtW9datWqVzz6Tk5Ot1q1bm32WK1fO+uCDDwKe80eNGmV16tQpVTn1HNyhQ4dUywcPHmxVrVrVZ5m+Py4uLsPXkPSuP3qM16hRw/M6UBzoe20XLlwwcbh69epU+0JocXJspFensT344INWmzZtfJbVr1/fevzxxzN8HVH6d/Tal15M2uXxf2j9ynb48GGzTL8TAOFVr/VGvoesFOqxQc6HnOLkeq03cj7kNCfHBjlf+OCOP6RJhyDxbu0fNmyYGXN6+fLlsnv3btMLRXtmZbbHg97qPHfuXPn666/NkBCdO3f2+Ztdu3aVvn37mr/1zjvvmNvs9T3+t8vfc889snPnTjNkitJ9ffXVV9K+ffsMleWvv/6SrVu3+vT8yZUrl3m9YcMG81rXay867230Nud//OMfnm0yqlGjRj49KdauXWtul77jjjs829StW9f07NGeEnAmN8SFDs9StmxZKVOmjBl2QHtxevd4O3bsmM8x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", "text/plain": [ "

" ] @@ -789,10 +20630,10 @@ ], "source": [ "_label_map = {\n", - " 'A — superdiagnostic / 100 Hz (baseline)': 'A\\n(super/100Hz)',\n", - " 'B — superdiagnostic / 500 Hz': 'B\\n(super/500Hz)',\n", - " 'C — diagnostic (27-class) / 100 Hz': 'C\\n(diag/100Hz)',\n", - " 'D — diagnostic (27-class) / 500 Hz': 'D\\n(diag/500Hz)',\n", + " 'A -- superdiagnostic / 100 Hz (baseline)': 'A\\n(super/100Hz)',\n", + " 'B -- superdiagnostic / 500 Hz': 'B\\n(super/500Hz)',\n", + " 'C -- diagnostic (27-class) / 100 Hz': 'C\\n(diag/100Hz)',\n", + " 'D -- diagnostic (27-class) / 500 Hz': 'D\\n(diag/500Hz)',\n", "}\n", "configs = [c['name'] for c in ABLATION_CONFIGS]\n", "short_labels = [_label_map[c] for c in configs]\n", diff --git a/pyhealth/datasets/ptbxl.py b/pyhealth/datasets/ptbxl.py index 32f41abd0..028e454b1 100644 --- a/pyhealth/datasets/ptbxl.py +++ b/pyhealth/datasets/ptbxl.py @@ -211,7 +211,13 @@ def load_data(self) -> dd.DataFrame: FileNotFoundError: If ptbxl_database.csv is not found in root """ root_path = Path(self.root) - files = sorted(root_path.glob("*.hea")) + # Collect .hea files: first try flat layout (files directly in root), + # then fall back to g1/…g22/ sub-directory layout. + files = sorted(root_path.glob("*.hea")) + if not files: + for subdir in sorted(root_path.iterdir()): + if subdir.is_dir() and subdir.name.startswith('g'): + files.extend(sorted(subdir.glob("*.hea"))) # Check existence of required .hea files if not files: @@ -234,21 +240,25 @@ def load_data(self) -> dd.DataFrame: line = line.strip() # Parse individual lines - if line.startswith("#Age:"): + if line.startswith("#Age:") or line.startswith("# Age:"): try: age = int(line.split(":")[1].strip()) except ValueError: age = None - elif line.startswith("#Sex:"): + elif line.startswith("#Sex:") or line.startswith("# Sex:"): sex = line.split(":")[1].strip() - elif line.startswith("#Dx:"): + elif line.startswith("#Dx:") or line.startswith("# Dx:"): dx = [x.strip() for x in line.split(":")[1].split(",")] # Map diagnosis codes to the abbreviations (may need them for tasks) dx_abbreviations = [SNOMED_CT_ABBREVIATION[x] for x in dx if x in SNOMED_CT_ABBREVIATION] # Train / test / validation splits using the strat_fold column in ptbxl_database.csv - strat_fold = db.loc[int(hea_file.stem.replace("HR","")), "strat_fold"] + ecg_id = int(hea_file.stem.replace("HR","")) + if ecg_id not in db.index: + logger.debug(f"Skipping {hea_file.name}: ecg_id {ecg_id} not in ptbxl_database.csv") + continue + strat_fold = db.loc[ecg_id, "strat_fold"] if strat_fold <= 8: split = "train" elif strat_fold == 9: @@ -261,7 +271,7 @@ def load_data(self) -> dd.DataFrame: "patient_id": hea_file.stem, "event_type": "ptbxl", "timestamp": pd.NaT, - "ptbxl/mat": str(root_path / f"{hea_file.stem}.mat"), + "ptbxl/mat": str(hea_file.with_suffix(".mat")), "ptbxl/age": age, "ptbxl/sex": sex, "ptbxl/dx_codes": ",".join(dx), From 3ff83a89f845c76c50c98f6e6937f9cc84d15c86 Mon Sep 17 00:00:00 2001 From: AnuragD2 Date: Tue, 21 Apr 2026 23:51:14 -0700 Subject: [PATCH 35/39] =?UTF-8?q?feat:=20update=20hyperparams=20=E2=80=94?= =?UTF-8?q?=20LR=3D1e-3,=20EPOCHS=3D35,=20MAX=5FPATIENTS=3D7500?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ptbxl_superdiagnostic_se_resnet.ipynb | 22277 +++++++++++----- 1 file changed, 16249 insertions(+), 6028 deletions(-) diff --git a/examples/ptbxl_superdiagnostic_se_resnet.ipynb b/examples/ptbxl_superdiagnostic_se_resnet.ipynb index 307005e1f..3623acda9 100644 --- a/examples/ptbxl_superdiagnostic_se_resnet.ipynb +++ b/examples/ptbxl_superdiagnostic_se_resnet.ipynb @@ -87,7 +87,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 1, "id": "b1dc5e9d", "metadata": {}, "outputs": [ @@ -125,7 +125,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 2, "id": "d88687c4", "metadata": {}, "outputs": [ @@ -172,7 +172,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 3, "id": "5ce43a37", "metadata": {}, "outputs": [ @@ -224,7 +224,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 4, "id": "0d594aad", "metadata": {}, "outputs": [], @@ -257,7 +257,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 5, "id": "45a40dcb", "metadata": {}, "outputs": [ @@ -265,8 +265,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "Batch size: 64 | LR: 0.01 | Epochs: 20\n", - "Dev mode: False | Max patients: 2100 | Run phase: ALL\n", + "Batch size: 64 | LR: 0.001 | Epochs: 35\n", + "Dev mode: False | Max patients: 7500 | Run phase: ALL\n", "Models: ['ResNet-18', 'SE-ResNet-50', 'Lambda-ResNet-18', 'BiLSTM']\n" ] } @@ -274,14 +274,14 @@ "source": [ "# Training hyper-parameters (fixed across all model x config combinations)\n", "BATCH_SIZE = 64\n", - "LEARNING_RATE = 0.01\n", - "EPOCHS = 20\n", + "LEARNING_RATE = 1e-3 # lowered from 0.01 — avoids early overshooting for CNNs\n", + "EPOCHS = 35 # increased from 20 — ResNet needs more epochs to converge\n", "SPLIT = [0.7, 0.1, 0.2]\n", "MONITOR = 'roc_auc_macro'\n", "\n", "DEV_MODE = False\n", "RUN_PHASE = 'ALL'\n", - "MAX_PATIENTS = 2100\n", + "MAX_PATIENTS = 7500 # increased from 2100 — better rare-class coverage (~4.3 hrs)\n", "QUICK_MODE = False\n", "\n", "MODELS = [\n", @@ -333,7 +333,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 6, "id": "19663067", "metadata": {}, "outputs": [ @@ -346,16 +346,15 @@ "Found cached event dataframe: /Users/anuragd/Library/Caches/pyhealth/e06c1785-82da-5d10-8959-fb69e8e75b53/global_event_df.parquet\n", "Found 21799 unique patient IDs\n", "Full-dataset patients: 21799\n", - "Capping to 2100 patients.\n", - "Building 2100-patient parquet cache...\n", - "Done.\n", + "Capping to 7500 patients.\n", + "Custom cache already exists: /Users/anuragd/Library/Caches/pyhealth/ptbxl_cap7500/e06c1785-82da-5d10-8959-fb69e8e75b53/global_event_df.parquet\n", "Initializing ptbxl dataset from /Users/anuragd/CS-598_HealthCareAssignment/DLH_Project/WFDB (dev mode: False)\n", - "Using provided cache_dir: /Users/anuragd/Library/Caches/pyhealth/ptbxl_cap2100/e06c1785-82da-5d10-8959-fb69e8e75b53\n", - "Found cached event dataframe: /Users/anuragd/Library/Caches/pyhealth/ptbxl_cap2100/e06c1785-82da-5d10-8959-fb69e8e75b53/global_event_df.parquet\n", + "Using provided cache_dir: /Users/anuragd/Library/Caches/pyhealth/ptbxl_cap7500/e06c1785-82da-5d10-8959-fb69e8e75b53\n", + "Found cached event dataframe: /Users/anuragd/Library/Caches/pyhealth/ptbxl_cap7500/e06c1785-82da-5d10-8959-fb69e8e75b53/global_event_df.parquet\n", "Dataset: ptbxl\n", "Dev mode: False\n", - "Number of patients: 2100\n", - "Number of events: 2100\n" + "Number of patients: 7500\n", + "Number of events: 7500\n" ] } ], @@ -404,7 +403,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 7, "id": "ae5daef7", "metadata": {}, "outputs": [ @@ -484,7 +483,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 8, "id": "f1c34a69", "metadata": {}, "outputs": [ @@ -500,94 +499,13 @@ " K=5 classes, T=1000 time-steps per lead\n", "======================================================================\n", "Setting task PTBXLSuperDiagnostic_100Hz for ptbxl base dataset...\n", - "Task cache paths: task_df=/Users/anuragd/Library/Caches/pyhealth/ptbxl_cap2100/e06c1785-82da-5d10-8959-fb69e8e75b53/tasks/PTBXLSuperDiagnostic_100Hz_a435b25e-18d8-5d3b-b000-8457f09e6895/task_df.ld, samples=/Users/anuragd/Library/Caches/pyhealth/ptbxl_cap2100/e06c1785-82da-5d10-8959-fb69e8e75b53/tasks/PTBXLSuperDiagnostic_100Hz_a435b25e-18d8-5d3b-b000-8457f09e6895/samples_cdbbc602-34e2-5a41-8643-4c76b08829f6.ld\n", - "Applying task transformations on data with 1 workers...\n", - "Detected Jupyter notebook environment, setting num_workers to 1\n", - "Single worker mode, processing sequentially\n", - "Worker 0 started processing 2100 patients. (Polars threads: 10)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - " 0%| | 0/2100 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", + "Optimizer params: {'lr': 0.001}\n", "Weight decay: 0.0001\n", "Max grad norm: None\n", - "Val dataloader: \n", + "Val dataloader: \n", "Monitor: roc_auc_macro\n", "Monitor criterion: max\n", - "Epochs: 20\n", + "Epochs: 35\n", "Patience: None\n", "\n" ] }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/anuragd/CS-598_HealthCareAssignment/DLH598_repo/PyHealth/pyhealth/datasets/base_dataset.py:1077: UserWarning: A newer version of litdata is available (0.2.61). Please consider upgrading with `pip install -U litdata`. Not all functionalities of the platform can be guaranteed to work with the current version.\n", + " return SampleDataset(\n" + ] + }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "440fd14663964608a2c8c5e820cb2ffb", + "model_id": "91c91b004cd44ba69a957f5a9bc8654e", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Epoch 0 / 20: 0%| | 0/22 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", - "Weight decay: 0.0001\n", - "Max grad norm: None\n", - "Val dataloader: \n", - "Monitor: roc_auc_macro\n", - "Monitor criterion: max\n", - "Epochs: 20\n", - "Patience: None\n", "\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "94f804cf3abc4675869e70555fc4edd6", + "model_id": "ea6e2a2bcce2451aad3bd847c83c3c50", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.001}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 35\n", + "Patience: None\n", + "\n" + ] + }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "4cc47abeec3746269c28dcbf9769ae3b", + "model_id": "c05c04f3af2942948e936529651cc1ad", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Epoch 12 / 20: 0%| | 0/22 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", - "Weight decay: 0.0001\n", - "Max grad norm: None\n", - "Val dataloader: \n", - "Monitor: roc_auc_macro\n", - "Monitor criterion: max\n", - "Epochs: 20\n", - "Patience: None\n", + "--- Train epoch-12, step-1053 ---\n", + "loss: 0.3045\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Evaluation: 100%|██████████| 12/12 [00:00<00:00, 17.84it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Eval epoch-12, step-1053 ---\n", + "roc_auc_macro: 0.8984\n", + "f1_macro: 0.7247\n", + "loss: 0.3165\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ "\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "1482792f487742daaa53c39f7ac27411", + "model_id": "f140bfb09a724c4eb9619adc76b45a16", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Epoch 0 / 20: 0%| | 0/22 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", + "Optimizer params: {'lr': 0.001}\n", "Weight decay: 0.0001\n", "Max grad norm: None\n", - "Val dataloader: \n", + "Val dataloader: \n", "Monitor: roc_auc_macro\n", "Monitor criterion: max\n", - "Epochs: 20\n", + "Epochs: 35\n", "Patience: None\n", "\n" ] @@ -4360,12 +4259,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "ac15e47ec55b48aeb7acf878c444000f", + "model_id": "2a2bfea6840f446fa0ab557edf3c886d", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.001}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 35\n", + "Patience: None\n", + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "08e30779960947469ef34b96141a4c86", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 35: 0%| | 0/80 [00:00\n", + "Optimizer params: {'lr': 0.001}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 35\n", + "Patience: None\n", + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "98353d05c8b644bcaac34038b10314e2", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 35: 0%| | 0/81 [00:00\n", + "Optimizer params: {'lr': 0.001}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 35\n", + "Patience: None\n", + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4d8610a55c4848e097c2ab41bd4771e7", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 35: 0%| | 0/81 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", + "Optimizer params: {'lr': 0.001}\n", "Weight decay: 0.0001\n", "Max grad norm: None\n", - "Val dataloader: \n", + "Val dataloader: \n", "Monitor: roc_auc_macro\n", "Monitor criterion: max\n", - "Epochs: 20\n", + "Epochs: 35\n", "Patience: None\n", "\n" ] @@ -5744,12 +12141,681 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "060cbccc589b48c9940e3470b8112dee", + "model_id": "b875fe5abb104f789dfc7c95be1716f6", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 35: 0%| | 0/80 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", + "Optimizer params: {'lr': 0.001}\n", "Weight decay: 0.0001\n", "Max grad norm: None\n", - "Val dataloader: \n", + "Val dataloader: \n", "Monitor: roc_auc_macro\n", "Monitor criterion: max\n", - "Epochs: 20\n", + "Epochs: 35\n", "Patience: None\n", "\n" ] @@ -7060,12 +14173,588 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "56a00d65a7344edbbe614c3bfefa0c8d", + "model_id": "9731e7d9cb2d4558ac15a6b2e36b9321", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 35: 0%| | 0/80 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", + "Optimizer params: {'lr': 0.001}\n", "Weight decay: 0.0001\n", "Max grad norm: None\n", - "Val dataloader: \n", + "Val dataloader: \n", "Monitor: roc_auc_macro\n", "Monitor criterion: max\n", - "Epochs: 20\n", + "Epochs: 35\n", "Patience: None\n", "\n" ] @@ -8377,12 +16017,541 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "18f9bdd6156046d7be393948f969336f", + "model_id": "70f5cac4cb0d47b8a959945203fbaa5a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 35: 0%| | 0/81 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", + "Optimizer params: {'lr': 0.001}\n", "Weight decay: 0.0001\n", "Max grad norm: None\n", - "Val dataloader: \n", + "Val dataloader: \n", "Monitor: roc_auc_macro\n", "Monitor criterion: max\n", - "Epochs: 20\n", + "Epochs: 35\n", "Patience: None\n", "\n" ] }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "fd3c461f9a534d818ca17881569d0a77", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 35: 0%| | 0/81 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", + "Optimizer params: {'lr': 0.001}\n", "Weight decay: 0.0001\n", "Max grad norm: None\n", - "Val dataloader: \n", + "Val dataloader: \n", "Monitor: roc_auc_macro\n", "Monitor criterion: max\n", - "Epochs: 20\n", + "Epochs: 35\n", "Patience: None\n", "\n" ] @@ -11143,22 +19873,591 @@ "name": "stderr", "output_type": "stream", "text": [ - "\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", - " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n" + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a77713eabfc74992938d7cfa5801ead8", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 35: 0%| | 0/80 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", + "Optimizer params: {'lr': 0.001}\n", "Weight decay: 0.0001\n", "Max grad norm: None\n", - "Val dataloader: \n", + "Val dataloader: \n", "Monitor: roc_auc_macro\n", "Monitor criterion: max\n", - "Epochs: 20\n", + "Epochs: 35\n", "Patience: None\n", "\n" ] }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2c07844b1cf94065afab8dda9ea4ff07", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Epoch 0 / 35: 0%| | 0/80 [00:00\n", + "Optimizer params: {'lr': 0.001}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 35\n", + "Patience: None\n", "\n" ] }, @@ -13208,12 +23514,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "480c5f55aeb84f08ac0c6e82277f5880", + "model_id": "b0a56472bec24042b469f781835bcd4d", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Epoch 17 / 20: 0%| | 0/22 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", - "Weight decay: 0.0001\n", - "Max grad norm: None\n", - "Val dataloader: \n", - "Monitor: roc_auc_macro\n", - "Monitor criterion: max\n", - "Epochs: 20\n", - "Patience: None\n", "\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "db05ddd056ca446cbceeae9b3ef51737", + "model_id": "4bb8d16a81554353885a602553baed0c", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Epoch 0 / 20: 0%| | 0/22 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", + "Optimizer params: {'lr': 0.001}\n", "Weight decay: 0.0001\n", "Max grad norm: None\n", - "Val dataloader: \n", + "Val dataloader: \n", "Monitor: roc_auc_macro\n", "Monitor criterion: max\n", - "Epochs: 20\n", + "Epochs: 35\n", "Patience: None\n", "\n" ] }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "66baaec8d0b348a1ae3913d1b20ca711", + "model_id": "32ad74d053ba4cbd8017177e4b6b3b7b", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Epoch 0 / 20: 0%| | 0/22 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", - "Weight decay: 0.0001\n", - "Max grad norm: None\n", - "Val dataloader: \n", - "Monitor: roc_auc_macro\n", - "Monitor criterion: max\n", - "Epochs: 20\n", - "Patience: None\n", + "--- Eval epoch-24, step-2025 ---\n", + "roc_auc_macro: 0.8163\n", + "f1_macro: 0.6541\n", + "loss: 0.4046\n", + "New best roc_auc_macro score (0.8163) at epoch-24, step-2025\n", "\n" ] }, @@ -16205,22 +26429,18 @@ "name": "stderr", "output_type": "stream", "text": [ - "\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", - " warnings.warn(\n", - "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", - " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n" + "\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "f4f0ecdd39d943d7bb28e8edf39bd1e6", + "model_id": "134f9d307e1d449b9831cdbc5b9e96b9", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.001}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 35\n", + "Patience: None\n", "\n" ] }, @@ -16739,12 +26972,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "db06646482e641e28fb3f44c3f3702af", + "model_id": "3dd8e773ca764945b9c2d3e65b98ec7a", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Epoch 11 / 20: 0%| | 0/22 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", - "Weight decay: 0.0001\n", - "Max grad norm: None\n", - "Val dataloader: \n", - "Monitor: roc_auc_macro\n", - "Monitor criterion: max\n", - "Epochs: 20\n", - "Patience: None\n", - "\n" + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "071d499d11b94775aaf8a3d84993945a", + "model_id": "3162a73253394959980eceefec46cca3", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Epoch 0 / 20: 0%| | 0/22 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", - "Weight decay: 0.0001\n", - "Max grad norm: None\n", - "Val dataloader: \n", - "Monitor: roc_auc_macro\n", - "Monitor criterion: max\n", - "Epochs: 20\n", - "Patience: None\n", - "\n" + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_ranking.py:379: UndefinedMetricWarning: Only one class is present in y_true. ROC AUC score is not defined in that case.\n", + " warnings.warn(\n", + "/Users/anuragd/Library/Python/3.9/lib/python/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "df836fc2560f4e9f90d0fd811a451d45", + "model_id": "90bb92cca6eb4a339e752a508763312f", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Epoch 0 / 20: 0%| | 0/22 [00:00\n", + "Optimizer params: {'lr': 0.001}\n", + "Weight decay: 0.0001\n", + "Max grad norm: None\n", + "Val dataloader: \n", + "Monitor: roc_auc_macro\n", + "Monitor criterion: max\n", + "Epochs: 35\n", + "Patience: None\n", + "\n" + ] + }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "7d9489e0b6c64dd58ab2ff3b7129147d", + "model_id": "5394fdc3fd854915a771dd5f9eefccbb", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Epoch 6 / 20: 0%| | 0/22 [00:00\n", - "Optimizer params: {'lr': 0.01}\n", - "Weight decay: 0.0001\n", - "Max grad norm: None\n", - "Val dataloader: \n", - "Monitor: roc_auc_macro\n", - "Monitor criterion: max\n", - "Epochs: 20\n", - "Patience: None\n", + "--- Eval epoch-14, step-1200 ---\n", + "roc_auc_macro: 0.7635\n", + "f1_macro: 0.1480\n", + "loss: 0.1302\n", + "New best roc_auc_macro score (0.7635) at epoch-14, step-1200\n", "\n" ] }, @@ -19252,12 +29612,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "534a0b5918fc4f269a15ca5e239da5d4", + "model_id": "a651e8e084db416aa2601045b1ceaa47", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Epoch 0 / 20: 0%| | 0/22 [00:00 examples/ptbxl_results_phase_ALL.csv\n", " model config roc_auc_macro f1_macro train_time_s\n", - " ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 0.762939 0.498161 33.149601\n", - " ResNet-18 B -- superdiagnostic / 500 Hz 0.804179 0.529553 114.700226\n", - " ResNet-18 C -- diagnostic (27-class) / 100 Hz 0.743760 0.082997 33.515967\n", - " ResNet-18 D -- diagnostic (27-class) / 500 Hz NaN 0.056788 112.712338\n", - " SE-ResNet-50 A -- superdiagnostic / 100 Hz (baseline) 0.802244 0.378764 149.364521\n", - " SE-ResNet-50 B -- superdiagnostic / 500 Hz 0.755365 0.367256 636.152798\n", - " SE-ResNet-50 C -- diagnostic (27-class) / 100 Hz 0.711834 0.092479 144.197475\n", - " SE-ResNet-50 D -- diagnostic (27-class) / 500 Hz NaN 0.057329 634.504002\n", - "Lambda-ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 0.795544 0.397067 270.942773\n", - "Lambda-ResNet-18 B -- superdiagnostic / 500 Hz 0.772388 0.521192 1253.451983\n", - "Lambda-ResNet-18 C -- diagnostic (27-class) / 100 Hz NaN 0.050867 258.623402\n", - "Lambda-ResNet-18 D -- diagnostic (27-class) / 500 Hz NaN 0.081759 1244.658968\n", - " BiLSTM A -- superdiagnostic / 100 Hz (baseline) 0.813596 0.645111 77.515355\n", - " BiLSTM B -- superdiagnostic / 500 Hz 0.812867 0.595405 334.533214\n", - " BiLSTM C -- diagnostic (27-class) / 100 Hz 0.783317 0.144312 78.607386\n", - " BiLSTM D -- diagnostic (27-class) / 500 Hz 0.746432 0.155699 333.266138\n" + " ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 0.846381 0.686147 134.944618\n", + " ResNet-18 B -- superdiagnostic / 500 Hz 0.879256 0.698460 575.779286\n", + " ResNet-18 C -- diagnostic (27-class) / 100 Hz 0.848717 0.382102 1213.200153\n", + " ResNet-18 D -- diagnostic (27-class) / 500 Hz 0.892381 0.383594 835.039841\n", + " SE-ResNet-50 A -- superdiagnostic / 100 Hz (baseline) 0.863307 0.684056 10027.422704\n", + " SE-ResNet-50 B -- superdiagnostic / 500 Hz 0.884526 0.721236 14136.546827\n", + " SE-ResNet-50 C -- diagnostic (27-class) / 100 Hz 0.845416 0.325626 778.648032\n", + " SE-ResNet-50 D -- diagnostic (27-class) / 500 Hz 0.837064 0.274920 4718.397009\n", + "Lambda-ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 0.796305 0.616217 1035.476037\n", + "Lambda-ResNet-18 B -- superdiagnostic / 500 Hz 0.786086 0.615013 5308.669516\n", + "Lambda-ResNet-18 C -- diagnostic (27-class) / 100 Hz 0.795265 0.262887 1034.279625\n", + "Lambda-ResNet-18 D -- diagnostic (27-class) / 500 Hz 0.783410 0.199866 5246.245391\n", + " BiLSTM A -- superdiagnostic / 100 Hz (baseline) 0.818348 0.654491 281.082750\n", + " BiLSTM B -- superdiagnostic / 500 Hz 0.839531 0.672561 1426.656572\n", + " BiLSTM C -- diagnostic (27-class) / 100 Hz 0.814146 0.196637 277.374086\n", + " BiLSTM D -- diagnostic (27-class) / 500 Hz 0.799358 0.195175 1379.068789\n" ] } ], @@ -20469,7 +30690,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 10, "id": "stash001", "metadata": {}, "outputs": [ @@ -20479,22 +30700,22 @@ "text": [ "Stashed 16 results → examples/ptbxl_results_phase_ALL.csv\n", " model config roc_auc_macro f1_macro train_time_s\n", - " ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 0.762939 0.498161 33.149601\n", - " ResNet-18 B -- superdiagnostic / 500 Hz 0.804179 0.529553 114.700226\n", - " ResNet-18 C -- diagnostic (27-class) / 100 Hz 0.743760 0.082997 33.515967\n", - " ResNet-18 D -- diagnostic (27-class) / 500 Hz NaN 0.056788 112.712338\n", - " SE-ResNet-50 A -- superdiagnostic / 100 Hz (baseline) 0.802244 0.378764 149.364521\n", - " SE-ResNet-50 B -- superdiagnostic / 500 Hz 0.755365 0.367256 636.152798\n", - " SE-ResNet-50 C -- diagnostic (27-class) / 100 Hz 0.711834 0.092479 144.197475\n", - " SE-ResNet-50 D -- diagnostic (27-class) / 500 Hz NaN 0.057329 634.504002\n", - "Lambda-ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 0.795544 0.397067 270.942773\n", - "Lambda-ResNet-18 B -- superdiagnostic / 500 Hz 0.772388 0.521192 1253.451983\n", - "Lambda-ResNet-18 C -- diagnostic (27-class) / 100 Hz NaN 0.050867 258.623402\n", - "Lambda-ResNet-18 D -- diagnostic (27-class) / 500 Hz NaN 0.081759 1244.658968\n", - " BiLSTM A -- superdiagnostic / 100 Hz (baseline) 0.813596 0.645111 77.515355\n", - " BiLSTM B -- superdiagnostic / 500 Hz 0.812867 0.595405 334.533214\n", - " BiLSTM C -- diagnostic (27-class) / 100 Hz 0.783317 0.144312 78.607386\n", - " BiLSTM D -- diagnostic (27-class) / 500 Hz 0.746432 0.155699 333.266138\n" + " ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 0.846381 0.686147 134.944618\n", + " ResNet-18 B -- superdiagnostic / 500 Hz 0.879256 0.698460 575.779286\n", + " ResNet-18 C -- diagnostic (27-class) / 100 Hz 0.848717 0.382102 1213.200153\n", + " ResNet-18 D -- diagnostic (27-class) / 500 Hz 0.892381 0.383594 835.039841\n", + " SE-ResNet-50 A -- superdiagnostic / 100 Hz (baseline) 0.863307 0.684056 10027.422704\n", + " SE-ResNet-50 B -- superdiagnostic / 500 Hz 0.884526 0.721236 14136.546827\n", + " SE-ResNet-50 C -- diagnostic (27-class) / 100 Hz 0.845416 0.325626 778.648032\n", + " SE-ResNet-50 D -- diagnostic (27-class) / 500 Hz 0.837064 0.274920 4718.397009\n", + "Lambda-ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 0.796305 0.616217 1035.476037\n", + "Lambda-ResNet-18 B -- superdiagnostic / 500 Hz 0.786086 0.615013 5308.669516\n", + "Lambda-ResNet-18 C -- diagnostic (27-class) / 100 Hz 0.795265 0.262887 1034.279625\n", + "Lambda-ResNet-18 D -- diagnostic (27-class) / 500 Hz 0.783410 0.199866 5246.245391\n", + " BiLSTM A -- superdiagnostic / 100 Hz (baseline) 0.818348 0.654491 281.082750\n", + " BiLSTM B -- superdiagnostic / 500 Hz 0.839531 0.672561 1426.656572\n", + " BiLSTM C -- diagnostic (27-class) / 100 Hz 0.814146 0.196637 277.374086\n", + " BiLSTM D -- diagnostic (27-class) / 500 Hz 0.799358 0.195175 1379.068789\n" ] } ], @@ -20525,7 +30746,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 11, "id": "1eb2e622", "metadata": {}, "outputs": [ @@ -20544,22 +30765,22 @@ "Lambda-ResNet-18 C -- diagnostic (27-class) / 100 Hz 27 1000 0.719476 0.126444 1827.354280\n", " BiLSTM A -- superdiagnostic / 100 Hz (baseline) 5 1000 0.838571 0.672886 515.175012\n", " BiLSTM C -- diagnostic (27-class) / 100 Hz 27 1000 0.820281 0.227032 621.552368\n", - " ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 5 1000 0.762939 0.498161 33.149601\n", - " ResNet-18 B -- superdiagnostic / 500 Hz 5 5000 0.804179 0.529553 114.700226\n", - " ResNet-18 C -- diagnostic (27-class) / 100 Hz 27 1000 0.743760 0.082997 33.515967\n", - " ResNet-18 D -- diagnostic (27-class) / 500 Hz 27 5000 NaN 0.056788 112.712338\n", - " SE-ResNet-50 A -- superdiagnostic / 100 Hz (baseline) 5 1000 0.802244 0.378764 149.364521\n", - " SE-ResNet-50 B -- superdiagnostic / 500 Hz 5 5000 0.755365 0.367256 636.152798\n", - " SE-ResNet-50 C -- diagnostic (27-class) / 100 Hz 27 1000 0.711834 0.092479 144.197475\n", - " SE-ResNet-50 D -- diagnostic (27-class) / 500 Hz 27 5000 NaN 0.057329 634.504002\n", - "Lambda-ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 5 1000 0.795544 0.397067 270.942773\n", - "Lambda-ResNet-18 B -- superdiagnostic / 500 Hz 5 5000 0.772388 0.521192 1253.451983\n", - "Lambda-ResNet-18 C -- diagnostic (27-class) / 100 Hz 27 1000 NaN 0.050867 258.623402\n", - "Lambda-ResNet-18 D -- diagnostic (27-class) / 500 Hz 27 5000 NaN 0.081759 1244.658968\n", - " BiLSTM A -- superdiagnostic / 100 Hz (baseline) 5 1000 0.813596 0.645111 77.515355\n", - " BiLSTM B -- superdiagnostic / 500 Hz 5 5000 0.812867 0.595405 334.533214\n", - " BiLSTM C -- diagnostic (27-class) / 100 Hz 27 1000 0.783317 0.144312 78.607386\n", - " BiLSTM D -- diagnostic (27-class) / 500 Hz 27 5000 0.746432 0.155699 333.266138\n", + " ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 5 1000 0.846381 0.686147 134.944618\n", + " ResNet-18 B -- superdiagnostic / 500 Hz 5 5000 0.879256 0.698460 575.779286\n", + " ResNet-18 C -- diagnostic (27-class) / 100 Hz 27 1000 0.848717 0.382102 1213.200153\n", + " ResNet-18 D -- diagnostic (27-class) / 500 Hz 27 5000 0.892381 0.383594 835.039841\n", + " SE-ResNet-50 A -- superdiagnostic / 100 Hz (baseline) 5 1000 0.863307 0.684056 10027.422704\n", + " SE-ResNet-50 B -- superdiagnostic / 500 Hz 5 5000 0.884526 0.721236 14136.546827\n", + " SE-ResNet-50 C -- diagnostic (27-class) / 100 Hz 27 1000 0.845416 0.325626 778.648032\n", + " SE-ResNet-50 D -- diagnostic (27-class) / 500 Hz 27 5000 0.837064 0.274920 4718.397009\n", + "Lambda-ResNet-18 A -- superdiagnostic / 100 Hz (baseline) 5 1000 0.796305 0.616217 1035.476037\n", + "Lambda-ResNet-18 B -- superdiagnostic / 500 Hz 5 5000 0.786086 0.615013 5308.669516\n", + "Lambda-ResNet-18 C -- diagnostic (27-class) / 100 Hz 27 1000 0.795265 0.262887 1034.279625\n", + "Lambda-ResNet-18 D -- diagnostic (27-class) / 500 Hz 27 5000 0.783410 0.199866 5246.245391\n", + " BiLSTM A -- superdiagnostic / 100 Hz (baseline) 5 1000 0.818348 0.654491 281.082750\n", + " BiLSTM B -- superdiagnostic / 500 Hz 5 5000 0.839531 0.672561 1426.656572\n", + " BiLSTM C -- diagnostic (27-class) / 100 Hz 27 1000 0.814146 0.196637 277.374086\n", + " BiLSTM D -- diagnostic (27-class) / 500 Hz 27 5000 0.799358 0.195175 1379.068789\n", " ResNet-18 B -- superdiagnostic / 500 Hz 5 5000 0.791043 0.528033 80.286776\n", " ResNet-18 D -- diagnostic (27-class) / 500 Hz 27 5000 NaN 0.044667 74.486725\n", " BiLSTM B -- superdiagnostic / 500 Hz 5 5000 0.793496 0.631577 262.473322\n", @@ -20606,13 +30827,13 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 12, "id": "88814df1", "metadata": {}, "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABv4AAAIDCAYAAADMsGn8AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8ekN5oAAAACXBIWXMAAA9hAAAPYQGoP6dpAADGu0lEQVR4nOzdCbxM9f/H8Y99X8qSfSuRXbY2opSQrYWSdippQ4mytEl+IknahFSKZClLlOWXSsgSClGWJGtlyc78H+/v73HmPzN37upeM3fu6/l4HO6cOXPm7HPO9/P9fr6ZfD6fzwAAAAAAAAAAAACka5kjvQAAAAAAAAAAAAAAzhyBPwAAAAAAAAAAACAGEPgDAAAAAAAAAAAAYgCBPwAAAAAAAAAAACAGEPgDAAAAAAAAAAAAYgCBPwAAAAAAAAAAACAGEPgDAAAAAAAAAAAAYgCBPwAAAAAAAAAAACAGEPgDAAAAAAAAAAAAYgCBPwAAAABponHjxlatWrUUf37hwoWWKVMm9z9wpnQsPfPMM5FejJh31113Wbly5VJ8zdAQi3Ts6Rjcu3dvmn7Pli1b3Pe8/PLLSV6m1MR1GwAAIPII/AEAEEPGjRvnClu8IWvWrFayZElXCPfHH3+k2fd6BUfnnXeeHT58OM77KgC8/vrrUzTvUaNGufVKqkOHDtmAAQPsuuuus3PPPdctV0KfnzRpkl1yySVWsGBBK1SokF155ZU2c+ZMiwZ79uyxRx991CpXrmy5cuWyokWLWv369e3JJ5906+nR/g3c74FDzpw5E/0ejpv/L6gMN3z//fdxpv/uu+/siiuusNy5c1uxYsXskUceCdonkeKtx+TJkyO9KFEr9HzJkSOHXXjhhda/f387evRomn2vghn6vlatWp1RQX0onTs6l5JTyL5hwwbr3r27XXbZZe4aoe/WMoSjbTJo0CCrUqWKO951bbj55pvtp59+skgLvXZp0HWySZMmNnv27CR//ocffjija3FC14/QIXS5v/nmmzjf5/P5rHTp0u79lF4DxfuOzp07h33/6aef9k+T1sGoaBTu+Ak3pDSImVb0+6blatCgQcSXIzm/s7FEx0Todadhw4Y2derUFB1b3v2QN2TOnNmKFy/uzv9w9yChRo8e7T733nvvxXlv8eLFbn6PP/54qlXKAQAA0S9rpBcAAACkvueee87Kly/vCmxVYKACCBUurl27NkmBoJTavXu3vfHGG9azZ89ULVgqXLiwK6xPChVeav3LlCljNWvWTLAw/LXXXnMBm5YtW9pLL73ktpe2lQpaPv30U7vhhhssUv766y+rW7euHThwwO655x5X4Lxv3z5bvXq128Zdu3a1vHnz+qdX8EIFP6GyZMmS5O/MyMeNR8dDvXr1gsZdcMEFQa9XrVplV199tV100UU2bNgw2759uwvYbNy4MUnBBkRe4Pmyf/9+mz59uj3//PP266+/2ocffpim3z1jxgxbvny51alTJ1Xmp8Dfs88+6/5OakspFQSPGDHCBfN0HOuYjs9tt91mn332mXXp0sUuvvhi27Fjh73++ut26aWX2po1a6xs2bIWad61SwGzXbt2uWtXixYt7PPPPw8KnB05csRVbEjta7G24fvvvx/0uT59+rhrtIJr8dF1dcKECa4SQaD//ve/7rqi4/RM6Tv0e6ZrYvbs2YPe++ijj9z7aRnwjmaNGjWKs98UJFVQ97777vOPC/ytjQa6RilgtHTpUtu0aVOc36izJb7fWW1XnWuhx1usqVWrlv++RdfFt956y9036rpw7bXXpujY0mc17vTp0/b777/bO++847an9rW+Lz733nuvC/opuKdrniqyyYkTJ9z3qSKB9zsBAAAyBgJ/AADEoObNm7uCSq+gQQUzgwcPdoW37du3T7PvVaHEkCFD7MEHH3StIiJBNaT//PNP1wpLrThCgzihgT+9r8JhryWGCnbVokUFKJEM/L377ru2bds2+/bbb12rnEAqgA4tUFNhdqdOnc7oOzPyceNRjf2bbropwWmeeuopO+ecc1xQOX/+/G6cCmEVGJk7d64r8EN0Cz1fdOzpPFMgRMFctUJNC6qQcPDgQVcAq/MqUlq3bm3//POP5cuXzwWt4wv8qcXvlClTXGGyztHA8+Sqq65y76nlYKQFXru8QnDtQ+3PwMBfSiowJOVarPmGXn9VmUTX0ISuywpOfvLJJy4IGxiQVDBQgeHUaIWn1u861lQpoU2bNkGtljdv3mw33nijCwymR2rZ3LRpU9diP9TOnTvdPUBCLSYrVKjghkAPPPCAG3emv6dpRftM+07n3v333++CgMpyEE3UuiwtKwtFC90rBh4nd9xxhwvCvvLKK/7jKLnHlu4/dN3wtG3b1rXM03UiocCf7mEVeNQ0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zpy+FStW+Kf75JNP3Hd9/fXXceZx8803+4oVK5bg97z11lvu82vWrAkaX6VKFd9VV12VrO0XzoIFC9z8tZwJOXz4cNDr48eP+6pVqxa0DKJ56RjYvHlznHXQuh44cMA/vk+fPm584LTaBxo3dOhQ/7hjx475atWq5StatKj7Xhk+fLibbtKkSf7p/v33X98FF1zgxmu94lt20f7KlCmTb+vWrYluI50vefLkccedhk2bNvlefvll93ltAx3PcvDgQV/BggV9Xbp0iXNuFihQwD/+77//dss4ZMiQBL9X28K7Fjz77LPuM8uXL3evtc1C5/H888+75fzll1+C5tO7d29flixZfNu2bXOvtQ76rM6nlNB3hu63QEuWLPGdf/75QdeVOnXq+P78889E561jIlu2bL6//voraP9ru95zzz3+cdqe3bp1S/aye9eu0EHH7Lhx4+JMH7qdvM/rtyI1rsWBEroGBX7vyJEjffny5fMf17qONGnSJOw1MPB6H9/+Cl1fbVdt/+zZs/vef/99N37mzJnueN+yZUvQdVh0Turc1Llw5MgR/7xmzJjhpuvfv79/nM7j4sWLB22LuXPnuum07Cm5dmubJefa/dlnn/myZs3q69Chg/tdat68ubtuL1y40JcSOue0jT2a56lTp4Km0bbXMfbcc88l67c+cFuvW7fOV6JECV+9evWCzo+E/PDDD+7zX375pXuta1WpUqV8jz76aJzl03S5cuXybd++Pehc1vju3bvHWaZA4a6xupcIvLdJ6Bj3foe863Zyjinv+A7ctlK7dm133UnI7t273XF+7bXXBu0znWOa55gxY+L8No0fPz7o2qTfNd0LJEbHt77H+x358ccffbfccoub58MPP5ykYyuQtx9CB10rdZ4kh3d/qkG/rYn9HgEAgNhEiz8AAGKQ+rxRSxDV0lZNeLXIUs131c72ahTPnz/f1SBWqxSvdZhSjqmWtWqQe60JlDJNLSg0LilUe12tlBJqvaWWBgUKFHCp3AJbp6lFkFrWLFiwwM4GpXaqVKmSa8mkZVILJKWwU236TZs2JfhZ1ZpXqiq1XAmsPa6UbR06dPCP0/ZTK5n4auTHRzX/lUpMNbY1X6VGVOsMpd56/vnnXUrCQKoRru8OHdTPVFJl5ONGaSiVXk8t1tT/mVozfv/9964li1pKeLxlUwqxUNoHibVY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79mzZ0+i8ytbtqzvzjvvTPZyaB/oO7RPYl18++2NN95w4wcPHhw0Xsehxnfo0MF38uTJoPeWLFniy507t6969eq+EydO+Mdr3vqMjrvjx4/HWYYvvvjC9/nnnydpeceMGePLli2bb/78+W6eCxcuTPK55GnZsqU7NkLH6ZyYPHlynOmPHj3q69mzZ6LL1rp1a98VV1zhS28OHTqU7M889NBDbhuePn06TZYJAAAAZ45nTJ4xIyGp+23nzp2+w4cPu791TCW36HjKlCnuM5s2bfKlJ3pWPnbsWLI+88MPP7h1nTdvXpotFwCkFKk+ASAFrrrqKvf/5s2bU22easGlWmJZsybeGFs1PXPnzm0PPPCAe927d2/bs2ePSzOROXPqXtonTJjgUkyqxZVSvOh1Qi20lPJRLY+UbvLRRx91NVgT8tdff7nWatWrV3etufRZpZNRzVSPWrl5tRLvvvvuOKltwvW/8O+//1rPnj2tdOnSliNHDtfa7eWXX9ZTS9B0mo/S6kybNs2qVavmpq1ataqr2RptGjZs6P7/9ddfg8arluo555zjWsCF1qpUCh21AFyzZk1QOh7V/lWrwDFjxrgULqGaNWvm9nlSqJauahg3adLELrroolTpy2HJkiU2c+ZMu/fee13rw1DaT9qfCdGxp/3YtGnTOO95+121n5UGKVeuXHbppZe67SQ6l5TySDU5GzdubFu2bAn6vGpQ33zzza5fBy2LjjPVaj1y5Eic71q/fr07L4oUKeK+R8fi008/Haf/kp9//tm1atS+vOKKK9x7J0+etOeff97OP/989z06zp966qmwfUloH2zdujVDpCoCAACINTxjhscz5tl13nnnuWeWlNI6a7vp+SWQtqf2xbZt29x+198lS5Z03RWInsN0DihNrTLRhB4TSdmnHh0fesZSCl09zxUvXtx1b+E9R+vZTvtI+06ta71nLT2Pyfz5892zt5alYMGCLrtKuC43lA5Vz9TTp09P8fYCgLRC4A8AUsC7YdSDR0roRlQPMBpUUK+b2vfee88V+ifloUz9sCmd4oIFC1xaCQV8lLKjdu3alpoUfNm0aZPdeuutrv833SwnFNTRA5nWTSliWrRoYSNGjEg0xeJvv/3mHg508680GU888YS76b/yyiv9/UgomOSlL9T83n//fTc0atQo7Dz14NW6dWv38KpUkZqv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tq2bPiEjR45081uzZo01b97cpk2bFvT+zz//bA8//LCtWLHCcuTIYa1bt7bhw4fT9yIAAAAAAECMlXVRzoXkoMUfACBmDR482A4dOmQ7d+60Bg0a2A033HDG89TNx5w5c2zp0qWWlkqUKGF9+/a1Ll26hH2/Y8eOVqlSJdu1a5e7afrxxx/t+eefT9NlAgAAAAAAQGRQzoWkIvAHAIh5qgl155132u+//2579uwxn89nI0aMsMqVK1vBggVdral169b5px82bJiVKVPG8uXLZ+XKlbPRo0f738uZM6d1797devfuHe/37d6922677TYrXry4u7FRTaljx47Zvn37XK2m/fv3W968ed2waNGisPPQzZtqTBUuXDjs+7/99pt16tTJrVuRIkVcTSjdGAEAAAAAACB2Uc6FxBD4AwDEvCNHjti7777rbi7OOecce+ONN9zrzz//3Pbu3etuPlq1amXHjx+3X375xdVAmjt3rh08eNCWLFli9evXD5rf448/7m4+VCMqlG62dHNSrFgx+/XXX/21lF544QUrVKiQzZ492woUKOBqaGlo2LBhitZJyzB+/Hi3bqrpNXXqVLcOAAAAAAAAiF2UcyExBP4AADGrT58+rqZTnjx5bMKECTZlyhTLmjWrvf766/bcc89ZxYoV3etHHnnE3Vjo5idLlizupuann35y48477zyrUaNG0Hzz58/vbpo0f00b6IcffrCNGzfakCFDXB5y3QQ99dRT7vtTk2pUffPNN662lmpclS5d2u65555U/Q4AAAAAAABEB8q5kFQE/gAAMWvQoEGuc2GlPihZsqStXr3ajd+yZYtLH6CbJW/4+++/bfv27Xb++efbe++95zod1s3Qtddea6tWrYoz765du7rPfPzxx0HjNW9957nnnuuf90033eRylMenatWq/pQIH374YaLrpe9t2rSpy4t++PBh++uvv9xNn9YJAAAAAAAAsYdyLiRV1iRPCQBAOqWboXfeeccaNWpk7dq1c7WGhg8fbtddd13Y6du3b+8G1YTq37+/3X777XHyiivnuDoZ7tevnzVr1sw/XvMuWrSo/fnnn2HnnTlz3Do3qnWVHEqtoGVTDa5MmTK5Zbn//vtd7SgAAAAAAADELsq5kBha/AEAMoSLL77YdW784osvWrdu3dyNzoYNG9x7Bw4csOnTp7tc5xr35Zdf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02s9qOXKlcuyZMnihnDT6LPxyZEjhxtCaWNrAICMStdA9cfjDX5DA/5OKz2Tl26hcePGrs8gLxVYWoqzPRKZNvD/5NJ6KXWZavxkz57dqlevbkOHDrW6devamVi4cKE1adLEbrzxRps8ebJ/vLaf+lEaN25ckuahba7pE6I+mDS/NWvWWPPmzW3atGlB7//888+uXybdaOr3uHXr1jZ8+HDLnTt3vNue32gA+N/vdKzhmQ8AAABpKb2UdVHOlTHKuRIq60rOc0q6fqK59NJLbd68eUHjvvzySzdedKDUqVMnaBo1kdRrbxoAANKbwYMH26FDh1z6swYNGtgNN9yQKvPVzcecOXNs6dKllpZKlChhffv2tS5duoR9v2PHjlapUiVXaKubph9//NGef/75NF0mAEB04pkPAAAAiG2Uc6W+qAr8aeeuWrXKDbJ582b397Zt2/zpWO644w7/9A888ID99ttv1qtXL9d/w6hRo2zSpEnWvXt3/zRK3/LOO+/Ye++9Z+vWrbOuXbvav//+a3fffXcE1hAAEGnDhg2zihUrWr58+ez88893tXI8W7ZscTVqxowZYxUqVLC8efO635g///zTrrnmGte64Morr3Q3IoH++9//uh/wggULWocOHVyKA8/XX3/taitpXrpxCc3H3alTJ3eDoHmr4HLBggVJXhcVdt555532+++/uz6JRJ3/jhgxwipXruyWRzWn9PsXuP5lypRx61+uXDkbPXp0UMoA/Yb27t073u/cvXu33XbbbVa8eHG33KopdezYMZd3XLWatO5aVw2LFi0KOw9tB9WYKly4cNj39duu7aL1U8o11YTSjREAIP3jmQ8AAABIPZRzUc4V9YG/H374wWrXru0G7wFOf/fv39+91gHpPRBK+fLlbebMma7GZ82aNV0TUO3YZs2a+afRgfnyyy+7eSgvrR4qv/jiizidvwMAMoayZcva/PnzXV8++s144okn7Ntvvw2aRjcl+gFWjaBXX33V2rdv75rg66ZDP9Ivvvhi0PTvv/+++4xuqP7++29/2gX9rR/zhx56yKUFUAHkBx98EPTZq6++2t2w6IbilltusZtuuinJnfUeOXLE3n33XXdjcc4557hxb7zxhhv3+eef2969e93NR6tWrez48eP2yy+/uBpIc+fOdd+xZMkSq1+/ftA8H3/8cbfuqhEVSjdb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2L59+/zvqxlwz549XS2AtWvXunFTpkxx/+uE//jjj+3HH3+0iy++2N566y03vlmzZq6Z7+rVq12QUBcFAEgP+vTp42ow5cmTx2644Qbr27evFS1aNFnzUL70Vq1audQGymeu9AkTJkxI9rLoBmjs2LEu57quz61bt072PAAAAAAAAJAxUc6FpCLwF2OWLl3qTtiSJUu6TjebN2/uj7wHBv8OHz5sp06dsn///deKFy/ubyJ88OBB9/eBAwf846+55hrLmjWre79u3br2xx9/RGDNACD5Bg0aZP/884+rAaXUB++9956/UkNSKd/5M888YytWrLC///7bHnnkEbvjjjts3bp1yZpPhw4d7Msvv7RXXnnF/a0OmQEAAAAAAICkoJwLSUXgL8bs2LHDBf08+jswUKfg3ciRI12aT+XczZcvnzVu3Ni998Ybb9h1113nxq9Zs8Z19BnamlDRf6UBTeu0ow0bNnTNlDUUKVLEtUb0vPzyy26eVapUcRcWAEiKCy64wFq0aOGuSymlChVqNV2gQAH7+eefk31jpfzrum6R/gAAAAAAAAApRTkXEkLgL4M5ceKEvf322y6wpyChWv998MEH7j2dpIrSa/yll17qahAEeuKJJ+ySSy6xBg0apHna0UWLFtmqVavcoCBf27Zt3fivvvrKFixY4KbXxei2225L1vqndiBS89O06gjVWwcA0WnLli02a9Ysd46Ho1bQR48eDRq8a5+uRcePH3eDrh1qLV2nTp1kL8PgwYPdNVHplAEAAAAAAICUoJwLCSHwF2PUWi+whZ/+1jiPTmql7SxTpoxlyZLF5QL+7rvvbM+ePa45b+3atd10N998sxvvGTVqlHs/OS3sziTtaODyb9682Ro1auReq+mychlny5bNvU5ODuO0CETq78mTJ/uXD0B0efLJJ931R8MVV1xhTZs2tf79+4edVq2hc+XKFTSIKgCoIkChQoWsWLFiLn+5OlAuV65cspdH12OvlTUAAAAAAACQVJRzIamyJnlKpAv169d3QSsFzNREd/bs2davXz//+wrCrV692uXvPeecc2zevHl20UUXub8V/FOQrXz58m68gloyc+ZMF/lXQExBw7RIO5ozZ067+uqr41wo1NnojTfe6FrUycaNG12rPwXw1JGpPn/hhRcmOxApXiDy1ltvjROI1LyTEohUy0EAZouuH2bRRtes+Ohao5zonnHjxrkhnBEjRiTp++L7vGpgpWQZAQAAAAAAEBnRVtZFOReSgxZ/MUaBuaFDh1qTJk1cWkq1YFP0Xvl+FYhTFL5379522WWXuWbA+/fvt/vvv999Tq36WrVqZTVr1rSvv/7annrqKTfPRx991LWMU7BL8xw4cGCapx31TJo0yXUOGthqTwE5BfG0bsnJH3wm/R/GF4gEAAAAAAAAAACIFrT4i0GtW7d2QyDl+/V069bNDaFuuukmN4TatGlTqqUdVYvEcGlHRWlH1X9fp06d3Ott27bZ9u3bXZAyMFin6aRZs2b+aVM7EKnv0bwViAz8DgUilSIUAAAAAAAAAAAg2tBsCWcl7eihQ4dc2lEF68KlHZXA9KJe6zr1NaiWeB4FNL0mw2r1V7Zs2TTv/9ATLhAJAAAAAAAAAAAQLQj8IerSjga2rmvfvn3QPDt37uwCdErH+fDDD9s777wT0UAkAAAAAAAAAABAtCDVJ6Iy7agsWbIkzrgcOXLYxIkTzzgQefr0aevVq5c/EDl69OigQKSmVXAxNBAZ2vmp1uW+++6zPXv2WNOmTa1hw4YuQAgAAAAAAAAAAHC2ZfL5fL6z/q3pzIEDB6xAgQKuRVr+/PkjvTgAEDFHjx61zZs3uzS7uXPnjvTiAHbkyBHbsmWLlS9f3nLmzBnpxQGAiOK5JeXYdgAAABm7rKtcuXKWK1euSC8OYIcPH7atW7fGKetKzjMLLf4AAEmWPXt2y5w5s0vXW6RIEfea9LeIFNVdUotrHYPZsmWL9OIAAAAAAIB0RuUJKldQ+YLKuijnQiTLuY4fP+6ORZW/qtw1pQj8AQCSTD86qm3y559/uuAfEGm6IS9VqpRlyZIl0osCAAAAAADSGZUnqFxh+/btLqMQEGnKslamTBlXDptSBP7SsWbPz7RoMqdfy0gvQrozY8YM69mzp+tz8Mknn7TOnTsHvf/RRx/Ziy++6KL96nPwvffec/0c3nrrrfbzzz/bqVOnXL+Cr7/+ursQrFy50h544AH7999/rUaNGm56WsEgtam2iX58Tp486Y5BIJJ0jSPoBwAAAAAAUipv3rxWsWJFO3HiRKQXBRlclixZLGvWrGfc8pTAHxAhCpr06NHDFixY4HLz1qlTx9q1a2eFChVy7yvYp6DgmjVr3LhbbrnFpkyZ4oJ+b731lsvjq2nat29v06dPd59V4HDUqFHWoEEDGzhwoI0dO9buu+++SK8qYpCXWpHAMgAAAAAAAGIh4ELFYsQKAn9IPc+0s6jyzFSLZkuXLrWqVatayZIl3evmzZvb3LlzXWDPo8CeOvMsWLCga8VXvHhxN97rvFOtrY4dO+avAbBt2zYX9JOrrrrKnn32WQJ/AAAAAAAAAABkEAT+ELuGRlFHrD19cUapfzQv6Cf6+48//vC/VjBv5MiRLsVnzpw57eqrr7bGjRv737/ppptca8FmzZpZ69at3bjzzz/f5syZ48ZNnTo1aH4AAAAAAAAAACC2pbx3QABpSjml3377bZfqU0FCtf774IMP/O9PnjzZ/vzzTzd+3rx5btyYMWNsyJAhVrduXdcXIM3TAQAAAAAAAADIOAj8ARFSokSJoBZ5+lvjPKtWrXIdeZYpU8YF8G644Qb77rvvguaRPXt217ef+viTKlWq2FdffWU//PCDa/WnTmkBAAAAAAAAAEDGQOAPiJD69evb2rVrXcDv0KFDNnv2bBesC0z9uXr1avv777/da7Xqq1SpkmsJuHXrVn8ffzNmzLDKlSu713v27HH/nzx50gYPHkz/fgAQY3TN12+BKnaMHj06zvsfffSRVa9e3aWJvuWWW1w/sNKxY0f3OY3v06ePf/qjR4+6iiWaX5MmTWzv3r1ndX0AAAAAAACQugj8ARGi1nxDhw51Ba21atWynj17WqFChaxFixYutada//Xu3dsuu+wyV4i7f/9+u//++13gT4W5GlezZk3Lnz+/PfDAA26e48ePdwW7avnXsGFDu+aaayK9mgCAVKJKHT169LD58+fbypUrXWrnffv2+d9X6mf9lixcuNBVLJEpU6a4/++44w5bv369+5xaj2seouBhhQoVbOPGjXbjjTfaSy+9FKG1AwAAAAAAQGog8AdEUOvWre2XX36xTZs2+VvnzZo1y5/ys1u3brZu3TrXz59aceTMmdNy585tixcvduNUsDty5EgXRBQV+G7YsMHNs1evXhFdNyASrZpeeOEFlx63cOHCQdPr3KhRo4Ybbr75Zjt8+PBZWxcgtSxdutSqVq3qWoTnzZvXmjdvbnPnzg2aRsE/Hd9qEf7vv/9a8eLF3fjrrrvOMmXKZNmyZXOVTbxU05999pndfvvt7u9OnTrZ559/HoE1AwAAAAAAQGoh8AcAiJlWTUqXu2TJkjjzHDBggEudq0GBwbfeeussrhGQOtQaXEE/j/4O7CtWgT1VBlFAXBVI8uXLZ40bNw6ax8GDB23mzJn+8YHzLFiwoP3zzz9nbX0AAAAAAACQ+gj8AQBiplVTvXr1/H8HUkpc73Pq00wBEiDWKBX022+/7VqEK6Cn4/2DDz7wv6/Xd911l3Xt2tVKly4d0WUFAAAAAABA2iDwByDDSGn6yF9//dXq1q1rF1xwgetPUYXngR5//PE4qSUR2VZN4TzyyCNu+p9++sn1lwmkNzp+A88F/e2lhpZVq1a51M9q1ZolSxa74YYbXH9+nieffNLOOecc12I23DzV2k+t/gAAAAAAAJB+EfgDkCGcSfpIFZY/88wzri/GvXv3ujR5np9//tl27twZgTXKuBJr1RSfESNGuABH7dq17eOPPz4rywqkpvr167vrk47jQ4cO2ezZs11628AgudLZ/v333+71vHnzXGUHefPNN92174033gia5/XXX2/vv/+++1vnkV4DAAAAAAAg/SLwByBDSGn6SI1Ti5mWLVu6aTp16mSff/65/zO9evWyF1988ayvTyw701ZNCcmcObPdeuut9umnn6bJssdaS1j1B1erVi3/UKBAARs+fLh7T0GkBg0auJaXHTt2dAFZ+f333+3KK69009epU8eWLVsWkfWKRTruhw4dak2aNHHbV5UVChUqZC1atHBBcJ0nvXv3tssuu8y1Xt6/f7+/detDDz1kW7Zscelw9dmxY8e68V26dHGVGtSi+ZNPPnGfBwAAAAAAQPqVNdILAGQEDWf0sGiy6PphkV6EqE4fmTNnTrv66qtd+ki18Dv33HP9fcIFfm7ixIkuBagCUEibVk0KNKlVU79+/cK2alLaQrVquuiiixKc58aNG11gSz777DOrXLlymq9HemoJu2DBAretFahr166dCyaJ0qgq0CoKgpcrV87atGnjXnfu3NlGjRrlgn8DBw50gaT77rvP/vOf/7hAoAJOX3zxhfXt29fmzJkT0fWMJa1bt3ZDoFmzZvn/7tatmxvC7etwcuXKZdOmTUuDJQUAAAAAAEAk0OIPAFKQPlItApU6UmlAET2tmhQgLFWqlAsK6v9hw4b5+/fTtDVq1LDffvvN+vfvH+G1TD8tYT2LFy+2YsWKWfny5d3rbdu2uaCfXHXVVf7UuAqSq6WgaN+o5SwAAAAAAACAs4MWfwAybPpItSwLlz5SlD5SraBuu+02++uvv1wgUAENL+2kgkdKj+e1NFOgSUEltURD5Fo1Pf/8824IpVaDSH5L2ECTJk2yDh06+F+ff/75riWf+pibOnWq/3NPPfWUXXPNNS4l6OnTp13AEAAAAAAAAMDZQYs/ABkufeShQ4dcIEgBi3DpI0XpI9XvmYJ9l1xyic2cOdON//DDD61Vq1au9diuXbtcn1kalHKSoB9ilQLf6hexffv2/nFjxoyxIUOGuHS3OXLkcP0tyoQJE6xr1662fft2e+edd+zee++N4JIDAAAAAAAAGQuBPwAZwpmkjxw8eLANGDDAtXBSgK9ly5aRXh0gzVrCalyob775xsqWLevSp3qqVKliX331lf3www8uiO71ofjuu+/azTff7P7WueL1EQgAAAAAAAAg7ZHqE0CGkdL0kQpoLF++PMF57927NxWXFDj7LWELFCjgWsKqn8TE0nzKnj17rEiRInby5EkXHFc/ilK6dGnXYvaWW26x77//3r0GAAAAAAAAcHYQ+AMAIIMKbAmr/vh69erlbwk7evRo1/pP49WH37Jly4I+O378eHv77bddGtDOnTu7fv3k5Zdfti5dutigQYMse/bsbhokX8MZPSyaLLp+WKQXAQAAAAAAAElA4A8AYDNmzHDpTxXkefLJJ10gx3Pw4EFr2LCh//XmzZvt2Weftccee8y1AtuwYYO/BVi9evVs2rRp9sEHH7hWYOojsWjRojZu3LigNJFIPy1hM2fO7PrrC6XjRUOoatWq2eLFi9NoaQEAAAAAAACkq8Df66+/bkOGDLGdO3dazZo17bXXXnOpyOIzfPhwe+ONN2zbtm1WuHBhu+mmm1wrg5w5c6Z4ngCQkShVY48ePWzBggUu3WOdOnWsXbt2ruWX5MuXz99Pm1p3lStXztq0aeNeT5w40T+fTp06WdOmTd3f6g9x0aJFVrBgQdfi66mnnnItxELRqgkAgIyHZz4AAAAASDuZLYqoAFmFzwMGDLAVK1a4B7ZmzZrZ7t27w04/YcIE6927t5t+3bp19u6777p5qIA5pfMEgIxm6dKlVrVqVStZsqTlzZvXmjdvbnPnzg07rVpyFStWzMqXLx80/tixYzZnzhxr27ate33ppZe6oJ+oFaD6kAMAAOCZDwAAAAAyUOBv2LBhrl+gu+++26pUqWJvvvmm5c6d28aMGRN2+u+++84uv/xy69ixo2uBcu2119qtt97qCrFTOk8AyGh27Njhgn4e/R1foG7SpEkuvWeo2bNnBwX7AinNp67PAAAAPPMBAAAAQAZJ9Xn8+HFbvny59enTJ6hfIaWNi6+voMsuu8z1I6WHPqVx+e2331y/RLfffnuK5+m1XNHgOXDggPtffV9piBaZzGfR5LRlsugSPXHtTNG1q6LqOE4LLQb+f/9k0WDW0y0smul4UApP77jQ3974QBr/6aef2rfffhvnPdW0v/nmm+OMV39/ut7+97//DXvccW4A4XFuAIjFc5VnPgAAAABImeQ8p0RN4G/v3r126tQpO++884LG6/X69evDfka1PvW5K664whVIq5+qBx54wJ/2JSXzFPUX8eyzz8YZv2fPHjt69KhFizL5oqtUcPfp/28xFBVy1bFoUd7+11datIj1tEdRd25E+fbOlSuXK0TzlnPjxo1Wu3btOMv9/fffW/HixS179uxB7x05csSlBn3++eeDxqtfwF69etnkyZNt//79Yb+bcwMIj3MDQEocPHjQohnPfAAAAACQ9s97URP4S4mFCxfaiy++aKNGjbIGDRrYpk2b7NFHH3WFz/369UvxfFVbVH1EBNb+LF26tBUpUsTy589v0WLbwehqYVf0VJT14eVbbtFis11h0aRo0aIWy6Lu3AizvWfMmGFPPPGEq6mh/zt37hx0Eb/yyiv9rzdv3mzPPPOMu76pgEp92xQuXNi99+qrr1rDhg3d9fCGG25wKbDkjjvusMceeyxJy6c+cHTNO3HihBUoUMC+/vprd20tVCg48PDVV19Zp06d4qyPWgFqeStUqOAft2XLFnvkkUdc0K9GjRrxfvdm22fRJNbPjWhqDRvtLWEjjXMDQErkzJnTYk1Gf+YDAAAAgOQ+70VN4E+F2FmyZLFdu3YFjdfrYsWKhf2MHvSU4sUrMK9evbr9+++/dt9999nTTz+donlKjhw53BBKKWM0RAtflKXWzBxlqUeVfDRa+KJrV0XVcZwhzo2Q7a2a6o8//rgtWLDABdrq1KljN954oz/QpnFqLSeq2a5gXtu2bd18MmXKZL1797aHHnoozncopZUCbcmlFnxDhw61q6++2gUi1UpPhV4tWrSw0aNHW4kSJdx4pe1ctmxZnPXRd6rfv8DxKqDbt2+f3XXXXe51+fLlberUqXG+m3Mj454bsb6tzxTnBoBYPFd55gMAAACAlEnOc0rUPNGo4FmF3/PmzfOPU0GzXl966aVhP3P48OE4K6uHPq+wPCXzBIC0pj5qqlataiVLlrS8efNa8+bNXarMcNQ3jQqtFDhLS61bt7ZffvnF1aJXQZqo/xwF/UTX2u3bt7tUn6HUv58Cf4EUMFTgTwFMDeGCfgAAIGPhmQ8AAAAA0l7UBP5EqVbeeecde++992zdunXWtWtXV5vz7rvv9qeuC+y0vVWrVvbGG2/Yxx9/7FLhffnll65GqMZ7D4OJzRMAzrYdO3a4oJ9Hf//xR/hUuZMmTYoTVBs2bJhLn6nr2aFDh4JSYdWsWdMF8RTAAwAgI1D67EqVKlnFihVdxZNASp9dq1Yt/6BW9cOHDw+aRq3wvRTanpdfftnNs0qVKvbKK6+clfXIKHjmAwAAAIC0FTWpPkWF2+pMvX///rZz5073cP7FF1/4O2rftm1bUG3Pvn37urR3+l+F5kpNpwfAgQMHJnmeABCtVItd/eep1Z9HBVlefzZKyak+/4YMGWIXX3yx61dPLQjVuu7WW291aTkBAIhlSp+toE9g+ux27dr502fny5cvTvrsNm3a+D//888/u2eE0D5tNb+1a9datmzZbPfu3Wd5rWIbz3wAAAAAkIECf6J+q0L7rgpszRIoa9asNmDAADekdJ4AcLYpfWZgCz/9Xb9+/TjTffPNN1a2bFkrVaqUf1xgAdY999xj3bp1c3/nz5/fP14Fng888ICdOnXKXxMeAIBYFJg+W7z02aoAk5T02apEM2rUKBck8rz11luuxZmCflK0aNGzsi4ZCc98AAAAAJBBUn0CQEagIJ9aESjgp1Sds2fPtmbNmiUpzeeff/7p/3v69OmusFN27doVFDBUbXiCfgCAWHcm6bPVR23dunWtTJkyQdNt3LjRtfrT7/W1117r+sAFAAAAACC9iLoWfwAQ61RzfejQodakSRM7ffq0a22glGQtWrRwfROpRaDGK2VnaLpOTauUZUp5deGFF9rbb7/tL8x88803XesEpft8//33I7R2AABEf/ps9f82YsQIF+ALlz5U76s14Zw5c1w/cd9++20ElhoAAAAAgOQj8AcAEdC6dWs3BJo1a5b/b/Vts3379jifiy+g9/DDD7shUUMzWVSp1D3SSwAAyIDps3/77TfbtGmTXXTRRe7133//bTVq1LDVq1e7VoM33HCDG68W+Z06dTpr6wMAAAAAwJki1ScAAACADJU+u3r16i5N9pYtW9xwzjnnuKCfqGKO18+cWv0pYAgAAAAAQHpBiz8AAAAAGS59dnw6d+5sd9xxh1WrVs3y5Mlj77zzTpqvBwAAAAAAqYUWfwAAAPGYMWOGVapUySpWrOiCCIEOHjxotWrV8g8FChSw4cOHu/c0rT6j/jjVCsmjlkWNGzd2rY2aN29u+/fvP+vrBMQatdD75ZdfXOrO++67z58+W0G/wPTZxYsXj3cee/fu9f+dI0cOmzhxomtJuGTJEqtdu/ZZWAsAAAAAAFIHgT8AABCzgbbff//drrzySjd9nTp1ktziR06ePGk9evSw+fPn28qVK23IkCG2b98+//v58uWzVatWuUHvFyxY0Nq0aePea9Cggc2dOzdOisCePXta165dbc2aNa7fsMGDB6d4WwEAAAAAAAChCPwBAICokBaBtv/85z/WsWNH95mBAwda3759k7w86turatWqVrJkScubN69roafvCGfx4sVWrFgxK1++vHutFn3e34HWrVtnV111lftb/0+ZMiXJywMAAAAAAAAkhj7+ACCtPdPOoka+SC8AkLRAm3iBtltvvTVJgbZw1AJQLQVFaTUTSvUXaseOHf5lEf39xx9/hJ120qRJ1qFDh0TnWaNGDRfsu//++93/8c0PAAAAAAAASAkCfwAAICqkRaDtqaeesmuuucalBD19+rQLGKY2n89nn376aZLmPXToUHvwwQftrbfespYtW1qePHlSfXkAAAAAAACQcZHqEwAApCteoK19+/aJTjthwgTXp9727dvtnXfesXvvvTfJ31OiRImgwKP+1rhQ33zzjUsxWqpUqUTnqWDm9OnTbcWKFa7VX+nSpZO8PAAAAAAAAEBiaPEHAACiQrhAW/369c8o0Pbuu+/awoUL3d9qYXfnnXcmeXn03WvXrnXLUaBAAZs9e7b169cvxa0PZe/evVaoUCGXglR9Dt53331JXh4AZg1n9LBosuj6YZFeBAAAAAAAgtDiDwAARIXAQNuhQ4dcoK1Zs2ZnFGhTi7p58+a5v7///vtktbDLmjWrS83ZpEkTq1WrlvXs2dMF7Vq0aOHSkorSh06dOtVuuummoM8qlacCk2ppWKlSJevR43/BCi2LXl944YWWO3fuZLVABAAAAAAAABJDiz8AABAVAgNtCqj16tXLH2gbPXq0axHoBdqWLVsWJ9D2/PPP286dO11gTYHBYcOG2csvv2xdunSxQYMGWfbs2e3tt99O1jK1bt3aDYFmzZrl/ztz5swuuBdKaTw1hNJyJTVoCQAAAAAAACQXgT8AABA1UjvQVq1aNVu8eHEaLS0AAAAAAAAQXUj1CQAAAAAAAAAAAMQAAn8AAAAAAAAAAABADCDwBwAAAAAAAAAAAMQAAn8AAAAAAAAAAABADCDwBwAAAAAAAAAAAMSArJFeAAAAkAE9086iSr5pFlUqdY/0EgAAkCQzZsywnj172unTp+3JJ5+0zp07B72/b98+u+eee2zDhg2WOXNm+/zzz+3888+3hg0b2sGDB900f/zxh9122202fPhwGzdunPXq1ctKlCjh3uvTp4916NAhIusGAAAApEe0+AMAAAAAAMl28uRJ69Gjh82fP99WrlxpQ4YMcYG+QI8++qgL3K1fv95++OEHK1asmBu/aNEiW7VqlRsqVapkbdu29X/mjjvu8L+X3KCfApGaX8WKFW306NFx3tfytWnTxipXrmxVqlSxX3/91Y1XILJWrVpuKFKkiD322GP++VWrVs0FLdeuXZvsbZTayzNs2DCrUaOGG3/ttdfarl27kr1MAAAAiG0E/gAAAAAAQLItXbrUqlataiVLlrS8efNa8+bNbe7cuf739+/f74J9HTt2dK9z585tefLkCZqHWvtt3rzZGjVqFJWBSP09efLkFC1fWiyPWlSuXr3ajW/VqpW9+OKLKd5eAAAAiE0E/gAAAAAAQLLt2LHDBf08+luBPI8CeoULF3ZpPGvXrm3du3d3wbBAn3zyid14442uRZ3no48+cq3aFDBMTou2tAhEqqWeWuOlRFosT/78+f3vHT582DJlypSiZQMAAEDsIvAHAAAAAABSnYJ8Cn498cQTtnz5ctuzZ4+NHTs2aJpJkyYFpfNUK7bffvvNtWqrX7++PfTQQxEPRKZUWi3PSy+9ZGXLlrXx48db3759z3g5AQAAEFsI/AEAAAAAgGQrUaJEUCBLf2tcYKCrfPnyrj86Ba7Ul51SVHq2bdtm27dvt8suu8w/rlChQpYjRw73d5cuXWzZsmURDUSmpZQuT+/evW3r1q1277332muvvXZWlhUAAADpB4E/AAAAAACQbGqRt3btWhfwO3TokM2ePduaNWvmf7948eJWtGhR17JNFi5caBdddFFQa7abb745KF3lzp07/X9PmzbNpcqMZCDyTKT18nTq1Mk+/fTTVFlWAAAAxA4CfwAAAAAAINmyZs1qQ4cOtSZNmrjgVc+ePV2LvRYtWrg0l/LKK6+4VJXVq1e3AwcOuFZ8ga3Z2rdvHzTP4cOHW7Vq1axmzZr23nvv2ciRIyMaiDwTabE8Gzdu9P89ffr0FPc/CAAAgNiVNdILAAAAAAAA0qfWrVu7IdCsWbP8f9etW9dWrFgR9rNLliyJM07912k400Dk6dOnrVevXv5A5OjRo11rOy8QeeLECResDA1EjhgxIs663HfffS4NZ9OmTa1hw4YuIBep5Rk8eLB9//33liVLFitdurS9+eabKdpWAAAAiF0E/gAAAAAAQExI7UCkgnRKtxkty6OAIQAAAJAQUn0CAAAAAAAAAAAAMYDAHwAAAAAAAAAAABADCPwBAAAAAAAAAAAAMYDAHwAAAAAAAAAAABADskZ6AQAAAAAAQJR7pp1FlWemRnoJAAAAgKhE4A8AAAAAAKQvQzNZNGlYqbtFk0XXD4v0IgAAACBCSPUJAAAAAAAAAAAAxAACfwAAAAAAAAAAAEAMIPAHAAAAAAAAAAAAxAACfwAAAAAAAAAAAEAMIPAHAAAAAAAAAAAAxAACfwAAAAAAAAAAAEAMIPAHAAAAAAAAAAAAxAACfwAAAAAAAAAAAEAMIPAHAAAAAAAAAAAAxAACfwAAAAAAAAAAAEAMIPAHAAAAAAAA4KyYMWOGVapUySpWrGijR4+O837jxo2tcuXKVqtWLTccOXLEjV+5cqU1aNDAqlWrZh07drQTJ0648ePGjbOiRYv6p584ceJZXycAAGIq8Pf999/boEGDrHv37rZx40Y37vDhw7ZixQo7dOhQaiwjAAAAACBCeOYDAKSWkydPWo8ePWz+/PkukDdkyBDbt29fnOkmT55sq1atckOuXLncuM6dO9uIESNs7dq1VrVqVRs7dqx/+jvuuMM/fYcOHc7qOgEAEDOBv+PHj9sNN9xgl19+uT399NPuh/f333//30wzZ7Zrr73WXn311dRcVgAAAADAWcIzHwAgtS1dutQF7UqWLGl58+a15s2b29y5c5P02W3btrkWf3LVVVfZlClT0nhpAQDIYIG/fv36uab5b7zxhm3YsMF8Pp//vZw5c9rNN99s06dPT63lBAAAAACcRTzzAQBS244dO1zQz6O///jjjzjTKZVn7dq1bdiwYf5x559/vs2ZM8f9PXXq1KDPffTRR1ajRg33uV27dqX5egAAEJOBP/2gdu3a1e677z4799xz47x/0UUX2W+//XamywcAAAAAiACe+QAAkfDhhx/a6tWrbeHCha6CycyZM934MWPGuNSgdevWtRw5cliWLFnc+FatWrnfI32mfv369tBDD0V4DQAASKeBv927d1v16tXjfV8/vur3AQAAAACQ/vDMBwBIbSVKlAhqqae/NS6Q1yKwQIEC1r59e1u2bJl7XaVKFfvqq6/shx9+sGbNmlnFihXd+EKFCrlAoHTp0sU/PQAAGVWKA3+lS5e29evXx/v+t99+axdccEFKZw8AAAAAiCCe+aKT0q9WqlTJFXiPHj06zvuNGze2ypUrW61atdxw5MgRN75Dhw7+cSpUb9u2rRuv1jPeeM23YMGCZ32dAGQcapG3du1aF/A7dOiQzZ492wXxPCdPnrS9e/f6+5rV++oTUPbs2eOfZvDgwa5FuuzcudP/+WnTpvmnBwAgo8qa0g8qZ7bybN9444124YUXunGZMmVy/7/zzjs2adIke+mll1JvSQEAAAAAZw3PfNFHhd09evSwBQsWuJYwderUsXbt2rnWLoEmT55s1apVCxo3ceJE/9+dOnWypk2bur+feOIJN4gCid98881ZWRcAGVPWrFlt6NCh1qRJEzt9+rT16tXLXcNatGjhrkG6tikQeOLECTt16pRL43nTTTe5z44fP97efvtt1+ds586d7ZprrnHjhw8f7ipFqCX6eeedZ2+99VaE1xIAgHTa4u/pp5+2yy67zBo1auR+rPUA2L17dytTpozdf//9dt1117nXyfX6669buXLlXGfxDRo0sKVLlyY4/T///GPdunWz4sWLu2b9eiCdNWvWGc0TAAAAADI6nvmij9ZLLVnUYi9v3rzWvHlzmzt3brLmcezYMZszZ46/xV8gBXPVMhAA0lLr1q3tl19+sU2bNvlb7em6rpSfefLkseXLl7v++n766SdXwcSrdNKzZ0/bsGGD+6wChh5No1aEP/74o7smli9fPmLrBgBAug78Zc+e3b744gsbO3asVahQwaUS0QNEjRo1bNy4cfb555/7O9lNKtVAVO3FAQMG2IoVK6xmzZqulo/6lghHTf5Vu2fLli2uRqN+/FXz1MsFnpJ5AgAAAAB45otGO3bsCFp3/R3YV1Zga83atWu7FpuhlDbv0ksvjZPSU6n1VGjutQQEAAAAkIFSfaqPANX+VK1PpQjRkBr0UKJOeO+++273+s0337SZM2famDFjrHfv3nGm1/i//vrLvvvuO8uWLZsbp1qeZzJPAAAAAMjoeOZLvz788EMXENy/f79rVaN++1q2bJloq74pU6a46b3tDAAAACADtfjLlSuXy5e9a9euVFsQ1eRUU/7A2oWZM2d2rxcvXhz2M5999pmrqai0L8rhrT4MXnzxRZcDPKXzBAAAAICMjme+6KQ0eIEt/PS3xgXyWgSqn6z27dvbsmXLggK6X375pQvwhVLLSdJ8AgAAABm0xZ+oE3Hlz04tSiuihzc9zAXS6/Xr14f9zG+//Wbz58+32267zeUCV27wBx980HUArDQvKZmnKH2NBs+BAwfc/+p0WEO0yGQ+iyan7X8512Mgk22qyxRduyqqjuO0wLmRPs4L4dzIuOdGdJ0XwrmRkc8NpB+cG2d3eXjmiz5169Z1++T33393gT2l7VTLTG+ZT5486fpELFy4sAuKapvdcccd/vdnzJhhDRs2dH1oBa6n0qKuW7fOrrzyygTWn9/uhHB9AgAAQLTc36U48Dd8+HBr0aKFq3F51113WdasKZ7VGa1o0aJF7e2333Z9S+jBVDUehwwZ4h4CU2rQoEH27LPPxhm/Z88eO3r0qEWLMvmi68li9+n/72siKuSqY9GivBWyaBKL/Z0E4txIH+eFcG5k3HMjqs4L4dzI0OcG0g/OjYQdPHgwVefHM1906tu3rzVu3NhtGwVBFfhUP4hDhw61/PnzW9u2bV0AUOOvvfZaa9Sokf9Y/eCDD9w+DT1233vvPTePffv2xf/F+fntTgjXJwAAAETL816Kn9z04KcUKvfff7898sgjLp2I0sEEypQpk+scPClUI1EPcqGpZPS6WLFiYT9TvHhx1/9AYIfyF110ke3cudPVbkzJPKVPnz6uc/jA2p+lS5e2IkWKuAepaLHtYHTVuCx6Km6n8hHlW27RYrNdYdFEhSexjHMjfZwXwrmRcc+NqDovhHMjQ58bSD82WwJBiQiItnMjZ86cqTo/nvmi0+233+6GQErf6Vm1alW8n506dWrY8U888UTiX3yA3+6E8NsNhNd45uMWLRa2fDnSiwAAwFl53ktx4O/cc8+1QoUKuY7CU0P27Nld7c158+a5GoqiGox6/dBDD4X9zOWXX24TJkxw0+mBVH755Rf3cKj5SXLnKTly5HBDKH2H9z3RwBdlqVYyR1EKuf+JntQmvujaVVF1HKcFzo30cV4I50bGPTei67wQzo2MfG4g/eDcOLvLwzMfgvHbnRCuT0D0nxucFwCA9Cw5v2MpDvwtXLjQUptqXN55552u34L69eu71DL//vuv3X333e599U2gWqZKyyJdu3a1kSNH2qOPPmoPP/ywbdy40XX0rtqoSZ0nAAAAACAunvkAAAAAIP05+500JKBDhw6uT4X+/fu71C21atWyL774wt9R+7Zt24KimkrFMmfOHOvevbvVqFHDPSDqgfDJJ59M8jwBAAAAAGcHz3wAAAAAEMWBP3UWrs7BZ86caVu3bnXjypYta9dff73ddtttQf0wJJXSscSXkiVcjdNLL73Uvv/++xTPEwAAAAAQHs98AAAAAJC+pDi59f79+11/C/fcc4/NnTvXTpw44QZ1Kq6UKldccYXrIB0AAAAAkP7wzAcAAAAAGajF39NPP23Lly+31157zbp06WLZsmVz4/UgOHr0aNfngqbR+wAAAACA9IVnvshr9vxMixZzIr0AAAAAANK2xd/UqVPtwQcfdIP3ACj6Wx2wa/j0009TOnsAAAAAQATxzAcgPZkxY4ZVqlTJKlas6ConhGrcuLFVrlzZ9QOq4ciRI258z549XR+iGm6++WY7fPiwG3/06FG74YYb3PyaNGlie/fuPevrBAAAcFYDf/v27XM3VPHRzdRff/2V0tkDAAAAACKIZz4A6cXJkyetR48eNn/+fFu5cqUNGTLEXcNCTZ482VatWuWGXLlyuXEDBgyw1atXu6FMmTL21ltvufEKHlaoUME2btxoN954o7300ktnfb0AAADOauDvggsusM8++yze9/Xe+eefn9LZAwAAAAAiiGc+AOnF0qVLrWrVqlayZEnLmzevNW/e3PVNmhT58+d3//t8PtfKL1OmTP5r3O233+7+7tSpk33++edpuAYAAABREPhTuhfdRLVo0cL9v2XLFjfMmTPHWrZs6Tp8f+ihh1JxUQEAAAAAZwvPfADS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Vq2SJUuWyJdffim9evXK8s9xww03mL89derULN83nBcrWlnUHnHZITExUU6fPi1NmjSRy5cvm7/1zDPPmMppIAcPHpQ2bdqY2NEf+vRHvZ49e8rKlSszdQ3JKj169DBxcOnSpSzfN3KWk2PDpgmh93WiTp06nnXr16+XLl26mPJv377dJGz6SEpKytR1JKto0kjdCQjPei35HrJLqMeGIudDuNdryfkQTE6ODRs5XxiwgDQkJCRYRYsW9bw+deqU9dBDD1k33HCDlS9fPqtixYrWzJkzzbp169ZZejidPn3as/327dvNsoMHD5rXs2bNsgoVKmR9/PHH5r1RUVFWixYtrB9//NHn7y5atMiqVauWWV++fHlr5MiR1sWLFz3rdZ9vv/221a5dO+vaa6+1RowY4Vl35513WlOnTk31WcqWLWtNnDgx1fLY2Fird+/enteXL1+2SpYsaY0bN868PnPmjJUnTx7zXdh2795tyrBhwwbzWj+fvtbP669JkyZW3759fb4j/0e3bt0823/xxRdW3rx5rd9//z2d/xkEU6jHhf330pKSkmKVKFHCmjBhgmeZxoH+3Xnz5pnX3333nfl733zzjWeb5cuXWxEREdaRI0fS/OyB4lHLEyguvMs/Z84cq3Tp0lf4n4HTY0UtXbrUqlSpkomVpk2bev7/7ePE//jcv3+/1b59e6tYsWLWddddZ9WtW9datWqVzz6Tk5Ot1q1bm32WK1fO+uCDDwKe80eNGmV16tQpVTn1HNyhQ4dUywcPHmxVrVrVZ5m+Py4uLsPXkPSuP3qM16hRw/M6UBzoe20XLlwwcbh69epU+0JocXJspFensT344INWmzZtfJbVr1/fevzxxzN8HVH6d/Tal15M2uXxf2j9ynb48GGzTL8TAOFVr/VGvoesFOqxQc6HnOLkeq03cj7kNCfHBjlf+OCOP6RJhyDxbu0fNmyYGXN6+fLlsnv3btMLRXtmZbbHg97qPHfuXPn666/NkBCdO3f2+Ztdu3aVvn37mr/1zjvvmNvs9T3+t8vfc889snPnTjNkitJ9ffXVV9K+ffsMleWvv/6SrVu3+vT8yZUrl3m9YcMG81rXay867230Nud//OMfnm0yqlGjRj49KdauXWtul77jjjs829StW9f07NGeEnAmN8SFDs9StmxZKVOmjBl2QHtxevd4O3bsmM8x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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAABv4AAAIDCAYAAADMsGn8AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8ekN5oAAAACXBIWXMAAA9hAAAPYQGoP6dpAADHaklEQVR4nOzdB5gT1frH8XfpHZQiVUBFEKQ3GyCKICBFRVCqDZWLDbCAYkVELlKuIooi2C4qKkXpSlFs9KqAoDQpUlSq9Pyf3/k/k5tks5Vdks1+P88T2MxMJtMzc95z3hPn8/l8BgAAAAAAAAAAACBDyxLpBQAAAAAAAAAAAABw5gj8AQAAAAAAAAAAADGAwB8AAAAAAAAAAAAQAwj8AQAAAAAAAAAAADGAwB8AAAAAAAAAAAAQAwj8AQAAAAAAAAAAADGAwB8AAAAAAAAAAAAQAwj8AQAAAAAAAAAAADGAwB8AAAAAAAAAAAAQAwj8AQAAAEgXV199tV166aWp/vz8+fMtLi7O/Q+cKR1Lzz77bKQXI+bdfvvtVq5cuVRfM/SKRTr2dAzu3bs3Xb9n8+bN7ntefvnlZC9TWuK6DQAAEHkE/gAAiCHvvPOOK2zxXtmyZbNSpUq5Qrjt27en2/d6BUfnnXeeHTlyJN54FQDecMMNqZr3qFGj3Hol16FDh+yZZ56x66+/3s4991y3XIl9fsKECXbZZZdZoUKFrHDhwtaoUSObNm2aRYM9e/bYQw89ZJUqVbLcuXNbsWLFrF69evb444+79fRo/wbu98BXrly5kvwejpv/FVSGe/3444/xpv/+++/tqquusjx58ljx4sXtwQcfDNonkeKtx6effhrpRYlaoedLzpw57eKLL7ann37ajh49mm7fq2CGvq9Vq1ZnVFAfSueOzqWUFLKvX7/eevXqZVdccYW7Rui7tQzhaJsMGjTIKleu7I53XRtuueUW++mnnyzSQq9deuk62bhxY5sxY0ayP79kyZIzuhYndv0IfYUu97fffhvv+3w+n5UpU8aNT+01ULzvuPvuu8OOf/LJJ/3TpHcwKhqFO37CvVIbxEwv+n3TctWvXz/iy5GS39lYomMi9LrToEEDmzRpUqqOLe9+yHtlyZLFSpQo4c7/cPcgocaMGeM+9+6778Yb98MPP7j5PfLII2lWKQcAAES/bJFeAAAAkPaef/55K1++vCuwVYGBCiBUuLhmzZpkBYJSa/fu3fb6669bnz590rRgqUiRIq6wPjlUeKn1P//886169eqJFoa/+uqrLmDTsmVLe+mll9z20rZSQctnn31mN910k0XKn3/+aXXq1LEDBw7YnXfe6Qqc9+3bZ6tWrXLbuEePHpYvXz7/9ApeqOAnVNasWZP9nZn5uPHoeKhbt27QsIsuuijo/YoVK+zaa6+1Sy65xIYNG2a///67C9hs2LAhWcEGRF7g+bJ//36bMmWKDRgwwH799Vf773//m67fPXXqVFu6dKnVrl07TeanwN9zzz3n/k5uSykVBL/yyisumKfjWMd0Qjp16mSff/65de/e3WrVqmU7duyw1157zS6//HJbvXq1lS1b1iLNu3YpYPbHH3+4a1eLFi3siy++CAqc/fPPP65iQ1pfi7UN33///aDP9evXz12jFVxLiK6r48ePd5UIAn399dfuuqLj9EzpO/R7pmtijhw5gsZ9+OGHbnx6BryjWcOGDePtNwVJFdS95557/MMCf2ujga5RChgtWrTINm7cGO836mxJ6HdW21XnWujxFmtq1Kjhv2/RdXH06NHuvlHXhaZNm6bq2NJnNez06dO2bds2e+utt9z21L7W9yXkrrvuckE/Bfd0zVNFNjlx4oT7PlUk8H4nAABA5kDgDwCAGNS8eXNXUOkVNKhgZvDgwa7wtn379un2vSqUGDJkiP3rX/9yrSIiQTWkd+7c6VphqRVHaBAnNPCn8Soc9lpiqGBXLVpUgBLJwN/bb79tW7dute+++861ygmkAujQAjUVZnfu3PmMvjMzHzce1dhv165dotM88cQTds4557igcoECBdwwFcIqMDJ79mxX4IfoFnq+6NjTeaZAiIK5aoWaHlQh4eDBg64AVudVpLRu3dr+/vtvy58/vwtaJxT4U4vfiRMnusJknaOB58k111zjxqnlYKQFXru8QnDtQ+3PwMBfaiowJOdarPmGXn9VmUTX0MSuywpOfvLJJy4IGxiQVDBQgeG0aIWn1u861lQpoU2bNkGtljdt2mQ333yzCwxmRGrZ3KRJE9diP9SuXbvcPUBiLSYvuOAC9wp03333uWFn+nuaXrTPtO907t17770uCKgsB9FErcvSs7JQtNC9YuBx0rVrVxeEHT58uP84SumxpfsPXTc8bdu2dS3zdJ1ILPCne1gFHjWN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IbBFWWp+63UO6Ddby6drh1JpJvQbpeuA1jehvgt1XurapOUKrHyka7r2jYKgCripEo+ucwmlD/bWQ9tL5772mfajAqz6XQyt8JPc39mUHFNnQttA54eup/p90/ZS6z9d0+vWret+c6Odru26BiqAp3tYBS31+6YAXmItLPWbo3SgOhcC+99V5Qyl+9SxqN8RbYfAyivad6GUEl19pQIAgAzOBwAAYsa4ceNUxdm3ePHieONOnTrlu/DCC93r5MmTbtivv/7q69q1q6948eK+7Nmz+0qVKuW74YYbfJ9++qn/cy+88IKvXr16vkKFCvly587tq1Spkm/gwIG+48eP+6d55pln3Pfu2bMn3vc2atTIjWvZsmW8cW+++aavdu3abr758+f3Va1a1ffYY4/5duzY4Z9m165d7rMar/lofkkpW7asmzbca9OmTf7pTpw44Xv11Vd9NWrU8OXLl8+9Gjdu7Js7d64vuU6fPu0rU6aMm7e2VajRo0f7GjZs6CtcuLAvZ86cbvs/+uijvv379yc631WrVrnpatWq5Tv33HN92bJl85UoUcJ3yy23+JYtWxY0bbdu3RJc39B1Dofjxuf7z3/+45Y3cFt37tzZt2HDhrDTL1iwwHfFFVf4cuXK5StatKivZ8+evgMHDviSS9Nq+bVsH3zwQbzxydl+4cybNy/RY0HLLW+//bavQoUK7pjUvHUMePsjkN5r3QLpeNLwIUOGhP3uTz75xD9M271KlSq+JUuW+C6//HK3vXR+jhw5Mt6yb9myxde6dWtfnjx5fEWKFPE99NBDvpkzZ7p5at6en3/+2dekSRN3vmq67t27+1auXOmm03okRedL3rx5w47TsZ01a1Y3TeB6NWvWzFewYEG3/DoXbr/9drdOsnfvXreNtB01X01Xv35934QJE4Lm7W2LUH/99Zf7TLhtevDgQV+/fv18F110kS9HjhxufXXcvfzyy0HHwvfff+/OCU2j+WhfJsbbh+Fe2j+B/vzzT1+vXr18F198sTtetAy33nqr77fffvMll84jb/7ffvtt0Lhjx465a1316tXd+aptqL9HjRqV5Hy9a1fgS/tI1/TXX3/dXZ8DhW6bcJ8PfG3bti1F1+JA2tcJXXcSu+YG0r4IvQZ61/ukrusJnb+hEroOf/zxx76aNWu6fa717tSpk+/333+P9/nPPvvMd8kll7jpKleu7Js4caJbxtDjKLnXbm2z5PzOy+rVq/2/S6GOHj3qW7t2rS+ldPwFnv+aT58+fdw+13JfeeWVvh9++CHecibntz7ctj5y5Iibj65nP/74Y9hlatWqlTuuDx8+nOBy65qk32NdjwKv0UOHDnX3KFqmBg0auGtloHDX/c8//9xXrVo1953lypXzDR482Dd27Nh4x11Cv7Peb0HgdTu5x1RC1+dwy5kQ/b7oeqztcd555/l69OjhrrPJuR4ndOwm59xM6bEVbv0CX5pev5uhvyWh9DtRunRpd90Ldz7ofqNkyZLuGuaN9+6xwr2uvfbaFK0XAACITnH6J9LBRwAAAAAAAAAAAABnhj7+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AAAAAAAAAAACIAQT+AAAAAAAAAAAAgBhA4A8AcMaeffZZi4uLs7179yY5bbly5ez2229P0+/X/DRfIJwJEybYueeea4cOHbJYddlll9ljjz0W6cUAAAAA0gTPmBmXnruKFStm//3vfy1WvfHGG3b++efbsWPHIr0oABAWgT8ASMQ777zjHja8V65cueziiy+2+++/3/744w83jR4GAqdJ6KV5SejwvHnzWuXKle2FF16wI0eOJLo8R48etYsuusgqVapkx48fjze+efPmVrBgQduxY4d7P3/+fPcdn3766Rlth3r16rn5vP766xYpWic9/K1YscJinbffvFfWrFndg1O7du1s7dq1CX5u6tSpdv3111vhwoX9x+ojjzxi+/btS/S7brrpJitevLjlyJHDfU+rVq1s4sSJKVrm9u3bu2V9/PHHEz2XlixZEnb8DTfcEPbBWsf88OHDrX79+u7YDjwHf/nllySX69SpU/bMM8/YAw88YPny5bNYpe3+2muv2a5duyK9KAAAAEgEz5j/j2fMyD5jBr5uvfVW/3SLFi2yf/3rX1a7dm3Lnj27G59S//nPfyx//vxB8401CgzrfBk9enSkFwUAwsoWfjAAINDzzz9v5cuXdw9F3377rXs4mT59uq1Zs8ZGjBgR1JJIwz/88EMXrChSpIh/+BVXXOH/+7rrrrOuXbu6v/XZBQsW2FNPPWUrV660Tz75JMHl0EOhvrtp06Y2aNAgF9DwfPTRRzZz5kx79dVXrWTJkmm27hs2bLDFixe7h0/V2OvRo4dF6qHsueeec8tRo0aNoHFvvfWWnT592mLNgw8+aHXr1rUTJ07YqlWrXK1CPbDpuFOgLpACfEOHDrXq1au7IJBauC1btsxGjhzpjo05c+ZYxYoVgz6j40fHdoUKFezee++1smXLuiChjuGbb77Z7e+OHTsmuZwHDhywL774wu0bHfsvvfRSqh4QQ6l2rwKZS5cudYFBLYuCd+vXr3fr9Oabb4YtnAik5dL099xzj8WyNm3aWIECBWzUqFFunwIAACC68YzJM2YknzEDBVbA1LE2ZswYq1atml1wwQXJqmwZSM+uCvz16tXLVWCNVTpvunXrZsOGDXOVTNPi+RcA0pQPAJCgcePG+XSpXLx4cdDw3r17u+Hjx4+P95khQ4a4cZs2bQo7T43r2bNnvOHt2rXzZcmSxffPP/8kuVwdO3b05cyZ07d+/Xr3/q+//vIVL17cV7duXd+pU6f8082bN8993yeffOJLraefftpXrFgx32effeaLi4sLu17PPPOM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mz4xI0eOdPNbvXq1NW/e3CZPnhw0/ueff7YHHnjAli1bZjlz5rTWrVvbiBEj6HsRAAAAAAAgxsq6KOdCStDiDwAQswYPHmyHDh2yXbt2Wf369e2mm24643nq5mPWrFm2aNEiS08lS5a0/v37W/fu3cOO79ixo1WsWNH++OMPd9O0cuVKGzBgQLouEwAAAAAAACKDci4kF4E/AEDMU02obt262bZt22zPnj3m8/nslVdesUqVKlmhQoVcram1a9f6px82bJidf/75lj9/fitXrpyNGTPGPy5XrlzWq1cv69u3b4Lft3v3buvUqZOVKFHC3dioptSxY8ds3759rlbT/v37LV++fO61YMGCsPPQzZtqTBUpUiTs+N9++806d+7s1q1o0aKuJpRujAAAAAAAABC7KOdCUgj8AQBi3j///GNvv/22u7k455xz7PXXX3fvv/jiC9u7d6+7+WjVqpUdP37cfvnlF1cDafbs2Xbw4EFbuHCh1atXL2h+jzzyiLv5UI2oULrZ0s1J8eLF7ddff/XXUnrhhRescOHCNmPGDCtYsKCroaVXgwYNUrVOWob33nvPrZtqek2aNMmtAwAAAAAAAGIX5VxICoE/AEDM6tevn6vplDdvXhs/frxNnDjRsmXLZq+99po9//zzVqFCBff+wQcfdDcWuvnJmjWru6n56aef3LDzzjvPqlWrFjTfAgUKuJsmzV/TBlqyZIlt2LDBhgwZ4vKQ6yboiSeecN+fllSj6ttvv3W1tVTjqkyZMnbnnXem6XcAAAAAAAAgOlDOheQi8AcAiFmDBg1ynQsr9UGpUqVs1apVbvjmzZtd+gDdLHmvv/76y37//Xe78MIL7d1333WdDutmqGnTprZixYp48+7Ro4f7zEcffRQ0XPPWd5577rn+ebdr187lKE9IlSpV/CkR/vvf/ya5XvreJk2auLzoR44csT///NPd9GmdAAAAAAAAEHso50JyZUv2lAAAZFC6GXrrrbesYcOGduONN7paQyNGjLDrr78+7PTt27d3L9WEevrpp61Lly7x8oor57g6GX7qqaesWbNm/uGad7FixWznzp1h550lS/w6N6p1lRJKraBlUw2uuLg4tyz33nuvqx0FAAAAAACA2EU5F5JCiz8AQKZQq1Yt17nxiy++aD179nQ3OuvXr3fjDhw4YFOmTHG5zjXsyy+/dDccutFQ7SSlSQinY8eOrgbSxx9/7B9Wt25dd1OkFAman1IkbNmyxeU8F9Wu0nB1jJyYkydP2tGjR93/p0+fdn8rN7uos2Yt16hRo9x4zU83fDVr1kzDLQYAAAAAAIBoRDkXEkPgDwCQaTz55JM2ZswYa9u2rd1+++2us2PlMb/kkkv8ucl106HaTbpxUd7yuXPn2jvvvJNgraaXXnrJ9u3b5x+m3OlTp0617du3u/mqg+OWLVvaxo0b3fiKFSvaXXfdZZUrV3bpEZS/PBx1kpw7d24bOHCg65xZfysdg+hmSMM+/PBD15FzuXLlXNoFpW4AAAAAAABA7KOcCwmJ84X21oh4FCHXAb1//3534gBAZqXaOJs2bbLy5ctbrly5Ir04AMckAATguSX12HYAAACZE+UKyCjHZEqeWWjxBwAAAAAAAAAAAMQAAn8AAAAAAAAAAABADCDwBwAAAAAAAAAAAMQAAn8AAAAAAAAAAABADCDwBwAAAAAAAAAAAMQAAn8AAAAAAAAAAABADCDwBwAAAAAAAAAAAMQAAn8AAAAAAAAAAABADCDwBwAAAAAAAAAAAMSAbJFeAABADHj2xrPwHZNSNPn69evtkUcesR9++MGOHz9uJUuWtDvuuMMef/xxu/rqq93w7Nmz+6fPlSuX7d27N+y83nnnHbvrrrssd+7cFhcXZ+edd5717NnTevXqdcarVa5cOfe9v/76q5uvrFixwmrWrGk+ny/Z8xgxYoS1bds2wWnmz59vjRs3trx58/qH3X777TZy5Ej/+8mTJ9ujjz5q27dvt1q1atmYMWOsUqVKZ7R+AAAAAAAAGQ5lXalGWVfk0eIPABCTWrZsadWrV7etW7faX3/9ZZ999pldcMEF/vGDBw+2Q4cO+V8J3Qh5qlat6qY7ePCgvffee/bkk0/a3Llz02RZdSP2/PPPW3orWLBg0DoH3gjp5rFTp042fPhw+/PPP+2aa66xNm3a2MmTJ9N9uQAAAAAAAJA4yrrio6wrPAJ/AICY49Uquvfeey1PnjyWNWtWq1Klit1yyy1pMv8rrrjCzW/p0qX+YcuWLXO1jM4991y76KKL7K233goad9lll1mBAgWsSJEi1qpVq6D5PfbYYzZu3Di3zOGoNtQrr7ziaiQVKlTI1eJau3atG6d10g3fbbfdZvny5bP77rsvVev0wQcfuOW/4YYb3M3ZU089Zbt377YFCxakan4AAAAAAABIG5R1pdwHmbisi8AfACDmFC5c2CpWrOjSHUyYMMG2bNmSZvPWjck333xja9assYsvvtgN27Vrl1133XXWo0cP27Nnj0sj8Mwzz9icOXPc+Pvvv9/dAP39998utYBSDATSfLp06WL9+/cP+52vv/66vf322/bFF1+4G72bbrrJzU9pHT755BM7//zz7cMPP3Q1m954440El13jlQaidOnSrsaTlsWzatUqq1Gjhv+9UkNUrlzZDQcAAAAAAEDkUNYVHmVd4RH4AwDEHOUmV55vpT947rnnXNoD/bB/+eWX/mn69evnahR5L93MJGb16tVuOtUQatSokfXp08dat27txr3//vvWsGFDa9++vatxdemll7obsfHjx/tvLHRDtmPHDsuZM6ebNtSzzz7rbnaWL18eb9xrr73m0iNUqFDBsmXLZg8++KD9888/tnDhwmRvE9WgUj71bdu22ZIlS9xNnW6oTp8+7b9R0voF0nulewAAAAAAAEDkUNYVH2VdCSPwBwCIScWLF7ehQ4faTz/95GomNW/e3G688UaX01sGDRrkaiV5L+9G6cUXX3RpBPTSZwLznms63RwoNYBynns5wTdv3mzTp08PurlSuoKdO3e68WPHjrWjR49a7dq13U1JYL5xT4kSJdxNTt++feON0/w7d+4cNH/lcv/999/Drnu4ddD20E2abtb095tvvmkrV660X375xY3XtPv37w+aj97nz58/1fsAAAAAAAAAaYOyLsq6kovAHwAg5ikXuWoZHT582DZt2pTotE888YS/Q+AZM2bEG58jRw5Xs0q1kEaNGuWGlSlTxt1oBd5c6aZJN0hy4YUXuk6SlSZhzJgx9sgjjwTlTPc8/vjjroZSaEfKmr/SHATO/8iRIy7XuWTJkiVF6+DVFAtUrVo1V0vKc+LECfv555/dTSAAAAAAAACiB2Vd8VHW9T8E/gAAMUc1hJRDfN26dXbq1Cl34zBs2DB3U6RaSGdKNxJPPvmkq22keStnuW5gPvvsM3cToZduLBYvXuym143QH3/84T6nGky6eVFtpFAFCxZ0NzKab6CePXva008/bevXr3fvDxw4YFOmTPGnJjjvvPMS7CzZM2/ePHcjqLQH+/btczna1WmzUiqIallpHXQDd+zYMRs4cKDrnDlcqgYAAAAA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/fffbn7r16+3zCTqfjOeahnpRUAGFq3PLRkB2w4AAABArDyz0OIPAAAgA1DLLqV09OjvwH7f4uLibOTIkS6dpwJ/St8YGvQ7ePCgTZs2zT88cJ6FChVywT8AAAAAAABkXAT+AAAAYoDSOr755psuBa4CemoB+MEHH/jH6/3tt99uPXr0sDJlykR0WQEAAAAAAJA+CPwBAABkAGrFF9jCT397/V7KihUrXF9w559/vmXNmtVuuukmlx7U8/jjj9s555zj0n+Gm6da+6nVHwAAAAAAADIuAn8AAAAZQL169WzNmjUuUHfo0CGbMWOGNWvWzD9eKTtXrVplf/31l3s/Z84cq1ixovv7jTfesOXLl9vrr78eNM8bbrjB3n//ffe3WgfqPZDRTJ061R3rFSpUsDFjxsRLb1ujRg3/S/0hjBgxwo3TOaF+LZUet2PHjq7VrGzevNmlw61atao1b97c9Z8AAAAAAEBGQeAPAIBMLK0LzLdt22aNGjVy09euXdsWL14ckfWKRWrNN3ToUGvcuLHbvmq5V7hwYWvRooVL7anWe3379rUrrrjCBSwUrLj33nvdZ++//34XzKhbt6777Lhx49zw7t2728aNG+2iiy6yTz75xH0eyEhOnjxpvXv3trlz57rr0pAhQ2zfvn3+8errUq1h9dJ4tWpt06aNG3f33XfbK6+84gLqVapU8Z8XOreUEldpczt37myDBw+O2PoBAAAAAJBScT51+IJEHThwwBV2qgCtQIECFi2aDZhm0WTWqeAC44jLP9miRYOKvSyaLLhhWKQXIV1xbiTi2UkWTRpM7W3RJNbPjXAF5pUrV7Z58+a53zkF6pQaUsGkULpdKFeunM2fP9/Kly/vph01apQL/g0cONCKFi1q99xzjz3wwAMuGKiA08yZM2348OE2a9asiKwfkBz8ZmSs341QumYp2Ddp0v8v58MPP+yuS7fddlvYaXv16mULFy5073Xd2rNnj/v7hx9+sOeee85dt3Rd/Prrr934nTt3umD7unXrLCOI1ueWjIBtBwAAACBWnllo8QcAQCa1aNEi18pFKSLz5cvnUtrNnj077LQqFC9evLgL+snWrVtd4bpcc801NnHiRPd3XFycaykouhEpUaLEWVsfAJmPWrvqGubR34F9YQaaMGGCdejQwf/+wgsv9FdMUODQ+1y1atX81zT9n9D8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", "text/plain": [ "
" ] From dd96e07133bd9ff860684f531d049b6381c11d09 Mon Sep 17 00:00:00 2001 From: AnuragD2 Date: Tue, 21 Apr 2026 23:55:22 -0700 Subject: [PATCH 36/39] chore: untrack generated artifacts (CSV + PNGs) --- examples/ptbxl_ablation_results.csv | 29 ------------------ examples/ptbxl_model_comparison_ablation.png | Bin 85995 -> 0 bytes examples/ptbxl_se_resnet_ablation_results.png | Bin 50341 -> 0 bytes 3 files changed, 29 deletions(-) delete mode 100644 examples/ptbxl_ablation_results.csv delete mode 100644 examples/ptbxl_model_comparison_ablation.png delete mode 100644 examples/ptbxl_se_resnet_ablation_results.png diff --git a/examples/ptbxl_ablation_results.csv b/examples/ptbxl_ablation_results.csv deleted file mode 100644 index 38a0b7383..000000000 --- a/examples/ptbxl_ablation_results.csv +++ /dev/null @@ -1,29 +0,0 @@ -model,config,label_type,sampling_rate,K,T,roc_auc_macro,f1_macro,train_time_s -ResNet-18,A -- superdiagnostic / 100 Hz (baseline),superdiagnostic,100,5,1000,0.8822800924422214,0.5773312448880163,245.33349299430847 -ResNet-18,C -- diagnostic (27-class) / 100 Hz,diagnostic,100,27,1000,0.7976528290960323,0.1731836175988993,249.20705914497375 -SE-ResNet-50,A -- superdiagnostic / 100 Hz (baseline),superdiagnostic,100,5,1000,0.8756324212374924,0.6750944493978221,1258.272660970688 -SE-ResNet-50,C -- diagnostic (27-class) / 100 Hz,diagnostic,100,27,1000,0.7179746910029848,0.1256204130133857,1236.1298429965973 -Lambda-ResNet-18,A -- superdiagnostic / 100 Hz (baseline),superdiagnostic,100,5,1000,0.8710290676081464,0.6714607062381933,1764.986752986908 -Lambda-ResNet-18,C -- diagnostic (27-class) / 100 Hz,diagnostic,100,27,1000,0.7194757776214,0.1264442354673686,1827.3542799949648 -BiLSTM,A -- superdiagnostic / 100 Hz (baseline),superdiagnostic,100,5,1000,0.8385706468313311,0.6728857241225712,515.1750118732452 -BiLSTM,C -- diagnostic (27-class) / 100 Hz,diagnostic,100,27,1000,0.8202809196685987,0.2270322400801511,621.5523679256439 -ResNet-18,A -- superdiagnostic / 100 Hz (baseline),superdiagnostic,100,5,1000,0.7629390953402366,0.4981607064856851,33.149600982666016 -ResNet-18,B -- superdiagnostic / 500 Hz,superdiagnostic,500,5,5000,0.8041790533977655,0.5295529580353364,114.70022583007812 -ResNet-18,C -- diagnostic (27-class) / 100 Hz,diagnostic,100,27,1000,0.7437596414651408,0.0829973626692842,33.51596713066101 -ResNet-18,D -- diagnostic (27-class) / 500 Hz,diagnostic,500,27,5000,,0.0567884119790278,112.71233773231506 -SE-ResNet-50,A -- superdiagnostic / 100 Hz (baseline),superdiagnostic,100,5,1000,0.80224405272653,0.3787636008457019,149.36452102661133 -SE-ResNet-50,B -- superdiagnostic / 500 Hz,superdiagnostic,500,5,5000,0.7553646129457944,0.3672557070980227,636.1527981758118 -SE-ResNet-50,C -- diagnostic (27-class) / 100 Hz,diagnostic,100,27,1000,0.7118343780332,0.0924789838720781,144.19747519493103 -SE-ResNet-50,D -- diagnostic (27-class) / 500 Hz,diagnostic,500,27,5000,,0.0573294504553054,634.5040018558502 -Lambda-ResNet-18,A -- superdiagnostic / 100 Hz (baseline),superdiagnostic,100,5,1000,0.7955438206260178,0.3970672129500629,270.942773103714 -Lambda-ResNet-18,B -- superdiagnostic / 500 Hz,superdiagnostic,500,5,5000,0.7723876093621177,0.5211916806450236,1253.451983213425 -Lambda-ResNet-18,C -- diagnostic (27-class) / 100 Hz,diagnostic,100,27,1000,,0.050866630176975,258.62340211868286 -Lambda-ResNet-18,D -- diagnostic (27-class) / 500 Hz,diagnostic,500,27,5000,,0.0817590591398973,1244.6589679718018 -BiLSTM,A -- superdiagnostic / 100 Hz (baseline),superdiagnostic,100,5,1000,0.8135959253589039,0.6451106757639339,77.51535534858704 -BiLSTM,B -- superdiagnostic / 500 Hz,superdiagnostic,500,5,5000,0.8128674499898032,0.5954051718075771,334.53321385383606 -BiLSTM,C -- diagnostic (27-class) / 100 Hz,diagnostic,100,27,1000,0.7833174078893279,0.1443118620835513,78.60738611221313 -BiLSTM,D -- diagnostic (27-class) / 500 Hz,diagnostic,500,27,5000,0.7464315651371636,0.1556990467987535,333.2661380767822 -ResNet-18,B -- superdiagnostic / 500 Hz,superdiagnostic,500,5,5000,0.7910428256059993,0.5280327234424165,80.28677606582642 -ResNet-18,D -- diagnostic (27-class) / 500 Hz,diagnostic,500,27,5000,,0.0446669233731228,74.48672533035278 -BiLSTM,B -- superdiagnostic / 500 Hz,superdiagnostic,500,5,5000,0.793495708090092,0.6315772446563357,262.47332191467285 -BiLSTM,D -- diagnostic (27-class) / 500 Hz,diagnostic,500,27,5000,,0.1162161975668708,261.98120498657227 diff --git a/examples/ptbxl_model_comparison_ablation.png b/examples/ptbxl_model_comparison_ablation.png deleted file mode 100644 index ec7be018a159b653efa654a8440a694c9296f900..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 85995 zcmeFZg3s00OK&{nQyJL&sygXIA`yDZLUkj7nC=i=e~dUFW&dsnyNHZOjKlKWHiszp6HU1 zQTdXQohQFU364B*{-6W?kn(u?(nHVL#>4BC8-z^bm4}OivxkG-YYtC{o4cK}lc=DS zpx6TrTMrKxcWEIZ$N%vSg3fMlh1fpL+k*e(vWuFrI~f_n4brc3Z!<=8$bc**6!)&19x z-*h4A|Ml~)1U+b4#5bKU*m;CcM`qW8EsP*{u^1;a$hmCR0iS(c| zpQbaz(wCoq`|M0scKV-g7OV}cvWsh^N{B!GcuT``dG<4>YTw3ijz(uV&Ca^}%CA>c zw;tHqj~7EIuiUn3;}o4jKt67w$}<`cM_yzu^@eD^DT)1NZ_z_L|#`;gaIVaYZK zl91j+@do$x+EtBrRP=oU zb+fNq&rYFnQ=v_va{KFp&cy~LHoL8Orz-HH=rGp97nTE##*Nn2Qk^B%)BWH*NkSQO zSNYhtCd#0`*lj!cvt#QjC`yoc*wc`I2^M|b`++2M;z7^qH{n;0LkDexVrsn21)mtT zeO$S|wK(bh2gzT49JCBcGPe;yyqr+Fo_dfpaC5acNs?9CL%7PO|M_0>*dtYMb}9EY zvHQ>CD{$8={B~*zF*CF7gL0mMje&ocV->z@mPps<_oqshpX}5v+-Oz}AZ>+TEQkW! zzk5P0C3NLm5Rrg#SZJM-A6qO_IZrT9G?�{>7c)|61B}`>SBI1C@lnb=Nxvi?7)} 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 From d12b11be82c0ba7c27f1a0fe214e8dbc46a2e763 Mon Sep 17 00:00:00 2001 From: "Kent R. Spillner" Date: Wed, 22 Apr 2026 18:45:25 -0500 Subject: [PATCH 37/39] Add tests for BiLSTM models for ECG diagnosis Assisted-by: Claude:Claude-4.6-Sonnet --- tests/core/test_bilstm_ecg.py | 282 ++++++++++++++++++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100644 tests/core/test_bilstm_ecg.py diff --git a/tests/core/test_bilstm_ecg.py b/tests/core/test_bilstm_ecg.py new file mode 100644 index 000000000..431112046 --- /dev/null +++ b/tests/core/test_bilstm_ecg.py @@ -0,0 +1,282 @@ +"""Tests for the BiLSTMECG model. + +Covers: + - model initialisation and attribute checks + - forward pass output keys and shapes + - backward pass (gradient flow) + - embed flag (not applicable — model returns the standard four-key dict) + - paper-aligned variants (lstm_d1_h64 and lstm_d3_h128) + - custom hyperparameters + - all three output modes (multilabel, multiclass, binary) + - variable-length input signals +""" + +import unittest + +import numpy as np +import torch + +from pyhealth.datasets import create_sample_dataset, get_dataloader +from pyhealth.models.bilstm_ecg import BiLSTMECG + +# --------------------------------------------------------------------------- +# Shared fixture helpers (mirrors test_resnet_ecg.py conventions) +# --------------------------------------------------------------------------- + +_N_LEADS = 12 +_LENGTH = 1000 # 10 s @ 100 Hz — typical PTB-XL low-rate recording +_N_LABELS = 5 # number of multilabel classes + + +def _make_samples(n: int, rng: np.random.RandomState, + label_mode: str = "multilabel") -> list: + """Return ``n`` synthetic ECG samples for the given label mode. + + For multilabel, PyHealth's MultiLabelProcessor expects a list of active + class indices, not a fixed-length binary vector. Every class in + ``range(_N_LABELS)`` is forced to appear at least once across the dataset + so the full vocabulary is established. + """ + samples = [] + for i in range(n): + if label_mode == "multilabel": + active = [j for j in range(_N_LABELS) if rng.randint(0, 2)] + forced = i % _N_LABELS + if forced not in active: + active.append(forced) + label = sorted(active) + elif label_mode == "multiclass": + label = int(rng.randint(0, 3)) + else: # binary + label = int(rng.randint(0, 2)) + samples.append({ + "patient_id": f"p{i}", + "visit_id": "v0", + "signal": rng.randn(_N_LEADS, _LENGTH).astype(np.float32), + "label": label, + }) + return samples + + +def _make_dataset(samples: list, label_mode: str): + return create_sample_dataset( + samples=samples, + input_schema={"signal": "tensor"}, + output_schema={"label": label_mode}, + dataset_name=f"test_bilstm_{label_mode}", + ) + + +def _make_model(dataset, **kwargs) -> BiLSTMECG: + """Construct a BiLSTMECG with the mandatory constructor arguments.""" + return BiLSTMECG( + dataset=dataset, + feature_keys=["signal"], + label_key="label", + mode=dataset.output_schema["label"], + **kwargs, + ) + + +def _assert_forward_output(tc: unittest.TestCase, ret: dict, + batch_size: int, n_classes: int) -> None: + """Assert the standard PyHealth forward-output contract.""" + tc.assertIn("loss", ret) + tc.assertIn("y_prob", ret) + tc.assertIn("y_true", ret) + tc.assertIn("logit", ret) + tc.assertEqual(ret["loss"].dim(), 0) + tc.assertEqual(ret["y_prob"].shape[0], batch_size) + tc.assertEqual(ret["y_prob"].shape[1], n_classes) + tc.assertEqual(ret["y_true"].shape[0], batch_size) + tc.assertEqual(ret["logit"].shape[0], batch_size) + tc.assertEqual(ret["logit"].shape[1], n_classes) + tc.assertTrue(torch.isfinite(ret["loss"])) + + +# --------------------------------------------------------------------------- +# Main test class +# --------------------------------------------------------------------------- + +class TestBiLSTMECG(unittest.TestCase): + """Tests for BiLSTMECG.""" + + def setUp(self): + rng = np.random.RandomState(0) + samples = _make_samples(5, rng, "multilabel") + self.dataset = _make_dataset(samples, "multilabel") + self.model = _make_model(self.dataset) + self.batch = next(iter(get_dataloader(self.dataset, batch_size=4, shuffle=False))) + + # -- initialisation ------------------------------------------------------- + + def test_initialization(self): + self.assertIsInstance(self.model, BiLSTMECG) + self.assertEqual(self.model.feature_key, "signal") + self.assertEqual(self.model.label_key, "label") + # Default paper variant: 1 layer, hidden_size=64 + self.assertIsInstance(self.model.lstm, torch.nn.LSTM) + self.assertTrue(self.model.lstm.bidirectional) + self.assertEqual(self.model.lstm.hidden_size, 64) + self.assertEqual(self.model.lstm.num_layers, 1) + self.assertEqual(self.model.lstm.input_size, _N_LEADS) + # FC head maps hidden*2 → n_classes + self.assertIsInstance(self.model.fc, torch.nn.Linear) + self.assertEqual(self.model.fc.in_features, 64 * 2) + self.assertEqual(self.model.fc.out_features, _N_LABELS) + + def test_lstm_is_bidirectional(self): + """Bidirectional flag is set and output dim is 2 × hidden_size.""" + self.assertTrue(self.model.lstm.bidirectional) + x = torch.randn(2, _N_LEADS, _LENGTH) + # permute to (B, T, C) as forward does + out, _ = self.model.lstm(x.permute(0, 2, 1)) + self.assertEqual(out.shape, (2, _LENGTH, 64 * 2)) + + def test_pooling_over_all_timesteps(self): + """AdaptiveAvgPool1d(1) reduces the time dimension to a single vector.""" + x = torch.randn(2, _N_LEADS, _LENGTH) + out, _ = self.model.lstm(x.permute(0, 2, 1)) # (B, T, hidden*2) + pooled = self.model.pool(out.permute(0, 2, 1)).squeeze(-1) + self.assertEqual(pooled.shape, (2, 64 * 2)) + + # -- forward -------------------------------------------------------------- + + def test_forward_multilabel(self): + with torch.no_grad(): + ret = self.model(**self.batch) + _assert_forward_output(self, ret, batch_size=4, n_classes=_N_LABELS) + # multilabel y_prob must be in [0, 1] (sigmoid output) + self.assertTrue(torch.all(ret["y_prob"] >= 0)) + self.assertTrue(torch.all(ret["y_prob"] <= 1)) + + def test_forward_multiclass(self): + rng = np.random.RandomState(1) + n_classes = 4 + samples = _make_samples(4, rng, "multiclass") + for i, s in enumerate(samples): + s["label"] = i % n_classes + ds = _make_dataset(samples, "multiclass") + model = _make_model(ds) + batch = next(iter(get_dataloader(ds, batch_size=4, shuffle=False))) + with torch.no_grad(): + ret = model(**batch) + _assert_forward_output(self, ret, batch_size=4, n_classes=n_classes) + # multiclass y_prob rows sum to ~1 (softmax output) + self.assertTrue(torch.allclose(ret["y_prob"].sum(dim=1), + torch.ones(4), atol=1e-5)) + + def test_forward_binary(self): + rng = np.random.RandomState(2) + samples = _make_samples(4, rng, "binary") + for i, s in enumerate(samples): + s["label"] = i % 2 + ds = _make_dataset(samples, "binary") + model = _make_model(ds) + batch = next(iter(get_dataloader(ds, batch_size=4, shuffle=False))) + with torch.no_grad(): + ret = model(**batch) + _assert_forward_output(self, ret, batch_size=4, n_classes=1) + self.assertTrue(torch.all(ret["y_prob"] >= 0)) + self.assertTrue(torch.all(ret["y_prob"] <= 1)) + + # -- backward ------------------------------------------------------------- + + def test_backward(self): + ret = self.model(**self.batch) + ret["loss"].backward() + has_grad = any( + p.requires_grad and p.grad is not None + for p in self.model.parameters() + ) + self.assertTrue(has_grad, "No parameters received gradients") + + def test_lstm_weights_receive_gradients(self): + """LSTM weight matrices (not just the FC head) receive gradients.""" + ret = self.model(**self.batch) + ret["loss"].backward() + lstm_params_with_grad = [ + name for name, p in self.model.lstm.named_parameters() + if p.requires_grad and p.grad is not None + ] + self.assertGreater(len(lstm_params_with_grad), 0, + "No LSTM parameters received gradients") + + # -- paper-aligned variants ----------------------------------------------- + + def test_paper_variant_lstm_d1_h64(self): + """lstm_d1_h64: 1 layer, hidden_size=64 (paper best variant).""" + model = _make_model(self.dataset, hidden_size=64, n_layers=1) + self.assertEqual(model.lstm.hidden_size, 64) + self.assertEqual(model.lstm.num_layers, 1) + self.assertEqual(model.fc.in_features, 128) + batch = next(iter(get_dataloader(self.dataset, batch_size=4, shuffle=False))) + with torch.no_grad(): + ret = model(**batch) + _assert_forward_output(self, ret, batch_size=4, n_classes=_N_LABELS) + + def test_paper_variant_lstm_d3_h128(self): + """lstm_d3_h128: 3 layers, hidden_size=128.""" + model = _make_model(self.dataset, hidden_size=128, n_layers=3) + self.assertEqual(model.lstm.hidden_size, 128) + self.assertEqual(model.lstm.num_layers, 3) + self.assertEqual(model.fc.in_features, 256) + batch = next(iter(get_dataloader(self.dataset, batch_size=4, shuffle=False))) + with torch.no_grad(): + ret = model(**batch) + _assert_forward_output(self, ret, batch_size=4, n_classes=_N_LABELS) + + # -- dropout behaviour ---------------------------------------------------- + + def test_dropout_disabled_for_single_layer(self): + """PyTorch raises a UserWarning if dropout > 0 with num_layers=1; + the implementation guards against this by passing 0.0 in that case.""" + model = _make_model(self.dataset, n_layers=1, dropout=0.5) + # PyTorch stores the effective dropout on the module + self.assertEqual(model.lstm.dropout, 0.0) + + def test_dropout_enabled_for_multi_layer(self): + """Dropout is applied between layers when n_layers > 1.""" + model = _make_model(self.dataset, n_layers=2, dropout=0.3) + self.assertAlmostEqual(model.lstm.dropout, 0.3) + + # -- custom hyperparameters ----------------------------------------------- + + def test_custom_hyperparameters(self): + model = _make_model(self.dataset, hidden_size=32, n_layers=2, dropout=0.1) + self.assertEqual(model.lstm.hidden_size, 32) + self.assertEqual(model.lstm.num_layers, 2) + self.assertEqual(model.fc.in_features, 64) # 32 * 2 (bidirectional) + batch = next(iter(get_dataloader(self.dataset, batch_size=2, shuffle=False))) + with torch.no_grad(): + ret = model(**batch) + self.assertIn("loss", ret) + self.assertEqual(ret["y_prob"].shape[1], _N_LABELS) + + # -- variable-length input ------------------------------------------------ + + def test_variable_signal_length(self): + """Model handles different signal lengths without retraining + because AdaptiveAvgPool1d(1) is length-agnostic.""" + for length in [500, 1000, 2500]: + signal = torch.randn(2, _N_LEADS, length) + batch = { + "signal": signal, + "label": self.batch["label"][:2], + } + with torch.no_grad(): + ret = self.model(**batch) + self.assertEqual(ret["logit"].shape, (2, _N_LABELS), + f"Wrong shape for signal length {length}") + + def test_high_rate_signal_length(self): + """5000-sample input (10 s @ 500 Hz, the paper's high-rate setting).""" + signal = torch.randn(4, _N_LEADS, 5000) + batch = {"signal": signal, "label": self.batch["label"]} + with torch.no_grad(): + ret = self.model(**batch) + _assert_forward_output(self, ret, batch_size=4, n_classes=_N_LABELS) + + +if __name__ == "__main__": + unittest.main() From f40d5519c6355d3da0d473b0401b0715358a18eb Mon Sep 17 00:00:00 2001 From: "Kent R. Spillner" Date: Wed, 22 Apr 2026 18:47:29 -0500 Subject: [PATCH 38/39] Match header style in test_ptbxl.py --- tests/core/test_bilstm_ecg.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/core/test_bilstm_ecg.py b/tests/core/test_bilstm_ecg.py index 431112046..1f4957b1a 100644 --- a/tests/core/test_bilstm_ecg.py +++ b/tests/core/test_bilstm_ecg.py @@ -1,4 +1,5 @@ -"""Tests for the BiLSTMECG model. +""" +Unit tests for the BiLSTMECG model. Covers: - model initialisation and attribute checks @@ -9,6 +10,11 @@ - custom hyperparameters - all three output modes (multilabel, multiclass, binary) - variable-length input signals + +Authors: + Anurag Dixit - anuragd2@illinois.edu + Kent Spillner - kspillne@illinois.edu + John Wells - jtwells2@illinois.edu """ import unittest From 279528bf968408ee46678f39ad43cb4cede672bc Mon Sep 17 00:00:00 2001 From: jtwells2 Date: Wed, 22 Apr 2026 20:13:18 -0400 Subject: [PATCH 39/39] Adding names to comments --- pyhealth/models/bilstm_ecg.py | 9 ++++++--- pyhealth/models/lambda_resnet.py | 8 +++++++- pyhealth/models/resnet.py | 8 +++++++- pyhealth/models/resnet_ecg_base.py | 8 +++++++- pyhealth/models/se_resnet.py | 8 +++++++- pyhealth/tasks/ptbxl_multilabel_classification.py | 9 ++++++--- tests/core/test_resnet_ecg.py | 8 +++++++- 7 files changed, 47 insertions(+), 11 deletions(-) diff --git a/pyhealth/models/bilstm_ecg.py b/pyhealth/models/bilstm_ecg.py index 2416bfd02..232f894c7 100644 --- a/pyhealth/models/bilstm_ecg.py +++ b/pyhealth/models/bilstm_ecg.py @@ -1,4 +1,5 @@ -"""Bidirectional LSTM for 12-lead ECG multi-label classification. +""" +Bidirectional LSTM for 12-lead ECG multi-label classification. This module provides :class:`BiLSTMECG`, a :class:`~pyhealth.models.BaseModel` subclass implementing the Bidirectional LSTM architecture benchmarked in: @@ -53,8 +54,10 @@ ``(12, T)`` loaded by ``SampleSignalDataset`` from a ``.pkl`` file. :math:`T` is typically 1000 at 100 Hz or 5000 at 500 Hz. -Author: - CS-598 DLH Project Team — PyHealth contribution +Authors: + Anurag Dixit - anuragd2@illinois.edu + Kent Spillner - kspillne@illinois.edu + John Wells - jtwells2@illinois.edu """ from typing import List, Optional diff --git a/pyhealth/models/lambda_resnet.py b/pyhealth/models/lambda_resnet.py index 0860429dd..fe6813ae0 100644 --- a/pyhealth/models/lambda_resnet.py +++ b/pyhealth/models/lambda_resnet.py @@ -1,4 +1,5 @@ -"""1-D Lambda-ResNet-18 ECG model. +""" +1-D Lambda-ResNet-18 ECG model. Implements the ``lambda_resnet1d18`` backbone used in: @@ -30,6 +31,11 @@ stability guards. See :mod:`pyhealth.models.resnet_ecg_base` for shared building blocks. + +Authors: + Anurag Dixit - anuragd2@illinois.edu + Kent Spillner - kspillne@illinois.edu + John Wells - jtwells2@illinois.edu """ from typing import List, Optional diff --git a/pyhealth/models/resnet.py b/pyhealth/models/resnet.py index f1e2a467e..ef539098a 100644 --- a/pyhealth/models/resnet.py +++ b/pyhealth/models/resnet.py @@ -1,4 +1,5 @@ -"""Plain 1-D ResNet-18 ECG model. +""" +Plain 1-D ResNet-18 ECG model. Implements the ``resnet1d18`` backbone used in: @@ -7,6 +8,11 @@ https://proceedings.mlr.press/v149/nonaka21a.html See :mod:`pyhealth.models.resnet_ecg_base` for the shared building blocks. + +Authors: + Anurag Dixit - anuragd2@illinois.edu + Kent Spillner - kspillne@illinois.edu + John Wells - jtwells2@illinois.edu """ from pyhealth.datasets import SampleDataset diff --git a/pyhealth/models/resnet_ecg_base.py b/pyhealth/models/resnet_ecg_base.py index 8635d8e04..702bccc47 100644 --- a/pyhealth/models/resnet_ecg_base.py +++ b/pyhealth/models/resnet_ecg_base.py @@ -1,4 +1,5 @@ -"""Shared building blocks for 1-D ResNet-based ECG models. +""" +Shared building blocks for 1-D ResNet-based ECG models. This module provides: @@ -16,6 +17,11 @@ Nonaka N. & Seita J. (2021). In-depth Benchmarking of Deep Neural Network Architectures for ECG Diagnosis. *PMLR* 149:1–19. https://proceedings.mlr.press/v149/nonaka21a.html + +Authors: + Anurag Dixit - anuragd2@illinois.edu + Kent Spillner - kspillne@illinois.edu + John Wells - jtwells2@illinois.edu """ from typing import Callable, Dict, List, Optional, Type, Union diff --git a/pyhealth/models/se_resnet.py b/pyhealth/models/se_resnet.py index cf3ff1b8f..dc0d0b37a 100644 --- a/pyhealth/models/se_resnet.py +++ b/pyhealth/models/se_resnet.py @@ -1,4 +1,5 @@ -"""1-D SE-ResNet-50 ECG model. +""" +1-D SE-ResNet-50 ECG model. Implements the ``se_resnet1d50`` backbone used in: @@ -20,6 +21,11 @@ the SENet-154 variant). See :mod:`pyhealth.models.resnet_ecg_base` for shared building blocks. + +Authors: + Anurag Dixit - anuragd2@illinois.edu + Kent Spillner - kspillne@illinois.edu + John Wells - jtwells2@illinois.edu """ from typing import Optional diff --git a/pyhealth/tasks/ptbxl_multilabel_classification.py b/pyhealth/tasks/ptbxl_multilabel_classification.py index 598df11ae..cab8a86b9 100644 --- a/pyhealth/tasks/ptbxl_multilabel_classification.py +++ b/pyhealth/tasks/ptbxl_multilabel_classification.py @@ -1,4 +1,5 @@ -"""PTB-XL multi-label ECG classification task. +""" +PTB-XL multi-label ECG classification task. This module provides :class:`PTBXLMultilabelClassification`, a :class:`~pyhealth.tasks.BaseTask` subclass that turns a @@ -76,8 +77,10 @@ | ``"diagnostic"`` | 500 | 27-class / 500 Hz | +-------------------+-----------+------------------------+ -Author: - CS-598 DLH Project Team +Authors: + Anurag Dixit - anuragd2@illinois.edu + Kent Spillner - kspillne@illinois.edu + John Wells - jtwells2@illinois.edu """ import logging diff --git a/tests/core/test_resnet_ecg.py b/tests/core/test_resnet_ecg.py index 7157b222e..4c3a2a221 100644 --- a/tests/core/test_resnet_ecg.py +++ b/tests/core/test_resnet_ecg.py @@ -1,4 +1,5 @@ -"""Tests for 1-D ResNet-based ECG models. +""" +Tests for 1-D ResNet-based ECG models. Covers ResNet18ECG, SEResNet50ECG, and LambdaResNet18ECG, exercising: - model initialisation and attribute checks @@ -8,6 +9,11 @@ - custom hyperparameter variants - all three output modes (multilabel, multiclass, binary) - forward_sliding_window evaluation helper + +Authors: + Anurag Dixit - anuragd2@illinois.edu + Kent Spillner - kspillne@illinois.edu + John Wells - jtwells2@illinois.edu """ import unittest