Ejemplo n.º 1
0
    def __init__(self, config, dataset):
        self.config = config
        self.dataset = dataset
        self.seq_len = self.dataset.seq_len
        self.n_inputs = self.dataset.n_inputs
        self.n_outputs = self.dataset.n_outputs
        self.forecast_steps = self.config.forecast_steps
        self.n_layers = self.config.num_layers
        self.n_hidden_units = self.config.num_hidden
        self.opt = Optimizers(self.config)
        self.initializer = Initializer(self.config)
        super().__init__(self.seq_len, self.n_inputs, self.n_outputs)

        # Build model
        self.model = self._build_model()
Ejemplo n.º 2
0
class RNNUqRangeEstimate(BaseModelClass):
    """
    RNN PUQ Range Estimate compiled model with the specified architecture

    Builds a keras model
    """
    def __init__(self, config, dataset):
        self.config = config
        self.dataset = dataset
        self.seq_len = self.dataset.seq_len
        self.n_inputs = self.dataset.n_inputs
        self.n_outputs = self.dataset.n_outputs
        self.forecast_steps = self.config.forecast_steps
        self.n_layers = self.config.num_layers
        self.n_hidden_units = self.config.num_hidden
        self.opt = Optimizers(self.config)
        self.initializer = Initializer(self.config)
        super().__init__(self.seq_len, self.n_inputs, self.n_outputs)

        # Build model
        self.model = self._build_model()

    def _build_model(self):
        """
        Builds a rnn uq range estimate model based on the architecture defined in the configs

        The input received is already padded from the data processing module for variable sequence length.
        Making is used to keep track of padded elements in the tensor. Keras layers such as Cropping1D and Concatenate
        do not use masking, hence custom layer RemoveMask is used to strip masking information from the outputs for
        such layers.

        Architecture Logic for Multi Step Forecast -> Append the output of previous forecast step to the next one

        1. Concatenate last time step aux features with outputs as outputs only contain financial fields
        2. Concatenate the above output to the inputs and strip the first element in the sequence to keep the input
            shape consistent
        3. Repeat 1,2 for subsequent outputs


        :return: compiled keras model which outputs (output_1, output_2, ...) where _1 refers to
        the forecast step. For example _1 : 12 month forecast, _2 : 24 month forecast and so on
        """

        outputs = []

        # Masking information is only used by certain layers such as LSTM. Hence two copies of inputs are used, one for
        # propagating the mask and second for storing inputs which are used in operations such as Cropping1D and
        # concatenate.
        inputs = x = keras.Input(shape=(self.seq_len, self.n_inputs),
                                 name='input_financials')
        prev_input = inputs

        last_time_step_aux = self.get_last_time_step_aux(x)

        lstm_count = 0
        output_count = 0

        initializer = self.initializer.get_initializer()

        for i in range(self.n_layers):
            lstm_count += 1
            if self.config.rnn_cell == 'lstm':
                x = layers.LSTM(
                    self.n_hidden_units,
                    kernel_initializer=initializer,
                    kernel_regularizer=tf.keras.regularizers.l2(
                        self.config.l2_alpha),
                    recurrent_regularizer=tf.keras.regularizers.l2(
                        self.config.recurrent_l2_alpha),
                    return_sequences=True,
                    kernel_constraint=MaxNorm(self.config.max_norm),
                    recurrent_dropout=self.config.recurrent_dropout,
                    name='lstm_%i' % lstm_count)(x, training=True)
                x = layers.BatchNormalization()(x)
                x = layers.Dropout(rate=self.config.dropout)(x, training=True)
            elif self.config.rnn_cell == 'gru':
                x = layers.GRU(self.n_hidden_units,
                               kernel_initializer=initializer,
                               kernel_regularizer=tf.keras.regularizers.l2(
                                   self.config.l2_alpha),
                               recurrent_regularizer=tf.keras.regularizers.l2(
                                   self.config.recurrent_l2_alpha),
                               return_sequences=True,
                               kernel_constraint=MaxNorm(self.config.max_norm),
                               recurrent_dropout=self.config.recurrent_dropout,
                               name='gru_%i' % lstm_count)(x, training=True)
                x = layers.BatchNormalization()(x)
                x = layers.Dropout(rate=self.config.dropout)(x, training=True)
            else:
                raise NotImplementedError

        output_count += 1
        # outputs for target values
        cur_output_tar = layers.Dense(self.n_outputs,
                                      name='OUTPUT_TARGET_%i' %
                                      output_count)(x)
        # outputs for variances of the target values
        cur_output_var = layers.Dense(self.n_outputs,
                                      name='OUTPUT_VARIANCE_%i' %
                                      output_count)(x)
        cur_output_var = SoftPlus()(cur_output_var)

        outputs.append(cur_output_tar)
        outputs.append(cur_output_var)

        for fcst_step in range(1, self.forecast_steps):
            # output_count, lstm_count keep track of layer ids. output_count and fcst_step are not the same as one
            # fcst_step could have multiple outputs.
            output_count += 1
            cur_output = outputs[-2]  # last target output
            last_time_step_fin = self.get_last_time_step(
                cur_output, output_count)
            # Combine latest prediction with last available aux features to make the input shape compatible
            last_time_step = layers.concatenate(
                [last_time_step_fin, last_time_step_aux],
                axis=2,
                name='concat_fin_aux_%i' % fcst_step)
            # combine latest prediction with input sequence
            cur_input = layers.concatenate(
                [prev_input, last_time_step],
                axis=1,
                name='combine_input_w_last_pred_%i' % fcst_step)
            cur_input = layers.Cropping1D(cropping=(1, 0),
                                          name='updated_input_w_last_pred_%i' %
                                          fcst_step)(cur_input)
            prev_input = cur_input

            # Add LSTM layer for intermediary prediction
            lstm_count += 1
            if self.config.rnn_cell == 'lstm':
                intm = layers.LSTM(
                    self.n_hidden_units,
                    return_sequences=True,
                    kernel_initializer=initializer,
                    kernel_regularizer=tf.keras.regularizers.l2(
                        self.config.l2_alpha),
                    recurrent_regularizer=tf.keras.regularizers.l2(
                        self.config.recurrent_l2_alpha),
                    kernel_constraint=MaxNorm(self.config.max_norm),
                    recurrent_dropout=self.config.recurrent_dropout,
                    name='lstm_%i' % lstm_count)(cur_input, training=True)
                intm = layers.BatchNormalization()(intm)
                intm = layers.Dropout(rate=self.config.dropout)(intm,
                                                                training=True)
            elif self.config.rnn_cell == 'gru':
                intm = layers.GRU(
                    self.n_hidden_units,
                    return_sequences=True,
                    kernel_initializer=initializer,
                    kernel_regularizer=tf.keras.regularizers.l2(
                        self.config.l2_alpha),
                    recurrent_regularizer=tf.keras.regularizers.l2(
                        self.config.recurrent_l2_alpha),
                    kernel_constraint=MaxNorm(self.config.max_norm),
                    recurrent_dropout=self.config.recurrent_dropout,
                    name='gru_%i' % lstm_count)(cur_input, training=True)
                intm = layers.BatchNormalization()(intm)
                intm = layers.Dropout(rate=self.config.dropout)(intm,
                                                                training=True)
            else:
                raise NotImplementedError

            outputs.append(
                layers.Dense(self.n_outputs,
                             name='OUTPUT_TARGET_%i' % output_count)(intm))

            intm_var = layers.Dense(self.n_outputs,
                                    name='OUTPUT_VARIANCE_%i' %
                                    output_count)(intm)
            outputs.append(SoftPlus()(intm_var))

        model = keras.Model(inputs=inputs, outputs=outputs)

        return model
Ejemplo n.º 3
0
class MLPPointEstimate(BaseModelClass):
    """
    MLP Point Estimate compiled model with the specified architecture

    Builds a keras model
    """
    def __init__(self, config, dataset):
        self.config = config
        self.dataset = dataset
        self.seq_len = self.dataset.seq_len
        self.n_inputs = self.dataset.n_inputs
        self.n_outputs = self.dataset.n_outputs
        self.forecast_steps = self.config.forecast_steps
        self.n_layers = self.config.num_layers
        self.n_hidden_units = self.config.num_hidden
        self.opt = Optimizers(self.config)
        self.initializer = Initializer(self.config)
        super().__init__(self.seq_len, self.n_inputs, self.n_outputs)

        # Build model
        self.model = self._build_model()

    def _build_model(self):
        """
        Builds a model based on the architecture defined in the configs

        The input received is already padded from the data processing module for variable sequence length.
        Making is used to keep track of padded elements in the tensor. Keras layers such as Cropping1D and Concatenate
        do not use masking, hence custom layer RemoveMask is used to strip masking information from the outputs for
        such layers.

        Architecture Logic for Multi Step Forecast -> Append the output of previous forecast step to the next one

        1. Concatenate last time step aux features with outputs as outputs only contain financial fields
        2. Concatenate the above output to the inputs and strip the first element in the sequence to keep the input
            shape consistent
        3. Repeat 1,2 for subsequent outputs


        :return: compiled keras model which outputs ((output_1, mask_1), (output_2, mask_2), ...) where _1 refers to
        the forecast step. For example _1 : 12 month forecast, _2 : 24 month forecast and so on
        """

        outputs = []

        # Masking information is only used by certain layers such as LSTM. Hence two copies of inputs are used, one for
        # propagating the mask and second for storing inputs which are used in operations such as Cropping1D and
        # concatenate.
        inputs = x = keras.Input(shape=(self.seq_len * self.n_inputs),
                                 name='input_financials')
        prev_input = inputs

        # last_time_step_aux = self.get_last_time_step_aux(x)

        hidden_layer_count = 0
        output_count = 0

        initializer = self.initializer.get_initializer()

        # TODO: make activation generic so as to use 'relu', 'leakyrelu', 'tanh' from config

        for i in range(self.n_layers):
            hidden_layer_count += 1
            x = layers.Dense(self.n_hidden_units,
                             kernel_initializer=initializer,
                             kernel_regularizer=tf.keras.regularizers.l2(
                                 self.config.l2_alpha),
                             kernel_constraint=MaxNorm(self.config.max_norm),
                             use_bias=False,
                             name='dense_%i' % hidden_layer_count)(x)
            x = layers.BatchNormalization()(x)
            x = layers.ReLU()(x)
            x = layers.Dropout(rate=self.config.dropout)(
                x, training=self.config.train)

        output_count += 1
        cur_output = layers.Dense(self.n_outputs,
                                  name='OUTPUT_%i' % output_count)(x)

        outputs.append(cur_output)

        for fcst_step in range(1, self.forecast_steps):
            print("Multi-step forecast not implemented for MLP")
            raise NotImplementedError
            # # output_count, lstm_count keep track of layer ids. output_count and fcst_step are not the same as one
            # # fcst_step could have multiple outputs.
            # output_count += 1
            # cur_output = outputs[-1]
            # last_time_step_fin = self.get_last_time_step(cur_output, output_count)
            # # Combine latest prediction with last available aux features to make the input shape compatible
            # last_time_step = layers.concatenate([last_time_step_fin, last_time_step_aux], axis=2,
            #                                     name='concat_fin_aux_%i' % fcst_step)
            # # combine latest prediction with input sequence
            # cur_input = layers.concatenate([prev_input, last_time_step], axis=1,
            #                                name='combine_input_w_last_pred_%i' % fcst_step)
            # cur_input = layers.Cropping1D(cropping=(1, 0), name='updated_input_w_last_pred_%i' % fcst_step)(cur_input)
            # prev_input = cur_input
            # # Add layer for intermediary prediction
            # hidden_layer_count += 1
            # dense_intm = layers.Dense(self.n_hidden_units,
            #                           kernel_initializer=initializer,
            #                           kernel_regularizer = tf.keras.regularizers.l2(self.config.l2_alpha),
            #                           use_bias=False,
            #                           name='dense_%i' % hidden_layer_count)(cur_input)
            # dense_intm = layers.BatchNormalization()(dense_intm)
            # dense_intm = layers.ReLU()(dense_intm)
            # dense_intm = layers.Dropout(rate=self.config.dropout)(dense_intm, training=self.config.train)
            # outputs.append(layers.Dense(self.n_outputs, name='OUTPUT_%i' % output_count)(dense_intm))

        model = keras.Model(inputs=inputs, outputs=outputs)

        return model
class MLPLinearPointEstimate(BaseModelClass):
    """
    MLP Point Estimate compiled model with the specified architecture

    Builds a keras model
    """
    def __init__(self, config, dataset):
        self.config = config
        self.dataset = dataset
        self.seq_len = self.dataset.seq_len
        self.n_inputs = self.dataset.n_inputs
        self.n_outputs = self.dataset.n_outputs
        self.forecast_steps = self.config.forecast_steps
        self.n_layers = self.config.num_layers
        self.n_hidden_units = self.config.num_hidden
        self.opt = Optimizers(self.config)
        self.initializer = Initializer(self.config)
        super().__init__(self.seq_len, self.n_inputs, self.n_outputs)

        # Build model
        self.model = self._build_model()

    def _build_model(self):
        """
        Builds a model based on the architecture defined in the configs

        The input received is already padded from the data processing module for variable sequence length.
        Making is used to keep track of padded elements in the tensor. Keras layers such as Cropping1D and Concatenate
        do not use masking, hence custom layer RemoveMask is used to strip masking information from the outputs for
        such layers.

        Architecture Logic for Multi Step Forecast -> Append the output of previous forecast step to the next one

        1. Concatenate last time step aux features with outputs as outputs only contain financial fields
        2. Concatenate the above output to the inputs and strip the first element in the sequence to keep the input
            shape consistent
        3. Repeat 1,2 for subsequent outputs


        :return: compiled keras model which outputs ((output_1, mask_1), (output_2, mask_2), ...) where _1 refers to
        the forecast step. For example _1 : 12 month forecast, _2 : 24 month forecast and so on
        """

        outputs = []

        # Masking information is only used by certain layers such as LSTM. Hence two copies of inputs are used, one for
        # propagating the mask and second for storing inputs which are used in operations such as Cropping1D and
        # concatenate.
        inputs = x = keras.Input(shape=(self.seq_len * self.n_inputs),
                                 name='input_financials')

        initializer = self.initializer.get_initializer()

        output_count = 1
        cur_output = layers.Dense(self.n_outputs,
                                  kernel_initializer=initializer,
                                  name='OUTPUT_%i' % output_count)(x)

        outputs.append(cur_output)

        model = keras.Model(inputs=inputs, outputs=outputs)

        return model