CoolFace
Modelpublic

raj5517/ppg-heart-rate-estimator

sourceHugging Facemitupdated 7mo agoView on Hugging Face
1likes16downloads
Model Card

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

ModelSizeMAEWithin ±5 BPM
Baseline FP32 Keras99.6 KB0.54 BPM100%
FP16 TFLite82.5 KB~0.55 BPM~100%
Dynamic Quant TFLite63.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

[image]

Error Distribution

[image]

MAE by HR Range

[image]

Training Curves

[image]

Usage

python
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

  • —GitHub: https://github.com/RAj5517/ppgheartrate_estimator