Pulse2Pulse Training¶
This page covers training the Pulse2Pulse GAN on MIMIC-IV-ECG and
generating samples from a trained checkpoint. Training runs through the shared runner, ecgen train
(see Training with ecgen train).
| File | Role |
|---|---|
configs/experiments/pulse2pulse_mimic.yaml |
Experiment config (model.name: pulse2pulse) |
scripts/shell/run_train_pulse2pulse_mimic.sh |
python -m ecgen train <config> [overrides] |
scripts/shell/run_train_pulse2pulse_standalone.sh |
Same, with settings from environment variables |
scripts/generate/generate_pulse2pulse.py |
Sample generation and plotting from a checkpoint |
configs/model/gan/pulse2pulse.yaml |
Model fragment (not read by anything) |
Training¶
Option A: ecgen train (or its wrapper)¶
ecgen train configs/experiments/pulse2pulse_mimic.yaml
ecgen train configs/experiments/pulse2pulse_mimic.yaml \
data.dataset.params.root=/data/mimic-iv-ecg data.dataset.params.max_samples=1000 \
trainer.max_epochs=5 wandb.enabled=false
./scripts/shell/run_train_pulse2pulse_mimic.sh [config] [overrides] # default configs/experiments/pulse2pulse_mimic.yaml
Option B: environment-variable wrapper¶
DATA_DIR=/data/mimic-iv-ecg BATCH_SIZE=64 GPU_ID=1 \
./scripts/shell/run_train_pulse2pulse_standalone.sh
The wrapper cds to the repo root and runs pulse2pulse_mimic.yaml with these overrides:
| Variable | Default | Override |
|---|---|---|
DATA_DIR |
/work/vajira/DATA/SEARCH/MIMIC_IV_ECG_raw_v1/mimic-iv-ecg-diagnostic-electrocardiogram-matched-subset-1.0 |
data.dataset.params.root |
EXP_NAME |
pulse2pulse_mimic |
experiment.name |
BATCH_SIZE |
128 |
data.batch_size |
MAX_EPOCHS |
300 |
trainer.max_epochs |
GPU_ID |
0 |
trainer.devices=[GPU_ID] |
NUM_WORKERS |
4 |
data.num_workers |
It also always sets data.dataset.params.max_samples=null (full dataset),
data.dataset.params.skip_missing_check=true and wandb.enabled=false; further key=value
arguments are appended and win.
Configuration¶
configs/experiments/pulse2pulse_mimic.yaml:
| Section | Values |
|---|---|
model |
name: pulse2pulse; Pulse2PulseConfig fields model_size: 50, num_channels: 8, seq_length: 5000 (multiple of 2500), lr: 1e-4, b1: 0.5, b2: 0.9, lmbda: 10.0 (gradient-penalty weight), n_critic: 5 |
data.dataset |
mimic_iv_ecg with leads: independent8 (I, II, V1–V6), seq_length: 5000, scale: zscore, max_samples: 10000 |
data |
batch_size: 128, num_workers: 4, split_params: {val: {max_samples: 1000}} (caps validation at 1000 records) |
checkpoint |
monitor: val/d_wasserstein, mode: max, save_top_k: 3 |
callbacks |
ecg_visualization (every_n_epochs: 10), generated_samples (every_n_epochs: 25, n_samples: 16) |
trainer |
max_epochs: 300, devices: [0], check_val_every_n_epoch: 5, log_every_n_steps: 50 |
wandb |
enabled: true, project: ecg-pulse2pulse |
model.params.num_channels must equal the number of selected leads; the runner's
compatibility check stops the run otherwise.
configs/model/gan/pulse2pulse.yaml (latent_dim: 100, base_channels: 64, lr_g/lr_d:
0.0002, b2: 0.999) does not match Pulse2PulseConfig and nothing reads it.
Data¶
Batches come from the generic ECGDataModule over MIMICIVECG (see
MIMIC-IV-ECG): train and val splits from the dataset's seeded,
subject-level split, each record (8, 5000), z-scored per record over the selected leads.
Pulse2PulseGAN reads batch.signal and ignores the measurement conditions.
Changed lead set
The leads are now I, II, V1–V6 (leads: independent8), which match the CSV columns that
ecgen.generate writes. The removed scripts/train/train_pulse2pulse.py fed the
first 8 leads in file order (I, II, III, aVR, aVF, aVL, V1, V2) and z-scored all 12 leads before
dropping 4. Models trained that way have a different lead layout from new ones.
Outputs¶
runs/<exp-name>/seed_<seed>/
├── checkpoints/
│ ├── epoch{EEE}-step{SSSSSS}.ckpt # top-3 by val/d_wasserstein (mode="max")
│ └── last.ckpt
├── samples/
│ ├── sample_epoch_NNNN.png # ECGVisualizationCallback: real vs fake, first sample
│ ├── generated_epoch_NNNN.pt # GeneratedSamplesCallback: (16, C, L) tensor
│ └── generated_epoch_NNNN.png
├── metrics/train_val_metrics.json
├── tb/ # TensorBoard logs
├── config_resolved.yaml
├── metadata.json
└── wandb/ # only with wandb.enabled
Sample callbacks rarely fire with the default schedule
Both callbacks run in on_validation_epoch_end and only act when
trainer.current_epoch % every_n_epochs == 0. With check_val_every_n_epoch: 5,
validation runs at current_epoch 4, 9, 14, … These values are never multiples of 10 or
25, so in practice the callbacks only write files during the sanity-check validation at
epoch 0 (verified with Lightning 2.0). To get periodic plots, use values that line up,
e.g. trainer.check_val_every_n_epoch=1, or pick every_n_epochs so that current_epoch + 1
is a multiple of the validation interval.
For the meaning of the logged train/* and val/* metrics, see
Pulse2Pulse GAN → Logged metrics.
Generating samples¶
python scripts/generate/generate_pulse2pulse.py \
--checkpoint runs/pulse2pulse_mimic/seed_42/checkpoints/last.ckpt \
--output-dir generated_samples \
--n-samples 16 \
--save-tensors
| Flag | Default | Description |
|---|---|---|
--checkpoint |
required | Lightning checkpoint |
--output-dir |
generated_samples |
Created if missing |
--n-samples |
16 |
Number of ECGs to generate |
--device |
cuda:0 |
Falls back to CPU if CUDA is unavailable |
--seed |
42 |
torch.manual_seed |
--save-tensors |
off | Also save generated_samples.pt |
The script writes:
generated_grid.png: a 4-column grid with all leads overlaid per samplegenerated_sample_0_detailed.png: one subplot per lead for sample 0generated_sample_{0..3}.png: per-lead plots for the firstmin(n_samples, 4)samplesgenerated_samples.pt: a(n_samples, C, L)CPU tensor, only with--save-tensors
The script loads the model with
Pulse2PulseGAN.load_from_checkpoint(args.checkpoint, map_location=device) and needs no extra
arguments; see Pulse2Pulse GAN → Loading a checkpoint.
Setup check¶
scripts/utils/test_pulse2pulse_setup.py instantiates the model, the legacy data config
(ecgen.data.pulse2pulse_mimic), callbacks, losses and metrics from the ecgen package as a quick
smoke test:
For an end-to-end check without data, train for one epoch on synthetic ECGs:
ecgen train configs/experiments/pulse2pulse_mimic.yaml data.dataset.name=synthetic \
'data.dataset.params={num_samples: 64, leads: independent8}' data.split_params={} \
trainer.max_epochs=1 trainer.check_val_every_n_epoch=1 \
trainer.accelerator=cpu trainer.devices=1 wandb.enabled=false
See also¶
- Pulse2Pulse GAN: architecture, loss, known issues
- Callbacks
- Training Guide
- Evaluation Metrics