PPG Heart Rate Estimator β CNN/LSTM with TFLite Deployment
Lightweight CNN/LSTM for heart rate estimation from raw PPG signals. Deployed as TensorFlow Lite for mobile and embedded targets.
Results
| Model | Size | MAE | Within Β±5 BPM |
|---|---|---|---|
| Baseline FP32 Keras | 99.6 KB | 0.54 BPM | 100% |
| FP16 TFLite | 82.5 KB | ~0.55 BPM | ~100% |
| Dynamic Quant TFLite | 63.5 KB | ~0.57 BPM | ~100% |
- Median error: 0.34 BPM
- P95 error: 1.86 BPM
- Max error: 4.36 BPM
Architecture
Input (1000, 1) β 8 sec @ 125Hz
β Conv1D(16,k=7) β BN β MaxPool(2)
β Conv1D(32,k=5) β BN β MaxPool(2)
β Conv1D(64,k=3) β BN β MaxPool(2)
β LSTM(32, return_sequences=True) β Dropout
β LSTM(16)
β Dense(32) β Dropout
β Dense(1) β BPM regression
Total params: 25,505 (~100KB FP32)
Predicted vs True
Error Distribution
MAE by HR Range
Training Curves
Usage
import tensorflow as tf
import numpy as np
interpreter = tf.lite.Interpreter(
model_path="ppg_hr_dynamic.tflite",
experimental_delegates=[tf.lite.load_delegate('tensorflowlite_flex')]
)
interpreter.allocate_tensors()
inp = interpreter.get_input_details()
out = interpreter.get_output_details()
# sample: (1, 1000, 1) float32 β normalized PPG window
sample = np.random.randn(1, 1000, 1).astype(np.float32)
interpreter.set_tensor(inp[0]['index'], sample)
interpreter.invoke()
hr_bpm = interpreter.get_tensor(out[0]['index'])[0][0]
print(f"Estimated HR: {hr_bpm:.1f} BPM")
Notes
LSTM layers require the Flex delegate for TFLite inference.
On Android: add tensorflow-lite-select-tf-ops dependency.
Links
- Downloads last month
- 32
Inference Providers NEW
This model isn't deployed by any Inference Provider. π Ask for provider support



