def build(self, config): """ Build ARIMA Model :param config: Other ARIMA hyperparameters. You may refer to https://alkaline-ml.com/pmdarima/modules/generated/pmdarima.arima.ARIMA.html#pmdarima.arima.ARIMA for the parameter names to specify. """ from zoo.chronos.model.arima import ARIMAModel model = ARIMAModel() model._build(**config) return model
def test_save_restore(self): self.model.fit_eval(data=self.data, validation_data=self.validation_data, **self.config) result_save = self.model.predict(horizon=self.horizon, rolling=False) model_file = "tmp.pkl" self.model.save(model_file) assert os.path.isfile(model_file) new_model = ARIMAModel() new_model.restore(model_file) assert new_model.model result_restore = new_model.predict(horizon=self.horizon, rolling=False) assert_array_almost_equal(result_save, result_restore, decimal=2), \ "Prediction values are not the same after restore: " \ "predict before is {}, and predict after is {}".format(result_save, result_restore) os.remove(model_file)
def setup_method(self, method): self.seq_len = 400 self.config = { "p": np.random.randint(0, 4), "q": np.random.randint(0, 4), "seasonality_mode": np.random.choice([True, False]), "P": 5, "Q": 5, "m": np.random.choice([4, 7]), "metric": "mse", } self.model = ARIMAModel() self.data = np.random.rand(self.seq_len) self.horizon = np.random.randint(2, 50) self.validation_data = np.random.rand(self.horizon)
def __init__( self, p=2, q=2, seasonality_mode=True, P=3, Q=1, m=7, metric="mse", ): """ Build a ARIMA Forecast Model. User need to set p, q, P, Q, m for the ARIMA model, the differencing term (d) and seasonal differencing term (D) are automatically estimated from the data. :param p: hyperparameter p for the ARIMA model, for details you may refer to https://alkaline-ml.com/pmdarima/modules/generated/pmdarima.arima.ARIMA.html#pmdarima.arima.ARIMA :param q: hyperparameter q for the ARIMA model, for details you may refer to https://alkaline-ml.com/pmdarima/modules/generated/pmdarima.arima.ARIMA.html#pmdarima.arima.ARIMA :param seasonality_mode: hyperparameter seasonality_mode for the ARIMA model, for details you may refer to https://alkaline-ml.com/pmdarima/modules/generated/pmdarima.arima.ARIMA.html#pmdarima.arima.ARIMA :param P: hyperparameter P for the ARIMA model, for details you may refer to https://alkaline-ml.com/pmdarima/modules/generated/pmdarima.arima.ARIMA.html#pmdarima.arima.ARIMA :param Q: hyperparameter Q for the ARIMA model, for details you may refer to https://alkaline-ml.com/pmdarima/modules/generated/pmdarima.arima.ARIMA.html#pmdarima.arima.ARIMA :param m: hyperparameter m for the ARIMA model, for details you may refer to https://alkaline-ml.com/pmdarima/modules/generated/pmdarima.arima.ARIMA.html#pmdarima.arima.ARIMA :param metric: the metric for validation and evaluation. For regression, we support Mean Squared Error: ("mean_squared_error", "MSE" or "mse"), Mean Absolute Error: ("mean_absolute_error","MAE" or "mae"), Mean Absolute Percentage Error: ("mean_absolute_percentage_error", "MAPE", "mape") Cosine Proximity: ("cosine_proximity", "cosine") """ self.model_config = { "p": p, "q": q, "seasonality_mode": seasonality_mode, "P": P, "Q": Q, "m": m, "metric": metric, } self.internal = ARIMAModel() super().__init__()
class ARIMAForecaster(Forecaster): """ ARIMA Forecaster AutoRegressive Integrated Moving Average (ARIMA) is a class of statistical models for analyzing and forecasting time series data. It consists of 3 components: AR (AutoRegressive), I (Integrated) and MA (Moving Average). In ARIMAForecaster we use the SARIMA model (Seasonal ARIMA), which is an extension of ARIMA that additionally supports the direct modeling of the seasonal component of the time series. """ def __init__( self, p=2, q=2, seasonality_mode=True, P=3, Q=1, m=7, metric="mse", ): """ Build a ARIMA Forecast Model. User need to set p, q, P, Q, m for the ARIMA model, the differencing term (d) and seasonal differencing term (D) are automatically estimated from the data. :param p: hyperparameter p for the ARIMA model, for details you may refer to https://alkaline-ml.com/pmdarima/modules/generated/pmdarima.arima.ARIMA.html#pmdarima.arima.ARIMA :param q: hyperparameter q for the ARIMA model, for details you may refer to https://alkaline-ml.com/pmdarima/modules/generated/pmdarima.arima.ARIMA.html#pmdarima.arima.ARIMA :param seasonality_mode: hyperparameter seasonality_mode for the ARIMA model, for details you may refer to https://alkaline-ml.com/pmdarima/modules/generated/pmdarima.arima.ARIMA.html#pmdarima.arima.ARIMA :param P: hyperparameter P for the ARIMA model, for details you may refer to https://alkaline-ml.com/pmdarima/modules/generated/pmdarima.arima.ARIMA.html#pmdarima.arima.ARIMA :param Q: hyperparameter Q for the ARIMA model, for details you may refer to https://alkaline-ml.com/pmdarima/modules/generated/pmdarima.arima.ARIMA.html#pmdarima.arima.ARIMA :param m: hyperparameter m for the ARIMA model, for details you may refer to https://alkaline-ml.com/pmdarima/modules/generated/pmdarima.arima.ARIMA.html#pmdarima.arima.ARIMA :param metric: the metric for validation and evaluation. For regression, we support Mean Squared Error: ("mean_squared_error", "MSE" or "mse"), Mean Absolute Error: ("mean_absolute_error","MAE" or "mae"), Mean Absolute Percentage Error: ("mean_absolute_percentage_error", "MAPE", "mape") Cosine Proximity: ("cosine_proximity", "cosine") """ self.model_config = { "p": p, "q": q, "seasonality_mode": seasonality_mode, "P": P, "Q": Q, "m": m, "metric": metric, } self.internal = ARIMAModel() super().__init__() def fit(self, data, validation_data): """ Fit(Train) the forecaster. :param data: A 1-D numpy array as the training data :param validation_data: A 1-D numpy array as the evaluation data """ self._check_data(data, validation_data) data = data.reshape(-1, 1) validation_data = validation_data.reshape(-1, 1) return self.internal.fit_eval(data=data, validation_data=validation_data, **self.model_config) def _check_data(self, data, validation_data): assert data.ndim == 1, \ "data should be an 1-D array), \ Got data dimension of {}."\ .format(data.ndim) assert validation_data.ndim == 1, \ "validation_data should be an 1-D array), \ Got validation_data dimension of {}."\ .format(validation_data.ndim) def predict(self, horizon, rolling=False): """ Predict using a trained forecaster. :param horizon: the number of steps forward to predict :param rolling: whether to use rolling prediction """ if self.internal.model is None: raise RuntimeError( "You must call fit or restore first before calling predict!") return self.internal.predict(horizon=horizon, rolling=rolling) def evaluate(self, validation_data, metrics=['mse'], rolling=False): """ Evaluate using a trained forecaster. :param validation_data: A 1-D numpy array as the evaluation data :param metrics: A list contains metrics for test/valid data. """ if validation_data is None: raise ValueError("Input invalid validation_data of None") if self.internal.model is None: raise RuntimeError( "You must call fit or restore first before calling evaluate!") return self.internal.evaluate(validation_data, metrics=metrics, rolling=rolling) def save(self, checkpoint_file): """ Save the forecaster. :param checkpoint_file: The location you want to save the forecaster. """ if self.internal.model is None: raise RuntimeError( "You must call fit or restore first before calling save!") self.internal.save(checkpoint_file) def restore(self, checkpoint_file): """ Restore the forecaster. :param checkpoint_file: The checkpoint file location you want to load the forecaster. """ self.internal.restore(checkpoint_file)
class TestARIMAModel(ZooTestCase): def setup_method(self, method): np.random.seed(0) self.seq_len = 400 self.config = { "p": np.random.randint(0, 4), "q": np.random.randint(0, 4), "seasonality_mode": np.random.choice([True, False]), "P": 5, "Q": 5, "m": np.random.choice([4, 7]), "metric": "mse", } self.model = ARIMAModel() self.data = np.random.rand(self.seq_len) self.horizon = np.random.randint(2, 50) self.validation_data = np.random.rand(self.horizon) def teardown_method(self, method): del self.model del self.data del self.validation_data def test_arima(self): # test fit_eval evaluate_result = self.model.fit_eval(data=self.data, validation_data=self.validation_data, **self.config ) # test predict result = self.model.predict(horizon=self.horizon) assert len(result) == self.horizon # test evaluate evaluate_result = self.model.evaluate(target=self.validation_data, metrics=['mae', 'smape']) assert len(evaluate_result) == 2 # test rolling predict rolling_result = self.model.predict(horizon=self.horizon, rolling=True) assert len(rolling_result) == self.horizon def test_error(self): with pytest.raises(ValueError, match="x should be None"): self.model.predict(x=1) with pytest.raises(ValueError, match="We don't support input x currently"): self.model.evaluate(target=self.validation_data, x=1) with pytest.raises(ValueError, match="Input invalid target of None"): self.model.evaluate(target=None) with pytest.raises(Exception, match="Needs to call fit_eval or restore first before calling predict"): self.model.predict(horizon=self.horizon) with pytest.raises(Exception, match="We don't support updating model " "without rolling prediction currently" ): self.model.predict(horizon=self.horizon, update=True, rolling=False) with pytest.raises(Exception, match="Needs to call fit_eval or restore first before calling evaluate"): self.model.evaluate(target=self.validation_data, x=None) with pytest.raises(Exception, match="Needs to call fit_eval or restore first before calling save"): model_file = "tmp.pkl" self.model.save(model_file) def test_save_restore(self): self.model.fit_eval(data=self.data, validation_data=self.validation_data, **self.config) result_save = self.model.predict(horizon=self.horizon, rolling=False) model_file = "tmp.pkl" self.model.save(model_file) assert os.path.isfile(model_file) new_model = ARIMAModel() new_model.restore(model_file) assert new_model.model result_restore = new_model.predict(horizon=self.horizon, rolling=False) assert_array_almost_equal(result_save, result_restore, decimal=2), \ "Prediction values are not the same after restore: " \ "predict before is {}, and predict after is {}".format(result_save, result_restore) os.remove(model_file)