Skip to content

Commit

Permalink
removes old sleep models
Browse files Browse the repository at this point in the history
  • Loading branch information
Trybnetic committed Sep 6, 2024
1 parent 0b2aafa commit afff681
Show file tree
Hide file tree
Showing 6 changed files with 5 additions and 154 deletions.
2 changes: 1 addition & 1 deletion paat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from .features import calculate_actigraph_counts, calculate_vector_magnitude, calculate_brond_counts
from .io import read_gt3x, read_metadata
from .calibration import calibrate
from .sleep import detect_sleep_weitz2022, detect_sleep_triaxial_weitz2022, detect_time_in_bed_weitz2024
from .sleep import detect_time_in_bed_weitz2024
from .wear_time import detect_non_wear_time_naive, detect_non_wear_time_hees2011, detect_non_wear_time_syed2021

try:
Expand Down
Binary file removed paat/models/SleepModel.pt
Binary file not shown.
Binary file removed paat/models/SleepModel_triaxial.pt
Binary file not shown.
154 changes: 3 additions & 151 deletions paat/sleep.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@

import pandas as pd
import numpy as np
from torch import nn
import torch
import tensorflow as tf
from tensorflow.keras import models

Expand All @@ -21,152 +19,6 @@

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

from . import features

class _SleepModel(nn.Module):
def __init__(self, input_dim, hid_dim, output_dim, n_layers, dropout, batch_first=False):
super().__init__()

self.output_dim = output_dim
self.hid_dim = hid_dim
self.n_layers = n_layers
self.name = "LSTM"

self.rnn = nn.LSTM(input_dim, hid_dim, n_layers, dropout=dropout, batch_first=batch_first)

self.fc_out = nn.Linear(hid_dim, output_dim)

self.sigmoid = nn.Sigmoid()

self.dropout = nn.Dropout(dropout)

def forward(self, X, lens):
"""
Performs model's forward pass
"""

packed_input = nn.utils.rnn.pack_padded_sequence(X, lens.to('cpu'), batch_first=True, enforce_sorted=False)
packed_output, _ = self.rnn(packed_input)
output, lens = nn.utils.rnn.pad_packed_sequence(packed_output, batch_first=True)

return self.sigmoid(self.fc_out(output))


def detect_sleep_weitz2022(data, sample_freq, means=None, stds=None):
"""
Infer time in bed from raw acceleration signal using frequency features.
.. warning::
This method turned out to work not as accurately as initially thought. Use with care!
Parameters
----------
data : DataFrame
a DataFrame containg the raw acceleration data
sample_freq : int
the sampling frequency in which the data was recorded
means : array_like (optional)
a numpy array with the channel means, will be calculated for the sample
if not specified
stds : array_like (optional)
a numpy array with the channel stds, will be calculated for the sample
if not specified
Returns
-------
is_sleep : np.array (n_samples,)
a numpy array indicating whether the values of the acceleration data is
sleep on minute resolution
"""

feature_vec = features.calculate_frequency_features(data)

X = torch.from_numpy(feature_vec).float()

# If no means and stds are given, calculate it
if not means or not stds:
means, stds = X.mean(axis=0), X.std(axis=0)

# Normalize input
X = (X - means) / stds

X = X.unsqueeze(0)
lengths = torch.Tensor([X.shape[1]])

# Load model hard coded. Should later be changed to ONNX or similar
model = _SleepModel(160, 4, 1, 1, dropout=0, batch_first=True)
model_path = os.path.join(os.path.pardir, os.path.dirname(__file__), 'models', 'SleepModel.pt')
model.load_state_dict(torch.load(model_path))
model.eval()

# Predict sleep periods
predictions = (model(X, lengths) >= .5).squeeze().numpy()
predictions = np.repeat(predictions, 60 * sample_freq)

return predictions


def detect_sleep_triaxial_weitz2022(data, sample_freq, resampled_frequency="1min", means=None, stds=None, model=None):
"""
Infer time in bed from raw acceleration signal.
.. warning::
This method turned out to work not as accurately as initially thought. Use with care!
Parameters
----------
data : DataFrame
a DataFrame containg the raw acceleration data
sample_freq : int
the sampling frequency in which the data was recorded
resampled_frequency : str (optional)
a str indicating to what frequency the data should be resampled. This depends
on the model used to predict, defaults to 1min.
means : array_like (optional)
a numpy array with the channel means, will be calculated for the sample
if not specified
stds : array_like (optional)
a numpy array with the channel stds, will be calculated for the sample
if not specified
model : nn.Module (optional)
a loaded pytorch custom model.
Returns
-------
predicted_time_in_bed : np.array (n_samples,)
a numpy array indicating whether the values of the acceleration data were spent in bed
"""
if resampled_frequency:
data = data[['X', 'Y', 'Z']].resample(resampled_frequency).mean()

X = torch.from_numpy(data[['X', 'Y', 'Z']].values).float()

# If no means and stds are given, calculate subject's mean and std
# to normalize by this
if not means or not stds:
means, stds = X.mean(axis=0), X.std(axis=0)

# Normalize input
X = (X - means) / stds
lengths = torch.Tensor(X.shape[0]).float().unsqueeze(0)

X = X.unsqueeze(0)
lengths = torch.Tensor([X.shape[1]])

# Load model if not specified
if not model:
model = _SleepModel(3, 2, 1, 1, dropout=0, batch_first=True)
model_path = os.path.join(os.path.pardir, os.path.dirname(__file__), 'models', 'SleepModel_triaxial.pt')
model.load_state_dict(torch.load(model_path))
model.eval()

predictions = (model(X, lengths) >= .5).squeeze().numpy()
seconds = pd.Timedelta(resampled_frequency).seconds
predictions = np.repeat(predictions, seconds * sample_freq)

return predictions

def detect_time_in_bed_weitz2024(data, sample_freq, resampled_frequency="1min", means=None, stds=None, model=None):
"""
Expand All @@ -187,8 +39,8 @@ def detect_time_in_bed_weitz2024(data, sample_freq, resampled_frequency="1min",
stds : array_like (optional)
a numpy array with the channel stds, will be calculated for the sample
if not specified
model : nn.Module (optional)
a loaded pytorch custom model.
model : keras.Model (optional)
a loaded keras custom model.
Returns
-------
Expand All @@ -214,7 +66,7 @@ def detect_time_in_bed_weitz2024(data, sample_freq, resampled_frequency="1min",
model_path = os.path.join(os.path.pardir, os.path.dirname(__file__), 'models', 'TIB_model.h5')
model = models.load_model(model_path)

predictions = (model.predict(X[np.newaxis], verbose=0).squeeze() >= .5)
predictions = (model.predict(X[np.newaxis], verbose=0).squeeze() > .5)

seconds = pd.Timedelta(resampled_frequency).seconds
predictions = np.repeat(predictions, seconds * sample_freq)
Expand Down
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ joblib = "^1.0.1"
numpy = "1.23.5"
bitstring = "^3.1.7"
tensorflow = ">=2.11.0,<2.16"
torch = "^1.10.1"
agcounts = "^0.1.1"
toml = "^0.10.2"
tables = "^3.7.0"
Expand Down
2 changes: 1 addition & 1 deletion tests/test_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def test_pipeline():
data.loc[:, "Non Wear Time"] = paat.detect_non_wear_time_syed2021(data, sample_freq)

# Detect sleep episodes
data.loc[:, "Sleep"] = paat.detect_sleep_weitz2022(data, sample_freq)
data.loc[:, "Sleep"] = paat.detect_time_in_bed_weitz2024(data, sample_freq)

# Classify moderate-to-vigorous and sedentary behavior
data.loc[:, ["MVPA", "SB"]] = paat.calculate_pa_levels(data, sample_freq)
Expand Down

0 comments on commit afff681

Please sign in to comment.