996) Evaluates prediction-set coverage/miscoverage and efficiency on the test split.
1010
1111Example (from repo root):
12- python examples/conformal_eeg/tuev_kmeans_conformal.py --root downloads/tuev/v2.0.1/edf --n-clusters 5
12+ python examples/conformal_eeg/tuev_kmeans_conformal.py --root /srv/local/data/TUH/tuh_eeg_events/v2.0.0/edf --n-clusters 5
13+ python examples/conformal_eeg/tuev_kmeans_conformal.py --quick-test --log-file quicktest_kmeans.log
1314
1415Notes:
1516- ClusterLabel uses K-means clustering on embeddings to compute cluster-specific thresholds.
2021
2122import argparse
2223import random
24+ import sys
2325from pathlib import Path
2426
2527import numpy as np
2628import torch
2729
30+
31+ class _Tee :
32+ """Writes to both a stream and a file."""
33+
34+ def __init__ (self , stream , file ):
35+ self ._stream = stream
36+ self ._file = file
37+
38+ def write (self , data ):
39+ self ._stream .write (data )
40+ self ._file .write (data )
41+ self ._file .flush ()
42+
43+ def flush (self ):
44+ self ._stream .flush ()
45+ self ._file .flush ()
46+
47+
2848from pyhealth .calib .predictionset .cluster import ClusterLabel
2949from pyhealth .calib .utils import extract_embeddings
3050from pyhealth .datasets import TUEVDataset , get_dataloader , split_by_sample_conformal
@@ -40,13 +60,13 @@ def parse_args() -> argparse.Namespace:
4060 parser .add_argument (
4161 "--root" ,
4262 type = str ,
43- default = "downloads/tuev/ v2.0.1 /edf" ,
63+ default = "/srv/local/data/TUH/tuh_eeg_events/ v2.0.0 /edf" ,
4464 help = "Path to TUEV edf/ folder." ,
4565 )
46- parser .add_argument ("--subset" , type = str , default = "both" , choices = ["train" , "eval" , "both" ])
66+ parser .add_argument ("--subset" , type = str , default = "both" , choices = ["train" , "eval" , "both" ])
4767 parser .add_argument ("--seed" , type = int , default = 42 )
48- parser .add_argument ("--batch-size" , type = int , default = 32 )
49- parser .add_argument ("--epochs" , type = int , default = 3 )
68+ parser .add_argument ("--batch-size" , type = int , default = 64 )
69+ parser .add_argument ("--epochs" , type = int , default = 20 )
5070 parser .add_argument ("--alpha" , type = float , default = 0.1 , help = "Miscoverage rate (e.g., 0.1 => 90% target coverage)." )
5171 parser .add_argument (
5272 "--ratios" ,
@@ -69,6 +89,17 @@ def parse_args() -> argparse.Namespace:
6989 default = None ,
7090 help = "Device string, e.g. 'cuda:0' or 'cpu'. Defaults to auto-detect." ,
7191 )
92+ parser .add_argument (
93+ "--log-file" ,
94+ type = str ,
95+ default = None ,
96+ help = "Path to log file. Stdout and stderr are teed to this file." ,
97+ )
98+ parser .add_argument (
99+ "--quick-test" ,
100+ action = "store_true" ,
101+ help = "Smoke test: dev=True, max 2000 samples, 2 epochs, ~5-10 min." ,
102+ )
72103 return parser .parse_args ()
73104
74105
@@ -84,6 +115,23 @@ def main() -> None:
84115 args = parse_args ()
85116 set_seed (args .seed )
86117
118+ orig_stdout , orig_stderr = sys .stdout , sys .stderr
119+ log_file = None
120+ if args .log_file :
121+ log_file = open (args .log_file , "w" , encoding = "utf-8" )
122+ sys .stdout = _Tee (orig_stdout , log_file )
123+ sys .stderr = _Tee (orig_stderr , log_file )
124+
125+ try :
126+ _run (args )
127+ finally :
128+ if log_file is not None :
129+ sys .stdout = orig_stdout
130+ sys .stderr = orig_stderr
131+ log_file .close ()
132+
133+
134+ def _run (args : argparse .Namespace ) -> None :
87135 device = args .device or ("cuda:0" if torch .cuda .is_available () else "cpu" )
88136 root = Path (args .root )
89137 if not root .exists ():
@@ -92,11 +140,19 @@ def main() -> None:
92140 "Pass --root to point to your downloaded TUEV edf/ directory."
93141 )
94142
143+ epochs = 2 if args .quick_test else args .epochs
144+ quick_test_max_samples = 2000 # cap samples so quick-test finishes in ~5-10 min
145+ if args .quick_test :
146+ print ("*** QUICK TEST MODE (dev=True, 2 epochs, max 2000 samples) ***" )
147+
95148 print ("=" * 80 )
96149 print ("STEP 1: Load TUEV + build task dataset" )
97150 print ("=" * 80 )
98- dataset = TUEVDataset (root = str (root ), subset = args .subset )
151+ dataset = TUEVDataset (root = str (root ), subset = args .subset , dev = args . quick_test )
99152 sample_dataset = dataset .set_task (EEGEventsTUEV (), cache_dir = "examples/conformal_eeg/cache" )
153+ if args .quick_test and len (sample_dataset ) > quick_test_max_samples :
154+ sample_dataset = sample_dataset .subset (range (quick_test_max_samples ))
155+ print (f"Capped to { quick_test_max_samples } samples for quick-test." )
100156
101157 print (f"Task samples: { len (sample_dataset )} " )
102158 print (f"Input schema: { sample_dataset .input_schema } " )
@@ -129,7 +185,7 @@ def main() -> None:
129185 trainer .train (
130186 train_dataloader = train_loader ,
131187 val_dataloader = val_loader ,
132- epochs = args . epochs ,
188+ epochs = epochs ,
133189 monitor = "accuracy" if val_loader is not None else None ,
134190 )
135191
0 commit comments