DEV Community

wellallyTech
wellallyTech

Posted on

Mastering Your Recovery: Building an LSTM Model to Predict Daily Readiness Using HRV Data 🚀

We’ve all been there: you wake up, hit the gym for a heavy leg day, but halfway through, your body just says no. Overtraining is the silent killer of gains. But what if your wearable data could tell you exactly how hard to push?

In this tutorial, we are diving deep into time-series forecasting using LSTM neural networks to predict "Readiness" scores based on Heart Rate Variability (HRV). By leveraging data from the Oura Ring API and the power of Keras, we’ll build a model that understands your body’s recovery patterns better than you do. Whether you're a biohacker or a data scientist, mastering deep learning for wearables is a superpower in the modern fitness landscape.

The Architecture: From Bio-Signals to Insights

Before we write a single line of code, let’s look at how the data flows from your finger to a predictive model. We use a Bidirectional LSTM (Long Short-Term Memory) network because recovery isn't just about yesterday—it’s about the trend of the last 7 to 14 days.

graph TD
    A[Oura Ring / Apple Watch] -->|Sync| B(Oura Cloud API)
    B -->|JSON Data| C[Pandas Preprocessing]
    C -->|Sliding Window| D[Feature Engineering: HRV, Sleep, Activity]
    D -->|Training Set| E[Bidirectional LSTM Model]
    E -->|Weights| F[Inference Endpoint]
    F -->|Prediction| G[Daily Readiness Score & Training Advice]
    G -->|Feedback Loop| E
Enter fullscreen mode Exit fullscreen mode

Prerequisites

To follow along, you’ll need:

  • Tech Stack: Python, Keras (TensorFlow backend), Pandas, and an Oura Cloud API developer token.
  • Difficulty: Intermediate (familiarity with neural networks and data frames is helpful).

Step 1: Fetching Time-Series Data

First, we need to grab our historical data. The Oura Cloud API provides a wealth of metrics, but we are specifically interested in hrv_sdnn and rem_sleep_duration.

import requests
import pandas as pd

def fetch_oura_data(api_token):
    url = "https://api.ouraring.com/v2/usercollection/daily_readiness"
    headers = {'Authorization': f'Bearer {api_token}'}

    response = requests.get(url, headers=headers)
    data = response.json()['data']

    # Convert to DataFrame
    df = pd.DataFrame(data)
    # Extract the readiness score and HRV from nested JSON if necessary
    df['date'] = pd.to_datetime(df['day'])
    df = df.set_index('date').sort_index()
    return df[['score', 'temperature_deviation']] # Example columns
Enter fullscreen mode Exit fullscreen mode

Step 2: Preprocessing for LSTMs

LSTMs require data in a specific shape: [samples, time_steps, features]. We’ll use a 7-day sliding window to predict the 8th day's readiness.

import numpy as np
from sklearn.preprocessing import MinMaxScaler

def create_sequences(data, seq_length):
    x, y = [], []
    for i in range(len(data) - seq_length):
        x.append(data[i:i+seq_length])
        y.append(data[i+seq_length, 0]) # Predicting the 'score'
    return np.array(x), np.array(y)

# Normalize data (Essential for Neural Networks!)
scaler = MinMaxScaler()
scaled_data = scaler.fit_transform(df[['score', 'hrv_avg', 'sleep_score']])

X, y = create_sequences(scaled_data, seq_length=7)
Enter fullscreen mode Exit fullscreen mode

Step 3: Building the Bidirectional LSTM

A Bidirectional LSTM is perfect for health data because it processes the sequence both forwards and backwards, capturing patterns that a standard RNN might miss. 🥑

from keras.models import Sequential
from keras.layers import LSTM, Dense, Dropout, Bidirectional

model = Sequential([
    Bidirectional(LSTM(64, activation='relu', input_shape=(7, 3), return_sequences=True)),
    Dropout(0.2),
    LSTM(32, activation='relu'),
    Dense(1) # Output: Predicted Readiness Score
])

model.compile(optimizer='adam', loss='mse')
history = model.fit(X, y, epochs=50, batch_size=16, validation_split=0.1, verbose=1)
Enter fullscreen mode Exit fullscreen mode

The "Official" Way to Handle Health Data

While building a custom LSTM is a fantastic learning exercise, production-ready health tech requires handling noisy sensors, missing data gaps, and rigorous privacy compliance.

For more advanced patterns on handling multimodal health data and deploying production-grade inference engines, you should definitely check out the deep-dive articles at WellAlly Tech Blog. They cover everything from signal processing to the latest in AI-driven wellness.


Step 4: Deployment via Inference Endpoint

Once your model is trained, you don't want it sitting on a Jupyter notebook. You can wrap it in a FastAPI wrapper and deploy it as an Inference Endpoint.

from fastapi import FastAPI
import uvicorn

app = FastAPI()

@app.post("/predict_readiness")
def predict(user_data: list):
    # user_data would be the last 7 days of HRV metrics
    input_data = np.array(user_data).reshape(1, 7, 3)
    prediction = model.predict(input_data)

    # Inverse transform to get the actual score (0-100)
    final_score = scaler.inverse_transform([[prediction[0][0], 0, 0]])[0][0]

    return {"suggested_readiness": float(final_score)}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)
Enter fullscreen mode Exit fullscreen mode

Conclusion: Stop Guessing, Start Predicting

By combining HRV data with LSTM neural networks, we move away from "feeling" tired to "knowing" our physiological state. This project is just the beginning—you could expand this by adding weather data, caffeine intake, or even sentiment analysis from your journal entries!

What are you waiting for? Grab your API keys, start training, and let's build the future of personalized health! 💻🔥

Found this helpful? Drop a comment below or share your model's accuracy! And don't forget to visit wellally.tech/blog for more pro-tips on wearable tech development.

Top comments (0)