Source code for zoo.automl.model.tcmf.DeepGLO

# Copyright 2018 Analytics Zoo Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

#
# This file is adapted from the DeepGlo Project. https://github.com/rajatsen91/deepglo
#
# Note: This license has also been called the "New BSD License" or "Modified BSD License". See also
# the 2-clause BSD License.
#
# Copyright (c) 2019 The DeepGLO Project.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted
# provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions
# and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of
# conditions and the following disclaimer in the documentation and/or other materials provided
# with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to
# endorse or promote products derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.


from __future__ import print_function
import torch
import numpy as np
import pandas as pd

import torch.nn as nn
import torch.optim as optim
from torch.autograd import Variable

from zoo.automl.model.tcmf.data_loader import TCMFDataLoader
from zoo.automl.model.tcmf.local_model import TemporalConvNet, LocalModel
from zoo.automl.model.tcmf.time import TimeCovariates

import copy

import pickle
import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
console = logging.StreamHandler()
logger.addHandler(console)


[docs]def get_model(A, y, lamb=0): """ Regularized least-squares """ n_col = A.shape[1] return np.linalg.lstsq( A.T.dot(A) + lamb * np.identity(n_col), A.T.dot(y), rcond=None )
[docs]class DeepGLO(object): def __init__( self, vbsize=150, hbsize=256, num_channels_X=[32, 32, 32, 32, 1], num_channels_Y=[32, 32, 32, 32, 1], kernel_size=7, dropout=0.2, rank=64, kernel_size_Y=7, lr=0.0005, val_len=24, end_index=-24, normalize=False, start_date="2016-1-1", freq="H", covariates=None, use_time=True, dti=None, svd=False, period=None, forward_cov=False, ): self.start_date = start_date self.freq = freq self.covariates = covariates self.use_time = use_time self.dti = dti self.dropout = dropout self.period = period self.forward_cov = forward_cov self.Xseq = TemporalConvNet( num_inputs=1, num_channels=num_channels_X, kernel_size=kernel_size, dropout=dropout, init=True, ) self.vbsize = vbsize self.hbsize = hbsize self.num_channels_X = num_channels_X self.num_channels_Y = num_channels_Y self.kernel_size_Y = kernel_size_Y self.rank = rank self.kernel_size = kernel_size self.lr = lr self.val_len = val_len self.end_index = end_index self.normalize = normalize self.svd = svd
[docs] def tensor2d_to_temporal(self, T): T = T.view(1, T.size(0), T.size(1)) T = T.transpose(0, 1) return T
[docs] def temporal_to_tensor2d(self, T): T = T.view(T.size(0), T.size(2)) return T
[docs] def calculate_newX_loss_vanilla(self, Xn, Fn, Yn, Xf, alpha): Yout = torch.mm(Fn, Xn) cr1 = nn.L1Loss() cr2 = nn.MSELoss() l1 = cr2(Yout, Yn) / torch.mean(Yn ** 2) l2 = cr2(Xn, Xf) / torch.mean(Xf ** 2) return (1 - alpha) * l1 + alpha * l2
[docs] def recover_future_X( self, last_step, future, num_epochs=50, alpha=0.5, vanilla=True, tol=1e-7, ): rg = max( 1 + 2 * (self.kernel_size - 1) * 2 ** (len(self.num_channels_X) - 1), 1 + 2 * (self.kernel_size_Y - 1) * 2 ** (len(self.num_channels_Y) - 1), ) X = self.X[:, last_step - rg: last_step] X = self.tensor2d_to_temporal(X) outX = self.predict_future(model=self.Xseq, inp=X, future=future) outX = self.temporal_to_tensor2d(outX) Xf = outX[:, -future::] Yn = self.Ymat[:, last_step: last_step + future] Yn = torch.from_numpy(Yn).float() Fn = self.F Xt = torch.zeros(self.rank, future).float() Xn = torch.normal(Xt, 0.1) lprev = 0 for i in range(num_epochs): Xn = Variable(Xn, requires_grad=True) optim_Xn = optim.Adam(params=[Xn], lr=self.lr) optim_Xn.zero_grad() loss = self.calculate_newX_loss_vanilla( Xn, Fn.detach(), Yn.detach(), Xf.detach(), alpha ) loss.backward() optim_Xn.step() # Xn = torch.clamp(Xn.detach(), min=0) if np.abs(lprev - loss.item()) <= tol: break if i % 1000 == 0: print(f"Recovery Loss of epoch {i} is: " + str(loss.item())) lprev = loss.item() return Xn.detach()
[docs] def step_factX_loss(self, inp, out, last_vindex, last_hindex, reg=0.0): Xout = self.X[:, last_hindex + 1: last_hindex + 1 + out.size(2)] Fout = self.F[self.D.I[last_vindex: last_vindex + out.size(0)], :] Xout = Variable(Xout, requires_grad=True) out = self.temporal_to_tensor2d(out) optim_X = optim.Adam(params=[Xout], lr=self.lr) Hout = torch.matmul(Fout, Xout) optim_X.zero_grad() loss = torch.mean(torch.pow(Hout - out.detach(), 2)) l2 = torch.mean(torch.pow(Xout, 2)) r = loss.detach() / l2.detach() loss = loss + r * reg * l2 loss.backward() optim_X.step() # Xout = torch.clamp(Xout, min=0) self.X[:, last_hindex + 1: last_hindex + 1 + inp.size(2)] = Xout.detach() return loss
[docs] def step_factF_loss(self, inp, out, last_vindex, last_hindex, reg=0.0): Xout = self.X[:, last_hindex + 1: last_hindex + 1 + out.size(2)] Fout = self.F[self.D.I[last_vindex: last_vindex + out.size(0)], :] Fout = Variable(Fout, requires_grad=True) optim_F = optim.Adam(params=[Fout], lr=self.lr) out = self.temporal_to_tensor2d(out) Hout = torch.matmul(Fout, Xout) optim_F.zero_grad() loss = torch.mean(torch.pow(Hout - out.detach(), 2)) l2 = torch.mean(torch.pow(Fout, 2)) r = loss.detach() / l2.detach() loss = loss + r * reg * l2 loss.backward() optim_F.step() self.F[ self.D.I[last_vindex: last_vindex + inp.size(0)], : ] = Fout.detach() return loss
[docs] def step_temporal_loss_X(self, inp, last_vindex, last_hindex): Xin = self.X[:, last_hindex: last_hindex + inp.size(2)] Xout = self.X[:, last_hindex + 1: last_hindex + 1 + inp.size(2)] for p in self.Xseq.parameters(): p.requires_grad = False Xin = Variable(Xin, requires_grad=True) Xout = Variable(Xout, requires_grad=True) optim_out = optim.Adam(params=[Xout], lr=self.lr) Xin = self.tensor2d_to_temporal(Xin) Xout = self.tensor2d_to_temporal(Xout) hatX = self.Xseq(Xin) optim_out.zero_grad() loss = torch.mean(torch.pow(Xout - hatX.detach(), 2)) loss.backward() optim_out.step() # Xout = torch.clamp(Xout, min=0) temp = self.temporal_to_tensor2d(Xout.detach()) self.X[:, last_hindex + 1: last_hindex + 1 + inp.size(2)] = temp return loss
[docs] def predict_future_batch(self, model, inp, future=10): out = model(inp) output = out[:, :, out.size(2) - 1].view(out.size(0), out.size(1), 1) out = torch.cat((inp, output), dim=2) for i in range(future - 1): inp = out out = model(inp) output = out[:, :, out.size(2) - 1].view(out.size(0), out.size(1), 1) out = torch.cat((inp, output), dim=2) out = self.temporal_to_tensor2d(out) out = np.array(out.detach()) return out
[docs] def predict_future(self, model, inp, future=10, bsize=90): n = inp.size(0) ids = np.arange(0, n, bsize) ids = list(ids) + [n] out = self.predict_future_batch(model, inp[ids[0]: ids[1], :, :], future) for i in range(1, len(ids) - 1): temp = self.predict_future_batch( model, inp[ids[i]: ids[i + 1], :, :], future ) out = np.vstack([out, temp]) out = torch.from_numpy(out).float() return self.tensor2d_to_temporal(out)
[docs] def predict_global( self, ind, last_step=100, future=10, normalize=False, bsize=90 ): if ind is None: ind = np.arange(self.Ymat.shape[0]) self.Xseq = self.Xseq.eval() rg = max( 1 + 2 * (self.kernel_size - 1) * 2 ** (len(self.num_channels_X) - 1), 1 + 2 * (self.kernel_size_Y - 1) * 2 ** (len(self.num_channels_Y) - 1), ) X = self.X[:, last_step - rg: last_step] n = X.size(0) T = X.size(1) X = self.tensor2d_to_temporal(X) outX = self.predict_future( model=self.Xseq, inp=X, future=future, bsize=bsize ) outX = self.temporal_to_tensor2d(outX) F = self.F Y = torch.matmul(F, outX) Y = np.array(Y[ind, :].detach()) del F for p in self.Xseq.parameters(): p.requires_grad = True if normalize: Y = Y - self.mini Y = Y * self.s[ind, None] + self.m[ind, None] return Y else: return Y
[docs] def train_Xseq(self, Ymat, num_epochs=20, early_stop=False, tenacity=3): seq = self.Xseq num_channels = self.num_channels_X kernel_size = self.kernel_size vbsize = min(self.vbsize, Ymat.shape[0] / 2) for p in seq.parameters(): p.requires_grad = True TC = LocalModel( Ymat=Ymat, num_inputs=1, num_channels=num_channels, kernel_size=kernel_size, vbsize=vbsize, hbsize=self.hbsize, normalize=False, end_index=self.end_index - self.val_len, val_len=self.val_len, lr=self.lr, ) TC.train_model(num_epochs=num_epochs, early_stop=early_stop, tenacity=tenacity) self.Xseq = TC.seq
[docs] def train_factors( self, reg_X=0.0, reg_F=0.0, mod=5, early_stop=False, tenacity=3, ind=None, seed=False, ): self.D.epoch = 0 self.D.vindex = 0 self.D.hindex = 0 for p in self.Xseq.parameters(): p.requires_grad = True l_F = [0.0] l_X = [0.0] l_X_temporal = [0.0] iter_count = 0 vae = float("inf") scount = 0 Xbest = self.X.clone() Fbest = self.F.clone() while self.D.epoch < self.num_epochs: last_epoch = self.D.epoch last_vindex = self.D.vindex last_hindex = self.D.hindex inp, out, vindex, hindex = self.D.next_batch() step_l_F = self.step_factF_loss(inp, out, last_vindex, last_hindex, reg=reg_F) l_F = l_F + [step_l_F.item()] step_l_X = self.step_factX_loss(inp, out, last_vindex, last_hindex, reg=reg_X) l_X = l_X + [step_l_X.item()] if seed is False and iter_count % mod == 1: l2 = self.step_temporal_loss_X(inp, last_vindex, last_hindex) l_X_temporal = l_X_temporal + [l2.item()] iter_count = iter_count + 1 if self.D.epoch > last_epoch: print("Entering Epoch#{}".format(self.D.epoch)) print("Factorization Loss F:{}".format(np.mean(l_F))) print("Factorization Loss X:{}".format(np.mean(l_X))) print("Temporal Loss X:{}".format(np.mean(l_X_temporal))) if ind is None: ind = np.arange(self.Ymat.shape[0]) else: ind = ind inp = self.predict_global( ind, last_step=self.end_index - self.val_len, future=self.val_len, ) R = self.Ymat[ind, self.end_index - self.val_len: self.end_index] S = inp[:, -self.val_len::] ve = np.abs(R - S).mean() / np.abs(R).mean() # print("Validation Loss (Global): ", ve) print("Validation Loss (Global):{}".format(ve)) if ve <= vae: vae = ve scount = 0 Xbest = self.X.clone() Fbest = self.F.clone() # Xseqbest = TemporalConvNet( # num_inputs=1, # num_channels=self.num_channels_X, # kernel_size=self.kernel_size, # dropout=self.dropout, # ) # Xseqbest.load_state_dict(self.Xseq.state_dict()) Xseqbest = pickle.loads(pickle.dumps(self.Xseq)) else: scount += 1 if scount > tenacity and early_stop: # print("Early Stopped") print("Early Stopped") self.X = Xbest self.F = Fbest self.Xseq = Xseqbest break
[docs] def create_Ycov(self): t0 = self.end_index + 1 self.D.epoch = 0 self.D.vindex = 0 self.D.hindex = 0 Ycov = copy.deepcopy(self.Ymat[:, 0:t0]) Ymat_now = self.Ymat[:, 0:t0] self.Xseq = self.Xseq.eval() while self.D.epoch < 1: last_epoch = self.D.epoch last_vindex = self.D.vindex last_hindex = self.D.hindex inp, out, vindex, hindex = self.D.next_batch() Xin = self.tensor2d_to_temporal(self.X[:, last_hindex: last_hindex + inp.size(2)]) Xout = self.temporal_to_tensor2d(self.Xseq(Xin)) Fout = self.F[self.D.I[last_vindex: last_vindex + out.size(0)], :] output = np.array(torch.matmul(Fout, Xout).detach()) Ycov[ last_vindex: last_vindex + output.shape[0], last_hindex + 1: last_hindex + 1 + output.shape[1], ] = output for p in self.Xseq.parameters(): p.requires_grad = True if self.period is None: Ycov_wc = np.zeros(shape=[Ycov.shape[0], 1, Ycov.shape[1]]) if self.forward_cov: Ycov_wc[:, 0, 0:-1] = Ycov[:, 1::] else: Ycov_wc[:, 0, :] = Ycov else: Ycov_wc = np.zeros(shape=[Ycov.shape[0], 2, Ycov.shape[1]]) if self.forward_cov: Ycov_wc[:, 0, 0:-1] = Ycov[:, 1::] else: Ycov_wc[:, 0, :] = Ycov Ycov_wc[:, 1, self.period - 1::] = Ymat_now[:, 0: -(self.period - 1)] return Ycov_wc
[docs] def train_Yseq(self, num_epochs=20, num_workers=1, ): Ycov = self.create_Ycov() self.Yseq = LocalModel( self.Ymat, num_inputs=1, num_channels=self.num_channels_Y, kernel_size=self.kernel_size_Y, dropout=self.dropout, vbsize=self.vbsize, hbsize=self.hbsize, lr=self.lr, val_len=self.val_len, test=True, end_index=self.end_index - self.val_len, normalize=False, start_date=self.start_date, freq=self.freq, covariates=self.covariates, use_time=self.use_time, dti=self.dti, Ycov=Ycov, ) val_loss = self.Yseq.train_model(num_epochs=num_epochs, num_workers=num_workers, early_stop=False) return val_loss
[docs] def train_all_models( self, Ymat, init_epochs=100, alt_iters=10, y_iters=200, tenacity=7, mod=5, max_FX_epoch=300, max_TCN_epoch=300, num_workers=1, ): self.end_index = Ymat.shape[1] if self.normalize: self.s = np.std(Ymat[:, 0:self.end_index], axis=1) # self.s[self.s == 0] = 1.0 self.s += 1.0 self.m = np.mean(Ymat[:, 0:self.end_index], axis=1) self.Ymat = (Ymat - self.m[:, None]) / self.s[:, None] self.mini = np.abs(np.min(self.Ymat)) self.Ymat = self.Ymat + self.mini else: self.Ymat = Ymat n, T = self.Ymat.shape t0 = self.end_index + 1 if t0 > T: self.Ymat = np.hstack([self.Ymat, self.Ymat[:, -1].reshape(-1, 1)]) if self.svd: indices = np.random.choice(self.Ymat.shape[0], self.rank, replace=False) X = self.Ymat[indices, 0:t0] mX = np.std(X, axis=1) mX[mX == 0] = 1.0 X = X / mX[:, None] Ft = get_model(X.transpose(), self.Ymat[:, 0:t0].transpose(), lamb=0.1) F = Ft[0].transpose() self.X = torch.from_numpy(X).float() self.F = torch.from_numpy(F).float() else: R = torch.zeros(self.rank, t0).float() X = torch.normal(R, 0.1) C = torch.zeros(n, self.rank).float() F = torch.normal(C, 0.1) self.X = X.float() self.F = F.float() self.D = TCMFDataLoader( Ymat=self.Ymat, vbsize=self.vbsize, hbsize=self.hbsize, end_index=self.end_index, val_len=self.val_len, shuffle=False, ) # print("-"*50+"Initializing Factors.....") logger.info("Initializing Factors") self.num_epochs = init_epochs self.train_factors() if alt_iters % 2 == 1: alt_iters += 1 # print("Starting Alternate Training.....") logger.info("Starting Alternate Training.....") for i in range(1, alt_iters): if i % 2 == 0: logger.info("Training Factors. Iter#:{}".format(i)) self.num_epochs = max_FX_epoch self.train_factors( seed=False, early_stop=True, tenacity=tenacity, mod=mod ) else: # logger.info( # "--------------------------------------------Training Xseq Model. Iter#:{}" # .format(i) # + "-------------------------------------------------------" # ) logger.info("Training Xseq Model. Iter#:{}".format(i)) self.num_epochs = max_TCN_epoch T = np.array(self.X.detach()) self.train_Xseq( Ymat=T, num_epochs=self.num_epochs, early_stop=True, tenacity=tenacity, ) logger.info("Start training Yseq.....") val_loss = self.train_Yseq(num_epochs=y_iters, num_workers=num_workers, ) return val_loss
[docs] def get_time_covs(self, start_date, num_ts): if self.use_time: time = TimeCovariates( start_date=start_date, freq=self.freq, normalized=True, num_ts=num_ts ) if self.dti is not None: time.dti = self.dti time_covariates = time.get_covariates() if self.covariates is None: covariates = time_covariates else: covariates = np.vstack([time_covariates, self.covariates]) else: covariates = self.covariates return covariates
[docs] def get_prediction_time_covs(self, rg, horizon, last_step): covs_past = self.Yseq.covariates[:, last_step - rg: last_step] if self.freq[0].isalpha(): freq = "1" + self.freq else: freq = self.freq future_start_date = pd.Timestamp(self.start_date) + pd.Timedelta(freq) * last_step covs_future = self.get_time_covs(start_date=future_start_date, num_ts=horizon) covs = np.concatenate([covs_past, covs_future], axis=1) return covs
[docs] def predict_horizon( self, ind=None, future=10, normalize=False, bsize=90, num_workers=1, ): last_step = self.end_index if ind is None: ind = np.arange(self.Ymat.shape[0]) self.Yseq.seq = self.Yseq.seq.eval() self.Xseq = self.Xseq.eval() rg = max( 1 + 2 * (self.kernel_size - 1) * 2 ** (len(self.num_channels_X) - 1), 1 + 2 * (self.kernel_size_Y - 1) * 2 ** (len(self.num_channels_Y) - 1), ) covs = self.get_prediction_time_covs(rg, future, last_step) yc = self.predict_global( ind=ind, last_step=last_step, future=future, normalize=False, bsize=bsize, ) if self.period is None: ycovs = np.zeros(shape=[yc.shape[0], 1, yc.shape[1]]) if self.forward_cov: ycovs[:, 0, 0:-1] = yc[:, 1::] else: ycovs[:, 0, :] = yc else: ycovs = np.zeros(shape=[yc.shape[0], 2, yc.shape[1]]) if self.forward_cov: ycovs[:, 0, 0:-1] = yc[:, 1::] else: ycovs[:, 0, :] = yc period = self.period while last_step + future - (period - 1) > last_step + 1: period += self.period # The last coordinate is not used. ycovs[:, 1, period - 1::] = self.Ymat[ :, last_step - rg: last_step + future - (period - 1)] Y = self.Yseq.predict_future( data_in=self.Ymat[ind, last_step - rg: last_step], covariates=covs, ycovs=ycovs, future=future, bsize=bsize, normalize=False, num_workers=num_workers, ) if normalize: Y = Y - self.mini Y = Y * self.s[ind, None] + self.m[ind, None] return Y else: return Y
[docs] def predict( self, ind=None, last_step=100, future=10, normalize=False, bsize=90 ): if ind is None: ind = np.arange(self.Ymat.shape[0]) self.Xseq = self.Xseq self.Yseq.seq = self.Yseq.seq.eval() self.Xseq = self.Xseq.eval() rg = max( 1 + 2 * (self.kernel_size - 1) * 2 ** (len(self.num_channels_X) - 1), 1 + 2 * (self.kernel_size_Y - 1) * 2 ** (len(self.num_channels_Y) - 1), ) covs = self.Yseq.covariates[:, last_step - rg: last_step + future] # print(covs.shape) yc = self.predict_global( ind=ind, last_step=last_step, future=future, normalize=False, bsize=bsize, ) if self.period is None: ycovs = np.zeros(shape=[yc.shape[0], 1, yc.shape[1]]) if self.forward_cov: ycovs[:, 0, 0:-1] = yc[:, 1::] else: ycovs[:, 0, :] = yc else: ycovs = np.zeros(shape=[yc.shape[0], 2, yc.shape[1]]) if self.forward_cov: ycovs[:, 0, 0:-1] = yc[:, 1::] else: ycovs[:, 0, :] = yc period = self.period while last_step + future - (period - 1) > last_step + 1: period += self.period # this seems like we are looking ahead, but it will not use the last coordinate, # which is the only new point added ycovs[:, 1, period - 1::] = self.Ymat[ :, last_step - rg: last_step + future - (period - 1)] Y = self.Yseq.predict_future( data_in=self.Ymat[ind, last_step - rg: last_step], covariates=covs, ycovs=ycovs, future=future, bsize=bsize, normalize=False, ) if normalize: Y = Y - self.mini Y = Y * self.s[ind, None] + self.m[ind, None] return Y else: return Y
[docs] def rolling_validation(self, Ymat, tau=24, n=7, bsize=90, alpha=0.3): prevX = self.X.clone() prev_index = self.end_index out = self.predict( last_step=self.end_index, future=tau, bsize=bsize, normalize=self.normalize, ) out_global = self.predict_global( np.arange(self.Ymat.shape[0]), last_step=self.end_index, future=tau, normalize=self.normalize, bsize=bsize, ) predicted_values = [] actual_values = [] predicted_values_global = [] S = out[:, -tau::] S_g = out_global[:, -tau::] predicted_values += [S] predicted_values_global += [S_g] R = Ymat[:, self.end_index: self.end_index + tau] actual_values += [R] print("Current window wape:{}".format(wape(S, R))) self.Xseq = self.Xseq.eval() self.Yseq.seq = self.Yseq.seq.eval() for i in range(n - 1): Xn = self.recover_future_X( last_step=self.end_index + 1, future=tau, num_epochs=100000, alpha=alpha, vanilla=True ) self.X = torch.cat([self.X, Xn], dim=1) self.end_index += tau out = self.predict( last_step=self.end_index, future=tau, bsize=bsize, normalize=self.normalize, ) out_global = self.predict_global( np.arange(self.Ymat.shape[0]), last_step=self.end_index, future=tau, normalize=self.normalize, bsize=bsize, ) S = out[:, -tau::] S_g = out_global[:, -tau::] predicted_values += [S] predicted_values_global += [S_g] R = Ymat[:, self.end_index: self.end_index + tau] actual_values += [R] print("Current window wape:{}".format(wape(S, R))) predicted = np.hstack(predicted_values) predicted_global = np.hstack(predicted_values_global) actual = np.hstack(actual_values) dic = {} dic["wape"] = wape(predicted, actual) dic["mape"] = mape(predicted, actual) dic["smape"] = smape(predicted, actual) dic["mae"] = np.abs(predicted - actual).mean() dic["rmse"] = np.sqrt(((predicted - actual) ** 2).mean()) dic["nrmse"] = dic["rmse"] / np.sqrt(((actual) ** 2).mean()) dic["wape_global"] = wape(predicted_global, actual) dic["mape_global"] = mape(predicted_global, actual) dic["smape_global"] = smape(predicted_global, actual) dic["mae_global"] = np.abs(predicted_global - actual).mean() dic["rmse_global"] = np.sqrt(((predicted_global - actual) ** 2).mean()) dic["nrmse_global"] = dic["rmse"] / np.sqrt(((actual) ** 2).mean()) baseline = Ymat[:, Ymat.shape[1] - n * tau - tau: Ymat.shape[1] - tau] dic["baseline_wape"] = wape(baseline, actual) dic["baseline_mape"] = mape(baseline, actual) dic["baseline_smape"] = smape(baseline, actual) self.X = prevX self.end_index = prev_index return dic