Sleep awake detection from leg-worn wearables using deep sensor fusion.
The 8 matches
- [1] § Methods › Model architecture ↔ Train/model.py, lines 11–86 · score 0.83 · fully connected layers, global max, Conv2D, BiLSTM, channel, binary
- [2] § Methods › Model architecture › Regularization and overfitting control ↔ Train/model.py, lines 110–159 · score 0.82 · Global Max, LeakyReLU, dense layers, Batch Normalization, dropout rate, block
- [3] § Methods › Model training and evaluation › Learning rate and loss function ↔ Train/model.py, lines 11–86 · score 0.75 · cosine decay, learning rate scheduler, decay step, Adam, optimized, loss
- [4] § Methods › Signal preprocessing ↔ Train/ppg_preprocess.py, lines 136–218 · score 0.74 · noise power, Signal power, detected peak, spectral, SNR, band
- [5] § Methods › Model architecture ↔ Train/Late_Fusion.ipynb, lines 1–38 · score 0.58 · late fusion architecture, separate sub, tensor, concatenating, classification, sensor
- [6] § Methods › Model architecture ↔ Train/ppg_preprocess.py, lines 422–484 · score 0.56 · standard deviation, heart rate, interval, HRV, signals
- [7] § Results ↔ Train/Late_Fusion.ipynb, lines 269–332 · score 0.56 · Receiver Operating Characteristic, ROC AUC, Curve, sensitivity, fold, windows
- [8] § Methods › Neighborhood majority voting-based label smoothing ↔ Train/Late_Fusion.ipynb, lines 269–332 · score 0.51 · neighboring window, refine, majority, probabilities, smoothing, threshold
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Python · 701 lines · 27 KB · no license · 3 matches
- import tensorflow as tf
- from tensorflow.keras import layers, models, Input, Model
- import numpy as np
- from tensorflow.keras.layers import Input, Lambda
- from tensorflow.keras.optimizers import Adam
- #import tensorflow_addons as tfa
- from tensorflow.keras.utils import plot_model
- from tensorflow import keras
- def build_stacked_model(input_shape, num_classes, model_config):
- """
- Builds a classification model using early fusion with BiLSTM for sensor data.
- Args:
- input_shape (tuple): Shape of the input data after stacking (e.g., (1500, 3, 3)).
- model_config (dict): Dictionary containing all the model configurations and hyperparameters.
- Returns:
- tf.keras.Model: The compiled classification model.
- """
- inputs = Input(shape=input_shape)
- x = layers.Conv2D(model_config['num_filters_1'], (3, 3), padding='same',
- kernel_regularizer=tf.keras.regularizers.l2(model_config['l2_regularization']),
- kernel_initializer='he_normal')(inputs)
- x = layers.BatchNormalization()(x)
- x = layers.LeakyReLU()(x)
- x = layers.Conv2D(model_config['num_filters_2'], (3, 3), padding='same',
- kernel_regularizer=tf.keras.regularizers.l2(model_config['l2_regularization']),
- kernel_initializer='he_normal')(x)
- x = layers.BatchNormalization()(x)
- x = layers.LeakyReLU()(x)
- x = layers.MaxPooling2D(pool_size=(2, 2))(x)
- x = layers.Dropout(model_config['dropout_rate'])(x)
- x = layers.Conv2D(model_config['num_filters_3'], (3, 3), padding='same',
- kernel_regularizer=tf.keras.regularizers.l2(model_config['l2_regularization']),
- kernel_initializer='he_normal')(x)
- x = layers.BatchNormalization()(x)
- x = layers.LeakyReLU()(x)
- x = layers.MaxPooling2D(pool_size=(2, 1))(x)
- x = layers.Dropout(model_config['dropout_rate'])(x)
- # Reshape for BiLSTM
- # Convert to shape compatible with LSTM input: (batch_size, time_steps, features)
- # _, time_steps, features, channels = x.shape.as_list()
- _, time_steps, features, channels = list(x.shape)
- reshaped = layers.Reshape((time_steps, features * channels))(x)
- # BiLSTM layers for temporal dependency extraction
- x = layers.Bidirectional(layers.LSTM(model_config['lstm_units'], return_sequences=True))(reshaped)
- x = layers.GlobalMaxPooling1D()(x)
- # Fully connected layers
- x = layers.Dense(model_config['dense_units'], activation='relu')(x)
- x = layers.Dropout(model_config['dropout_rate'])(x)
- output = layers.Dense(1, activation='sigmoid', dtype='float32')(x)
- # Compile the model
- initial_learning_rate = model_config['lr']
- decay_steps = model_config['lr_decay_steps']
- # Learning rate schedule
- lr_schedule = tf.keras.optimizers.schedules.CosineDecay(
- initial_learning_rate=initial_learning_rate,
- decay_steps=decay_steps,
- alpha=model_config['lr_alpha']
- )
- optimizer = Adam(learning_rate=lr_schedule)
- # Loss function
- loss_fn = tf.keras.losses.BinaryFocalCrossentropy(gamma=model_config['loss_gamma'], alpha=model_config['loss_alpha'])
- # Create and compile the model
- model = Model(inputs=inputs, outputs=output)
- model.compile(optimizer=optimizer,
- loss=loss_fn,
- metrics=[keras.metrics.AUC(),
- keras.metrics.SpecificityAtSensitivity(0.99),
- keras.metrics.SensitivityAtSpecificity(0.99)])
- return model
- def attention_block(inputs):
- attention_probs = layers.Dense(inputs.shape[-1], activation='softmax')(inputs)
- attention_mul = layers.multiply([inputs, attention_probs])
- return attention_mul
- def transformer_block(inputs, num_heads, ff_dim, dropout_rate):
- # Multi-head self-attention
- attention_output = layers.MultiHeadAttention(num_heads=num_heads, key_dim=inputs.shape[-1])(inputs, inputs)
- # Add & normalize
- attention_output = layers.Dropout(dropout_rate)(attention_output)
- attention_output = layers.LayerNormalization(epsilon=1e-6)(attention_output + inputs)
- # Feed-forward network
- ff_output = layers.Dense(ff_dim, activation="relu")(attention_output)
- ff_output = layers.Dense(inputs.shape[-1])(ff_output)
- # Add & normalize
- ff_output = layers.Dropout(dropout_rate)(ff_output)
- output = layers.LayerNormalization(epsilon=1e-6)(ff_output + attention_output)
- return output
- def build_sensor_model_2d_TRANS(input_shape, model_config):
- """
- Builds a sub-model for processing sensor data.
- Args:
- input_shape (tuple): Shape of the input data for the sensor.
- model_config (dict): Dictionary containing all the model configurations and hyperparameters.
- Returns:
- tf.keras.Model: The sub-model for the sensor.
- """
- inputs = Input(shape=input_shape)
- x = layers.Conv2D(model_config['num_filters_1'], model_config['kernel_size_1'], padding='same',
- kernel_regularizer=tf.keras.regularizers.l2(model_config['l2_regularization']),
- kernel_initializer='he_normal')(inputs)
- x = layers.BatchNormalization()(x)
- x = layers.LeakyReLU()(x)
- x = layers.Conv2D(model_config['num_filters_2'], model_config['kernel_size_2'], padding='same',
- kernel_regularizer=tf.keras.regularizers.l2(model_config['l2_regularization']),
- kernel_initializer='he_normal')(x)
- x = layers.BatchNormalization()(x)
- x = layers.LeakyReLU()(x)
- x = layers.MaxPooling2D(model_config['pooling_size'])(x)
- x = layers.Dropout(model_config['dropout_rate'])(x)
- x = layers.Conv2D(model_config['num_filters_3'], model_config['kernel_size_2'], padding='same',
- kernel_regularizer=tf.keras.regularizers.l2(model_config['l2_regularization']),
- kernel_initializer='he_normal')(x)
- x = layers.BatchNormalization()(x)
- x = layers.LeakyReLU()(x)
- x = layers.MaxPooling2D(model_config['pooling_size'])(x)
- x = layers.Dropout(model_config['dropout_rate'])(x)
- batch_size, height, width, channels = x.shape.as_list()
- new_shape = (height, width * channels)
- x = layers.Reshape(new_shape)(x)
- # Transformer block
- x = transformer_block(x, num_heads=model_config['num_heads'],
- ff_dim=model_config['ff_dim'], dropout_rate=model_config['dropout_rate'])
- # LSTM block or dense layers
- x = layers.Bidirectional(layers.LSTM(model_config['lstm_units'], return_sequences=True))(x)
- x = layers.GlobalMaxPooling1D()(x)
- return Model(inputs, x)
- def build_sensor_model_2d_2(input_shape, model_config):
- """
- Builds a sub-model for processing sensor data.
- Args:
- input_shape (tuple): Shape of the input data for the sensor.
- model_config (dict): Dictionary containing all the model configurations and hyperparameters.
- Returns:
- tf.keras.Model: The sub-model for the sensor.
- """
- inputs = Input(shape=input_shape)
- x = layers.Conv2D(model_config['num_filters_1'], model_config['kernel_size_1'], padding='same',
- kernel_regularizer=tf.keras.regularizers.l2(model_config['l2_regularization']),
- kernel_initializer='he_normal')(inputs)
- x = layers.BatchNormalization()(x)
- x = layers.LeakyReLU()(x)
- x = layers.Conv2D(model_config['num_filters_2'], model_config['kernel_size_2'], padding='same',
- kernel_regularizer=tf.keras.regularizers.l2(model_config['l2_regularization']),
- kernel_initializer='he_normal')(x)
- x = layers.BatchNormalization()(x)
- x = layers.LeakyReLU()(x)
- x = layers.MaxPooling2D(model_config['pooling_size'])(x)
- # Removed Dropout here to avoid applying it on a 4D tensor
- x = layers.Conv2D(model_config['num_filters_3'], model_config['kernel_size_2'], padding='same',
- kernel_regularizer=tf.keras.regularizers.l2(model_config['l2_regularization']),
- kernel_initializer='he_normal')(x)
- x = layers.BatchNormalization()(x)
- x = layers.LeakyReLU()(x)
- x = layers.MaxPooling2D(model_config['pooling_size'])(x)
- # Removed Dropout here as well
- # Attention block
- # x = attention_block(x)
- # Use Reshape to prepare for LSTM layer
- shape_before_lstm = x.shape[1] * x.shape[2] # Combine height and width
- x = layers.Reshape((shape_before_lstm, x.shape[-1]))(x)
- x = layers.Dropout(model_config['dropout_rate'])(x) # Apply Dropout after Reshape
- x = layers.Bidirectional(layers.LSTM(model_config['lstm_units'], return_sequences=True))(x)
- x = layers.GlobalMaxPooling1D()(x)
- return Model(inputs, x)
- def build_sensor_model_2d_sm_2(input_shape, model_config):
- """
- Builds a sub-model for processing sensor data with simpler structure.
- Args:
- input_shape (tuple): Shape of the input data for the sensor.
- model_config (dict): Dictionary containing all the model configurations and hyperparameters.
- Returns:
- tf.keras.Model: The sub-model for the sensor.
- """
- inputs = Input(shape=input_shape)
- x = layers.Conv2D(model_config['num_filters_1_sm'], model_config['kernel_size_1_sm'], padding='same',
- kernel_regularizer=tf.keras.regularizers.l2(model_config['l2_regularization']),
- kernel_initializer='he_normal')(inputs)
- x = layers.BatchNormalization()(x)
- x = layers.LeakyReLU()(x)
- x = layers.MaxPooling2D(model_config['pooling_size'])(x)
- # Removed Dropout here as well
- # Flatten and Reshape to prepare for Dense and GlobalMaxPooling1D
- x = layers.Flatten()(x)
- x = layers.Dense(32, activation='relu')(x)
- # Reshape to add a dimension for compatibility with GlobalMaxPooling1D
- x = layers.Reshape((32, 1))(x) # Reshaping to (batch, 32, 1)
- x = layers.GlobalMaxPooling1D()(x)
- return Model(inputs, x)
- def build_sensor_model_2d(input_shape, model_config):
- """
- Builds a sub-model for processing sensor data.
- Args:
- input_shape (tuple): Shape of the input data for the sensor.
- model_config (dict): Dictionary containing all the model configurations and hyperparameters.
- Returns:
- tf.keras.Model: The sub-model for the sensor.
- """
- inputs = Input(shape=input_shape)
- x = layers.Conv2D(model_config['num_filters_1'], model_config['kernel_size_1'], padding='same',
- kernel_regularizer=tf.keras.regularizers.l2(model_config['l2_regularization']),
- kernel_initializer='he_normal')(inputs)
- x = layers.BatchNormalization()(x)
- x = layers.LeakyReLU()(x)
- x = layers.Conv2D(model_config['num_filters_2'], model_config['kernel_size_2'], padding='same',
- kernel_regularizer=tf.keras.regularizers.l2(model_config['l2_regularization']),
- kernel_initializer='he_normal')(x)
- x = layers.BatchNormalization()(x)
- x = layers.LeakyReLU()(x)
- x = layers.MaxPooling2D(model_config['pooling_size'])(x)
- x = layers.Dropout(model_config['dropout_rate'])(x)
- x = layers.Conv2D(model_config['num_filters_3'], model_config['kernel_size_2'], padding='same',
- kernel_regularizer=tf.keras.regularizers.l2(model_config['l2_regularization']),
- kernel_initializer='he_normal')(x)
- x = layers.BatchNormalization()(x)
- x = layers.LeakyReLU()(x)
- x = layers.MaxPooling2D(model_config['pooling_size'])(x)
- x = layers.Dropout(model_config['dropout_rate'])(x)
- # Attention block
- #x = attention_block(x)
- batch_size, height, width, channels = x.shape.as_list()
- new_shape = (height, width * channels)
- x = layers.Reshape(new_shape)(x)
- x = layers.Dropout(model_config['dropout_rate'])(x)
- x = layers.Bidirectional(layers.LSTM(model_config['lstm_units'], return_sequences=True))(x)
- x = layers.GlobalMaxPooling1D()(x)
- return Model(inputs, x)
- def build_sensor_model_2d_sm(input_shape, model_config):
- """
- Builds a sub-model for processing sensor data.
- Args:
- input_shape (tuple): Shape of the input data for the sensor.
- model_config (dict): Dictionary containing all the model configurations and hyperparameters.
- Returns:
- tf.keras.Model: The sub-model for the sensor.
- """
- inputs = Input(shape=input_shape)
- x = layers.Conv2D(model_config['num_filters_1_sm'], model_config['kernel_size_1_sm'], padding='same',
- kernel_regularizer=tf.keras.regularizers.l2(model_config['l2_regularization']),
- kernel_initializer='he_normal')(inputs)
- x = layers.BatchNormalization()(x)
- x = layers.LeakyReLU()(x)
- x = layers.MaxPooling2D(model_config['pooling_size'])(x)
- x = layers.Dropout(model_config['dropout_rate'])(x)
- # Attention block
- #x = attention_block(x)
- batch_size, height, width, channels = x.shape.as_list()
- new_shape = (height, width * channels)
- x = layers.Reshape(new_shape)(x)
- x = layers.Dropout(model_config['dropout_rate'])(x)
- x = layers.Dense(32, activation='relu')(x)
- #x = layers.Bidirectional(layers.LSTM(model_config['lstm_units_sm'], return_sequences=True))(x)
- x = layers.GlobalMaxPooling1D()(x)
- return Model(inputs, x)
- def build_combined_model(input_shapes, num_classes, model_config): # late fusion model architecture
- """
- Builds a classification model that combines data from three sensors.
- Args:
- input_shapes (list of tuples): Shapes of the input data for each sensor.
- num_classes (int): Number of output classes.
- model_config (dict): Dictionary containing all the model configurations and hyperparameters.
- Returns:
- tf.keras.Model: The compiled classification model.
- """
- # Create sub-models for each sensor
- # sensor1_model = build_sensor_model_2d(input_shapes[0], model_config)
- # sensor2_model = build_sensor_model_2d(input_shapes[1], model_config)
- # sensor3_model = build_sensor_model_2d(input_shapes[2], model_config)
- # sensor4_model = build_sensor_model_2d_sm(input_shapes[3], model_config)
- sensor1_model = build_sensor_model_2d_2(input_shapes[0], model_config)
- sensor2_model = build_sensor_model_2d_2(input_shapes[1], model_config)
- sensor3_model = build_sensor_model_2d_2(input_shapes[2], model_config)
- sensor4_model = build_sensor_model_2d_sm_2(input_shapes[3], model_config)
- #sensor1_model = build_sensor_model_2d_TRANS(input_shapes[0], model_config)
- #sensor2_model = build_sensor_model_2d_TRANS(input_shapes[1], model_config)
- #sensor3_model = build_sensor_model_2d_TRANS(input_shapes[2], model_config)
- #sensor4_model = build_sensor_model_2d_sm(input_shapes[3], model_config)
- # Define inputs for each sensor
- sensor1_input = Input(shape=input_shapes[0])
- sensor2_input = Input(shape=input_shapes[1])
- sensor3_input = Input(shape=input_shapes[2])
- sensor4_input = Input(shape=input_shapes[3])
- # Get the outputs from each sub-model
- sensor1_output = sensor1_model(sensor1_input)
- sensor2_output = sensor2_model(sensor2_input)
- sensor3_output = sensor3_model(sensor3_input)
- sensor4_output = sensor4_model(sensor4_input)
- # Concatenate the outputs
- concatenated = layers.concatenate([sensor1_output, sensor2_output, sensor3_output, sensor4_output])
- # Add Dense and Dropout layers
- x = layers.Dense(model_config['dense_units'], activation='relu')(concatenated)
- x = layers.Dropout(model_config['dropout_rate'])(x)
- output = layers.Dense(1, activation='sigmoid', dtype='float32')(x)
- #output = layers.Dense(1)(x)
- initial_learning_rate = model_config['lr']
- decay_steps = model_config['lr_decay_steps']
- # Create the learning rate schedule
- lr_schedule = tf.keras.optimizers.schedules.CosineDecay(
- initial_learning_rate=initial_learning_rate,
- decay_steps=decay_steps,
- alpha=model_config['lr_alpha']
- )
- optimizer = Adam(learning_rate=lr_schedule)
- # Create and compile the model
- model = Model(inputs=[sensor1_input, sensor2_input, sensor3_input, sensor4_input], outputs=output)
- #loss_fn = tf.keras.losses.BinaryCrossentropy()
- loss_fn = tf.keras.losses.BinaryFocalCrossentropy(gamma=model_config['loss_gamma'], alpha=model_config['loss_alpha'])
- model.compile(optimizer=optimizer,
- loss=loss_fn,
- metrics=[keras.metrics.AUC(),
- keras.metrics.SpecificityAtSensitivity(0.99),
- keras.metrics.SensitivityAtSpecificity(0.99)]
- )
- return model
- def build_combined_model_removed_1(input_shapes, num_classes, model_config):
- """
- Builds a classification model that combines data from three sensors.
- Args:
- input_shapes (list of tuples): Shapes of the input data for each sensor.
- num_classes (int): Number of output classes.
- model_config (dict): Dictionary containing all the model configurations and hyperparameters.
- Returns:
- tf.keras.Model: The compiled classification model.
- """
- # Create sub-models for each sensor
- # sensor1_model = build_sensor_model_2d(input_shapes[0], model_config)
- # sensor2_model = build_sensor_model_2d(input_shapes[1], model_config)
- # sensor3_model = build_sensor_model_2d(input_shapes[2], model_config)
- # sensor4_model = build_sensor_model_2d_sm(input_shapes[3], model_config)
- sensor1_model = build_sensor_model_2d_2(input_shapes[0], model_config)
- sensor2_model = build_sensor_model_2d_2(input_shapes[1], model_config)
- sensor3_model = build_sensor_model_2d_2(input_shapes[2], model_config)
- sensor4_model = build_sensor_model_2d_sm_2(input_shapes[3], model_config)
- #sensor1_model = build_sensor_model_2d_TRANS(input_shapes[0], model_config)
- #sensor2_model = build_sensor_model_2d_TRANS(input_shapes[1], model_config)
- #sensor3_model = build_sensor_model_2d_TRANS(input_shapes[2], model_config)
- #sensor4_model = build_sensor_model_2d_sm(input_shapes[3], model_config)
- # Define inputs for each sensor
- sensor1_input = Input(shape=input_shapes[0])
- sensor2_input = Input(shape=input_shapes[1])
- sensor3_input = Input(shape=input_shapes[2])
- sensor4_input = Input(shape=input_shapes[3])
- # Get the outputs from each sub-model
- sensor1_output = sensor1_model(sensor1_input)
- # sensor2_output = sensor2_model(sensor2_input)
- # sensor3_output = sensor3_model(sensor3_input)
- # sensor4_output = sensor4_model(sensor4_input)
- # Concatenate the outputs
- # concatenated = layers.concatenate([sensor1_output, sensor2_output, sensor3_output, sensor4_output])
- concatenated = sensor1_output
- # Add Dense and Dropout layers
- x = layers.Dense(model_config['dense_units'], activation='relu')(concatenated)
- x = layers.Dropout(model_config['dropout_rate'])(x)
- output = layers.Dense(1, activation='sigmoid', dtype='float32')(x)
- #output = layers.Dense(1)(x)
- initial_learning_rate = model_config['lr']
- decay_steps = model_config['lr_decay_steps']
- # Create the learning rate schedule
- lr_schedule = tf.keras.optimizers.schedules.CosineDecay(
- initial_learning_rate=initial_learning_rate,
- decay_steps=decay_steps,
- alpha=model_config['lr_alpha']
- )
- optimizer = Adam(learning_rate=lr_schedule)
- # Create and compile the model
- model = Model(inputs=[sensor1_input, sensor2_input, sensor3_input, sensor4_input], outputs=output)
- #loss_fn = tf.keras.losses.BinaryCrossentropy()
- loss_fn = tf.keras.losses.BinaryFocalCrossentropy(gamma=model_config['loss_gamma'], alpha=model_config['loss_alpha'])
- model.compile(optimizer=optimizer,
- loss=loss_fn,
- metrics=[keras.metrics.AUC(),
- keras.metrics.SpecificityAtSensitivity(0.99),
- keras.metrics.SensitivityAtSpecificity(0.99)]
- )
- return model
- def feature_model_1D(input_dim,model_config):
- inputs = Input(shape=(input_dim,), name="input_features")
- # First Dense Layer
- x = tf.keras.layers.Dense(128, activation='relu', name="dense_1")(inputs)
- x = tf.keras.layers.BatchNormalization(name="batch_norm_1")(x)
- x = tf.keras.layers.Dropout(0.3, name="dropout_1")(x)
- # Second Dense Layer
- x = tf.keras.layers.Dense(64, activation='relu', name="dense_2")(x)
- x = tf.keras.layers.BatchNormalization(name="batch_norm_2")(x)
- x = tf.keras.layers.Dropout(0.3, name="dropout_2")(x)
- # Third Dense Layer
- x = tf.keras.layers.Dense(32, activation='relu', name="dense_3")(x)
- x = tf.keras.layers.BatchNormalization(name="batch_norm_3")(x)
- x = tf.keras.layers.Dropout(0.3, name="dropout_3")(x)
- # Output Layer
- outputs = tf.keras.layers.Dense(1, activation='sigmoid', name="output")(x)
- # Create the model
- model = tf.keras.Model(inputs=inputs, outputs=outputs, name="sleep_awake_classifier")
- loss_fn = tf.keras.losses.BinaryFocalCrossentropy(gamma=3, alpha=0.9)
- model.compile(
- optimizer=tf.keras.optimizers.Adam(learning_rate=model_config['lr']),
- loss=loss_fn,
- metrics=[keras.metrics.AUC(),
- keras.metrics.SpecificityAtSensitivity(0.99),
- keras.metrics.SensitivityAtSpecificity(0.99)]
- )
- return model
- def wrap_model_for_single_input(original_model, input_shapes):
- """
- Wraps a multi-input model to accept a single concatenated input tensor.
- Args:
- original_model (tf.keras.Model): The original multi-input model.
- input_shapes (list of tuples): Shapes of the inputs for the original model.
- Returns:
- tf.keras.Model: The wrapped model with a single input tensor.
- """
- # Calculate the total number of features in the concatenated input
- total_features = sum(np.prod(shape) for shape in input_shapes)
- # Define a new single input tensor
- single_input = Input(shape=(total_features,))
- # Split the single input tensor into the original input tensors
- split_tensors = []
- start_idx = 0
- for shape in input_shapes:
- num_features = np.prod(shape)
- end_idx = start_idx + num_features
- # Extract the slice corresponding to this input and reshape
- slice_tensor = single_input[..., start_idx:end_idx]
- reshaped_tensor = tf.reshape(slice_tensor, [-1, *shape]) # Ensure correct reshaping
- split_tensors.append(reshaped_tensor)
- start_idx = end_idx
- # Pass the split inputs to the original model
- output = original_model(split_tensors)
- # Create and return the wrapped model
- wrapped_model = Model(inputs=single_input, outputs=output)
- return wrapped_model
- if __name__ == '__main__':
- # input_shapes = (1500,3,3)
- # num_classes = 2 # Adjust as per your number of classes
- # model_config = {
- # 'lr': 0.001,
- # 'num_filters_1': 8,
- # 'num_filters_2': 16,
- # 'num_filters_3':32,
- # 'num_filters_4':64,
- # 'num_filters_1_sm': 8,
- # 'num_filters_2_sm': 16,
- # 'kernel_size_1': (3, 1),
- # 'kernel_size_2': (3, 1),
- # 'kernel_size_1_sm': (2, 1),
- # 'lstm_units': 32,
- # 'dropout_rate': 0.2,
- # 'dense_units': 64,
- # 'l2_regularization': 0.001,
- # 'pooling_size': (2, 1),
- # 'num_heads': 8, # Number of heads in Transformer block
- # 'ff_dim': 128, # Feed-forward layer size in Transformer block
- # 'loss_gamma': 4,
- # 'loss_alpha': 0.2,
- # 'lr_alpha': 0.5,
- # 'lr_decay_steps': 12100
- # }
- # model_config = {
- # 'lr': 0.001,
- # 'num_filters_1': 16,
- # 'num_filters_2': 32,
- # 'num_filters_3':64,
- # 'dropout_rate': 0.3,
- # 'dense_units': 128,
- # 'l2_regularization': 0.0001,
- # 'loss_gamma': 4,
- # 'loss_alpha': 0.2,
- # 'lr_alpha': 0.1,
- # 'lr_decay_steps': 12100
- # }
- # model = build_stacked_model(input_shapes, num_classes, model_config)
- # model.summary()
- # random_data = np.random.rand(1, 1500,3,3).astype(np.float32)
- # output = model.predict(random_data)
- # # Define input shape and number of classes
- ppg_input_shape = (1500, 3, 1) # Adjust as per your data
- gyro_input_shape = (1500, 3, 1) # Adjust as per your data
- acc_input_shape = (1500, 3, 1) # Adjust as per your data
- temp_input_shape = (1500, 3, 1)
- input_shapes = [ppg_input_shape, gyro_input_shape, acc_input_shape, temp_input_shape]
- num_classes = 2 # Adjust as per your number of classes
- model_config = {
- 'lr': 0.001,
- 'num_filters_1': 8,
- 'num_filters_2': 16,
- 'num_filters_3':32,
- 'num_filters_4':64,
- 'num_filters_1_sm': 8,
- 'num_filters_2_sm': 16,
- 'kernel_size_1': (3, 1),
- 'kernel_size_2': (3, 1),
- 'kernel_size_1_sm': (2, 1),
- 'lstm_units': 32,
- 'dropout_rate': 0.2,
- 'dense_units': 64,
- 'l2_regularization': 0.001,
- 'pooling_size': (2, 1),
- 'num_heads': 8, # Number of heads in Transformer block
- 'ff_dim': 128, # Feed-forward layer size in Transformer block
- 'loss_gamma': 4,
- 'loss_alpha': 0.2,
- 'lr_alpha': 0.5,
- 'lr_decay_steps': 12100
- }
- # Build and summarize the model
- model = build_combined_model(input_shapes, num_classes, model_config)
- # Wrap the model for a single concatenated input
- wrapped_model = wrap_model_for_single_input(
- model,
- input_shapes=input_shapes
- )
- # Save the wrapped model
- wrapped_model.save('wrapped_model')
- converter = tf.lite.TFLiteConverter.from_saved_model('wrapped_model')
- converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS, tf.lite.OpsSet.SELECT_TF_OPS]
- converter._experimental_lower_tensor_list_ops = False
- tflite_model = converter.convert()
- try:
- tflite_model = converter.convert()
- # Save the TensorFlow Lite model
- with open('wrapped_model.tflite', 'wb') as f:
- f.write(tflite_model)
- print("TensorFlow Lite model conversion successful.")
- except Exception as e:
- print(f"Error during TFLite conversion: {e}")
- plot_model(model, to_file='model_plot.png', show_shapes=True, show_layer_names=True,expand_nested=True)
- model.summary()
- # # Create synthetic test input data
- # ppg_data = np.random.rand(1, *ppg_input_shape).astype(np.float32) # Batch size of 1
- # gyro_data = np.random.rand(1, *gyro_input_shape).astype(np.float32)
- # acc_data = np.random.rand(1, *acc_input_shape).astype(np.float32)
- # temp_data = np.random.rand(1, *temp_input_shape).astype(np.float32)
- # # Print shapes of the synthetic data
- # print("PPG data shape:", ppg_data.shape)
- # print("Gyro data shape:", gyro_data.shape)
- # print("Acc data shape:", acc_data.shape)
- # print("Temp data shape:", temp_data.shape)
- # # Pass the synthetic data through the model to get the output
- # output = model.predict([ppg_data, gyro_data, acc_data, temp_data])
- # #output = model.predict([ppg_data, gyro_data, acc_data])
- # # Print the model output
- # print("Model output:", output)
- # input_dim = 345 # Replace with the number of features in your dataset
- # model = feature_model_1D(input_dim, model_config)
- # model.summary()
model.py at commit d066920, no license · at the source
Overview
- Department of Computer Science and Electrical Engineering, University of Maryland Baltimore County,Baltimore, MD USA
- Humans in Complex Systems Division, US Army DEVCOM Army Research Laboratory,Aberdeen, MD USA
- D-Prime LLC, McLean, VA 22101 USA
- Tanzen Medical Inc.,Severna Park, MD 21146 USA
Abstract
The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.
Repository
Its files are read in the Code ↔ Paper reader above, with 8 matches between paragraphs and lines of code.
Madeby7/Sleep-State-Detection
d0669200bfae7882b6457d48fb3fc41d04e49551, 23 February 2026Availability: 1 check, the latest on 30 September 2026: the link answers
- 30 September 2026: the link answers
5 files
- Train/
Data_loader_new.py , Python, 716 lines - Train/
Late_Fusion.ipynb , Jupyter, 492 lines, 3 matches - Train/
model.py , Python, 701 lines, 3 matches - Train/
ppg_preprocess.py , Python, 662 lines, 2 matches - README.md, Text, 361 lines
The paper's code and data availability statement is in the Data section.
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 4 scripts, each with its path and the digest of its content;
- 8 matches between paragraphs of the paper and lines of the code (method lexical-v1);
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
No dataset and no data link were found in the paper.
Code and data availability statement
The paper has a code and data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:
- it points to the authors' code: Madeby7/
Sleep-State-Detection - it says that the data are available on request
Read it in the paper: doi.org/10.1038/s41598-026-42310-8.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 1, 30 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 7 authors, 9 keywords, 16 MeSH terms, 1 funder, 23 references.
Cite
This paper
Anwar, Y., Bansal, K., Kucukosmanoglu, M., Dang, Q., Feltch, C., Brooks, J., & Banerjee, N. (2026). Sleep awake detection from leg-worn wearables using deep sensor fusion. Scientific reports, 16(1), 9930. https://
BibTeX
@article{anwar2026sleep,
author = {Anwar, Yumna and Bansal, Kanika and Kucukosmanoglu, Murat and Dang, Quang and Feltch, Cody and Brooks, Justin and Banerjee, Nilanjan},
title = {{Sleep awake detection from leg-worn wearables using deep sensor fusion}},
journal = {Scientific reports},
year = {2026},
month = mar,
volume = {16},
number = {1},
pages = {9930},
publisher = {Nature Publishing Group},
issn = {2045-2322},
doi = {10.1038/
url = {https://
pmid = {41820476},
pmcid = {PMC13018206}
}
RIS
TY - JOUR
AU - Anwar, Yumna
AU - Bansal, Kanika
AU - Kucukosmanoglu, Murat
AU - Dang, Quang
AU - Feltch, Cody
AU - Brooks, Justin
AU - Banerjee, Nilanjan
TI - Sleep awake detection from leg-worn wearables using deep sensor fusion
T2 - Scientific reports
J2 - Sci Rep
PY - 2026
DA - 2026/
VL - 16
IS - 1
SP - 9930
SN - 2045-2322
PB - Nature Publishing Group
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"type": "article-journal",
"title": "Sleep awake detection from leg-worn wearables using deep sensor fusion",
"container-title": "Scientific reports",
"author": [
{
"family": "Anwar",
"given": "Yumna"
},
{
"family": "Bansal",
"given": "Kanika"
},
{
"family": "Kucukosmanoglu",
"given": "Murat"
},
{
"family": "Dang",
"given": "Quang"
},
{
"family": "Feltch",
"given": "Cody"
},
{
"family": "Brooks",
"given": "Justin"
},
{
"family": "Banerjee",
"given": "Nilanjan"
}
],
"container-title-short":
"volume": "16",
"issue": "1",
"page": "9930",
"DOI": "10.1038/
"PMID": "41820476",
"PMCID": "PMC13018206",
"ISSN": "2045-2322",
"publisher": "Nature Publishing Group",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
3,
12
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.1093/sleepadvances/zpag051 [code]
- What matters beyond model choice for wearable sleep staging? How personalization, evaluation choices, and easy-to-classify wake impact performance.Journal: Sleep advances : a journal of the Sleep Research SocietyIn common: Keras, TensorFlow, seaborn, 5 other tools, other, methods / tools, 3 references
- [2] doi:10.1038/s41597-025-05174-7 [code]
- A large-scale MEG and EEG dataset for object recognition in naturalistic scenesJournal: n/aIn common: Keras, TensorFlow, Plotly, 6 other tools, methods / tools
- [3] doi:10.1038/s42003-026-10957-8 [code]
- Brain defence by the extracellular matrix protein Cochlin.Journal: Communications biologyIn common: Keras, TensorFlow, Plotly, 6 other tools
- [4] doi:10.1038/s41467-026-75455-1 [code]
- Shared latent representations of speech production for cross-patient speech decoding.Journal: Nature communicationsIn common: Keras, TensorFlow, Plotly, 6 other tools
- [5] doi:10.1186/s12880-026-02481-2 [code]
- Deep learning-based neuroanatomical profiling reveals population-specific brain changes in multiple sclerosis: a large-scale Middle Eastern study.Journal: BMC medical imagingIn common: Keras, TensorFlow, Plotly, 6 other tools
- [6] doi:10.1016/j.xcrm.2026.102766 [code]
- A longitudinal single-cell and spatial multiomic atlas of pediatric high-grade glioma.Journal: Cell reports. MedicineIn common: Keras, TensorFlow, Plotly, 6 other tools
- [7] doi:10.1162/imag.a.1164 [code]
- Bias and generalizability of brain age prediction models: A multi-cohort evaluation with anatomical and interpretability insights.Journal: Imaging neuroscience (Cambridge, Mass.)In common: Keras, TensorFlow, Plotly, 6 other tools
- [8] doi:10.1523/eneuro.0023-26.2026 [code]
- Real-Time Segmentation and Classification of Birdsong Syllables for Learning Experiments.Journal: eNeuroIn common: Keras, TensorFlow, Plotly, 6 other tools
- [9] doi:10.3389/fnsys.2026.1822122 [code]
- Convergence-divergence circuits for multimodal integration of innate and learned opponent valences.Journal: Frontiers in systems neuroscienceIn common: Keras, TensorFlow, Plotly, 6 other tools
- [10] doi:10.64898/2026.05.06.26352540 [code]
- Generating synthetic tau-PET scans in Alzheimer’s disease from MRI, blood biomarkers and demographics with deep learningJournal: medRxiv (preprint)In common: Keras, TensorFlow, seaborn, 5 other tools, other
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 1 repository of the authors' code, each at its verified commit and with its license, 4 scripts, and 8 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:34ac4d12148b5e35…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
Request its removal
To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).
Discussion, reproductions, activity
Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.
Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
