Aucune description
Aller au fichier
Fichiers du dépôt (dernière modification en premier)
Fichier Dernier message de commit Date du dernier commit
2026-08-29 20:17:48 +02:00
model Livraison du modèle : poids, inférence, entraînement reproductible 2026-08-29 20:17:48 +02:00
src/calypso_bvp_denoising Livraison du modèle : poids, inférence, entraînement reproductible 2026-08-29 20:17:48 +02:00
tests Livraison du modèle : poids, inférence, entraînement reproductible 2026-08-29 20:17:48 +02:00
train Livraison du modèle : poids, inférence, entraînement reproductible 2026-08-29 20:17:48 +02:00
use Livraison du modèle : poids, inférence, entraînement reproductible 2026-08-29 20:17:48 +02:00
.gitignore Livraison du modèle : poids, inférence, entraînement reproductible 2026-08-29 20:17:48 +02:00
pyproject.toml Livraison du modèle : poids, inférence, entraînement reproductible 2026-08-29 20:17:48 +02:00
README.md Livraison du modèle : poids, inférence, entraînement reproductible 2026-08-29 20:17:48 +02:00
requirements.txt Livraison du modèle : poids, inférence, entraînement reproductible 2026-08-29 20:17:48 +02:00

calypso-bvp-denoising

Débruitage appris du signal BVP au poignet, développé pour le projet CALYPSO.

Le système répare les portions d'un signal BVP corrompues par le mouvement, en préservant telles quelles les portions propres. Il combine deux réseaux : Segade, qui détecte les zones d'artefact point par point, et SPEAR, un auto-encodeur convolutif qui reconstruit le signal dans ces zones. La finalité est de rendre exploitables les marqueurs cardiaques (fréquence cardiaque, variabilité) extraits du signal réparé.

Ce dépôt contient le modèle entraîné, prêt à l'emploi, et tout ce qu'il faut pour le reproduire, le ré-entraîner ou l'améliorer.

calypso-denoising/
├── model/
│   ├── spear_best.pt              poids du débruiteur livré (36 Mo)
│   ├── segade_fold0_best.pt       poids du détecteur d'artefacts (9 Mo)
│   └── config.yaml                protocole du modèle livré
├── use/
│   ├── denoise.py                 débruiter un signal : un .npy entre, un .npy sort
│   └── example/demo.py            démonstration avant/après sur signal synthétique
├── train/
│   ├── build_pool.py              construire le jeu d'entraînement (DeepBeat + Segade)
│   ├── pool_full_v3_index.npz     index exact du jeu du modèle livré
│   └── train.py                   entraîner SPEAR : graine fixée, garde-fous, convergence
├── tests/run_checks.py            vérifications de base
└── src/calypso_bvp_denoising/     le package : modèles, corruptions, pertes, inférence

Installation

Python 3.12 minimum (exigé par les versions figées de NumPy et SciPy).

git clone https://git.interactions-team.fr/aya.bayna/arche-denoising.git && cd arche-denoising
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
pip install -e .

requirements.txt fige les versions avec lesquelles le modèle livré a été construit et vérifié. Le GPU est optionnel : l'inférence tourne très bien sur CPU ; l'entraînement utilise automatiquement CUDA ou Apple MPS quand ils sont disponibles.

Pour vérifier que tout est en place :

python tests/run_checks.py
python use/example/demo.py

La démo simule deux minutes de PPG, en corrompt douze secondes, débruite, et écrit use/example/demo_avant_apres.png avec les trois signaux tracés l'un sous l'autre.

Utiliser le modèle

Entrée attendue : un signal BVP 1D à 64 Hz, dans ses unités d'origine, d'au moins 30 secondes. 64 Hz est la seule fréquence prise en charge ; rééchantillonnez d'abord tout autre signal (par exemple scipy.signal.resample_poly).

python use/denoise.py mon_signal.npy          # écrit mon_signal_denoised.npy (+ mask, + rejected)

Ou depuis Python :

from use.denoise import load_models, denoise_raw

spear, segade = load_models("model/spear_best.pt", "model/segade_fold0_best.pt", "cpu")
denoised, mask, rejected = denoise_raw(x, spear, segade)

Ce qui se passe en interne, fenêtre de 30 s par fenêtre de 30 s, dans l'ordre exact du protocole de validation du modèle :

  1. recette Guo : passe-bande Butterworth 0,9-5 Hz causal, puis min-max [0,1] ;
  2. si les battements de la fenêtre filtrée sont déjà réguliers, elle est laissée telle quelle (auto-conditionnement : on ne répare pas ce qui fonctionne) ;
  3. Segade produit un masque binaire d'artefact (seuil 0,5) ; une fenêtre corrompue à plus de 75 % est jugée irrécupérable, laissée telle quelle et signalée dans rejected, à exclure des analyses en aval ;
  4. sinon, les zones marquées sont effacées, reconstruites par SPEAR, et recollées ; hors zones d'artefact, la fenêtre filtrée est conservée telle quelle.

La sortie est donc dans l'espace du filtre ([0,1] par fenêtre), et l'amplitude d'origine est volontairement abandonnée : c'est sur ce signal que s'extraient les marqueurs cardiaques (pics, intervalles battement à battement), qui ne dépendent pas de l'échelle d'amplitude.

Reproduire ou ré-entraîner le modèle

1. Récupérer les données

Le jeu d'entraînement vient de DeepBeat (Stanford), distribué sur Synapse. Créez un compte sur synapse.org, installez synapseclient, authentifiez-vous (synapse login), puis :

mkdir -p data/raw/deepbeat && cd data/raw/deepbeat
synapse get syn22006404 --downloadLocation .   # train.npz    (18,1 Go)
synapse get syn22006006 --downloadLocation .   # validate.npz (3,3 Go)
synapse get syn22006407 --downloadLocation .   # test.npz     (114 Mo)

Prévoir 22 Go de disque pour les fichiers bruts. Ils ne servent qu'à construire le jeu ; une fois pool_full_v3.npy produit (1,5 Go), ils peuvent être supprimés.

2. Construire le jeu d'entraînement

Le jeu exact du modèle livré se reconstruit depuis son index, fourni dans le dépôt :

python train/build_pool.py --from-index train/pool_full_v3_index.npz

L'index contient les indices précis des 234 999 fenêtres uniques retenues, issues de 109 participants (le critère « zéro artefact selon Segade » est strict : sur les 167 participants de la base, 109 ont au moins une fenêtre entièrement propre). Le jeu est stratifié par participant, avec un plafond commun de fenêtres par personne et une déduplication globale. Le script vérifie l'index (clés, permutation complète, indices uniques et dans les bornes), refuse d'écrire un jeu contenant un doublon, et affiche la composition et l'équilibre pour contrôle.

Pour refaire le protocole de zéro (inventaire complet des trois réservoirs, environ deux heures de parcours), lancer sans --from-index.

3. Entraîner

python train/train.py --pool data/pool_full_v3.npy --ckpt-dir checkpoints/mon_run

C'est le protocole du modèle livré : graine 20260805 partout (réseau, ordre des lots, corruptions), corruption combinée (bruit plausible, modèle repris de la boîte à outils RRest, plus masquage aléatoire), perte RMSE plus un terme spectral de bande cardiaque 0,9-3 Hz pondéré par λ = 0,7, batch 128, et arrêt par convergence : l'entraînement continue tant que le score de surveillance progresse d'au moins 0,0002, et s'arrête après 5 cycles consécutifs sans progrès. Le modèle livré s'est arrêté au cycle 54 ; son meilleur cycle, le 52, est celui du checkpoint.

Le script embarque quatre garde-fous : il refuse de démarrer si la bande de la perte diffère de celle déclarée, si le jeu contient un doublon, ou si les fenêtres n'ont pas la bonne longueur, et il journalise tout dans un fichier. Chaque cycle sauve last.pt avec l'état complet (poids, optimiseur, générateurs aléatoires, compteur de convergence, protocole) : après une interruption, relancer la même commande reprend la trajectoire exacte qu'un entraînement ininterrompu aurait suivie, et une commande dont le jeu, la graine, le λ ou la bande diffèrent est refusée.

Pour partir du modèle livré plutôt que de zéro (fine-tuning) :

python train/train.py --pool data/pool_full_v3.npy --init model/spear_best.pt

À savoir : même protocole, mêmes données, même graine redonnent le même entraînement ; de petites différences numériques restent possibles d'un matériel à l'autre (ordre des opérations flottantes sur GPU).

Les deux réseaux

SPEAR (src/.../models/spear.py) est une réimplémentation de l'auto-encodeur de débruitage de l'article SPEAR (arXiv 2307.05339) : encodeur et décodeur symétriques de 4 blocs convolutifs 1D chacun, filtres 16/32/64/128, noyaux 32/64/128/320, sans skip-connection, sortie sigmoïde dans [0,1]. 9,4 M de paramètres. La longueur d'entrée est libre tant qu'elle est divisible par 16 ; le modèle livré travaille en fenêtres de 1920 points (30 s à 64 Hz) et a été entraîné sur des fenêtres de 1600 points (les fenêtres DeepBeat, 25 s à 64 Hz).

Segade (src/.../models/segade.py) est le détecteur d'artefacts de Guo et al. (segmentation point par point du signal en propre/artefact). Le checkpoint fourni a été entraîné sur PPG DaLiA, le jeu public annoté en artefacts par des humains.

Protocole du modèle livré

Repris dans model/config.yaml, et vérifiable dans les métadonnées du checkpoint lui-même (epoch, seed, lam, band, pool). Les paramètres sont solidaires : changer l'un sans les autres change le comportement du système.

paramètre valeur
fréquence d'échantillonnage 64 Hz
fenêtre de traitement 30 s (1920 points)
jeu d'entraînement pool_full_v3 : 234 999 fenêtres uniques, 109 participants, stratifié par participant, index exact fourni
prétraitement recette Guo : passe-bande Butterworth ordre 2, 0,9-5 Hz, causal (sosfilt), puis min-max [0,1]
sélection du jeu Segade seuil 0,5, fenêtres à 0 % d'artefact, doublons écartés
corruption d'entraînement combinée : bruit (modèle RRest) + masquage, 10 augmentations par fenêtre
perte RMSE + λ × erreur spectrale relative en bande 0,9-3 Hz, λ = 0,7
optimiseur et arrêt Adam, lr 1e-3, batch 128 ; arrêt par convergence (patience 5, seuil 0,0002) ; modèle livré = cycle 52 sur 55
graine 20260805 (réseau, ordre des lots, corruptions)
seuil Segade à l'inférence 0,5, masque brut sans dilatation
rejet de fenêtre plus de 75 % d'artefact
auto-conditionnement fenêtre laissée telle quelle si moins de 3 % des intervalles battement à battement dévient de plus de 30 % de la médiane

Vérifier l'intégrité des poids

27a7ea290fb76ded26cd3e319cbdcb60f2ce870eb2e698fa4b0b8cd60c88e999  model/spear_best.pt
fc6305ad2dd34241e60ece96f58df1265bcac3e4392dba75641b7504ad7407cd  model/segade_fold0_best.pt
shasum -a 256 -c <<< "27a7ea290fb76ded26cd3e319cbdcb60f2ce870eb2e698fa4b0b8cd60c88e999  model/spear_best.pt"

Provenance et réutilisation

  • Code SPEAR : réimplémenté pour le projet d'après l'article, avec deux ajouts CALYPSO, signalés dans les docstrings : le terme de perte en bande cardiaque (training/losses.py) et l'auto-conditionnement à l'inférence (inference/denoise.py).
  • Code Segade : architecture et fonction de perte réimplémentées d'après le code publié par les auteurs (repo chengstark/Segade) ; le modèle de bruit d'entraînement reprend la boîte à outils RRest.
  • Poids : les deux checkpoints ont été entraînés dans le cadre du projet CALYPSO (2026) ; SPEAR sur le jeu DeepBeat décrit plus haut, Segade sur PPG DaLiA.
  • Données : rien n'est redistribué ici. DeepBeat se télécharge depuis Synapse sous ses propres conditions d'utilisation. Ce dépôt est destiné à l'usage de l'équipe INTERACTIONS dans le cadre du projet CALYPSO.

Références

  • SPEAR : Jain et al., A Self-Supervised Algorithm for Denoising Photoplethysmography Signals for Heart Rate Estimation from Wearables, arXiv 2307.05339 ; code des auteurs : pranay-jain/SPEAR-PPG-Denoiser.
  • Segade : Guo et al., A supervised machine learning semantic segmentation approach for detecting artifacts in plethysmography signals from wearables, Physiological Measurement, 2021 ; code des auteurs : chengstark/Segade.
  • Modèle de bruit : boîte à outils RRest de P. Charlton.
  • Données d'entraînement : DeepBeat (Stanford), Synapse syn21985690 ; PPG DaLiA (Reiss et al., 2019) pour Segade.