Skip to content

About

No description, website, or topics provided.

Resources

Stars

9 stars

Watchers

1 watching

Forks

Latest commit

 

History

11 Commits

Folders and files

Repository files navigation

PULSE

Patient Unified Longitudinal Signal Engine for longitudinal multimodal clinical data with missing visits and modalities.

Installation

pip install -e .

Requires Python 3.8+, PyTorch 1.9+, NumPy, and optionally tqdm.

To install the base dependencies from the repository:

pip install -r requirements.txt

Benchmark-specific dependencies are documented in each benchmark folder README.

Quick Start

import torch
from torch.utils.data import DataLoader

from pulse.data.dataset import LongitudinalDataset, collate_visits, create_synthetic_data
from pulse.model import PulseModel
from pulse.training.trainer import Trainer

device = "cuda:0" if torch.cuda.is_available() else "cpu"

modality_dims = {"modality_a": 61, "modality_b": 251}
model = PulseModel(
    modality_dims=modality_dims,
    latent_dim=16,
    hidden_dim=32,
    temporal_model="recurrent",  # or "transformer"
).to(device)

patient_ids, visits_data, missing_masks = create_synthetic_data(
    n_patients=1000,
    n_visits=3,
    modality_dims=modality_dims,
    missing_rate=0.3,
)
dataset = LongitudinalDataset(
    patient_ids=patient_ids,
    visits_data=visits_data,
    missing_masks=missing_masks,
    train_mask_rate=0.6,
)
dataloader = DataLoader(dataset, batch_size=4, shuffle=True, collate_fn=collate_visits)

optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
trainer = Trainer(model, optimizer, device=device)
history = trainer.train(dataloader, n_epochs=50)

PULSE input-output demo

Paper-to-Code Mapping (Reproducibility)

  • scripts/Ablation/ PULSE ablations on the two-visit metabolomics imputation task:
    • historical-state ablation: scripts/Ablation/run_metabolomics_ablations.py
    • alignment variants (stop-gradient, point-wise): scripts/Ablation/run_metabolomics_ablations.py
    • loss ablations (remove L_align, remove L_adv): scripts/Ablation/run_metabolomics_ablations.py
    • active-masking sensitivity: scripts/Ablation/run_mask_probability_sensitivity.py
  • scripts/Benchmark-single-cell-Method/ Four models for metabolomics imputation:
    • MIDAS: scripts/Benchmark-single-cell-Method/run_midas.py
    • scVAEIT: scripts/Benchmark-single-cell-Method/run_scvaeit.py
    • StabMap: scripts/Benchmark-single-cell-Method/run_stabmap_from_csv.py
    • PULSE: scripts/Benchmark-single-cell-Method/run_pulse.py
  • scripts/Bencmark-EHR-Modeling/ Four models for EHR/proteomics-generation:
    • PULSE: scripts/Bencmark-EHR-Modeling/pulse_benchmark.py
    • Transformer: scripts/Bencmark-EHR-Modeling/transformer_benchmark.py
    • Llama 3.1: scripts/Bencmark-EHR-Modeling/llama31_benchmark.py
  • scripts/PULSE_MIMIC/ Four models for MIMIC-III:
    • note-only Llama: scripts/PULSE_MIMIC/run_notes_only.py
    • lab-only Transformer: scripts/PULSE_MIMIC/run_lab_transformer.py
    • late-fusion (Llama + labs): scripts/PULSE_MIMIC/run_simply_combined.py
    • PULSE: scripts/PULSE_MIMIC/run_pulse.py

Recommended Data Format for Your Specific Task

  • visits_data: List[Dict[str, Tensor]], one dictionary per visit.
  • missing_masks: List[Dict[str, int]], one dictionary per visit.
  • Mask convention:
    • 0: currently available
    • 1: actively masked during training
    • 2: naturally missing

Example:

visits_data = [
    {"modality_a": modality_a_t1, "modality_b": modality_b_t1},
    {"modality_a": modality_a_t2, "modality_b": None},
    {"modality_a": modality_a_t3, "modality_b": modality_b_t3},
]
missing_masks = [
    {"modality_a": 0, "modality_b": 0},
    {"modality_a": 0, "modality_b": 2},
    {"modality_a": 0, "modality_b": 0},
]

Recommended Training and Inference for Your Specific Task

Train with active masking:

CUDA_VISIBLE_DEVICES=0 python scripts/train.py --device cuda:0 --n_patients 100 --n_visits 3 --epochs 10 --train_mask_rate 0.6 --modality_a_name modality_a --modality_a_dim 61 --modality_b_name modality_b --modality_b_dim 251 --save_dir ./ckpts

Train with transformer-based temporal modeling:

CUDA_VISIBLE_DEVICES=0 python scripts/train.py --device cuda:0 --temporal_model transformer --temporal_num_heads 4 --temporal_num_layers 1

Inference from a checkpoint:

CUDA_VISIBLE_DEVICES=0 python scripts/inference.py --device cuda:0 --checkpoint ./ckpts/checkpoint_epoch_10.pt

Framework Summary

For visit N, PULSE:

  1. Encodes only the currently available modalities.
  2. Builds the historical state with a temporal autoregressive module.
  3. Aligns available modality distributions with the historical latent state.
  4. Computes the visit state with equal-weight fusion:
Z^N = (Z_P + sum(Z_M)) / (M_total + 1)

Here M_total counts only the currently observed modalities and does not include the historical state Z_P.

  1. Decodes every modality from the fused visit state.

Temporal Module Options

The autoregressive component is not restricted to an RNN formulation.

  • temporal_model="recurrent" uses a GRU-style hidden-state update.
  • temporal_model="transformer" uses causal self-attention over previous visit states and is better suited to longer visit sequences.

Both options plug into the same PULSE fusion and decoding pipeline.

API Summary

Component Description
PulseModel(..., temporal_model="recurrent") Main model with recurrent or transformer temporal dynamics
model.forward(visits_data, missing_masks, kl_annealing_weight=1.0, recon_targets=None) Returns reconstructions and losses
model.impute_missing(visits_data, missing_masks) Returns per-visit imputations
LongitudinalDataset(..., train_mask_rate=0.6) Dataset with single-modality active masking
Trainer(model, optimizer).train(...) Training loop with KL annealing and checkpointing

License

MIT

About

No description, website, or topics provided.

Resources

Stars

9 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages