rishsoraganvi commited on
Commit
43b5f74
Β·
verified Β·
1 Parent(s): 637ec8e

Create README.md

Browse files
Files changed (1) hide show
  1. README.md +219 -0
README.md ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ language:
3
+ - en
4
+ license: apache-2.0
5
+ datasets:
6
+ - ptb-xl
7
+ tags:
8
+ - ecg
9
+ - cardiology
10
+ - signal-processing
11
+ - medical
12
+ - unet
13
+ - clip
14
+ - lead-generation
15
+ - pytorch
16
+ - 1d-unet
17
+ - film-conditioning
18
+ metrics:
19
+ - rmse
20
+ pipeline_tag: other
21
+ library_name: pytorch
22
+ ---
23
+
24
+ # πŸ«€ ECG Lead Generator β€” 7 β†’ 12 Leads
25
+
26
+ A **CLIP-conditioned 1D U-Net** that reconstructs 5 missing precordial ECG leads (V2–V6)
27
+ from 7 available leads (I, II, III, aVR, aVL, aVF, V1), enabling full 12-lead ECG synthesis
28
+ from reduced-lead recordings.
29
+
30
+ ---
31
+
32
+ ## Clinical Motivation
33
+
34
+ Standard 12-lead ECGs require 10 body-surface electrodes. In wearables, ambulatory monitoring,
35
+ and emergency pre-hospital settings, only limb leads + V1 may be feasible to acquire.
36
+ This model reconstructs the missing precordial leads with clinical fidelity using a
37
+ visual-language prior from CLIP.
38
+
39
+ ---
40
+
41
+ ## Architecture
42
+
43
+ ```
44
+ Input [B, 7, 2500] ──► 1D U-Net (CLIP-FiLM conditioned) ──► Output [B, 5, 2500]
45
+ β–²
46
+ CLIP-ViT-L/14 (frozen)
47
+ ECG red-grid image β†’ 1024-d embedding
48
+ FiLM-injected at every encoder/decoder scale
49
+ ```
50
+
51
+ | Component | Detail |
52
+ |---|---|
53
+ | Backbone | 1D U-Net, 4 encoder + 4 decoder scales |
54
+ | Conditioning | CLIP-ViT-L/14 β†’ 1024-d pooler output |
55
+ | Conditioning mechanism | FiLM (Feature-wise Linear Modulation) at every scale |
56
+ | Parameters | **13,703,507** (LeadGenerator only; CLIP is frozen) |
57
+ | Base channels | 64 |
58
+ | Sequence length | 2500 samples (5 s @ 500 Hz) |
59
+ | Loss | Huber loss |
60
+ | Optimiser | AdamW + CosineAnnealingLR |
61
+
62
+ ### Why CLIP conditioning?
63
+ Each ECG is rendered as a **red-grid clinical image** (standard paper layout) before being
64
+ passed through CLIP-ViT-L. The resulting 1024-d embedding captures morphological patterns
65
+ visually and is injected into the U-Net via FiLM β€” allowing the generator to produce
66
+ lead-consistent waveforms conditioned on the global ECG appearance.
67
+
68
+ ---
69
+
70
+ ## Performance
71
+
72
+ Evaluated on a held-out 10% split of PTB-XL (500 Hz, 200 records).
73
+
74
+ | Lead | RMSE ↓ | DTW (normalised) ↓ |
75
+ |------|--------|--------------------|
76
+ | V2 | 0.41751 | 0.10016 |
77
+ | V3 | 0.52274 | 0.10500 |
78
+ | V4 | 0.45217 | 0.09813 |
79
+ | V5 | 0.35278 | 0.07953 |
80
+ | V6 | 0.37252 | 0.09069 |
81
+ | **Mean** | **0.42355** | **0.09470** |
82
+
83
+ > Evaluated on 200 held-out PTB-XL records (500 Hz). V5 achieves the best reconstruction
84
+ > quality (RMSE 0.353), consistent with its anatomical proximity to V4 and V6 which are
85
+ > both present in the training conditioning signal.
86
+
87
+ ---
88
+
89
+ ## How to Use
90
+
91
+ ### Install dependencies
92
+
93
+ ```bash
94
+ pip install torch huggingface_hub safetensors transformers
95
+ ```
96
+
97
+ ### Load the model
98
+
99
+ ```python
100
+ import torch
101
+ from huggingface_hub import hf_hub_download
102
+ from safetensors.torch import load_file
103
+ import json
104
+
105
+ # Load config
106
+ cfg_path = hf_hub_download("rishsoraganvi/ecg-lead-generator", "config.json")
107
+ with open(cfg_path) as f:
108
+ cfg = json.load(f)
109
+
110
+ # Paste or import LeadGenerator from model.py in this repo
111
+ from model import LeadGenerator
112
+
113
+ model = LeadGenerator(
114
+ ni=cfg["n_in"], # 7
115
+ no=cfg["n_out"], # 5
116
+ ch=cfg["base_ch"], # 64
117
+ cd=cfg["clip_dim"], # 1024
118
+ )
119
+
120
+ weights_path = hf_hub_download("rishsoraganvi/ecg-lead-generator", "model.safetensors")
121
+ model.load_state_dict(load_file(weights_path))
122
+ model.eval()
123
+ ```
124
+
125
+ ### Run inference
126
+
127
+ ```python
128
+ import torch
129
+ import numpy as np
130
+ from transformers import CLIPProcessor, CLIPModel
131
+
132
+ # 1. Load CLIP (frozen)
133
+ clip_model = CLIPModel.from_pretrained("openai/clip-vit-large-patch14")
134
+ clip_enc = clip_model.vision_model.eval()
135
+ clip_proc = CLIPProcessor.from_pretrained("openai/clip-vit-large-patch14")
136
+
137
+ # 2. Prepare 7-lead ECG input: numpy array [7, 2500], 500 Hz, z-normalised
138
+ ecg_7lead = np.random.randn(7, 2500).astype(np.float32) # replace with real data
139
+
140
+ # 3. Render ECG as red-grid image β†’ CLIP embedding
141
+ # (use render_redgrid() from the notebook or your preprocessing pipeline)
142
+ from PIL import Image
143
+ img = render_redgrid(ecg_7lead) # PIL RGB image
144
+ inp = clip_proc(images=[img], return_tensors="pt")
145
+ with torch.no_grad():
146
+ clip_emb = clip_enc(**inp).pooler_output # [1, 1024]
147
+
148
+ # 4. Generate missing leads
149
+ x = torch.FloatTensor(ecg_7lead).unsqueeze(0) # [1, 7, 2500]
150
+ with torch.no_grad():
151
+ pred = model(x, clip_emb) # [1, 5, 2500]
152
+
153
+ # pred contains V2, V3, V4, V5, V6
154
+ ```
155
+
156
+ ---
157
+
158
+ ## Training Details
159
+
160
+ | Setting | Value |
161
+ |---|---|
162
+ | Dataset | PTB-XL (PhysioNet, v1.0.3) |
163
+ | Sampling rate | 500 Hz |
164
+ | Training samples | 2,000 records |
165
+ | Train / Val / Test | 80 / 10 / 10 |
166
+ | Preprocessing | Butterworth bandpass 0.5–40 Hz + z-score normalisation |
167
+ | Epochs | 60 |
168
+ | Batch size | 32 |
169
+ | Learning rate | 1e-3 |
170
+ | Weight decay | 1e-4 |
171
+ | Gradient clipping | 1.0 |
172
+ | Hardware | Vast.ai A100 GPU |
173
+ | CLIP model | `openai/clip-vit-large-patch14` (frozen) |
174
+
175
+ ---
176
+
177
+ ## Repository Structure
178
+
179
+ ```
180
+ ecg-lead-generator/
181
+ β”œβ”€β”€ model.safetensors # Model weights (safetensors format)
182
+ β”œβ”€β”€ config.json # Model configuration
183
+ β”œβ”€β”€ model.py # LeadGenerator architecture
184
+ └── README.md # This file
185
+ ```
186
+
187
+ ---
188
+
189
+ ## Limitations
190
+
191
+ - Trained on 2,000 PTB-XL records β€” a larger training set is recommended for production use
192
+ - Validated on 500 Hz recordings only
193
+ - Not validated on all pathological ECG subtypes present in clinical practice
194
+ - **Not a medical device** β€” intended for research and educational purposes only
195
+
196
+ ---
197
+
198
+ ## Citation
199
+
200
+ If you use this model in your work, please cite PTB-XL:
201
+
202
+ ```bibtex
203
+ @article{wagner2020ptb,
204
+ title={PTB-XL, a large publicly available electrocardiography dataset},
205
+ author={Wagner, Patrick and Strodthoff, Nils and Bousseljot, Ralf-Dieter and others},
206
+ journal={Scientific Data},
207
+ volume={7},
208
+ number={1},
209
+ pages={154},
210
+ year={2020},
211
+ publisher={Nature Publishing Group}
212
+ }
213
+ ```
214
+
215
+ ---
216
+
217
+ ## Author
218
+
219
+ **Rishabh Soraganvi** β€” [GitHub](https://github.com/rishsoraganvi) Β· [Hugging Face](https://huggingface.co/rishsoraganvi)