deepecgkit 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. deepecgkit/__init__.py +42 -0
  2. deepecgkit/__main__.py +6 -0
  3. deepecgkit/cli/__init__.py +61 -0
  4. deepecgkit/cli/commands/__init__.py +16 -0
  5. deepecgkit/cli/commands/evaluate.py +83 -0
  6. deepecgkit/cli/commands/info.py +73 -0
  7. deepecgkit/cli/commands/predict.py +94 -0
  8. deepecgkit/cli/commands/train.py +331 -0
  9. deepecgkit/cli/config.py +23 -0
  10. deepecgkit/cli/evaluation.py +251 -0
  11. deepecgkit/cli/logger.py +26 -0
  12. deepecgkit/cli/main.py +429 -0
  13. deepecgkit/cli/registry.py +32 -0
  14. deepecgkit/datasets/__init__.py +33 -0
  15. deepecgkit/datasets/af_classification.py +301 -0
  16. deepecgkit/datasets/base.py +146 -0
  17. deepecgkit/datasets/ltafdb.py +363 -0
  18. deepecgkit/datasets/mitbih_afdb.py +287 -0
  19. deepecgkit/datasets/modules.py +222 -0
  20. deepecgkit/datasets/preprocessing.py +208 -0
  21. deepecgkit/datasets/ptbxl.py +567 -0
  22. deepecgkit/datasets/splitting.py +225 -0
  23. deepecgkit/datasets/unified_af.py +316 -0
  24. deepecgkit/evaluation/__init__.py +40 -0
  25. deepecgkit/evaluation/evaluator.py +398 -0
  26. deepecgkit/evaluation/metrics.py +164 -0
  27. deepecgkit/evaluation/visualization.py +285 -0
  28. deepecgkit/models/__init__.py +51 -0
  29. deepecgkit/models/af_classifier.py +177 -0
  30. deepecgkit/models/convnext_v2_1d.py +183 -0
  31. deepecgkit/models/crnn.py +117 -0
  32. deepecgkit/models/deep_res_cnn.py +215 -0
  33. deepecgkit/models/ecg_dualnet.py +230 -0
  34. deepecgkit/models/fcn_wang.py +88 -0
  35. deepecgkit/models/gru_model.py +99 -0
  36. deepecgkit/models/inception_time.py +197 -0
  37. deepecgkit/models/kanres_wide_x.py +150 -0
  38. deepecgkit/models/kanres_x.py +126 -0
  39. deepecgkit/models/lstm_model.py +96 -0
  40. deepecgkit/models/mamba1d.py +232 -0
  41. deepecgkit/models/medformer.py +229 -0
  42. deepecgkit/models/resnet1d.py +189 -0
  43. deepecgkit/models/resnet1d_wang.py +182 -0
  44. deepecgkit/models/se_resnet1d.py +232 -0
  45. deepecgkit/models/simple_cnn.py +82 -0
  46. deepecgkit/models/tcn.py +178 -0
  47. deepecgkit/models/transformer_ecg.py +127 -0
  48. deepecgkit/models/xresnet1d.py +265 -0
  49. deepecgkit/models/xresnet1d_benchmark.py +227 -0
  50. deepecgkit/registry.py +97 -0
  51. deepecgkit/training/__init__.py +9 -0
  52. deepecgkit/training/train.py +171 -0
  53. deepecgkit/utils/__init__.py +72 -0
  54. deepecgkit/utils/download.py +123 -0
  55. deepecgkit/utils/weights.py +107 -0
  56. deepecgkit-0.1.0.dist-info/METADATA +99 -0
  57. deepecgkit-0.1.0.dist-info/RECORD +61 -0
  58. deepecgkit-0.1.0.dist-info/WHEEL +5 -0
  59. deepecgkit-0.1.0.dist-info/entry_points.txt +2 -0
  60. deepecgkit-0.1.0.dist-info/licenses/LICENSE +201 -0
  61. deepecgkit-0.1.0.dist-info/top_level.txt +1 -0
deepecgkit/cli/main.py ADDED
@@ -0,0 +1,429 @@
1
+ """Main CLI entry point with argument parsing and command routing."""
2
+
3
+ import argparse
4
+ import sys
5
+ from typing import Optional
6
+
7
+ import deepecgkit.cli as _cli
8
+ from deepecgkit.cli.config import load_config
9
+ from deepecgkit.cli.logger import CLILogger
10
+ from deepecgkit.cli.registry import DATASET_NAMES, MODEL_NAMES
11
+
12
+
13
+ def main(argv: Optional[list] = None) -> int:
14
+ """Main CLI entry point."""
15
+ parser = argparse.ArgumentParser(
16
+ prog="deepecg",
17
+ description="DeepECG-Kit: Deep learning toolkit for ECG analysis",
18
+ formatter_class=argparse.RawDescriptionHelpFormatter,
19
+ epilog="""
20
+ Examples:
21
+ deepecg train -m kanres -d af-classification
22
+ deepecg train -m kanres -d af-classification --force-download
23
+ deepecg evaluate --checkpoint model.ckpt -d af-classification
24
+ deepecg predict --checkpoint model.ckpt --input ecg.npy
25
+ deepecg resume --checkpoint model.ckpt --epochs 100
26
+ deepecg list-models
27
+ deepecg info -m kanres
28
+
29
+ Note: Datasets are automatically downloaded when first used.
30
+ """,
31
+ )
32
+
33
+ parser.add_argument(
34
+ "--verbose",
35
+ "-v",
36
+ action="store_true",
37
+ help="Enable verbose output",
38
+ )
39
+ parser.add_argument(
40
+ "--quiet",
41
+ "-q",
42
+ action="store_true",
43
+ help="Suppress non-essential output",
44
+ )
45
+ parser.add_argument(
46
+ "--config",
47
+ "-c",
48
+ type=str,
49
+ help="Path to configuration file (YAML or JSON)",
50
+ )
51
+
52
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
53
+
54
+ train_parser = subparsers.add_parser("train", help="Train a model on a dataset")
55
+ train_parser.add_argument(
56
+ "--model",
57
+ "-m",
58
+ type=str,
59
+ required=True,
60
+ choices=MODEL_NAMES,
61
+ help="Model architecture to train",
62
+ )
63
+ train_parser.add_argument(
64
+ "--dataset",
65
+ "-d",
66
+ type=str,
67
+ required=True,
68
+ choices=DATASET_NAMES,
69
+ help="Dataset to train on",
70
+ )
71
+ train_parser.add_argument(
72
+ "--data-dir",
73
+ type=str,
74
+ default=None,
75
+ help="Directory containing the dataset (default: auto-detect)",
76
+ )
77
+ train_parser.add_argument(
78
+ "--output-dir",
79
+ "-o",
80
+ type=str,
81
+ default=None,
82
+ help="Output directory (default: runs/{timestamp}-{model}-{dataset})",
83
+ )
84
+ train_parser.add_argument(
85
+ "--weights",
86
+ "-w",
87
+ type=str,
88
+ default=None,
89
+ help="Path to weights file or pretrained weight name",
90
+ )
91
+ train_parser.add_argument(
92
+ "--epochs",
93
+ "-e",
94
+ type=int,
95
+ default=50,
96
+ help="Number of training epochs (default: 50)",
97
+ )
98
+ train_parser.add_argument(
99
+ "--batch-size",
100
+ "-b",
101
+ type=int,
102
+ default=32,
103
+ help="Batch size (default: 32)",
104
+ )
105
+ train_parser.add_argument(
106
+ "--learning-rate",
107
+ "--lr",
108
+ type=float,
109
+ default=1e-3,
110
+ help="Learning rate (default: 0.001)",
111
+ )
112
+ train_parser.add_argument(
113
+ "--val-split",
114
+ type=float,
115
+ default=0.2,
116
+ help="Validation split ratio (default: 0.2)",
117
+ )
118
+ train_parser.add_argument(
119
+ "--test-split",
120
+ type=float,
121
+ default=0.1,
122
+ help="Test split ratio (default: 0.1)",
123
+ )
124
+ train_parser.add_argument(
125
+ "--num-workers",
126
+ type=int,
127
+ default=4,
128
+ help="Number of data loading workers (default: 4)",
129
+ )
130
+ train_parser.add_argument(
131
+ "--accelerator",
132
+ type=str,
133
+ default="auto",
134
+ choices=["auto", "cpu", "gpu", "mps"],
135
+ help="Accelerator to use (default: auto)",
136
+ )
137
+ train_parser.add_argument(
138
+ "--devices",
139
+ type=int,
140
+ default=1,
141
+ help="Number of devices to use (default: 1)",
142
+ )
143
+ train_parser.add_argument(
144
+ "--force-download",
145
+ action="store_true",
146
+ help="Force re-download the dataset even if it exists",
147
+ )
148
+ train_parser.add_argument(
149
+ "--early-stopping-patience",
150
+ type=int,
151
+ default=10,
152
+ help="Early stopping patience (default: 10)",
153
+ )
154
+ train_parser.add_argument(
155
+ "--seed",
156
+ type=int,
157
+ default=42,
158
+ help="Random seed (default: 42)",
159
+ )
160
+ train_parser.add_argument(
161
+ "--multi-label",
162
+ action="store_true",
163
+ default=False,
164
+ help="Use multi-label classification with BCE loss (default: single-label with CE loss)",
165
+ )
166
+ train_parser.add_argument(
167
+ "--sampling-rate",
168
+ type=int,
169
+ default=None,
170
+ help="Target sampling rate in Hz for ECG signals (e.g., 100 or 500 for PTB-XL)",
171
+ )
172
+ train_parser.add_argument(
173
+ "--binary-classification",
174
+ action="store_true",
175
+ default=False,
176
+ help="Use binary classification (AF vs Non-AF) for AF datasets (default: 4-class)",
177
+ )
178
+ train_parser.add_argument(
179
+ "--normalization",
180
+ type=str,
181
+ default=None,
182
+ choices=["zscore", "minmax", "none"],
183
+ help="Normalization method for ECG signals (default: dataset-specific)",
184
+ )
185
+
186
+ eval_parser = subparsers.add_parser("evaluate", help="Evaluate a trained model")
187
+ eval_parser.add_argument(
188
+ "--checkpoint",
189
+ type=str,
190
+ required=True,
191
+ help="Path to model checkpoint",
192
+ )
193
+ eval_parser.add_argument(
194
+ "--dataset",
195
+ "-d",
196
+ type=str,
197
+ required=True,
198
+ choices=DATASET_NAMES,
199
+ help="Dataset to evaluate on",
200
+ )
201
+ eval_parser.add_argument(
202
+ "--data-dir",
203
+ type=str,
204
+ default=None,
205
+ help="Directory containing the dataset",
206
+ )
207
+ eval_parser.add_argument(
208
+ "--batch-size",
209
+ "-b",
210
+ type=int,
211
+ default=32,
212
+ help="Batch size (default: 32)",
213
+ )
214
+ eval_parser.add_argument(
215
+ "--num-workers",
216
+ type=int,
217
+ default=4,
218
+ help="Number of data loading workers (default: 4)",
219
+ )
220
+ eval_parser.add_argument(
221
+ "--accelerator",
222
+ type=str,
223
+ default="auto",
224
+ choices=["auto", "cpu", "gpu", "mps"],
225
+ help="Accelerator to use (default: auto)",
226
+ )
227
+ eval_parser.add_argument(
228
+ "--devices",
229
+ type=int,
230
+ default=1,
231
+ help="Number of devices to use (default: 1)",
232
+ )
233
+ eval_parser.add_argument(
234
+ "--force-download",
235
+ action="store_true",
236
+ help="Force re-download the dataset even if it exists",
237
+ )
238
+ eval_parser.add_argument(
239
+ "--split",
240
+ type=str,
241
+ default="test",
242
+ choices=["test", "val"],
243
+ help="Dataset split to evaluate on (default: test)",
244
+ )
245
+
246
+ predict_parser = subparsers.add_parser("predict", help="Run inference on ECG data")
247
+ predict_parser.add_argument(
248
+ "--checkpoint",
249
+ type=str,
250
+ required=True,
251
+ help="Path to model checkpoint",
252
+ )
253
+ predict_parser.add_argument(
254
+ "--input",
255
+ "-i",
256
+ type=str,
257
+ required=True,
258
+ help="Path to input ECG file (.npy, .csv, or WFDB .dat/.hea)",
259
+ )
260
+ predict_parser.add_argument(
261
+ "--output",
262
+ "-o",
263
+ type=str,
264
+ default=None,
265
+ help="Path to save predictions (JSON format)",
266
+ )
267
+ predict_parser.add_argument(
268
+ "--batch-size",
269
+ "-b",
270
+ type=int,
271
+ default=1,
272
+ help="Batch size (default: 1)",
273
+ )
274
+ predict_parser.add_argument(
275
+ "--accelerator",
276
+ type=str,
277
+ default="auto",
278
+ choices=["auto", "cpu", "gpu", "mps"],
279
+ help="Accelerator to use (default: auto)",
280
+ )
281
+
282
+ resume_parser = subparsers.add_parser("resume", help="Resume training from a checkpoint")
283
+ resume_parser.add_argument(
284
+ "--checkpoint",
285
+ type=str,
286
+ required=True,
287
+ help="Path to checkpoint to resume from",
288
+ )
289
+ resume_parser.add_argument(
290
+ "--epochs",
291
+ "-e",
292
+ type=int,
293
+ default=None,
294
+ help="Additional epochs to train (default: continue original)",
295
+ )
296
+ resume_parser.add_argument(
297
+ "--output-dir",
298
+ "-o",
299
+ type=str,
300
+ default=None,
301
+ help="Directory to save new checkpoints",
302
+ )
303
+ resume_parser.add_argument(
304
+ "--accelerator",
305
+ type=str,
306
+ default="auto",
307
+ choices=["auto", "cpu", "gpu", "mps"],
308
+ help="Accelerator to use (default: auto)",
309
+ )
310
+ resume_parser.add_argument(
311
+ "--devices",
312
+ type=int,
313
+ default=1,
314
+ help="Number of devices to use (default: 1)",
315
+ )
316
+ resume_parser.add_argument(
317
+ "--early-stopping-patience",
318
+ type=int,
319
+ default=10,
320
+ help="Early stopping patience (default: 10)",
321
+ )
322
+
323
+ info_parser = subparsers.add_parser("info", help="Show model information")
324
+ info_parser.add_argument(
325
+ "--model",
326
+ "-m",
327
+ type=str,
328
+ required=True,
329
+ choices=MODEL_NAMES,
330
+ help="Model to show information for",
331
+ )
332
+
333
+ subparsers.add_parser("list-models", help="List all available models")
334
+ subparsers.add_parser("list-datasets", help="List all available datasets")
335
+
336
+ args = parser.parse_args(argv)
337
+
338
+ config = {}
339
+ if args.config:
340
+ try:
341
+ config = load_config(args.config)
342
+ except Exception as e:
343
+ print(f"[ERROR] Failed to load config: {e}", file=sys.stderr)
344
+ return 1
345
+
346
+ logger = CLILogger(
347
+ verbose=args.verbose or config.get("verbose", False),
348
+ quiet=args.quiet or config.get("quiet", False),
349
+ )
350
+
351
+ if args.command is None:
352
+ parser.print_help()
353
+ return 1
354
+
355
+ if args.command == "train":
356
+ train_args = {
357
+ "model_name": args.model,
358
+ "dataset_name": args.dataset,
359
+ "data_dir": args.data_dir,
360
+ "output_dir": args.output_dir,
361
+ "weights": args.weights,
362
+ "epochs": args.epochs,
363
+ "batch_size": args.batch_size,
364
+ "learning_rate": args.learning_rate,
365
+ "val_split": args.val_split,
366
+ "test_split": args.test_split,
367
+ "num_workers": args.num_workers,
368
+ "accelerator": args.accelerator,
369
+ "devices": args.devices,
370
+ "force_download": args.force_download,
371
+ "early_stopping_patience": args.early_stopping_patience,
372
+ "seed": args.seed,
373
+ "multi_label": args.multi_label,
374
+ "sampling_rate": args.sampling_rate,
375
+ "binary_classification": args.binary_classification,
376
+ "normalization": args.normalization,
377
+ }
378
+ for key, value in config.get("train", {}).items():
379
+ if key in train_args and train_args[key] == parser.get_default(key):
380
+ train_args[key] = value
381
+ return _cli.train(logger=logger, **train_args)
382
+
383
+ elif args.command == "evaluate":
384
+ return _cli.evaluate(
385
+ checkpoint=args.checkpoint,
386
+ dataset_name=args.dataset,
387
+ data_dir=args.data_dir,
388
+ batch_size=args.batch_size,
389
+ num_workers=args.num_workers,
390
+ accelerator=args.accelerator,
391
+ devices=args.devices,
392
+ force_download=args.force_download,
393
+ split=args.split,
394
+ logger=logger,
395
+ )
396
+
397
+ elif args.command == "predict":
398
+ return _cli.predict(
399
+ checkpoint=args.checkpoint,
400
+ input_path=args.input,
401
+ output_path=args.output,
402
+ batch_size=args.batch_size,
403
+ accelerator=args.accelerator,
404
+ logger=logger,
405
+ )
406
+
407
+ elif args.command == "resume":
408
+ return _cli.resume(
409
+ checkpoint=args.checkpoint,
410
+ epochs=args.epochs,
411
+ output_dir=args.output_dir,
412
+ accelerator=args.accelerator,
413
+ devices=args.devices,
414
+ early_stopping_patience=args.early_stopping_patience,
415
+ logger=logger,
416
+ )
417
+
418
+ elif args.command == "info":
419
+ return _cli.show_info(model_name=args.model, logger=logger)
420
+
421
+ elif args.command == "list-models":
422
+ _cli.list_models()
423
+ return 0
424
+
425
+ elif args.command == "list-datasets":
426
+ _cli.list_datasets()
427
+ return 0
428
+
429
+ return 0
@@ -0,0 +1,32 @@
1
+ """Registry helpers and constants for the CLI."""
2
+
3
+ import deepecgkit.datasets
4
+ import deepecgkit.models # noqa: F401
5
+ from deepecgkit.registry import (
6
+ _DATASET_REGISTRY,
7
+ _MODEL_REGISTRY,
8
+ get_dataset_info,
9
+ get_dataset_names,
10
+ get_model_names,
11
+ )
12
+
13
+ MODEL_NAMES = get_model_names()
14
+ DATASET_NAMES = get_dataset_names()
15
+ DATASET_NUM_CLASSES = {n: get_dataset_info(n)["num_classes"] for n in DATASET_NAMES}
16
+ DATASET_INPUT_CHANNELS = {n: info["input_channels"] for n, info in _DATASET_REGISTRY.items()}
17
+ MODEL_DESCRIPTIONS = {n: info["description"] for n, info in _MODEL_REGISTRY.items()}
18
+
19
+ MODEL_WEIGHTS = {
20
+ "afmodel": ["afmodel-30s"],
21
+ "kanres": ["kanres-af-30s"],
22
+ }
23
+
24
+
25
+ def _get_model_registry():
26
+ """Return model registry mapping names to classes."""
27
+ return {name: info["class"] for name, info in _MODEL_REGISTRY.items()}
28
+
29
+
30
+ def _get_dataset_registry():
31
+ """Return dataset registry mapping names to classes."""
32
+ return {name: info["class"] for name, info in _DATASET_REGISTRY.items()}
@@ -0,0 +1,33 @@
1
+ """
2
+ Dataset modules for ECG data handling.
3
+
4
+ This module provides dataset classes and data loading utilities for ECG analysis.
5
+ """
6
+
7
+ from .af_classification import AFClassificationDataset
8
+ from .base import BaseECGDataset
9
+ from .ltafdb import LTAFDBDataset
10
+ from .mitbih_afdb import MITBIHAFDBDataset
11
+ from .modules import ECGDataModule
12
+ from .preprocessing import (
13
+ ECGSegmenter,
14
+ ECGStandardizer,
15
+ RhythmAnnotationExtractor,
16
+ convert_to_tensor,
17
+ )
18
+ from .ptbxl import PTBXLDataset
19
+ from .unified_af import UnifiedAFDataset
20
+
21
+ __all__ = [
22
+ "AFClassificationDataset",
23
+ "BaseECGDataset",
24
+ "ECGDataModule",
25
+ "ECGSegmenter",
26
+ "ECGStandardizer",
27
+ "LTAFDBDataset",
28
+ "MITBIHAFDBDataset",
29
+ "PTBXLDataset",
30
+ "RhythmAnnotationExtractor",
31
+ "UnifiedAFDataset",
32
+ "convert_to_tensor",
33
+ ]