flowchart LR
IN["9 modalities @ 100 Hz<br/>ECG Β· ABP Β· PPG Β· CVP Β· CO2<br/>AWP Β· ICP Β· RESP-Imp Β· RESP-Flow"]
IN --> SC["Scaler<br/>per-variate loc / scale"]
SC --> PT["Patchify<br/>200 samples = 2 s / token"]
PT --> PE["Residual-MLP projection<br/>+ modality embedding"]
PE --> TR["Transformer encoder<br/>GQA Β· GLU FFN Β· RoPE Β· LSCNorm"]
SC -.->|"loc / scale as AdaLN conditioning"| TR
TR --> O1["reconstruction head<br/>β features Β· anomaly scoring"]
TR --> O2["cross-modal heads<br/>β cross-modal reconstruction"]
TR --> O3["block next-patch head<br/>β forecasting Β· roll-out"]
CARMEN is a foundation model for the continuous waveforms recorded at the bedside and in the operating room. A single Transformer encoder (~30M parameters) is pretrained across 9 signal modalities and transfers to three families of task without architectural surgery: feature extraction for downstream heads, cross-modal waveform reconstruction, and waveform forecasting.
Two design choices carry most of the weight. Every modality is tokenized the same
way β raw patches, one shared encoder β so a single model covers all nine instead of
one model per signal. And because per-patient normalization would otherwise throw
away the absolute level of a pressure waveform, the (loc, scale) stripped out by the
scaler is fed back into every layer as AdaLN modulation (LSCNorm), keeping
clinically meaningful magnitudes available to the encoder.
The nine modalities, grouped by what drives the waveform:
Important
CARMEN is pretrained at 100 Hz with a patch size of 200 samples (2 s/token). Resample your signals to 100 Hz before use.
Note
This repository is inference-only β the pretraining loop is not included.
git clone https://github.com/dlcjfgmlnasa/CARMEN.git && cd CARMEN
pip install -e . # or: pip install -r requirements.txtRequires Python β₯ 3.10, PyTorch β₯ 2.2, einops β₯ 0.7.
Pretrained weights will be published here upon acceptance of the paper. Until then this repository ships the model implementation and inference API only β the release asset referenced below is not yet available. You can still build the model from a config and run a forward pass without weights (see
examples/00_smoke_test.py).
Weights are distributed as a GitHub Release asset and are not committed to git.
Download a checkpoint into checkpoints/ β see checkpoints/README.md:
curl -L -o checkpoints/carmen.pt \
https://github.com/dlcjfgmlnasa/CARMEN/releases/download/v1.0/carmen.ptEach checkpoint embeds its own ModelConfig, so the architecture is reconstructed
automatically β you never specify it by hand.
import torch
from carmen import DownstreamModelWrapper, make_batch
# 1. Load the pretrained encoder (frozen, eval mode)
wrapper = DownstreamModelWrapper("checkpoints/carmen.pt", device="cpu")
# 2. Pack raw 1-D signals (100 Hz) into a batch β one patient, multiple modalities
t = torch.linspace(0, 30, 3000)
batch = make_batch(
[("ecg", torch.sin(2*torch.pi*1.2*t)),
("ppg", torch.sin(2*torch.pi*1.2*t - 0.6)),
("abp", 80 + 30*torch.sin(2*torch.pi*1.2*t - 0.3))],
patch_size=wrapper.patch_size,
)
# 3. One feature vector per patient
features = wrapper.extract_features(batch) # (B, d_model)No checkpoint yet? python examples/00_smoke_test.py verifies the install against a
randomly initialized model.
CARMEN.from_pretrained(path) returns the bare encoder; DownstreamModelWrapper adds
loading, freezing, pooling and LoRA on top. batch below is what make_batch returns.
from carmen import CARMEN, DownstreamModelWrapper
model = CARMEN.from_pretrained("checkpoints/carmen.pt") # bare encoder
wrapper = DownstreamModelWrapper("checkpoints/carmen.pt", device="cuda") # + freeze / pool / LoRA
# ββ Representations ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
feats = wrapper.extract_features(batch) # (B, d_model) pooled, frozen
feats = wrapper.extract_features(batch, pool="none") # (B, N, d_model) per patch
enc = model.extract_features(batch) # dict of raw encoder outputs
# ββ Generation βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
abp = model.generate_cross_modal(batch, target_signal_type=1)["waveform"]
# (B, N, patch_size)
pred = model.forecast(batch) # (B, N, K, patch_size)
roll = model.generate(batch, n_steps=10) # (n_steps, B, patch_size)
# ββ Adaptation & scoring βββββββββββββββββββββββββββββββββββββββββββββββββ
wrapper.inject_lora(rank=8) # LoRA on q_proj / v_proj
score = wrapper.get_reconstruction_loss(batch, mask) # scalar MSE, anomaly scoringTwo attention modes β task="masked" vs task="next_pred"
forward(batch, task=...) selects both the attention pattern and the heads that run.
task="masked" β bidirectional attention. Adds reconstructed and
cross_pred_per_type; this is what extract_features and generate_cross_modal use.
task="next_pred" β causal attention. Adds next_pred (B, N, K, patch_size);
this is what forecast and generate use.
Either way you also get the encoder outputs β encoded, patches, patch_mask,
loc, scale, patch_sample_id, patch_variate_id, time_id.
Feeding your own data
make_batch covers the common case. For full control, build one BiosignalSample per
channel and collate them with PackCollate:
from carmen import BiosignalSample, PackCollate, CHANNEL_NAME_TO_SIGNAL_TYPE
sample = BiosignalSample(
values=ecg, # 1-D tensor @ 100 Hz
length=ecg.numel(),
channel_idx=0, recording_idx=0, n_channels=1, win_start=0,
sampling_rate=100.0,
signal_type=CHANNEL_NAME_TO_SIGNAL_TYPE["ECG II"], # -> 0
session_id="patient-001", # samples sharing a session are paired cross-modally
start_sample=0,
)
batch = PackCollate(max_length=8192, patch_size=200)([sample])collate_mode="any_variate" (default) groups a patient's modalities into one row so
the encoder can attend across them; collate_mode="ci" treats each signal as an
independent row.
π Β quickstart.ipynb β end to end: build β features β generate
00_smoke_test.pyβ build from config and run a forward, no weights needed01_extract_features.pyβ load a checkpoint, extract pooled features02_downstream_probe.pyβ linear probe and LoRA on frozen features03_cross_modal_generation.pyβ ECG + PPG β ABP04_forecasting.pyβ block forecast and autoregressive roll-out
python examples/00_smoke_test.py
python examples/01_extract_features.py checkpoints/carmen.ptcarmen/
βββ model.py CARMEN encoder + inference API
βββ config.py ModelConfig (embedded in every checkpoint)
βββ checkpoint.py checkpoint save / load
βββ wrapper.py DownstreamModelWrapper (load / freeze / LoRA), LinearProbe
βββ batch.py make_batch / to_device β raw signals -> PackedBatch
βββ loss.py MaskedPatchLoss (reconstruction scoring)
βββ data/ PackCollate (bin-packing), BiosignalSample, signal-type maps
βββ modules/ attention (GQA), GLU FFN, RMSNorm / LSCNorm, patch embedding,
packed scalers, RoPE / attention bias
examples/ runnable examples + quickstart notebook
checkpoints/ put downloaded weights here (gitignored)
- Cross-modal reliability. Not every source β target pair is physiologically
supported. The pairs the model was trained to transfer across are listed in
carmen.CROSS_PRED_ALLOWED_PAIRS(ECGβABP, ECGβPPG, ABPβPPG, AWPβRESP_Flow). - Denormalized output is approximate.
generate_cross_modal(..., denormalize=True)rescales with the source signal'sloc/scale, because the target's own level is unknown. Treat the absolute magnitude accordingly. any_variatebatching is non-deterministic.PackCollatetrims a patient's variates to one common length so they pair up; with unequal-length inputs that length is drawn at random. Seedrandom.seed()if you need reproducible batches, or usecollate_mode="ci".
This research was supported by a grant of the Korea Health Technology R&D Project through the Korea Health Industry Development Institute (KHIDI), funded by the Ministry of Health & Welfare, Republic of Korea (grant number : RS-2024-00439677 , NTIS number:2460003917)
A paper describing CARMEN is in preparation. Until it is out, please cite this repository:
@software{carmen2026,
title = {CARMEN: A Cardiorespiratory Foundation Model for Continuous Physiological Waveforms},
author = {The CARMEN Authors},
year = {2026},
url = {https://github.com/dlcjfgmlnasa/CARMEN}
}Released under the Apache License 2.0.