Esempio n. 1
0
    def save(
            self,
            modelSavePath
    ):
        """
        Save the model parameters at the provided path

        :param modelSavePath: Path where the parameters are to be saved
        :return: None
        """

        GlobalLogger.getLogger().log('Saving Model', 1, self.save.__name__)

        saveDict = {
            'forecastHorizon': self.forecastHorizon,
            'lag': self.lag,
            'activation': self.activation,
            'numUnitsPerLayer': self.numUnitsPerLayer,
            'numLayers': self.numLayers,
            'numTargetVariables': self.numTargetVariables,
            'numExoVariables': self.numExoVariables,
            'weights': self.model.get_weights()
        }

        with open(modelSavePath, 'wb') as fl:
            pickle.dump(saveDict, fl)

        GlobalLogger.getLogger().log('Saving Complete', 1, self.save.__name__)
Esempio n. 2
0
    def load(modelLoadPath):
        """
        Loads the model from the provided filepath

        :param modelLoadPath: path from where to load the model
        :return: model which is loaded from the given path
        """

        model = LstmForecast(loadModel=True)
        GlobalLogger.getLogger().log('Loading Model', 1,
                                     LstmForecast.load.__name__)

        with open(modelLoadPath, 'rb') as loadFile:
            loadDict = pickle.load(loadFile)

        model.forecastHorizon = loadDict['forecastHorizon']
        model.layerClass = loadDict['layerClass']
        model.layerParameters = loadDict['layerParameters']
        model.numRnnLayers = loadDict['numRnnLayers']
        model.numTargetVariables = loadDict['numTargetVariables']
        model.numExoVariables = loadDict['numExoVariables']

        model.buildModel()
        model.model.set_weights(loadDict['weights'])

        GlobalLogger.getLogger().log('Loading Complete', 1,
                                     LstmForecast.load.__name__)

        return model
Esempio n. 3
0
    def load(modelLoadPath):
        """
        Loads the model from the provided filepath

        :param modelLoadPath: path from where to load the model
        :return: model which is loaded from the given path
        """

        model = DeepNN(loadModel=True)

        with open(modelLoadPath, 'rb') as fl:
            loadDict = pickle.load(fl)

        GlobalLogger.getLogger().log('Loading Model', 1, DeepNN.load.__name__)

        model.forecastHorizon = loadDict['forecastHorizon']
        model.lag = loadDict['lag']
        model.activation = loadDict['activation']
        model.numUnitsPerLayer = loadDict['numUnitsPerLayer']
        model.numLayers = loadDict['numLayers']
        model.numTargetVariables = loadDict['numTargetVariables']
        model.numExoVariables = loadDict['numExoVariables']

        model.buildModel()
        model.model.set_weights(loadDict['weights'])

        GlobalLogger.getLogger().log('Loading Complete', 1, DeepNN.load.__name__)

        return model
Esempio n. 4
0
    def save(self, modelSavePath):
        """
        Save the model parameters at the provided path

        :param modelSavePath: Path where the parameters are to be saved
        :return: None
        """

        GlobalLogger.getLogger().log('Saving Model', 1, self.save.__name__)

        saveDict = {
            'forecastHorizon': self.forecastHorizon,
            'layerClass': self.layerClass,
            'layerParameters': self.layerParameters,
            'numRnnLayers': self.numRnnLayers,
            'numTargetVariables': self.numTargetVariables,
            'numExoVariables': self.numExoVariables,
            'weights': self.model.get_weights()
        }

        saveFile = open(modelSavePath, 'wb')
        pickle.dump(saveDict, saveFile)
        saveFile.close()

        GlobalLogger.getLogger().log('Saving Complete', 1, self.save.__name__)
Esempio n. 5
0
    def __init__(
            self,
            typeOfData='simple',
            periodType='long',
            trendType='linear_inc',
            biasType='none'
    ):
        """
        Initialize the Difficult Data Generator

        :param typeOfData: Affects only the coefficients of the ARMA data generator.
        Choices: simple, long_term, extreme_short, extreme_long, extreme_different.
        Note that the prefix 'extreme' just refers to the noise being generated from
        an extreme valued distribution (for example Gumbel, Lognormal)
        :param periodType: Type of period the data should follow. Choices: long, short
        :param trendType: Type of trend the data should follow. Choices: linear_inc,
        linear_dec, poly_inc, poly_dec
        :param biasType: Type of bias the data should have. Choices: none, pos, neg.
        Note that the 'none' bias type is a string and NOT the Python None
        """

        GlobalLogger.getLogger().log(
            f'Type of data: {typeOfData}, period type: {periodType}, '
            + f'trend type: {trendType}, bias type: {biasType}',
            1,
            self.__init__.__name__
        )

        self.standardGen = StandardGenerator(typeOfData)

        if periodType == 'long':
            period = 10.0
        elif periodType == 'short':
            period = 1.0
        else:
            raise Exception("Invalid Period Type")

        if trendType == 'linear_inc':
            coeff = [0.0, 1.0]
        elif trendType == 'linear_dec':
            coeff = [0.0, -1.0]
        elif trendType == 'poly_inc':
            coeff = [0.0, 0.2, 0.5]
        elif trendType == 'poly_dec':
            coeff = [0.0, -0.2, -0.5]
        else:
            raise Exception("Invalid Trend Type")

        if biasType == 'none':
            coeff[0] = 0
        elif biasType == 'pos':
            coeff[0] = 5.0
        elif biasType == 'neg':
            coeff[0] = -5.0
        else:
            raise Exception("Invalid Bias Type")

        self.periodicGen = PeriodicGenerator(period=period)
        self.polyGen = PolynomialGenerator(coeff)
Esempio n. 6
0
    def __init__(self, typeOfData='simple'):
        """
        Initialize the Standard Data Generator

        :param typeOfData: Affects only the coefficients of the ARMA data generator.
        Choices: simple, long_term, extreme_short, extreme_long, extreme_different.
        Note that the prefix 'extreme' just refers to the noise being generated from
        an extreme valued distribution (for example Gumbel, Lognormal)
        """
        GlobalLogger.getLogger().log(
            f'Initializing Standard Generator using type: {typeOfData}', 1,
            self.__init__.__name__)

        if typeOfData == 'simple':
            obsCoef = np.array([0.5, -0.2, 0.2])
            noiseCoef = np.array([0.5, -0.2])

            noiseGenFunc = np.random.normal
            noiseGenParams = (0.0, 1.0)

        elif typeOfData == 'long_term':
            obsCoef, noiseCoef = \
                StandardGenerator.generateRandomArmaCoeff(50, 50)

            noiseGenFunc = np.random.normal
            noiseGenParams = (0.0, 1.0)

        elif typeOfData == 'extreme_short':
            obsCoef, noiseCoef = \
                StandardGenerator.generateRandomArmaCoeff(5, 5)

            noiseGenFunc = np.random.gumbel
            noiseGenParams = (-5., 10.0)

        elif typeOfData == 'extreme_long':
            obsCoef, noiseCoef = \
                StandardGenerator.generateRandomArmaCoeff(50, 50)

            noiseGenFunc = np.random.gumbel
            noiseGenParams = (-5., 10.0)

        elif typeOfData == 'extreme_different':
            obsCoef, noiseCoef = \
                StandardGenerator.generateRandomArmaCoeff(5, 5)

            noiseGenFunc = np.random.lognormal
            noiseGenParams = (0.0, 1.0)

        else:
            raise Exception("Invalid Type of Data")

        self.armaGen = ArmaGenerator(obsCoef, noiseCoef, noiseGenFunc,
                                     noiseGenParams)
Esempio n. 7
0
    def buildModel(self, layerList):
        """ Builds Model Architecture """

        GlobalLogger.getLogger().log(
            'Building Model Architecture',
            1,
            self.buildModel.__name__
        )

        self.model = tf.keras.Sequential(layers=layerList)

        inputDimension = self.numTargetVariables + self.numExoVariables
        self.model.build(input_shape=(None, None, inputDimension))
Esempio n. 8
0
    def predict(self, targetSeries, exogenousSeries=None):
        """
        Forecast using the model parameters on the provided input data
        :param targetSeries: Univariate Series of the Target Variable, it
        should be a numpy array of shape (n,)
        :param exogenousSeries: Series of exogenous Variables, it should be a
        numpy array of shape (n, numExoVariables), it can be None only if
        numExoVariables is 0 in which case the exogenous variables are not
        considered
        :return: Forecast targets predicted by the model, it has shape (n,), the
        horizon of the targets is the same as self.forecastHorizon
        """

        logger = GlobalLogger.getLogger()
        logger.log('Begin Prediction', 1, self.predict.__name__)

        assert (Utility.isExoShapeValid(exogenousSeries,
                                        self.inputDimension - 1))
        X = Utility.prepareDataPred(targetSeries, exogenousSeries)

        n = X.shape[0]
        Ypred = [None] * n

        for t in range(n):
            Ypred[t] = self.predictTimestep(X, t)

        Ypred = np.array(Ypred)
        logger.log(f'Output Shape: {Ypred.shape}', 2, self.predict.__name__)

        return Ypred
Esempio n. 9
0
    def buildMemory(self, X, currentTime):
        """
        Build Model Memory using the timesteps seen up till now
        :param X: Features, has shape (n, self.inputShape)
        :param currentTime: current timestep, memory would be built only using the
        timestep earlier than the current timestep
        :return: None
        """

        logger = GlobalLogger.getLogger()
        logger.log(f'Building Memory', 1, self.buildMemory.__name__)
        logger.log(f'Current Time: {currentTime}', 2,
                   self.buildMemory.__name__)
        assert (currentTime >= self.windowSize)

        sampleLow = 0
        sampleHigh = currentTime - self.windowSize

        self.memory = [None] * self.memorySize
        self.context = [None] * self.memorySize

        for i in range(self.memorySize):
            windowStartTime = np.random.randint(sampleLow, sampleHigh + 1)

            self.memory[i], self.context[i] = self.runGruOnWindow(
                X, windowStartTime)

        self.memory = tf.stack(self.memory)
        self.context = tf.stack(self.context)

        logger.log(
            f'Memory Shape: {self.memory.shape}, Context Shape: {self.context.shape}',
            2, self.buildMemory.__name__)
Esempio n. 10
0
    def runGruOnWindow(self, X, windowStartTime):
        """
        Runs GRU on the window and returns the final state
        :param X: Features, has shape (n, self.inputShape)
        :param windowStartTime: Starting timestep of the window
        :return: The final state after running on the window, it has shape (self.encoderStateSize,)
        """

        logger = GlobalLogger.getLogger()
        logger.log(f'Window Start Time: {windowStartTime}', 2,
                   self.runGruOnWindow.__name__)

        gruMemoryState, gruContextState = self.getInitialEncoderStates()

        for t in range(windowStartTime, windowStartTime + self.windowSize):
            gruMemoryState, _ = self.gruMemory(np.expand_dims(X[t], 0),
                                               gruMemoryState)

            gruContextState, _ = self.gruContext(np.expand_dims(X[t], 0),
                                                 gruContextState)

        gruMemoryState = tf.squeeze(gruMemoryState, axis=0)
        gruContextState = tf.squeeze(gruContextState, axis=0)

        logger.log(
            f'GRU memory state shape: {gruMemoryState.shape},' +
            f' context state shape: {gruContextState.shape}', 2,
            self.runGruOnWindow.__name__)

        return gruMemoryState, gruContextState
Esempio n. 11
0
    def predict(
            self,
            targetSeries,
            exogenousSeries=None,
    ):
        """
        Forecast using the model parameters on the provided input data

        :param targetSeries: Series of the Target Variable, it
        should be a numpy array of shape (n, numTargetVariables)
        :param exogenousSeries: Series of exogenous Variables, it should be a
        numpy array of shape (n, numExoVariables), it can be None only if
        numExoVariables is 0 in which case the exogenous variables are not
        considered
        :return: Forecast targets predicted by the model, it has shape (n,), the
        horizon of the targets is the same as self.forecastHorizon
        """

        logger = GlobalLogger.getLogger()

        logger.log(f'Target Series Shape: {targetSeries.shape}', 2, self.predict.__name__)
        if exogenousSeries is not None:
            logger.log(
                f'Exogenous Series Shape: {exogenousSeries.shape}', 2, self.predict.__name__
            )

        logger.log('Prepare Data', 1, self.predict.__name__)

        assert targetSeries.shape[1] == self.numTargetVariables
        assert (Utility.isExoShapeValid(exogenousSeries, self.numExoVariables))

        X = Utility.prepareDataPred(targetSeries, exogenousSeries)

        logger.log('Begin Prediction', 1, self.predict.__name__)
        return tf.squeeze(self.model.predict(np.expand_dims(X, axis=0), verbose=0), axis=0)
Esempio n. 12
0
    def generate(self, n):
        """Generates Sequence of the Provided Length"""

        logger = GlobalLogger.getLogger()
        logger.log(f'Generating Data of length {n}', 1, self.generate.__name__)

        p = self.obsCoef.shape[0]
        q = self.noiseCoef.shape[0]

        x = np.zeros(n)
        eps = np.zeros(n)

        for t in range(n):

            obsVal = 0
            for i in range(min(t, p)):
                obsVal += self.obsCoef[i] * x[t - i - 1]

            if self.obsFunc is not None:
                obsVal = self.obsFunc(obsVal)
            x[t] += obsVal

            noiseVal = 0
            for j in range(min(t, q)):
                noiseVal += self.noiseCoef[j] * eps[t - j - 1]

            if self.noiseFunc is not None:
                noiseVal = self.noiseFunc(noiseVal)
            x[t] += noiseVal

            eps[t] = self.noiseGenFunc(*self.noiseGenParams)
            x[t] += eps[t]

        return x
Esempio n. 13
0
    def evaluate(
            self,
            targetSeries,
            exogenousSeries=None,
            returnPred=False
    ):
        """
        Forecast using the model parameters on the provided data, evaluates
        the forecast result using the loss and returns it

        V.imp: Predicted value is NOT outputted for the first lag inputs,
        since then networks begins predicting from the lag term onwards. Also
        the last forecastHorizon terms are not taken as the input, they are
        taken as part of the true output which would be used for evaluation

        :param targetSeries: Multivariate Series of the Target Variable, it
        should be a numpy array of shape
        (self.lag + numTimesteps + self.forecastHorizon, numTargetVariables).
        numTimesteps is the number of timesteps on which our model must predict,
        the values ahead are for evaluating the predicted results with respect
        to them (i.e. they are true targets for our prediction)
        :param exogenousSeries: Series of exogenous Variables, it should be a
        numpy array of shape (self.lag + numTimesteps, numExoVariables), it can be None
        only if numExoVariables is 0 in which case the exogenous variables
        are not considered
        :param returnPred: If True, then return predictions along with loss, else
        return on loss
        :return: If True, then return predictions along with loss of the predicted
        and true targets, else return only loss. The predictions would have shape
        (numTimesteps, numTargetVariables)
        """

        logger = GlobalLogger.getLogger()

        logger.log(f'Target Series Shape: {targetSeries.shape}', 2, self.evaluate.__name__)
        if exogenousSeries is not None:
            logger.log(
                f'Exogenous Series Shape: {exogenousSeries.shape}', 2, self.evaluate.__name__
            )

        logger.log('Prepare Data', 1, self.evaluate.__name__)

        assert (self.checkShapeValid(targetSeries, exogenousSeries))

        X, Ytrue = DeepNN.prepareDataTrainDNN(
            targetSeries, exogenousSeries, self.forecastHorizon, self.lag
        )

        logger.log('Begin Evaluation', 1, self.predict.__name__)
        Ypred = self.model.predict(X, verbose=0)

        assert (Ytrue.shape == Ypred.shape)

        loss = DeepNN.lossFunc(Ytrue, Ypred)

        if returnPred:
            return loss, Ypred
        else:
            return loss
Esempio n. 14
0
    def buildModel(self):
        """ Builds Model Architecture """

        GlobalLogger.getLogger().log('Building Model Architecture', 1,
                                     self.buildModel.__name__)

        self.model = tf.keras.Sequential()

        for i in range(self.numRnnLayers):
            self.model.add(self.layerClass(**self.layerParameters))

        self.model.add(
            tf.keras.layers.TimeDistributed(
                tf.keras.layers.Dense(self.numTargetVariables,
                                      activation=None)))

        inputDimension = self.numTargetVariables + self.numExoVariables
        self.model.build(input_shape=(None, None, inputDimension))
Esempio n. 15
0
    def train(
            self,
            trainSequences,
            numIterations=1,
            optimizer=tf.optimizers.Adam(),
            modelSavePath=None,
            verboseLevel=1,
            returnLosses=True
    ):
        """
        Train the model on the provided data sequences

        :param trainSequences: Sequences of data, each seq in this must either
        be a numpy array of shape (n + forecastHorizon, d1) or a 2-tuple whose
        first element is a numpy array of shape (n + forecastHorizon, d1),
        and second element is a numpy array of shape (n + forecastHorizon, d2)
        :param numIterations: Number of iterations of training to be performed
        :param optimizer: Optimizer using which to train the parameters of the model
        :param modelSavePath: If not None, then save the model to this path after
        every iteration of training
        :param verboseLevel: Verbosity Level, higher value means more information
        :param returnLosses: If True, then return losses of every iteration, else
        does not return losses
        :return: If returnLosses is True, then return list of losses of every
        iteration, else None
        """

        logger = GlobalLogger.getLogger()
        logger.log('Compiling Model', 1, self.train.__name__)

        self.model.compile(
            optimizer=optimizer, loss=tf.keras.losses.MeanSquaredError()
        )

        callbacks = None
        if modelSavePath is not None:
            callbacks = [SaveCallback(
                self,
                modelSavePath
            )]

        logger.log('Begin Training Model', 1, self.train.__name__)
        history = self.model.fit(
            ForecastDataSequence(
                trainSequences,
                self.forecastHorizon,
                self.numTargetVariables,
                self.numExoVariables
            ),
            epochs=numIterations,
            verbose=verboseLevel,
            callbacks=callbacks
        )

        if returnLosses:
            return history.history['loss']
Esempio n. 16
0
    def __init__(self,
                 forecastHorizon=1,
                 memorySize=80,
                 windowSize=60,
                 encoderStateSize=10,
                 lstmStateSize=10,
                 numExoVariables=0,
                 loadModel=False):
        """
        Initialize the model parameters and hyperparameters

        :param forecastHorizon: How much further in the future the model has to
        predict the target series variable
        :param memorySize: Size of the explicit memory unit used by the model, it
        should be a scalar value
        :param windowSize: Size of each window which is to be compressed and stored
        as a memory cell
        :param encoderStateSize: Size of the hidden state of the GRU encoder
        :param lstmStateSize: Size of the hidden state of the LSTM used in the model
        :param numExoVariables: Number of exogenous variables the model takes as input
        :param loadModel: True or False - do not use this parameter !,
        this is for internal use only (i.e. it is an implementation detail)
        If True, then object is normally created, else object is created
        without any member values being created. This is used when model
        is created by the static load method
        """

        tf.keras.backend.set_floatx('float64')

        if loadModel:
            return

        logger = GlobalLogger.getLogger()
        logger.log('Initializing Members', 1, self.__init__.__name__)

        self.forecastHorizon = forecastHorizon
        self.memorySize = memorySize
        self.windowSize = windowSize
        self.encoderStateSize = encoderStateSize
        self.lstmStateSize = lstmStateSize
        self.inputDimension = numExoVariables + 1
        self.memory = None
        self.q = None

        logger.log('Building Model Parameters', 1, self.__init__.__name__)

        self.lstm = self.gruEncoder = None
        self.outDense = self.embeddingDense = None
        self.buildModel()

        self.lstmStateList = self.getInitialLstmStates()
        logger.log(
            f'LSTM state shapes: {self.lstmStateList[0].shape}, {self.lstmStateList[1].shape}',
            2, self.predict.__name__)

        self.b = tf.Variable(0, dtype=tf.float64)
Esempio n. 17
0
    def generate(self, n):
        """Generates Sequence of the Provided Length"""

        logger = GlobalLogger.getLogger()
        logger.log(f'Generating Data of length {n}', 1, self.generate.__name__)

        x = np.zeros(n)

        for t in range(n):
            x[t] = np.sin(self.initialPhase + self.angFreq * t)

        return x
Esempio n. 18
0
    def evaluate(
            self,
            targetSeries,
            exogenousSeries=None,
            returnPred=False
    ):
        """
        Forecast using the model parameters on the provided data, evaluates
        the forecast result using the loss and returns it

        :param targetSeries: Series of the Target Variable, it
        should be a numpy array of shape
        (numTimesteps + self.forecastHorizon, numTargetVariables).
        numTimesteps is the number of timesteps on which our model must predict,
        the values ahead are for evaluating the predicted results with respect
        to them (i.e. they are true targets for our prediction)
        :param exogenousSeries: Series of exogenous Variables, it should be a
        numpy array of shape (numTimesteps, numExoVariables), it can be None
        only if numExoVariables is 0 in which case the exogenous variables
        are not considered
        :param returnPred: If True, then return predictions along with loss, else
        return on loss
        :return: If True, then return predictions along with loss of the predicted
        and true targets, else return only loss
        """

        logger = GlobalLogger.getLogger()

        logger.log(f'Target Series Shape: {targetSeries.shape}', 2, self.evaluate.__name__)
        if exogenousSeries is not None:
            logger.log(
                f'Exogenous Series Shape: {exogenousSeries.shape}', 2, self.evaluate.__name__
            )

        logger.log('Prepare Data', 1, self.evaluate.__name__)

        assert targetSeries.shape[1] == self.numTargetVariables
        assert Utility.isExoShapeValid(exogenousSeries, self.numExoVariables)

        X, Ytrue = Utility.prepareDataTrain(targetSeries, exogenousSeries, self.forecastHorizon)

        logger.log('Begin Evaluation', 1, self.predict.__name__)
        Ypred = tf.squeeze(self.model.predict(np.expand_dims(X, axis=0), verbose=0), axis=0)
        loss = tf.keras.losses.MeanSquaredError()(
            Ytrue,
            Ypred
        )

        if returnPred:
            return loss, Ypred
        else:
            return loss
Esempio n. 19
0
    def trainSequence(self, X, Y, seqStartTime, seqEndTime, optimizer):
        """

        :param X: Features, has shape (n, self.inputShape)
        :param Y: Targets, has shape (n,)
        :param seqStartTime: Sequence Start Time
        :param seqEndTime: Sequence End Time
        :param optimizer: The optimization algorithm
        :return: The loss value resulted from training on the sequence
        """

        logger = GlobalLogger.getLogger()

        logger.log('Begin Training on Sequence', 1,
                   self.trainSequence.__name__)
        logger.log(
            f'Sequence start: {seqStartTime}, Sequence end: {seqEndTime}', 2,
            self.trainSequence.__name__)

        with tf.GradientTape() as tape:
            self.buildMemory(X, Y, seqStartTime)

            Ypred = []
            for t in range(seqStartTime, seqEndTime + 1):
                pred = self.predictTimestep(X, t)
                Ypred.append(pred)

            Ypred = tf.convert_to_tensor(Ypred, dtype=tf.float64)
            logger.log(f'Prediction Shape: {Ypred.shape}', 2,
                       self.trainSequence.__name__)

            loss = tf.keras.losses.MSE(Y[seqStartTime:seqEndTime + 1], Ypred)
            logger.log(f'Loss: {loss}', 2, self.trainSequence.__name__)

        trainableVars = \
            self.gruEncoder.trainable_variables \
            + self.lstm.trainable_variables \
            + self.outDense.trainable_variables \
            + self.embeddingDense.trainable_variables \
            + [self.b]

        logger.log('Performing Gradient Descent', 1,
                   self.trainSequence.__name__)

        grads = tape.gradient(loss, trainableVars)
        assert (len(trainableVars) == len(grads))

        optimizer.apply_gradients(zip(grads, trainableVars))

        return loss
Esempio n. 20
0
    def generate(self, n):
        """Generates Sequence of the Provided Length"""

        logger = GlobalLogger.getLogger()
        logger.log(f'Generating Data of length {n}', 1, self.generate.__name__)

        x = np.zeros(n)

        for t in range(n):
            pw = 1
            for coeff in self.polynomialCoeffs:
                x[t] += coeff * pw
                pw *= t

        return x
Esempio n. 21
0
    def buildModel(self):
        """ Builds Model Architecture """

        GlobalLogger.getLogger().log(
            'Building Model Architecture',
            1,
            self.buildModel.__name__
        )

        inputDimension = (self.numExoVariables + self.numTargetVariables) * (self.lag + 1)

        self.model = tf.keras.Sequential()
        for i in range(self.numLayers):
            self.model.add(tf.keras.layers.Dense(
                units=self.numUnitsPerLayer,
                activation=self.activation
            ))

        self.model.add(tf.keras.layers.Dense(
            units=self.numTargetVariables,
            activation=None
        ))

        self.model.build(input_shape=(None, inputDimension))
Esempio n. 22
0
    def evaluate(self, targetSeries, exogenousSeries=None, returnPred=False):
        """
        Forecast using the model parameters on the provided data, evaluates
        the forecast result using the loss and returns it

        :param targetSeries: Univariate Series of the Target Variable, it
        should be a numpy array of shape (numTimesteps + self.forecastHorizon,).
        numTimesteps is the number of timesteps on which our model must predict,
        the values ahead are for evaluating the predicted results with respect
        to them (i.e. they are true targets for our prediction)
        :param exogenousSeries: Series of exogenous Variables, it should be a
        numpy array of shape (numTimesteps, numExoVariables), it can be None
        only if numExoVariables is 0 in which case the exogenous variables
        are not considered
        :param returnPred: If True, then return predictions along with loss, else
        return on loss
        :return: If True, then return predictions along with loss of the predicted
        and true targets, else return only loss
        """

        logger = GlobalLogger.getLogger()
        logger.log('Begin Evaluating', 1, self.evaluate.__name__)

        n = targetSeries.shape[0] - self.forecastHorizon
        logger.log(f'Evaluate Sequence Length: {n}', 2, self.evaluate.__name__)
        assert (n >= 0)

        if exogenousSeries is not None:
            logger.log(f'Exogenous Series Shape: {exogenousSeries.shape}', 2,
                       self.evaluate.__name__)
            assert (exogenousSeries.shape[0] == n)

        Ypred = self.predict(targetSeries[:n], exogenousSeries)

        loss = tf.keras.losses.MSE(targetSeries[self.forecastHorizon:], Ypred)

        logger.log(f'Computed Loss: {loss}', 2, self.evaluate.__name__)

        if returnPred:
            return loss, Ypred
        else:
            return loss
Esempio n. 23
0
    def __init__(self,
                 obsCoef,
                 noiseCoef,
                 noiseGenFunc,
                 noiseGenParams,
                 obsFunc=None,
                 noiseFunc=None):
        """
        Initialize Data Generator

        :param obsCoef: Observation Coefficients, it is a numpy array of shape (p,)
        :param noiseCoef: Noise Coefficients, it is a numpy array of shape (q,)
        :param noiseGenFunc: Noise Generation function (eg. np.random.normal)
        :param noiseGenParams: Parameters to be passed to the Noise Generation Function
        :param obsFunc: Function to be applied on the previous observation terms while computing
        current observation. It may add non-linearity to the time series.
        :param noiseFunc: Function to be applied on the previous noise terms while computing
        current observation. It may add non-linearity to the time series.
        more information
        """

        logger = GlobalLogger.getLogger()
        logger.log('Initialize ARMA coefficients', 1, self.__init__.__name__)

        logger.log(
            f'Shapes - obsCoef: {obsCoef.shape}, noiseCoef: {noiseCoef.shape}',
            2, self.__init__.__name__)
        logger.log(
            f'Noise Func: {noiseGenFunc.__name__}, Params: {noiseGenParams}',
            2, self.__init__.__name__)

        assert (len(obsCoef.shape) == 1 and len(noiseCoef.shape) == 1)

        self.obsCoef = obsCoef
        self.noiseCoef = noiseCoef
        self.noiseGenFunc = noiseGenFunc
        self.noiseGenParams = noiseGenParams
        self.obsFunc = obsFunc
        self.noiseFunc = noiseFunc
Esempio n. 24
0
    def load(modelLoadPath):
        """
        Loads the model from the provided filepath

        :param modelLoadPath: path from where to load the model
        :return: model which is loaded from the given path
        """

        logger = GlobalLogger.getLogger()

        model = ExtremeTime(loadModel=True)

        with open(modelLoadPath, 'rb') as fl:
            logger.log('Load Dictionary from Model Params file', 1,
                       ExtremeTime.load.__name__)
            loadDict = pickle.load(fl)

        logger.log('Loading Params', 1, ExtremeTime.load.__name__)

        model.forecastHorizon = loadDict['forecastHorizon']
        model.memorySize = loadDict['memorySize']
        model.windowSize = loadDict['windowSize']
        model.inputDimension = loadDict['inputDimension']
        model.encoderStateSize = loadDict['encoderStateSize']
        model.lstmStateSize = loadDict['lstmStateSize']
        model.memory = loadDict['memory']
        model.q = loadDict['q']

        model.buildModel()
        model.gruEncoder.set_weights(loadDict['gruEncoder'])
        model.lstm.set_weights(loadDict['lstm'])
        model.outDense.set_weights(loadDict['outDense'])
        model.embeddingDense.set_weights(loadDict['embeddingDense'])

        model.lstmStateList = loadDict['lstmStateList']
        model.b = tf.Variable(loadDict['b'])

        return model
Esempio n. 25
0
    def save(self, modelSavePath):
        """
        Save the model parameters at the provided path

        :param modelSavePath: Path where the parameters are to be saved
        :return: None
        """

        logger = GlobalLogger.getLogger()

        assert (self.memory is not None)
        logger.log(f'Memory Shape: {self.memory.shape}', 2, self.save.__name__)

        logger.log('Constructing Dictionary from model params', 1,
                   self.save.__name__)

        saveDict = {
            'forecastHorizon': self.forecastHorizon,
            'memorySize': self.memorySize,
            'windowSize': self.windowSize,
            'inputDimension': self.inputDimension,
            'encoderStateSize': self.encoderStateSize,
            'lstmStateSize': self.lstmStateSize,
            'memory': self.memory,
            'q': self.q,
            'gruEncoder': self.gruEncoder.get_weights(),
            'lstmStateList': self.lstmStateList,
            'outDense': self.outDense.get_weights(),
            'embeddingDense': self.embeddingDense.get_weights(),
            'lstm': self.lstm.get_weights(),
            'b': self.b.read_value()
        }

        logger.log('Saving Dictionary', 1, self.save.__name__)

        with open(modelSavePath, 'wb') as fl:
            pickle.dump(saveDict, fl)
Esempio n. 26
0
    def predict(
            self,
            targetSeries,
            exogenousSeries=None,
    ):
        """
        Forecast using the model parameters on the provided input data

        V.imp: Predicted value is NOT outputted for the first lag inputs,
        since then networks begins predicting from the lag term onwards

        :param targetSeries: Multivariate Series of the Target Variable, it
        should be a numpy array of shape (lag + nPred, numTargetVariables)
        :param exogenousSeries: Series of exogenous Variables, it should be a
        numpy array of shape (lag + nPred, numExoVariables), it can be None only if
        numExoVariables is 0 in which case the exogenous variables are not
        considered
        :return: Forecast targets predicted by the model, it has shape
        (nPred, numTargetVariables), the horizon of the targets is the
        same as self.forecastHorizon
        """

        logger = GlobalLogger.getLogger()

        logger.log(f'Target Series Shape: {targetSeries.shape}', 2, self.predict.__name__)
        if exogenousSeries is not None:
            logger.log(
                f'Exogenous Series Shape: {exogenousSeries.shape}', 2, self.predict.__name__
            )

        logger.log('Prepare Data', 1, self.predict.__name__)
        assert (self.checkShapeValid(targetSeries, exogenousSeries))

        X = DeepNN.prepareDataPredDNN(targetSeries, exogenousSeries, self.lag)

        logger.log('Begin Prediction', 1, self.predict.__name__)
        return tf.squeeze(self.model.predict(np.expand_dims(X, axis=0), verbose=0))
Esempio n. 27
0
    def predictTimestep(self, X, currentTime):
        """
        Predict on a Single Timestep

        :param X: Features, has shape (n, self.inputShape)
        :param currentTime: Current Timestep
        :return: The predicted value on current timestep and the next state
        """

        logger = GlobalLogger.getLogger()

        self.state, _ = self.gruInput(np.expand_dims(X[currentTime], axis=0),
                                      self.state)

        embedding = tf.squeeze(self.state)

        attentionWeights = self.computeAttention(embedding)
        logger.log(f'Attention Shape: {attentionWeights.shape}', 2,
                   self.predictTimestep.__name__)

        weightedContext = \
            tf.expand_dims(attentionWeights, axis=1) * self.context

        concatVector = tf.concat([
            embedding,
            tf.reshape(weightedContext, (tf.size(weightedContext), ))
        ],
                                 axis=0)

        logger.log(f'Concat Vector Shape: {concatVector.shape}', 2,
                   self.predictTimestep.__name__)

        pred = tf.squeeze(self.outDense(tf.expand_dims(concatVector, axis=0)))
        logger.log(f'Prediction: {pred}', 2, self.predictTimestep.__name__)

        return pred
Esempio n. 28
0
    def predictTimestep(self, X, currentTime):
        """
        Predict on a Single Timestep

        :param X: Features, has shape (n, self.inputShape)
        :param currentTime: Current Timestep
        :return: The predicted value on current timestep
        """

        logger = GlobalLogger.getLogger()

        self.lstmStateList = self.lstm(np.expand_dims(X[currentTime], axis=0),
                                       self.lstmStateList)[1]

        lstmHiddenState = self.lstmStateList[0]

        embedding = tf.squeeze(self.embeddingDense(lstmHiddenState))
        logger.log(f'Embedding Shape: {embedding.shape}', 2,
                   self.predictTimestep.__name__)

        attentionWeights = self.computeAttention(embedding)
        logger.log(f'Attention Shape: {attentionWeights.shape}', 2,
                   self.predictTimestep.__name__)

        o1 = tf.squeeze(self.outDense(lstmHiddenState))
        logger.log(f'Output1: {o1}', 2, self.predictTimestep.__name__)

        o2 = tf.reduce_sum(attentionWeights * self.q)
        logger.log(f'Output2: {o2}', 2, self.predictTimestep.__name__)

        bSigmoid = tf.nn.sigmoid(self.b)
        pred = bSigmoid * o1 + (1 - bSigmoid) * o2

        logger.log(f'Prediction: {pred}', 2, self.predictTimestep.__name__)

        return pred
Esempio n. 29
0
    def save(self, modelSavePath):
        """
        Save the model parameters at the provided path
        :param modelSavePath: Path where the parameters are to be saved
        :return: None
        """

        logger = GlobalLogger.getLogger()

        assert (self.memory is not None)
        logger.log(f'Memory Shape: {self.memory.shape}', 2, self.save.__name__)

        logger.log('Constructing Dictionary from model params', 1,
                   self.save.__name__)

        saveDict = {
            'forecastHorizon': self.forecastHorizon,
            'memorySize': self.memorySize,
            'windowSize': self.windowSize,
            'inputDimension': self.inputDimension,
            'embeddingSize': self.embeddingSize,
            'contextSize': self.contextSize,
            'memory': self.memory,
            'context': self.context,
            'gruInput': self.gruInput.get_weights(),
            'gruMemory': self.gruMemory.get_weights(),
            'gruContext': self.gruContext.get_weights(),
            'outDense': self.outDense.get_weights(),
            'state': self.state
        }

        logger.log('Saving Dictionary', 1, self.save.__name__)

        fl = open(modelSavePath, 'wb')
        pickle.dump(saveDict, fl)
        fl.close()
Esempio n. 30
0
    def load(modelLoadPath):
        """
        Loads the model from the provided filepath

        :param modelLoadPath: path from where to load the model
        :return: model which is loaded from the given path
        """

        logger = GlobalLogger.getLogger()

        model = ExtremeTime2(loadModel=True)

        with open(modelLoadPath, 'rb') as fl:
            logger.log('Load Dictionary from Model Params file', 1,
                       ExtremeTime2.load.__name__)
            loadDict = pickle.load(fl)

        logger.log('Loading Params', 1, ExtremeTime2.load.__name__)

        model.forecastHorizon = loadDict['forecastHorizon']
        model.memorySize = loadDict['memorySize']
        model.windowSize = loadDict['windowSize']
        model.inputDimension = loadDict['inputDimension']
        model.embeddingSize = loadDict['embeddingSize']
        model.contextSize = loadDict['contextSize']
        model.memory = loadDict['memory']
        model.context = loadDict['context']

        model.buildModel()
        model.gruInput.set_weights(loadDict['gruInput'])
        model.gruMemory.set_weights(loadDict['gruMemory'])
        model.gruContext.set_weights(loadDict['gruContext'])
        model.outDense.set_weights(loadDict['outDense'])
        model.state = loadDict['state']

        return model