from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Activation, Lambda, \
Convolution2D, TimeDistributed, \
Reshape, LSTM, Dropout, BatchNormalization
from tensorflow.keras.regularizers import l2
from tensorflow.keras.optimizers import Adam
import numpy as np
from argparse import Namespace
from .base_hyperparameter_generator import generate_base_hyperparameter_set
from ..task import Task
[docs]class DeepConvLSTM:
"""Generate DeepConvLSTM model and hyperparameters.
"""
model_name = "DeepConvLSTM"
def __init__(self, x_shape, number_of_classes, metrics=['accuracy'],
deepconvlstm_min_conv_layers=1,
deepconvlstm_max_conv_layers=10,
deepconvlstm_min_conv_filters=10,
deepconvlstm_max_conv_filters=100,
deepconvlstm_min_lstm_layers=1,
deepconvlstm_max_lstm_layers=5,
deepconvlstm_min_lstm_dims=10,
deepconvlstm_max_lstm_dims=100, **base_parameters):
"""
Parameters
----------
x_shape : tuple
Shape of the input dataset: (num_samples, num_timesteps, num_channels)
number_of_classes : int
Number of classes for classification task
metrics : list
Metrics to calculate on the validation set.
See https://keras.io/metrics/ for possible values.
deepconvlstm_min_conv_layers : int
minimum number of Conv layers in DeepConvLSTM model
deepconvlstm_max_conv_layers : int
maximum number of Conv layers in DeepConvLSTM model
deepconvlstm_min_conv_filters : int
minimum number of filters per Conv layer in DeepConvLSTM model
deepconvlstm_max_conv_filters : int
maximum number of filters per Conv layer in DeepConvLSTM model
deepconvlstm_min_lstm_layers : int
minimum number of Conv layers in DeepConvLSTM model
deepconvlstm_max_lstm_layers : int
maximum number of Conv layers in DeepConvLSTM model
deepconvlstm_min_lstm_dims : int
minimum number of hidden nodes per LSTM layer in DeepConvLSTM model
deepconvlstm_max_lstm_dims : int
maximum number of hidden nodes per LSTM layer in DeepConvLSTM model
"""
self.x_shape = x_shape
self.number_of_classes = number_of_classes
self.metrics = metrics
# Set default parameters
self.settings = {
'deepconvlstm_min_conv_layers': deepconvlstm_min_conv_layers,
'deepconvlstm_max_conv_layers': deepconvlstm_max_conv_layers,
'deepconvlstm_min_conv_filters': deepconvlstm_min_conv_filters,
'deepconvlstm_max_conv_filters': deepconvlstm_max_conv_filters,
'deepconvlstm_min_lstm_layers': deepconvlstm_min_lstm_layers,
'deepconvlstm_max_lstm_layers': deepconvlstm_max_lstm_layers,
'deepconvlstm_min_lstm_dims': deepconvlstm_min_lstm_dims,
'deepconvlstm_max_lstm_dims': deepconvlstm_max_lstm_dims,
}
# Add missing parameters from default
for key, value in base_parameters.items():
if key not in self.settings:
self.settings[key] = value
[docs] def generate_hyperparameters(self):
"""Generate a hyperparameter set that defines a DeepConvLSTM model.
Returns
----------
hyperparameters : dict
parameters for a DeepConvLSTM model
"""
params = Namespace(**self.settings)
hyperparameters = generate_base_hyperparameter_set(params.low_lr,
params.high_lr,
params.low_reg,
params.high_reg)
number_of_conv_layers = np.random.randint(params.deepconvlstm_min_conv_layers,
params.deepconvlstm_max_conv_layers + 1)
hyperparameters['filters'] = np.random.randint(params.deepconvlstm_min_conv_filters,
params.deepconvlstm_max_conv_filters + 1,
number_of_conv_layers).tolist()
number_of_lstm_layers = np.random.randint(params.deepconvlstm_min_lstm_layers,
params.deepconvlstm_max_lstm_layers + 1)
hyperparameters['lstm_dims'] = np.random.randint(params.deepconvlstm_min_lstm_dims,
params.deepconvlstm_max_lstm_dims + 1,
number_of_lstm_layers).tolist()
return hyperparameters
[docs] def create_model(self, filters, lstm_dims, learning_rate=0.01,
regularization_rate=0.01, task=Task.classification):
"""Generate a model with convolution and LSTM layers.
See Ordonez et al., 2016, http://dx.doi.org/10.3390/s16010115
Parameters
----------
filters : list of ints
number of filters for each convolutional layer
lstm_dims : list of ints
number of hidden nodes for each LSTM layer
learning_rate : float
learning rate
regularization_rate : float
regularization rate
task: str
Task type, either 'classification' or 'regression'
Returns
-------
model : Keras model
The compiled Keras model
"""
dim_length = self.x_shape[1] # number of samples in a time series
dim_channels = self.x_shape[2] # number of channels
dim_output = self.number_of_classes
weightinit = 'lecun_uniform' # weight initialization
model = Sequential() # initialize model
model.add(BatchNormalization(input_shape=(dim_length, dim_channels)))
# reshape a 2 dimensional array per file/person/object into a
# 3 dimensional array
model.add(
Reshape(target_shape=(dim_length, dim_channels, 1)))
for filt in filters:
# filt: number of filters used in a layer
model.add(
Convolution2D(filt, kernel_size=(3, 1), padding='same',
kernel_regularizer=l2(regularization_rate),
kernel_initializer=weightinit))
model.add(BatchNormalization())
model.add(Activation('relu'))
# reshape 3 dimensional array back into a 2 dimensional array,
# but now with more dept as we have the the filters for each channel
model.add(Reshape(target_shape=(dim_length, filters[-1] * dim_channels)))
for lstm_dim in lstm_dims:
model.add(LSTM(units=lstm_dim, return_sequences=True,
activation='tanh'))
model.add(Dropout(0.5)) # dropout before the dense layer
# set up final dense layer such that every timestamp is given one
# classification
model.add(
TimeDistributed(
Dense(units=dim_output, kernel_regularizer=l2(regularization_rate))))
if task is Task.classification:
model.add(Activation("softmax"))
# Final classification layer - per timestep
model.add(Lambda(lambda x: x[:, -1, :], output_shape=[dim_output]))
if task is Task.classification:
loss_function = 'categorical_crossentropy'
elif task is Task.regression:
loss_function = 'mean_squared_error'
model.compile(loss=loss_function,
optimizer=Adam(lr=learning_rate),
metrics=self.metrics)
return model