def test_piecewise_linear( gamma: float, slopes: np.ndarray, knot_spacings: np.ndarray, hybridize: bool, ) -> None: ''' Test to check that minimizing the CRPS recovers the quantile function ''' num_samples = 500 # use a few samples for timeout failure gammas = mx.nd.zeros((num_samples, )) + gamma slopess = mx.nd.zeros((num_samples, len(slopes))) + mx.nd.array(slopes) knot_spacingss = mx.nd.zeros( (num_samples, len(knot_spacings))) + mx.nd.array(knot_spacings) pwl_sqf = PiecewiseLinear(gammas, slopess, knot_spacingss) samples = pwl_sqf.sample() # Parameter initialization gamma_init = gamma - START_TOL_MULTIPLE * TOL * gamma slopes_init = slopes - START_TOL_MULTIPLE * TOL * slopes knot_spacings_init = knot_spacings # We perturb knot spacings such that even after the perturbation they sum to 1. mid = len(slopes) // 2 knot_spacings_init[:mid] = (knot_spacings[:mid] - START_TOL_MULTIPLE * TOL * knot_spacings[:mid]) knot_spacings_init[mid:] = (knot_spacings[mid:] + START_TOL_MULTIPLE * TOL * knot_spacings[mid:]) init_biases = [gamma_init, slopes_init, knot_spacings_init] # check if it returns original parameters of mapped gamma_hat, slopes_hat, knot_spacings_hat = maximum_likelihood_estimate_sgd( PiecewiseLinearOutput(len(slopes)), samples, init_biases=init_biases, hybridize=hybridize, learning_rate=PositiveFloat(0.01), num_epochs=PositiveInt(20), ) # Since the problem is highly non-convex we may not be able to recover the exact parameters # Here we check if the estimated parameters yield similar function evaluations at different quantile levels. quantile_levels = np.arange(0.1, 1.0, 0.1) # create a LinearSplines instance with the estimated parameters to have access to .quantile pwl_sqf_hat = PiecewiseLinear( mx.nd.array(gamma_hat), mx.nd.array(slopes_hat).expand_dims(axis=0), mx.nd.array(knot_spacings_hat).expand_dims(axis=0), ) # Compute quantiles with the estimated parameters quantiles_hat = np.squeeze( pwl_sqf_hat.quantile(mx.nd.array(quantile_levels).expand_dims(axis=0), axis=1).asnumpy()) # Compute quantiles with the original parameters # Since params is replicated across samples we take only the first entry quantiles = np.squeeze( pwl_sqf.quantile( mx.nd.array(quantile_levels).expand_dims(axis=0).repeat( axis=0, repeats=num_samples), axis=1, ).asnumpy()[0, :]) for ix, (quantile, quantile_hat) in enumerate(zip(quantiles, quantiles_hat)): assert np.abs(quantile_hat - quantile) < TOL * quantile, ( f"quantile level {quantile_levels[ix]} didn't match:" f" " f"q = {quantile}, q_hat = {quantile_hat}")
( NegativeBinomialOutput(), mx.nd.random.normal(shape=(3, 4, 5, 6)), [None, mx.nd.ones(shape=(3, 4, 5))], (3, 4, 5), (), ), ( UniformOutput(), mx.nd.random.normal(shape=(3, 4, 5, 6)), [None, mx.nd.ones(shape=(3, 4, 5))], (3, 4, 5), (), ), ( PiecewiseLinearOutput(num_pieces=3), mx.nd.random.normal(shape=(3, 4, 5, 6)), [None, mx.nd.ones(shape=(3, 4, 5))], (3, 4, 5), (), ), ( MixtureDistributionOutput([GaussianOutput(), StudentTOutput()]), mx.nd.random.normal(shape=(3, 4, 5, 6)), [None, mx.nd.ones(shape=(3, 4, 5))], (3, 4, 5), (), ), ( MixtureDistributionOutput( [
def forecast_dataset(dataset, epochs=100, learning_rate=1e-3, num_samples=100, model="SimpleFeedForward", r_method="ets", alpha=0, distrib="Gaussian"): if distrib == "Gaussian": distr_output = GaussianOutput() elif distrib == "Laplace": distr_output = LaplaceOutput() elif distrib == "PiecewiseLinear": distr_output = PiecewiseLinearOutput(num_pieces=2) elif distrib == "Uniform": distr_output = UniformOutput() elif distrib == "Student": distr_output = StudentTOutput() else: distr_output = None if model != "GaussianProcess": ctx = mx.Context("gpu") else: ctx = mx.Context("cpu") # Trainer trainer = Trainer(epochs=epochs, learning_rate=learning_rate, num_batches_per_epoch=100, ctx=ctx, hybridize=True if model[0] != "c" else False) # Estimator (if machine learning model) if model == "SimpleFeedForward": # 10s / epochs for context 60*24 estimator = SimpleFeedForwardEstimator( num_hidden_dimensions=[10], prediction_length=dataset.prediction_length, context_length=dataset.context_length, freq=dataset.freq, trainer=trainer, distr_output=distr_output) elif model == "cSimpleFeedForward": # 10s / epochs for context 60*24 estimator = CustomSimpleFeedForwardEstimator( prediction_length=dataset.prediction_length, context_length=dataset.context_length, freq=dataset.freq, trainer=trainer, num_cells=40, alpha=alpha, distr_output=distr_output, distr_output_type=distrib) elif model == "CanonicalRNN": # 80s /epochs for context 60*24, idem for 60*1 estimator = canonical.CanonicalRNNEstimator( freq=dataset.freq, context_length=dataset.context_length, prediction_length=dataset.prediction_length, trainer=trainer, distr_output=distr_output, ) elif model == "DeepAr": estimator = deepar.DeepAREstimator( freq=dataset.freq, context_length=dataset.context_length, prediction_length=dataset.prediction_length, trainer=trainer, distr_output=distr_output, ) elif model == "DeepFactor": # 120 s/epochs if one big time serie, 1.5s if 183 time series estimator = deep_factor.DeepFactorEstimator( freq=dataset.freq, context_length=dataset.context_length, prediction_length=dataset.prediction_length, trainer=trainer, distr_output=distr_output, ) elif model == "DeepState": # Very slow on cpu estimator = deepstate.DeepStateEstimator( freq=dataset.freq, prediction_length=dataset.prediction_length, trainer=trainer, cardinality=list([1]), use_feat_static_cat=False) elif model == "GaussianProcess": # CPU / GPU problem estimator = gp_forecaster.GaussianProcessEstimator( freq=dataset.freq, prediction_length=dataset.prediction_length, trainer=trainer, cardinality=1) elif model == "NPTS": estimator = npts.NPTSEstimator( freq=dataset.freq, prediction_length=dataset.prediction_length) elif model == "MQCNN": estimator = seq2seq.MQCNNEstimator( prediction_length=dataset.prediction_length, freq=dataset.freq, context_length=dataset.context_length, trainer=trainer, quantiles=list([0.005, 0.05, 0.25, 0.5, 0.75, 0.95, 0.995])) elif model == "MQRNN": estimator = seq2seq.MQRNNEstimator( prediction_length=dataset.prediction_length, freq=dataset.freq, context_length=dataset.context_length, trainer=trainer, quantiles=list([0.005, 0.05, 0.25, 0.5, 0.75, 0.95, 0.995])) elif model == "RNN2QR": # Must be investigated estimator = seq2seq.RNN2QRForecaster( prediction_length=dataset.prediction_length, freq=dataset.freq, context_length=dataset.context_length, trainer=trainer, cardinality=dataset.cardinality, embedding_dimension=1, encoder_rnn_layer=1, encoder_rnn_num_hidden=1, decoder_mlp_layer=[1], decoder_mlp_static_dim=1) elif model == "SeqToSeq": # Must be investigated estimator = seq2seq.Seq2SeqEstimator( prediction_length=dataset.prediction_length, freq=dataset.freq, context_length=dataset.context_length, trainer=trainer, cardinality=[1], embedding_dimension=1, decoder_mlp_layer=[1], decoder_mlp_static_dim=1, encoder=Seq2SeqEncoder()) elif model == "Transformer": # Make the computer lag the first time estimator = transformer.TransformerEstimator( prediction_length=dataset.prediction_length, freq=dataset.freq, context_length=dataset.context_length, trainer=trainer) else: estimator = None # Predictor (directly if non machine learning model and from estimator if machine learning) if model == "Prophet": predictor = prophet.ProphetPredictor( freq=dataset.freq, prediction_length=dataset.prediction_length, ) elif model == "R": predictor = r_forecast.RForecastPredictor( freq=dataset.freq, prediction_length=dataset.prediction_length, method_name=r_method) elif model == "SeasonalNaive": predictor = seasonal_naive.SeasonalNaivePredictor( freq=dataset.freq, prediction_length=dataset.prediction_length, season_length=24) else: predictor = estimator.train(dataset.train_ds) if model[0] != "c": predictor.serialize(Path("temp")) predictor = Predictor.deserialize( Path("temp"), ctx=mx.cpu(0)) # fix for deepstate # Evaluate forecast_it, ts_it = make_evaluation_predictions( dataset=dataset.test_ds, # test dataset predictor=predictor, # predictor num_samples=num_samples, # num of sample paths we want for evaluation ) return list(forecast_it), list(ts_it)