| title | PulseWatcher |
|---|---|
| emoji | 🫀 |
| colorFrom | red |
| colorTo | blue |
| sdk | streamlit |
| sdk_version | 1.32.0 |
| app_file | dashboard/app.py |
| pinned | false |
Real-time ECG anomaly detection using an LSTM Autoencoder trained exclusively on normal heartbeats.
Anomalies are detected through reconstruction error — no anomaly labels required during training.
MIT-BIH Dataset (PhysioNet)
│
▼
Beat Segmentation + Normalisation
(187 timesteps, min-max normalised per beat)
│
▼
LSTM Encoder → Latent Space (64 units) → LSTM Decoder
│
▼
Reconstruction Error (MSE per timestep)
│
▼
Threshold (82nd percentile of normal train errors)
│
┌────┴────┐
▼ ▼
NORMAL ANOMALY
Key insight: The model never sees anomalous beats during training. It learns only what normal looks like. When an anomalous beat is fed in, reconstruction fails — the error spike is the detection signal.
| Metric | Score |
|---|---|
| Precision | 89.82% |
| Recall | 71.35% |
| F1 Score | 79.53% |
| ROC-AUC | 0.8678 |
The threshold is set at the 82nd percentile of normal training errors — chosen for the best F1 balance between catching anomalies and keeping false alarms low. The optimal operating point is a clinical decision: a cardiac ICU may favour higher recall; a general screening setting may favour higher precision.
| Model | Precision | Recall | F1 | AUC |
|---|---|---|---|---|
| LSTM Autoencoder (ours) | 89.82% | 71.35% | 79.53% | 0.8678 |
| 1D CNN Autoencoder | 73.77% | 28.35% | 40.96% | 0.6737 |
LSTM wins across every metric. ECG beats are temporal sequences with known structure (P→QRS→T) — LSTM's sequential memory is a natural fit. CNN treats the signal as a spatial pattern and loses the temporal ordering that makes arrhythmias detectable.
| Feature | Description |
|---|---|
| Live ECG Simulation | Beat-by-beat streaming with st.empty() loop. Anomaly score bar updates in real time. |
| Explainability Heatmap | ECG waveform colour-coded by reconstruction error: cyan (low) → amber (mid) → crimson (high). |
| CSV / ECG Upload | Upload any ECG signal. Auto-normalises, resamples to 187 pts, runs inference instantly. |
| PDF Report | One-click dark-themed clinical PDF: beat class, error, anomaly score, top-5 error timesteps. |
| Dynamic Metrics | All metrics loaded live from models/metrics.json — no hardcoded strings. |
- Architecture: LSTM Autoencoder (encoder-decoder)
- Hidden units: 64
- Parameters: ~110K
- Input shape:
(N, 187, 1) - Training: Normal beats only (unsupervised)
- Inference: <5ms per beat on CPU
Why unsupervised? In clinical settings, labelled anomaly data is rare and expensive. Training on normal beats only allows the model to generalise to anomaly types it has never seen, as long as they deviate from normal morphology.
ecg-anomaly-detection/
├── dashboard/
│ └── app.py # Streamlit dashboard (all features)
├── src/
│ ├── model.py # LSTM Autoencoder architecture
│ ├── train.py # Training loop
│ ├── evaluate.py # Threshold sweep + metrics
│ ├── cnn_autoencoder.py # CNN baseline model
│ └── benchmark.py # Model comparison script
├── models/
│ ├── lstm_autoencoder.pt # Trained LSTM weights
│ ├── threshold.npy # Optimal threshold (82nd percentile)
│ ├── train_errors.npy # Training reconstruction errors
│ ├── metrics.json # Live metrics for dashboard
│ └── benchmark.json # LSTM vs CNN comparison results
├── requirements.txt
└── README.md
git clone https://github.com/shivams496/ecg-anomaly-detection
cd ecg-anomaly-detection
pip install -r requirements.txtStart the dashboard:
streamlit run dashboard/app.pyRun threshold sweep (optional — results already saved):
python -m src.evaluateRun model benchmark:
python -m src.benchmarkMIT-BIH Arrhythmia Dataset via PhysioNet.
| Split | Normal | Anomaly |
|---|---|---|
| Train | 59,816 | — |
| Test | 14,955 | 33,308 |
48 recordings · 30 min each · 360 Hz · 47 patients · 15+ arrhythmia classes
- Single-beat classification only — rhythm-level disorders (e.g. AFib) require inter-beat interval (RR) analysis, not just beat morphology.
- MIT-BIH distribution — trained on Holter monitor recordings. Signals from different device types or electrode placements may need threshold recalibration.
- Unusual-but-benign beats — patients with non-standard baseline ECGs may generate higher reconstruction errors without true pathology.
- Not FDA/CE approved — for research and educational use only.
- RR interval analysis for rhythm-level anomaly detection (AFib, heart block)
- Multi-lead ECG support (currently single-lead)
- Patient-specific threshold calibration
- HL7/FHIR integration for hospital data pipelines
- Transformer autoencoder ablation study
"Why LSTM over Transformer?" MIT-BIH beats are 187 timesteps — short structured sequences with known temporal order (P→QRS→T). Transformers excel at long sequences with long-range dependencies. LSTM captures within-beat temporal structure naturally. The benchmark confirms it: LSTM F1 79% vs CNN F1 41%.
"Why this threshold?" The threshold is a clinical tuning parameter set at the 82nd percentile of normal training errors. This yields the best F1 score — balancing anomaly detection sensitivity with false alarm rate. The right operating point shifts based on clinical context: a cardiac ICU may want higher recall; a general ward may prefer higher precision.
"How would this run in a hospital?" <5ms inference on CPU. Dockerised. Threshold adjustable per cohort without retraining. The Hugging Face deployment proves it runs without a GPU.
Python · PyTorch · Streamlit · NumPy · SciPy · scikit-learn · ReportLab · Pandas · WFDB
B.Tech Final Year Project — ECG Anomaly Detection