Upload README.md with huggingface_hub
Browse files
README.md
CHANGED
|
@@ -1,3 +1,445 @@
|
|
| 1 |
---
|
| 2 |
license: mit
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
license: mit
|
| 3 |
+
library_name: pytorch
|
| 4 |
+
pipeline_tag: feature-extraction
|
| 5 |
+
tags:
|
| 6 |
+
- electrodermal-activity
|
| 7 |
+
- eda
|
| 8 |
+
- wearables
|
| 9 |
+
- physiological-signals
|
| 10 |
+
- time-series
|
| 11 |
+
- contrastive-learning
|
| 12 |
+
- foundation-model
|
| 13 |
---
|
| 14 |
+
|
| 15 |
+
# πΈ UME: Foundation Model for Electrodermal Activity (EDA) Signals
|
| 16 |
+
|
| 17 |
+
[](https://www.python.org/downloads/)
|
| 18 |
+
[](https://pytorch.org/)
|
| 19 |
+
[](https://lightning.ai/)
|
| 20 |
+
[](https://opensource.org/licenses/MIT)
|
| 21 |
+
[](https://doi.org/10.1145/3857981)
|
| 22 |
+
[](https://arxiv.org/abs/2603.16878)
|
| 23 |
+
|
| 24 |
+
See also π« **EDAMAME**, the large-scale EDA pretraining collection we curated to train UME: [repository](https://github.com/LeonardoAlchieri/EDAMAME) Β· [website](https://pc.inf.usi.ch/datasets/edamame/) (see [Pretraining data](#pretraining-data)).
|
| 25 |
+
|
| 26 |
+
---
|
| 27 |
+
|
| 28 |
+
<a id="quick-use"></a>
|
| 29 |
+
|
| 30 |
+
## π Quick Use (UME)
|
| 31 |
+
|
| 32 |
+
```bash
|
| 33 |
+
pip install git+https://github.com/LeonardoAlchieri/eda-foundation-models.git
|
| 34 |
+
```
|
| 35 |
+
|
| 36 |
+
```python
|
| 37 |
+
import torch
|
| 38 |
+
from ume import UMEPipeline
|
| 39 |
+
|
| 40 |
+
# Downloads the base UME weights from the Hugging Face Hub
|
| 41 |
+
pipeline = UMEPipeline.from_pretrained()
|
| 42 |
+
|
| 43 |
+
# NOTE: default UME requires 60s windows (at 4Hz sampling rate) and 3 channels (mixed, phasic and tonic)
|
| 44 |
+
x = torch.randn(8, 240, 3) # (batch, seq_len, channels)
|
| 45 |
+
embeddings = pipeline.embed(x)
|
| 46 |
+
print(embeddings.shape) # (8, 64)
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
To load from a local clone instead, pass the weights file: `UMEPipeline.from_pretrained(weights_path="model.safetensors")` (see [Checkpoints and Weights](#checkpoints)). A runnable walkthrough is in [`notebook/pipeline_demo.ipynb`](notebook/pipeline_demo.ipynb); for other ways to install, see [Installation](#installation).
|
| 50 |
+
|
| 51 |
+
---
|
| 52 |
+
|
| 53 |
+
## π Table of Contents
|
| 54 |
+
|
| 55 |
+
- [Overview](#overview)
|
| 56 |
+
- [Model Card](#model-card)
|
| 57 |
+
- [Installation](#installation)
|
| 58 |
+
- [Fine-Tune](#fine-tune)
|
| 59 |
+
- [Retrain](#retrain)
|
| 60 |
+
- [Pretraining data](#pretraining-data)
|
| 61 |
+
- [UME Architecture (Base)](#architecture)
|
| 62 |
+
- [UME Configurations](#configurations)
|
| 63 |
+
- [Checkpoints and Weights](#checkpoints)
|
| 64 |
+
- [Citation](#citation)
|
| 65 |
+
- [License](#license)
|
| 66 |
+
|
| 67 |
+
---
|
| 68 |
+
|
| 69 |
+
<a id="overview"></a>
|
| 70 |
+
|
| 71 |
+
## π― Overview
|
| 72 |
+
|
| 73 |
+
This repo contains the code and weights for **UME**, the first foundation model for wearable EDA data. **UME** is based on EfficientNet architecture and is trained using sample-wise contrastive learning, using the set of data augmentations from [Katie Matton](https://github.com/kmatton/contrastive-learning-for-eda).
|
| 74 |
+
|
| 75 |
+
We release the weights for base UME, the default and suggested configuration, as well as other configurations (tiny, small and large). See below for more information on repo usage.
|
| 76 |
+
|
| 77 |
+
---
|
| 78 |
+
|
| 79 |
+
<a id="model-card"></a>
|
| 80 |
+
|
| 81 |
+
## πͺͺ Model Card
|
| 82 |
+
|
| 83 |
+
- **What it is:** an encoder that turns 60-second windows of wrist electrodermal activity (EDA) into 64-dimensional embeddings.
|
| 84 |
+
- **Architecture:** EfficientNet-style 1D CNN (stem convolution, 16 MBConv1D blocks with squeeze-and-excitation, head convolution, global average pooling, linear projection). See [`ume/model/efficientnet_contrastive.py`](ume/model/efficientnet_contrastive.py) and [UME Architecture](#architecture).
|
| 85 |
+
- **Training objective:** sample-wise contrastive learning (InfoNCE, temperature 0.1) between two augmented views of the same window, using the EDA augmentations of [Matton et al. (2024)](https://arxiv.org/abs/2404.08783).
|
| 86 |
+
- **Files:** see [Checkpoints and Weights](#checkpoints).
|
| 87 |
+
- **Developed by:** Leonardo Alchieri, UniversitΓ della Svizzera italiana (USI).
|
| 88 |
+
- **Paper:** [doi.org/10.1145/3857981](https://doi.org/10.1145/3857981) Β· [arXiv:2603.16878](https://arxiv.org/abs/2603.16878)
|
| 89 |
+
|
| 90 |
+
### Input
|
| 91 |
+
|
| 92 |
+
- Shape `(batch, 240, 3)`: 240 samples = 60 s at 4 Hz.
|
| 93 |
+
- Channels, in order: mixed EDA, phasic component, tonic component.
|
| 94 |
+
- Signals at other sampling rates or window lengths should be resampled and cut to match.
|
| 95 |
+
|
| 96 |
+
### Intended use
|
| 97 |
+
|
| 98 |
+
- Extracting features from wearable EDA for research on downstream tasks such as stress, cognitive load, or sleep/wake classification.
|
| 99 |
+
- Fine-tuning on a labelled research dataset with `UMEPipeline.fit` (see [Fine-Tune](#fine-tune)).
|
| 100 |
+
|
| 101 |
+
### Out-of-scope use
|
| 102 |
+
|
| 103 |
+
- Clinical diagnosis, or any medical or safety-critical decision about an individual.
|
| 104 |
+
- Inferring sensitive traits or emotional states of people without their informed consent, including workplace or surveillance monitoring.
|
| 105 |
+
- Signals that are not wrist EDA (e.g. PPG, ECG) or that are sampled very differently, without first validating performance.
|
| 106 |
+
|
| 107 |
+
### Training data
|
| 108 |
+
|
| 109 |
+
Pretrained without labels on 17 public and research EDA datasets from the EDAMAME collection (see [Pretraining data](#pretraining-data)). The data is not distributed with the model. Recordings come mostly from research studies with wrist-worn devices such as the Empatica E4, which shapes the populations, devices and settings the model has seen.
|
| 110 |
+
|
| 111 |
+
### Evaluation
|
| 112 |
+
|
| 113 |
+
Downstream evaluation (frozen features and a classical classifier, validated leave-one-person-out) is run by `train.py` through the optional `edamame_downstream` package. Results are reported in the [paper](https://doi.org/10.1145/3857981).
|
| 114 |
+
|
| 115 |
+
### Limitations and risks
|
| 116 |
+
|
| 117 |
+
- **Population and device bias.** Performance can drop on populations, devices, body locations or recording conditions that are underrepresented in the pretraining data.
|
| 118 |
+
- **Artifacts.** EDA is sensitive to motion, temperature and sensor contact. The model does not detect or flag poor-quality signal.
|
| 119 |
+
- **Individual differences.** EDA varies a lot between people. Validate with person-independent splits (e.g. leave-one-person-out) rather than random splits, which overestimate performance.
|
| 120 |
+
- **Privacy.** Physiological signals are personal data. Handle any data you use with the model according to its consent terms and applicable law.
|
| 121 |
+
|
| 122 |
+
---
|
| 123 |
+
|
| 124 |
+
<a id="installation"></a>
|
| 125 |
+
|
| 126 |
+
## π§ Installation
|
| 127 |
+
|
| 128 |
+
### Pixi (recommended)
|
| 129 |
+
|
| 130 |
+
The environment is defined in [`pixi.toml`](pixi.toml). Install [Pixi](https://pixi.sh), then:
|
| 131 |
+
|
| 132 |
+
```bash
|
| 133 |
+
git clone https://github.com/LeonardoAlchieri/eda-foundation-models.git
|
| 134 |
+
cd eda-foundation-models
|
| 135 |
+
pixi install --frozen
|
| 136 |
+
```
|
| 137 |
+
|
| 138 |
+
`--frozen` installs the default environment straight from `pixi.lock`. Without it, Pixi also checks the optional `downstream` environment, which needs the sibling repository described below.
|
| 139 |
+
|
| 140 |
+
### pip
|
| 141 |
+
|
| 142 |
+
```bash
|
| 143 |
+
pip install -e . # inference and fine-tuning (UMEPipeline)
|
| 144 |
+
pip install -e ".[train]" # + everything train.py needs for pretraining
|
| 145 |
+
```
|
| 146 |
+
|
| 147 |
+
Other extras: `mantis` (Mantis-8M backbone), `dev` (pytest, black, flake8, isort), `notebook`. See [INSTALL.md](INSTALL.md).
|
| 148 |
+
|
| 149 |
+
### Optional: downstream evaluation
|
| 150 |
+
|
| 151 |
+
After pretraining, `train.py` can evaluate the new model on labelled downstream tasks. This needs the separate [`edamame_downstream`](https://github.com/LeonardoAlchieri/pretrained_foundation_models_physiological_data) package, cloned next to this repository:
|
| 152 |
+
|
| 153 |
+
```bash
|
| 154 |
+
git clone https://github.com/LeonardoAlchieri/pretrained_foundation_models_physiological_data.git ../pretrained_foundation_models_physiological_data
|
| 155 |
+
pixi install -e downstream
|
| 156 |
+
pixi run -e downstream python train.py --config-name conf-ume
|
| 157 |
+
```
|
| 158 |
+
|
| 159 |
+
Without it, `train.py` pretrains as usual and skips the downstream step with a warning. Set `downstream.enabled=false` to skip it explicitly.
|
| 160 |
+
|
| 161 |
+
---
|
| 162 |
+
|
| 163 |
+
<a id="fine-tune"></a>
|
| 164 |
+
|
| 165 |
+
## π Fine-Tune (UME)
|
| 166 |
+
|
| 167 |
+
Beyond frozen-feature extraction, the pipeline can be **supervised fine-tuned** for classification through a scikit-learn-style API (`fit` / `predict` / `predict_proba`). A classification head (`LayerNorm -> Linear` by default, matching Mantis and Pulse-PPG) is attached on top of the encoder embedding and trained with cross-entropy.
|
| 168 |
+
|
| 169 |
+
> **Note:** fine-tuning is currently supported **only for the EfficientNet (UME) encoder** (`model_name="efficientnet"`). MAE returns per-patch tokens rather than a pooled embedding, so it (and Mantis) raise `NotImplementedError`.
|
| 170 |
+
|
| 171 |
+
```python
|
| 172 |
+
import torch
|
| 173 |
+
from ume import UMEPipeline
|
| 174 |
+
|
| 175 |
+
pipeline = UMEPipeline.from_pretrained()
|
| 176 |
+
|
| 177 |
+
X = torch.randn(256, 240, 3) # (n_samples, seq_len, channels)
|
| 178 |
+
y = torch.randint(0, 2, (256,)).numpy() # labels may be ints or strings
|
| 179 |
+
|
| 180 |
+
pipeline.fit(X, y, fine_tuning_type="full", num_epochs=20)
|
| 181 |
+
|
| 182 |
+
probs = pipeline.predict_proba(X) # (n_samples, num_classes); column j -> pipeline.classes_[j]
|
| 183 |
+
preds = pipeline.predict(X) # labels in the original space passed to `fit`
|
| 184 |
+
```
|
| 185 |
+
|
| 186 |
+
Labels are encoded internally to contiguous integer ids (sorted unique values, exposed as `pipeline.classes_`) and mapped back by `predict`.
|
| 187 |
+
|
| 188 |
+
### Fine-tuning strategies
|
| 189 |
+
|
| 190 |
+
| `fine_tuning_type` | Behaviour |
|
| 191 |
+
|--------------------|-----------|
|
| 192 |
+
| `"full"` (default) | Fine-tune the whole encoder + head. Pass `backbone_lr` for a smaller encoder LR than the head's `learning_rate`. |
|
| 193 |
+
| `"head"` | Freeze the encoder (linear probe) and train the head only; the backbone stays in eval mode so BatchNorm statistics are not updated. |
|
| 194 |
+
| `"scratch"` | Re-initialise the encoder and train everything from random weights, ignoring the pretrained checkpoint β the "no pretraining" baseline. |
|
| 195 |
+
|
| 196 |
+
### Validation, early stopping and best-checkpoint selection
|
| 197 |
+
|
| 198 |
+
Supply a validation set either explicitly with `validation_data=(X_val, y_val)` or by holding out a stratified fraction of the training data with `validation_split` (e.g. `0.2`). `validation_data` takes precedence when both are given.
|
| 199 |
+
|
| 200 |
+
```python
|
| 201 |
+
pipeline.fit(
|
| 202 |
+
X, y,
|
| 203 |
+
fine_tuning_type="full",
|
| 204 |
+
learning_rate=1e-3,
|
| 205 |
+
backbone_lr=1e-4,
|
| 206 |
+
num_epochs=100,
|
| 207 |
+
validation_split=0.2,
|
| 208 |
+
class_weight="balanced", # inverse-frequency CE weights (or pass a tensor)
|
| 209 |
+
early_stopping=True,
|
| 210 |
+
monitor="val_macro_f1", # or "val_acc" (maximized) / "val_loss" (minimized)
|
| 211 |
+
patience=10,
|
| 212 |
+
restore_best_weights=True, # roll back to the best epoch when training ends
|
| 213 |
+
)
|
| 214 |
+
```
|
| 215 |
+
|
| 216 |
+
Without `early_stopping`, all `num_epochs` are run and no checkpoint selection happens. With it, training stops after `patience` epochs without a `min_delta` improvement in `monitor`, and `restore_best_weights=True` (the default) restores the encoder + head from the best epoch.
|
| 217 |
+
|
| 218 |
+
### Refit on the full dataset
|
| 219 |
+
|
| 220 |
+
On small labelled datasets, permanently holding out labels for validation hurts the final model. `refit_on_full_data=True` (which requires `early_stopping=True`) instead runs training in two phases:
|
| 221 |
+
|
| 222 |
+
1. **Phase 1** trains with the validation hold-out purely to find the optimal training budget β the *optimizer-step* count of the best epoch (steps, not epochs, because the full dataset yields more batches per epoch).
|
| 223 |
+
2. **Phase 2** resets the encoder + head to their initial weights, rebuilds the optimizer, and retrains on 100% of the data (train + validation combined) for exactly that step budget, with no validation set.
|
| 224 |
+
|
| 225 |
+
```python
|
| 226 |
+
pipeline.fit(
|
| 227 |
+
X, y,
|
| 228 |
+
validation_split=0.2,
|
| 229 |
+
early_stopping=True,
|
| 230 |
+
refit_on_full_data=True,
|
| 231 |
+
)
|
| 232 |
+
```
|
| 233 |
+
|
| 234 |
+
The refit model cannot be validated internally, so use an outer evaluation loop (e.g. leave-one-person-out) for unbiased performance estimates. `restore_best_weights` is ignored in this mode, since the refit replaces the phase-1 model entirely.
|
| 235 |
+
|
| 236 |
+
### Key `fit` arguments
|
| 237 |
+
|
| 238 |
+
| Argument | Default | Purpose |
|
| 239 |
+
|----------|---------|---------|
|
| 240 |
+
| `fine_tuning_type` | `"full"` | `"full"` / `"head"` / `"scratch"` (see above). |
|
| 241 |
+
| `learning_rate` | `1e-3` | LR for the head (and the backbone when `backbone_lr` is None). |
|
| 242 |
+
| `backbone_lr` | `None` | Optional separate, typically smaller, encoder LR. |
|
| 243 |
+
| `batch_size` / `num_epochs` | `64` / `100` | Mini-batch size and epoch count. |
|
| 244 |
+
| `weight_decay` | `0.0` | AdamW weight decay. |
|
| 245 |
+
| `validation_data` / `validation_split` | `None` / `0.0` | Explicit validation set, or stratified hold-out fraction. |
|
| 246 |
+
| `class_weight` | `None` | `"balanced"` for inverse-frequency CE weights, or a `(num_classes,)` tensor. |
|
| 247 |
+
| `early_stopping` / `patience` / `monitor` / `min_delta` | `False` / `10` / `"val_macro_f1"` / `0.0` | Early-stopping controls. |
|
| 248 |
+
| `restore_best_weights` | `True` | Roll back to the best monitored epoch at the end of training. |
|
| 249 |
+
| `refit_on_full_data` | `False` | Two-phase budget-then-refit training (see above). |
|
| 250 |
+
| `use_layer_norm` | `True` | `LayerNorm -> Linear` head; set `False` for a bare linear head. |
|
| 251 |
+
| `verbose` | `True` | Print per-epoch loss / macro-F1. |
|
| 252 |
+
|
| 253 |
+
---
|
| 254 |
+
|
| 255 |
+
<a id="retrain"></a>
|
| 256 |
+
|
| 257 |
+
## ποΈ Retrain (UME)
|
| 258 |
+
|
| 259 |
+
We provide Hydra config files to reproduce the experiments from our work. Modify them to try different configurations. Retraining needs the pretraining data, which is not included in this repository (see [Pretraining data](#pretraining-data)).
|
| 260 |
+
|
| 261 |
+
Notes on the defaults:
|
| 262 |
+
|
| 263 |
+
- **Logging.** Runs log to [Weights & Biases](https://wandb.ai) under your default entity. Add `pretrain/logger=csv` (or `tensorboard`, `none`) to train without a W&B account.
|
| 264 |
+
- **Epoch length.** An "epoch" is 3000 randomly sampled batches of 256 windows (`pretrain.trainer.limit_train_batches`), as for the released model. Set it to `null` to use the full training set per epoch.
|
| 265 |
+
- **Quick check.** Add `pretrain.trainer.fast_dev_run=5` to run a few batches end to end.
|
| 266 |
+
|
| 267 |
+
### 1. Train base UME
|
| 268 |
+
|
| 269 |
+
```bash
|
| 270 |
+
pixi run python train.py --config-name conf-ume
|
| 271 |
+
```
|
| 272 |
+
|
| 273 |
+
### 2. Train other model and dataset sizes
|
| 274 |
+
|
| 275 |
+
```bash
|
| 276 |
+
# Base (default)
|
| 277 |
+
python train.py --config-name conf-ume model_size=base
|
| 278 |
+
|
| 279 |
+
# Other presets
|
| 280 |
+
python train.py --config-name conf-ume model_size=tiny
|
| 281 |
+
python train.py --config-name conf-ume model_size=small
|
| 282 |
+
python train.py --config-name conf-ume model_size=large
|
| 283 |
+
```
|
| 284 |
+
|
| 285 |
+
```bash
|
| 286 |
+
# Base (default)
|
| 287 |
+
python train.py --config-name conf-ume pretrain.dataset.subsample=1.0
|
| 288 |
+
|
| 289 |
+
# Other presets
|
| 290 |
+
python train.py --config-name conf-ume pretrain.dataset.subsample=0.05
|
| 291 |
+
python train.py --config-name conf-ume pretrain.dataset.subsample=0.5
|
| 292 |
+
python train.py --config-name conf-ume pretrain.dataset.subsample=0.75
|
| 293 |
+
```
|
| 294 |
+
|
| 295 |
+
We also provide templates for sweeps for the model and dataset size (see [sweep-ume-ablation-size.yaml](configs/sweep-ume-ablation-size.yaml) and [sweep-ume-ablation-subsample.yaml](configs/sweep-ume-ablation-subsample.yaml)).
|
| 296 |
+
|
| 297 |
+
---
|
| 298 |
+
|
| 299 |
+
<a id="pretraining-data"></a>
|
| 300 |
+
|
| 301 |
+
## π« Pretraining data
|
| 302 |
+
|
| 303 |
+
UME was pretrained on π« **EDAMAME**, which we release in two versions:
|
| 304 |
+
|
| 305 |
+
- **Open**: freely downloadable.
|
| 306 |
+
- **Restricted**: the full collection, available after signing a data sharing agreement.
|
| 307 |
+
|
| 308 |
+
See the [EDAMAME repository](https://github.com/LeonardoAlchieri/EDAMAME) for a description of the collection and the [EDAMAME website](https://pc.inf.usi.ch/datasets/edamame/) for access to both versions. The data itself is **not** included in this repository.
|
| 309 |
+
|
| 310 |
+
The base UME was pretrained on these datasets from the collection (the list in [`configs/conf-ume.yaml`](configs/conf-ume.yaml)):
|
| 311 |
+
|
| 312 |
+
BiHeartS, big-ideas-glycemic-wearable, Dynamics, EmpaticaE4Stress, EPM, LAUREATE, MEFAR, M2Sleep, PPG_FieldStudy, SEED, SEED-II-Lab, SEED-II-Wild, Stress Predict Dataset, ToadStool, WEEE, WESD, Workplaces.
|
| 313 |
+
|
| 314 |
+
`train.py` reads the data from `pretrain.dataset.base_path` (default `data_online.nosync/`), with one folder per dataset:
|
| 315 |
+
|
| 316 |
+
```
|
| 317 |
+
data_online.nosync/
|
| 318 |
+
βββ <dataset_name>/
|
| 319 |
+
βββ *.h5 # one or more pandas HDFStore files
|
| 320 |
+
```
|
| 321 |
+
|
| 322 |
+
Each key in an HDF5 store is one recording, stored as a pandas DataFrame with the EDA channels as columns (mixed, phasic, tonic, sampled at 4 Hz). An optional `Timestamp` column is dropped. Each key's storer has an `attrs.metadata` dict with `user`, `session`, `dataset_name` and `len_data` (number of samples). The loader cuts 60 s windows (240 samples) on the fly with a stride of `shift` samples, and splits train and validation by recording, so windows from one recording never end up in both.
|
| 323 |
+
|
| 324 |
+
---
|
| 325 |
+
|
| 326 |
+
<a id="architecture"></a>
|
| 327 |
+
|
| 328 |
+
## ποΈ UME Architecture (Base)
|
| 329 |
+
|
| 330 |
+
UME is an EfficientNet-inspired 1D encoder for contrastive learning:
|
| 331 |
+
|
| 332 |
+
```
|
| 333 |
+
Input (N, T, C)
|
| 334 |
+
-> Stem Conv1D + BN + Swish
|
| 335 |
+
-> MBConv1D blocks (repeated)
|
| 336 |
+
-> Head Conv1D + Global Pool
|
| 337 |
+
-> Linear embedding
|
| 338 |
+
-> Contrastive loss on two augmented views
|
| 339 |
+
```
|
| 340 |
+
|
| 341 |
+
Base preset (default), set through the `model_size` presets in [`configs/conf-ume.yaml`](configs/conf-ume.yaml):
|
| 342 |
+
|
| 343 |
+
| Parameter | Base Value |
|
| 344 |
+
|-----------|------------|
|
| 345 |
+
| `stem_channels` | `64` |
|
| 346 |
+
| `mbconv_channels` | `64` |
|
| 347 |
+
| `num_mbconv_blocks` | `16` |
|
| 348 |
+
| `head_channels` | `248` |
|
| 349 |
+
| `embedding_dim` | `64` |
|
| 350 |
+
| `kernel_size` | `3` |
|
| 351 |
+
| `dropout` | `0.1` |
|
| 352 |
+
| `loss temperature` | `0.1` |
|
| 353 |
+
| optimizer | Adam, lr `5e-4`, weight decay `1e-3` |
|
| 354 |
+
|
| 355 |
+
Contrastive training setup (augmentations, optimizer, scheduler):
|
| 356 |
+
|
| 357 |
+
- [`configs/pretrain/contrastive.yaml`](configs/pretrain/contrastive.yaml)
|
| 358 |
+
- [`configs/pretrain/engine/contrastive.yaml`](configs/pretrain/engine/contrastive.yaml)
|
| 359 |
+
|
| 360 |
+
---
|
| 361 |
+
|
| 362 |
+
<a id="configurations"></a>
|
| 363 |
+
|
| 364 |
+
## π§ͺ UME Configurations
|
| 365 |
+
|
| 366 |
+
We recommend the **base UME** preset, and only its weights are released. The other presets and the pretraining-data subsamples used in our ablations can be retrained with the configs below.
|
| 367 |
+
|
| 368 |
+
### UME model-size presets
|
| 369 |
+
|
| 370 |
+
Defined in [`configs/conf-ume.yaml`](configs/conf-ume.yaml):
|
| 371 |
+
|
| 372 |
+
| Preset | Stem | MBConv | Blocks | Head | Embedding | Kernel | Dropout |
|
| 373 |
+
|--------|------|--------|--------|------|-----------|--------|---------|
|
| 374 |
+
| `tiny` | `32` | `32` | `4` | `96` | `32` | `5` | `0.1` |
|
| 375 |
+
| `small`| `48` | `48` | `8` | `160` | `48` | `7` | `0.1` |
|
| 376 |
+
| `base` | `64` | `64` | `16` | `248` | `64` | `3` | `0.1` |
|
| 377 |
+
| `large`| `96` | `96` | `24` | `384` | `96` | `11` | `0.1` |
|
| 378 |
+
|
| 379 |
+
Sweeps over model size and pretraining-data size: [`sweep-ume-ablation-size.yaml`](configs/sweep-ume-ablation-size.yaml) and [`sweep-ume-ablation-subsample.yaml`](configs/sweep-ume-ablation-subsample.yaml).
|
| 380 |
+
|
| 381 |
+
---
|
| 382 |
+
|
| 383 |
+
<a id="checkpoints"></a>
|
| 384 |
+
|
| 385 |
+
## πΎ Checkpoints and Weights
|
| 386 |
+
|
| 387 |
+
Three files hold the same base UME weights:
|
| 388 |
+
|
| 389 |
+
| File | Contents | Use it for |
|
| 390 |
+
|------|----------|------------|
|
| 391 |
+
| [`model.safetensors`](model.safetensors) + [`config.json`](config.json) | Encoder weights in [safetensors](https://huggingface.co/docs/safetensors) format, with the model config. Holds tensors only, so loading never runs code. This is what `from_pretrained()` downloads from the Hugging Face Hub. | Inference and fine-tuning (recommended). |
|
| 392 |
+
| [`artifacts/ume_base_inference.pt`](artifacts/ume_base_inference.pt) | Encoder `state_dict` and model config in one PyTorch file. Loads with `torch.load(..., weights_only=True)`. | Same as above, without the safetensors dependency. |
|
| 393 |
+
| [`artifacts/ume_base_training.ckpt`](artifacts/ume_base_training.ckpt) | Full PyTorch Lightning checkpoint, including optimizer and scheduler state. Needs `weights_only=False`. | Resuming or inspecting training. |
|
| 394 |
+
|
| 395 |
+
**Are they the same model?** Yes. All three hold the same 366 encoder tensors, bit-for-bit, and give identical embeddings. They differ only in what else they contain:
|
| 396 |
+
|
| 397 |
+
- `ume_base_training.ckpt` is about 3Γ larger because it also stores the training state (mostly the Adam optimizer state, plus epoch, step, callback and hyperparameter records). Its weight keys carry a `model.` prefix, which `from_pretrained` strips.
|
| 398 |
+
- `ume_base_training.ckpt` has no model config: `from_pretrained` infers the architecture from the tensor shapes and gets the same model. The other two files include the config.
|
| 399 |
+
- Only `ume_base_training.ckpt` needs `weights_only=False`, which can run code stored in the file, so load it only from a source you trust.
|
| 400 |
+
|
| 401 |
+
Use `ume_base_training.ckpt` only to resume pretraining. For inference and fine-tuning, use `model.safetensors` or `ume_base_inference.pt`.
|
| 402 |
+
|
| 403 |
+
`UMEPipeline.from_pretrained` accepts any of them through `weights_path`. With no `weights_path`, it downloads `model.safetensors` and `config.json` from the Hub repo given by `repo_id` (default: the released base UME); `revision` and `cache_dir` are passed to the download.
|
| 404 |
+
|
| 405 |
+
---
|
| 406 |
+
|
| 407 |
+
<a id="citation"></a>
|
| 408 |
+
|
| 409 |
+
## π Citation
|
| 410 |
+
|
| 411 |
+
If you use UME, please cite the paper ([doi.org/10.1145/3857981](https://doi.org/10.1145/3857981), [arXiv:2603.16878](https://arxiv.org/abs/2603.16878)). You can also cite this repository (GitHub's "Cite this repository" button uses [`CITATION.cff`](CITATION.cff)):
|
| 412 |
+
|
| 413 |
+
```bibtex
|
| 414 |
+
@software{ume_eda_foundation_model,
|
| 415 |
+
title = {{UME}: a foundation model for wearable electrodermal activity},
|
| 416 |
+
author = {Alchieri, Leonardo},
|
| 417 |
+
year = {2026},
|
| 418 |
+
url = {https://github.com/LeonardoAlchieri/eda-foundation-models}
|
| 419 |
+
}
|
| 420 |
+
```
|
| 421 |
+
|
| 422 |
+
The data augmentations used for contrastive pretraining come from:
|
| 423 |
+
|
| 424 |
+
```bibtex
|
| 425 |
+
@article{matton2024contrastive,
|
| 426 |
+
title={Contrastive Learning of Electrodermal Activity Representations for Stress Detection},
|
| 427 |
+
author={Matton, Katie and Lewis, Robert and Guttag, John and Picard, Rosalind},
|
| 428 |
+
journal={arXiv preprint arXiv:2404.08783},
|
| 429 |
+
year={2024}
|
| 430 |
+
}
|
| 431 |
+
```
|
| 432 |
+
|
| 433 |
+
---
|
| 434 |
+
|
| 435 |
+
<a id="license"></a>
|
| 436 |
+
|
| 437 |
+
## π License
|
| 438 |
+
|
| 439 |
+
This project is licensed under the MIT License. See [`LICENSE`](LICENSE).
|
| 440 |
+
|
| 441 |
+
---
|
| 442 |
+
|
| 443 |
+
<p align="center">
|
| 444 |
+
<i>People Centered Computing Lab @ USI, Switzerland.</i>
|
| 445 |
+
</p>
|