From 64279788c868e6069a7442e75f613cec2be46e4f Mon Sep 17 00:00:00 2001 From: WEN Hao Date: Thu, 29 Oct 2026 11:32:01 +0800 Subject: [PATCH] docs: style pass over the user guide/tutorial/examples; fix two broken snippets Language: apply the formal, declarative register of the paper writing to the pages added in the docs overhaul -- no casual/journalistic phrasing, no anthropomorphism: - 'walks through the package the way you would use it' -> 'follows the typical usage flow'; 'lifted from' -> 'taken from'; 'five-minute tour' -> 'short introduction'; 'live in' -> 'are provided in' - 'The trainer in one paragraph' -> 'Overview'; 'owns the training loop' -> 'implements'; 'you define what is trained' -> 'the subclass defines'; 'What you get for free' -> 'Provided by the base class'; 'your inference loop'/'in your DataLoaders' -> definite article - 'ship with' -> 'are provided with'/'provides' (x3); 'scalar predictions' -> 'predicted probabilities'; 'double as' -> 'also serve as'; 'Highlights to look for' -> 'Points to note' - garbled 'the number of classes/leads-out' -> 'the class list and the number of leads' Correctness (found by executing the snippets; they had never been run): - tutorial and user_guide/models used 'ECG_CRNN_CONFIG.copy()' / 'LUDBTrainCfg.copy()' -- dict.copy() on a CFG returns a plain dict, so the very next attribute assignment raises AttributeError (the pitfall the concepts page documents); both now use copy.deepcopy - 'ECG_CRNN(config, n_leads=12, n_classes=26)' -- wrong signature; the constructor is ECG_CRNN(classes, n_leads, config) - 'config.cnn.name = "resnet_block_leadwise"' -- no such block key; replaced with 'resnet_nature_comm_bottle_neck_se' - 'compute_output_shape(bs=2, input_len=4000)' -- wrong keyword names; the signature is compute_output_shape(seq_len, batch_size), verified to return (2, n_classes) All corrected snippets executed against the package before writing them down. --- docs/source/examples.rst | 8 ++++---- docs/source/tutorial.rst | 25 ++++++++++++++---------- docs/source/user_guide/evaluation.rst | 12 ++++++------ docs/source/user_guide/index.rst | 8 ++++---- docs/source/user_guide/models.rst | 21 +++++++++++--------- docs/source/user_guide/training.rst | 28 ++++++++++++++------------- 6 files changed, 56 insertions(+), 46 deletions(-) diff --git a/docs/source/examples.rst b/docs/source/examples.rst index 74375ae4..c04bfb33 100644 --- a/docs/source/examples.rst +++ b/docs/source/examples.rst @@ -6,8 +6,8 @@ Examples The :code:`benchmarks/` directory of the `repository `__ contains eight complete, self-contained training examples -- one per challenge / -database -- written against the modern library API. They double as the -reference usage examples for the whole package: each one shows reader and +database -- written against the modern library API. They also serve +as the reference usage for the whole package: each one shows reader and dataset usage, model construction, training with the library trainer, and evaluation with the official challenge metrics where applicable. @@ -54,8 +54,8 @@ The examples - Multi-task (classification + regression) on circulatory sounds, CinC Challenge 2022 - :code:`CRNN_CINC2022`, :code:`Wav2Vec2` variants -Highlights to look for ----------------------- +Points to note +-------------- - **Determinism**: every :code:`train.py` calls :func:`seed_everything` and uses :code:`worker_init_fn`; a given :code:`--seed` fully diff --git a/docs/source/tutorial.rst b/docs/source/tutorial.rst index cfca0d2c..f0c914e7 100644 --- a/docs/source/tutorial.rst +++ b/docs/source/tutorial.rst @@ -3,8 +3,9 @@ Tutorial ======== -A five-minute tour of the main building blocks. The complete, runnable -versions of these patterns live in the :ref:`examples `. +A short introduction to the main building blocks. Complete, runnable +versions of these patterns are provided in the :ref:`examples +`. 1. Read a database ------------------ @@ -31,7 +32,9 @@ segment length, ...) and yield tensors: from torch_ecg.databases.datasets import LUDBDataset, LUDBTrainCfg - train_cfg = LUDBTrainCfg.copy() + from copy import deepcopy + + train_cfg = deepcopy(LUDBTrainCfg) train_cfg.db_dir = "~/data/ludb" train_cfg.n_epochs = 10 ds_train = LUDBDataset(train_cfg, training=True, lazy=False) @@ -39,25 +42,27 @@ segment length, ...) and yield tensors: 3. Pick a model --------------- -Models take a config from :mod:`torch_ecg.model_configs` (or your own -CFG) plus the number of classes / leads: +Models take a config from :mod:`torch_ecg.model_configs` (or a custom +CFG), together with the class list and the number of leads: .. code-block:: python from torch_ecg.models import ECG_CRNN from torch_ecg.model_configs import ECG_CRNN_CONFIG - config = ECG_CRNN_CONFIG.copy() - config.cnn.name = "resnet_block_leadwise" - model = ECG_CRNN(config, n_leads=12, n_classes=26) + from copy import deepcopy + + config = deepcopy(ECG_CRNN_CONFIG) + config.cnn.name = "resnet_nature_comm_bottle_neck_se" + model = ECG_CRNN(classes=classes, n_leads=12, config=config) 4. Train and reload ------------------- Subclass :class:`~torch_ecg.components.trainer.BaseTrainer` (or reuse one of the benchmark trainers) and call :code:`train()`. Checkpoints written -by the trainer contain the full model config, so a trained model is a -one-liner to reload: +by the trainer contain the full model config, so a trained model can +be reloaded with a single call: .. code-block:: python diff --git a/docs/source/user_guide/evaluation.rst b/docs/source/user_guide/evaluation.rst index 8bdd0fbe..e0191a50 100644 --- a/docs/source/user_guide/evaluation.rst +++ b/docs/source/user_guide/evaluation.rst @@ -19,7 +19,7 @@ multi-label support: ) labels = np.array([[1, 0, 1], [0, 1, 0]]) - probs = np.array([[0.9, 0.1, 0.4], [0.2, 0.8, 0.3]]) # scalar predictions + probs = np.array([[0.9, 0.1, 0.4], [0.2, 0.8, 0.3]]) # predicted probabilities cm = confusion_matrix(labels, probs > 0.5) metrics = metrics_from_confusion_matrix(labels, probs) # sens/spec/prec/f1/auc per class + macro @@ -30,8 +30,8 @@ arrays to a superset of classes. Official challenge metrics -------------------------- -The exact scoring functions of the challenges ship with the -corresponding database modules -- use them instead of reimplementations: +The exact scoring functions of the challenges are provided with the +corresponding database modules; use them instead of reimplementations: .. code-block:: python @@ -48,7 +48,7 @@ Waveform delineation For P/QRS/T delineation (mask output of the U-Net models), :func:`~torch_ecg.utils.utils_metrics.compute_wave_delineation_metrics` turns truth/prediction masks into sensitivities, F1 and onset/offset -deviation statistics; LUDB additionally ships +deviation statistics; LUDB additionally provides ``compute_metrics`` / ``compute_metrics_waveform`` used by its example: .. code-block:: python @@ -72,5 +72,5 @@ locations, given as **lists of per-record arrays**: pred = [np.array([101, 299, 505]), np.array([82, 288, 511, 705, 900])] QRS_score(truth, pred, fs=500) # 0.85 -The CPSC2019 module additionally ships the official challenge metric -used by its example. +The CPSC2019 module additionally provides the official challenge +metric used by its example. diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst index 71c58740..dfff2bcd 100644 --- a/docs/source/user_guide/index.rst +++ b/docs/source/user_guide/index.rst @@ -3,9 +3,9 @@ User Guide ========== -This guide walks through the package the way you would use it: read a -database, wrap it in a dataset, pick and configure a model, train it -with the library trainer, and evaluate with (official) metrics. +This guide follows the typical usage flow: reading a database, +wrapping it in a dataset, selecting and configuring a model, training +with the library trainer, and evaluating with official metrics. .. toctree:: :maxdepth: 1 @@ -17,6 +17,6 @@ with the library trainer, and evaluate with (official) metrics. evaluation Every code block in this guide is either executed as part of the test -suite or lifted from the :ref:`examples ` (the `benchmarks/ +suite or taken from the :ref:`examples ` (the `benchmarks/ `__ directory), which double as continuously-tested reference usage. diff --git a/docs/source/user_guide/models.rst b/docs/source/user_guide/models.rst index 827e8b3f..d39ceada 100644 --- a/docs/source/user_guide/models.rst +++ b/docs/source/user_guide/models.rst @@ -6,22 +6,24 @@ Models and configurations Task models ----------- -The task-level models take a config, the number of leads and the number -of classes/leads-out: +The task-level models take the class list, the number of leads, and a +config: .. code-block:: python from torch_ecg.models import ECG_CRNN from torch_ecg.model_configs import ECG_CRNN_CONFIG - config = ECG_CRNN_CONFIG.copy() + from copy import deepcopy + + config = deepcopy(ECG_CRNN_CONFIG) config.cnn.name = "resnet_nature_comm_bottle_neck_se" - model = ECG_CRNN(config, n_leads=12, n_classes=26) + model = ECG_CRNN(classes=classes, n_leads=12, config=config) import torch - out = model(torch.randn(2, 12, 4000)) # (2, 26) - model.compute_output_shape(bs=2, input_len=4000) # (2, 26) -- shape is computable - # without running a forward pass + out = model(torch.randn(2, 12, 4000)) # (2, 26) for 26 classes + model.compute_output_shape(4000, 2) # (2, 26) -- computable without + # a forward pass The full catalogue of task models (classification, sequence labeling, U-Net segmentation, RR-LSTM, ...) is in :mod:`torch_ecg.models`; each @@ -35,7 +37,7 @@ Models are assembled from registered backbones and blocks; the ready made block configs are catalogued in :mod:`torch_ecg.model_configs` (VGG / ResNet families / TResNet / DenseNet / Xception / MobileNet / MultiScopic CNN / attention modules / RNN / MLP heads). Custom backbones -plug in through the registries: +are registered through the same registries: .. code-block:: python @@ -52,7 +54,8 @@ Checkpoints Checkpoints written via :meth:`~torch_ecg.utils.utils_nn.CkptMixin.save` carry the **full** -model config, so a trained model is one line to reload, on any machine: +model config, so a trained model can be reloaded with a single call, +on any machine: .. code-block:: python diff --git a/docs/source/user_guide/training.rst b/docs/source/user_guide/training.rst index f0daa541..dc884510 100644 --- a/docs/source/user_guide/training.rst +++ b/docs/source/user_guide/training.rst @@ -3,13 +3,14 @@ Training ======== -The trainer in one paragraph ----------------------------- - -:class:`~torch_ecg.components.trainer.BaseTrainer` owns the training -loop (epochs, device handling, criterion setup, logging, early -stopping, checkpoint retention, resume); you define **what** is -trained: the datasets, one optimization step, and the evaluation. The +Overview +-------- + +:class:`~torch_ecg.components.trainer.BaseTrainer` implements the +training loop (epochs, device handling, criterion setup, logging, +early stopping, checkpoint retention, resume); the subclass defines +what is trained: the datasets, one optimization step, and the +evaluation. The complete reference implementation is `benchmarks/train_crnn_cinc2021/trainer.py `__, @@ -42,7 +43,7 @@ Anatomy of a trainer subclass @torch.no_grad() def evaluate(self, data_loader): - ... # your inference loop (self._model.inference(...)) + ... # the inference loop (self._model.inference(...)) return {"weighted_f1": ...} # keys must contain the monitored metric The train config @@ -50,8 +51,9 @@ The train config A train config is a plain :class:`~torch_ecg.cfg.CFG`; the required fields are ``classes``, ``n_epochs``, ``batch_size``, ``log_step``, -``optimizer``, ``lr_scheduler``, ``learning_rate`` (plus -``extra_required_train_config_fields`` you define, e.g. ``db_dir``). +``optimizer``, ``lr_scheduler``, ``learning_rate`` (plus any +``extra_required_train_config_fields`` defined by the task, e.g. +``db_dir``). Start from a library ``TrainCfg``, adapt, inherit the rest: .. code-block:: python @@ -79,8 +81,8 @@ Construction and launch: ) best = trainer.train() # the best checkpoint dict; blocks until done -What you get for free ---------------------- +Provided by the base class +-------------------------- - **checkpoints**: best-on-monitor and periodic checkpoints via ``CkptMixin.save`` with the **full** model config (``keep_checkpoint_max`` @@ -90,7 +92,7 @@ What you get for free set up by the base class - **logging**: per-run log files auto-named from the config, tensorboard support -- **seeding**: pass ``worker_init_fn`` in your DataLoaders (the +- **seeding**: pass ``worker_init_fn`` in the DataLoaders (the benchmark shows how) and call :func:`~torch_ecg.utils.seed_everything` once in ``main()``; with a fixed seed a run is fully determined