Exemple #1
0
  def test_end_to_end_prediction_works_and_is_deterministic(
      self, dtype, use_xla, use_spike_and_slab):
    if not tf.executing_eagerly():
      return
    seed = test_util.test_seed(sampler_type='stateless')
    model, observed_time_series, is_missing = self._build_test_model(
        num_timesteps=5,
        batch_shape=[3],
        prior_class=gibbs_sampler.XLACompilableInverseGamma,
        sparse_weights_nonzero_prob=0.5 if use_spike_and_slab else None,
        dtype=dtype)

    @tf.function(jit_compile=use_xla)
    def do_sampling(observed_time_series, is_missing):
      return gibbs_sampler.fit_with_gibbs_sampling(
          model, tfp.sts.MaskedTimeSeries(
              observed_time_series, is_missing),
          num_results=4, num_warmup_steps=1, seed=seed)
    samples = do_sampling(observed_time_series[..., tf.newaxis], is_missing)
    predictive_dist = gibbs_sampler.one_step_predictive(
        model, samples, thin_every=1)

    # Test that the seeded calculation gives the same result on multiple runs.
    samples2 = do_sampling(observed_time_series[..., tf.newaxis], is_missing)
    predictive_dist2 = gibbs_sampler.one_step_predictive(
        model, samples2, thin_every=1)

    (predictive_mean_, predictive_stddev_,
     predictive_mean2_, predictive_stddev2_) = self.evaluate((
         predictive_dist.mean(), predictive_dist.stddev(),
         predictive_dist2.mean(), predictive_dist2.stddev()))
    self.assertAllEqual(predictive_mean_, predictive_mean2_)
    self.assertAllEqual(predictive_stddev_, predictive_stddev2_)
    def test_forecasts_are_sane(self):
        seed = test_util.test_seed()
        num_observed_steps = 5
        num_forecast_steps = 3
        model, observed_time_series, is_missing = self._build_test_model(
            num_timesteps=num_observed_steps + num_forecast_steps,
            batch_shape=[3])

        samples = gibbs_sampler.fit_with_gibbs_sampling(
            model,
            tfp.sts.MaskedTimeSeries(
                observed_time_series[..., :num_observed_steps, tf.newaxis],
                is_missing[..., :num_observed_steps]),
            num_results=5,
            num_warmup_steps=10,
            seed=seed,
            compile_steps_with_xla=False)
        predictive_dist = gibbs_sampler.one_step_predictive(
            model,
            samples,
            num_forecast_steps=num_forecast_steps,
            thin_every=1)
        predictive_mean, predictive_stddev = self.evaluate(
            (predictive_dist.mean(), predictive_dist.stddev()))

        self.assertAllEqual(predictive_mean.shape,
                            [3, num_observed_steps + num_forecast_steps])
        self.assertAllEqual(predictive_stddev.shape,
                            [3, num_observed_steps + num_forecast_steps])

        # Uncertainty should increase over the forecast period.
        self.assertTrue(
            np.all(predictive_stddev[..., num_observed_steps + 1:] >
                   predictive_stddev[..., num_observed_steps:-1]))
def _detect_anomalies_inner(observed_time_series,
                            seasonal_structure,
                            anomaly_threshold=0.01,
                            num_warmup_steps=50,
                            num_samples=100,
                            use_gibbs_predictive_dist=True,
                            seed=None):
    """Helper function for `detect_anomalies` to cache `tf.function` traces."""
    with tf.name_scope('build_default_model_for_gibbs_sampling'):
        observed_mean, observed_stddev, _ = sts_util.empirical_statistics(
            observed_time_series)
        # Center the series to have mean 0 and stddev 1. Alternately, we could
        # rescale the priors to match the series, but this is simpler.
        observed_mean = observed_mean[...,
                                      tf.newaxis]  # Broadcast with num_steps.
        observed_stddev = observed_stddev[..., tf.newaxis]
        observed_time_series = observed_time_series._replace(
            time_series=(observed_time_series.time_series -
                         observed_mean[..., tf.newaxis]) /
            observed_stddev[..., tf.newaxis])

        model, posterior_samples = _fit_seasonal_model_with_gibbs_sampling(
            observed_time_series,
            seasonal_structure=seasonal_structure,
            num_results=num_samples,
            num_warmup_steps=num_warmup_steps,
            seed=seed)
        parameter_samples = _parameter_samples_from_gibbs_posterior(
            model, posterior_samples)
        if use_gibbs_predictive_dist:
            predictive_dist = gibbs_sampler.one_step_predictive(
                model, posterior_samples)
        else:
            # Rebuild the model using appropriate Seasonal components, throwing away
            # the seasonal effects fit by the Gibbs sampler. Instead, the predictive
            # model incorporates the seasonal effects in its state space, so their
            # posterior is tracked exactly (rather than by sampling) and is updated
            # with each step of the series. This avoids the learning-from-the-future
            # behavior of the Gibbs-estimated effects.
            seasonal_model = (
                model.components[0] +  # LocalLinearTrend component.
                default_model.model_from_seasonal_structure(
                    seasonal_structure,
                    observed_time_series,
                    # Gibbs sampling didn't fit a drift scale(s).
                    allow_drift=False))
            predictive_dist = one_step_predictive(
                seasonal_model,
                observed_time_series,
                timesteps_are_event_shape=False,
                parameter_samples=parameter_samples)

        prob_lower = predictive_dist.cdf(observed_time_series.time_series[...,
                                                                          0])
        tail_probabilities = 2 * tf.minimum(prob_lower, 1 - prob_lower)
        lower_limit, upper_limit, predictive_mean = compute_predictive_bounds(
            predictive_dist, anomaly_threshold=anomaly_threshold)
        restore_scale = lambda x: x * observed_stddev + observed_mean
        return (restore_scale(lower_limit), restore_scale(upper_limit),
                restore_scale(predictive_mean), tail_probabilities)
    def test_end_to_end_prediction_works_and_is_deterministic(
            self, dtype, use_xla):
        if not tf.executing_eagerly():
            return
        seed = test_util.test_seed()
        model, observed_time_series, is_missing = self._build_test_model(
            num_timesteps=5, batch_shape=[3])

        samples = gibbs_sampler.fit_with_gibbs_sampling(
            model,
            tfp.sts.MaskedTimeSeries(observed_time_series[..., tf.newaxis],
                                     is_missing),
            num_results=4,
            num_warmup_steps=1,
            seed=seed,
            compile_steps_with_xla=use_xla)
        predictive_dist = gibbs_sampler.one_step_predictive(model,
                                                            samples,
                                                            thin_every=1)

        # Test that the seeded calculation gives the same result on multiple runs.
        samples2 = gibbs_sampler.fit_with_gibbs_sampling(
            model,
            tfp.sts.MaskedTimeSeries(observed_time_series, is_missing),
            num_results=4,
            num_warmup_steps=1,
            seed=seed,
            compile_steps_with_xla=use_xla)
        predictive_dist2 = gibbs_sampler.one_step_predictive(model,
                                                             samples2,
                                                             thin_every=1)

        (predictive_mean_, predictive_stddev_, predictive_mean2_,
         predictive_stddev2_) = self.evaluate(
             (predictive_dist.mean(), predictive_dist.stddev(),
              predictive_dist2.mean(), predictive_dist2.stddev()))
        self.assertAllEqual(predictive_mean_, predictive_mean2_)
        self.assertAllEqual(predictive_stddev_, predictive_stddev2_)
Exemple #5
0
  def test_forecasts_match_reference(self,
                                     use_slope,
                                     num_chains,
                                     time_series_shift,
                                     use_zero_step_prediction=False):
    seed = test_util.test_seed()
    num_observed_steps = 5
    num_forecast_steps = 4
    num_results = 10000

    # Dividing the number of results with number of chains so we sample the same
    # total number of MCMC samples.
    if not tf.nest.is_nested(num_chains):
      num_results = num_results // num_chains

    model, observed_time_series, is_missing = self._build_test_model(
        num_timesteps=num_observed_steps + num_forecast_steps,
        true_slope_scale=0.5 if use_slope else None,
        batch_shape=[3],
        time_series_shift=time_series_shift)

    @tf.function(autograph=False)
    def do_sampling():
      return gibbs_sampler.fit_with_gibbs_sampling(
          model,
          tfp.sts.MaskedTimeSeries(
              observed_time_series[..., :num_observed_steps, tf.newaxis],
              is_missing[..., :num_observed_steps]),
          num_chains=num_chains,
          num_results=num_results,
          num_warmup_steps=100,
          seed=seed)

    samples = self.evaluate(do_sampling())

    def reshape_chain_and_sample(x):
      if np.ndim(x) > 2:
        return np.reshape(x, [x.shape[0] * x.shape[1], *x.shape[2:]])
      return x

    if not tf.nest.is_nested(num_chains):
      samples = tf.nest.map_structure(reshape_chain_and_sample, samples)

    predictive_dist = gibbs_sampler.one_step_predictive(
        model,
        samples,
        num_forecast_steps=num_forecast_steps,
        thin_every=1,
        use_zero_step_prediction=use_zero_step_prediction)
    predictive_mean, predictive_stddev = self.evaluate((
        predictive_dist.mean(), predictive_dist.stddev()))
    self.assertAllEqual(predictive_mean.shape,
                        [3, num_observed_steps + num_forecast_steps])
    self.assertAllEqual(predictive_stddev.shape,
                        [3, num_observed_steps + num_forecast_steps])

    # big tolerance, but makes sure the predictive mean initializes near
    # the initial time series value
    self.assertAllClose(tf.reduce_mean(predictive_mean[:, 0]),
                        observed_time_series[0, 0],
                        atol=10.)

    if use_slope:
      parameter_samples = (samples.observation_noise_scale,
                           samples.level_scale,
                           samples.slope_scale,
                           samples.weights)
    else:
      parameter_samples = (samples.observation_noise_scale,
                           samples.level_scale,
                           samples.weights)

    # Note that although we expect the Gibbs-sampled forecasts to match a
    # reference implementation, we *don't* expect the one-step predictions to
    # match `tfp.sts.one_step_predictive`, because that makes predictions using
    # a filtered posterior (i.e., given only previous observations) whereas the
    # Gibbs-sampled latent `level`s will incorporate some information from
    # future observations.
    reference_forecast_dist = tfp.sts.forecast(
        model,
        observed_time_series=observed_time_series[..., :num_observed_steps],
        parameter_samples=parameter_samples,
        num_steps_forecast=num_forecast_steps)

    reference_forecast_mean, reference_forecast_stddev = self.evaluate((
        reference_forecast_dist.mean()[..., 0],
        reference_forecast_dist.stddev()[..., 0]))

    self.assertAllClose(predictive_mean[..., -num_forecast_steps:],
                        reference_forecast_mean,
                        atol=1.0 if use_slope else 0.3)
    self.assertAllClose(predictive_stddev[..., -num_forecast_steps:],
                        reference_forecast_stddev,
                        atol=2.0 if use_slope else 1.0)
    def test_forecasts_match_reference(self, use_slope):
        seed = test_util.test_seed()
        num_observed_steps = 5
        num_forecast_steps = 4
        model, observed_time_series, is_missing = self._build_test_model(
            num_timesteps=num_observed_steps + num_forecast_steps,
            true_slope_scale=0.5 if use_slope else None,
            batch_shape=[3])

        samples = tf.function(lambda: gibbs_sampler.fit_with_gibbs_sampling(  # pylint: disable=g-long-lambda
            model,
            tfp.sts.MaskedTimeSeries(
                observed_time_series[..., :num_observed_steps, tf.newaxis],
                is_missing[..., :num_observed_steps]),
            num_results=10000,
            num_warmup_steps=100,
            seed=seed))()
        predictive_dist = gibbs_sampler.one_step_predictive(
            model,
            samples,
            num_forecast_steps=num_forecast_steps,
            thin_every=1)
        predictive_mean, predictive_stddev = self.evaluate(
            (predictive_dist.mean(), predictive_dist.stddev()))
        self.assertAllEqual(predictive_mean.shape,
                            [3, num_observed_steps + num_forecast_steps])
        self.assertAllEqual(predictive_stddev.shape,
                            [3, num_observed_steps + num_forecast_steps])

        if use_slope:
            parameter_samples = (samples.observation_noise_scale,
                                 samples.level_scale, samples.slope_scale,
                                 samples.weights)
        else:
            parameter_samples = (samples.observation_noise_scale,
                                 samples.level_scale, samples.weights)

        # Note that although we expect the Gibbs-sampled forecasts to match a
        # reference implementation, we *don't* expect the one-step predictions to
        # match `tfp.sts.one_step_predictive`, because that makes predictions using
        # a filtered posterior (i.e., given only previous observations) whereas the
        # Gibbs-sampled latent `level`s will incorporate some information from
        # future observations.
        reference_forecast_dist = tfp.sts.forecast(
            model,
            observed_time_series=observed_time_series[
                ..., :num_observed_steps],
            parameter_samples=parameter_samples,
            num_steps_forecast=num_forecast_steps)

        reference_forecast_mean = self.evaluate(
            reference_forecast_dist.mean()[..., 0])
        reference_forecast_stddev = self.evaluate(
            reference_forecast_dist.stddev()[..., 0])

        self.assertAllClose(predictive_mean[..., -num_forecast_steps:],
                            reference_forecast_mean,
                            atol=1.0 if use_slope else 0.3)
        self.assertAllClose(predictive_stddev[..., -num_forecast_steps:],
                            reference_forecast_stddev,
                            atol=2.0 if use_slope else 1.0)