Example #1
0
def test_multi_round_snpe_on_linearGaussian_based_on_mmd(algorithm_str: str):
    """Test whether APT infers well a simple example where ground truth is available."""

    num_dim = 3
    true_observation = torch.zeros((1, num_dim))
    num_samples = 100

    prior = distributions.MultivariateNormal(
        loc=torch.zeros(num_dim), covariance_matrix=torch.eye(num_dim))
    target_samples = get_true_posterior_samples_linear_gaussian_mvn_prior(
        true_observation, num_samples=num_samples)

    neural_net = utils.posterior_nn(
        model="maf",
        prior=prior,
        context=true_observation,
    )

    snpe_common_args = dict(
        simulator=linear_gaussian,
        true_observation=true_observation,
        density_estimator=neural_net,
        prior=prior,
        z_score_obs=True,
        use_combined_loss=False,
        retrain_from_scratch_each_round=False,
        discard_prior_samples=False,
    )

    if algorithm_str == "snpe_b":
        infer = SnpeB(simulation_batch_size=10, **snpe_common_args)
    elif algorithm_str == "snpe_c":
        infer = SnpeC(
            num_atoms=10,
            simulation_batch_size=50,
            sample_with_mcmc=False,
            **snpe_common_args,
        )

    # Run inference.
    num_rounds, num_simulations_per_round = 2, 1000
    posterior = infer(num_rounds=num_rounds,
                      num_simulations_per_round=num_simulations_per_round)

    # Draw from posterior.
    samples = posterior.sample(num_samples)

    # Compute the mmd, and check if larger than expected.
    mmd = utils.unbiased_mmd_squared(target_samples, samples)
    max_mmd = 0.02

    print(f"mmd for {algorithm_str} is {mmd}.")

    assert (mmd < max_mmd
            ), f"MMD={mmd} is more than 2 stds above the average performance."
    samples = posterior.sample(num_samples)
def test_nonlinearGaussian_based_on_mmd():
    simulator = non_linear_gaussian

    # Ground truth parameters as specified in 'Sequential Neural Likelihood' paper.
    ground_truth_parameters = torch.tensor([-0.7, -2.9, -1.0, -0.9, 0.6])
    # ground truth observation using same seed as 'Sequential Neural Likelihood' paper.
    ground_truth_observation = torch.tensor([
        -0.97071232,
        -2.94612244,
        -0.44947218,
        -3.42318484,
        -0.13285634,
        -3.36401699,
        -0.85367595,
        -2.42716377,
    ])

    # assume batch dims
    parameter_dim = ground_truth_parameters.shape[0]
    observation_dim = ground_truth_observation.shape[0]

    prior = BoxUniform(
        low=-3 * torch.ones(parameter_dim),
        high=3 * torch.ones(parameter_dim),
    )

    infer = SnpeC(
        simulator=simulator,
        true_observation=ground_truth_observation[None, ],
        prior=prior,
        num_atoms=-1,
        z_score_obs=True,
        use_combined_loss=False,
        retrain_from_scratch_each_round=False,
        discard_prior_samples=False,
    )

    num_rounds, num_simulations_per_round = 2, 1000
    posterior = infer(num_rounds=num_rounds,
                      num_simulations_per_round=num_simulations_per_round)

    samples = posterior.sample(1000)

    # Sample from (analytically tractable) target distribution.
    target_samples = get_ground_truth_posterior_samples_nonlinear_gaussian(
        num_samples=1000)

    # Compute and check if MMD is larger than expected.
    mmd = utils.unbiased_mmd_squared(target_samples.float(), samples.float())

    max_mmd = 0.16  # mean mmd plus 2 stds.
    assert mmd < max_mmd, f"MMD={mmd} larger than mean plus 2 stds."
Example #3
0
def test_snl_on_linearGaussian_based_on_mmd(num_dim: int, prior_str: str):
    """Test SNL on linear Gaussian, comparing to ground truth posterior via MMD.

    NOTE: The MMD threshold is calculated based on a number of test runs and taking the mean plus 2 stds. 
    
    Args:
        num_dim: parameter dimension of the gaussian model
        prior_str: one of "gaussian" or "uniform"
    """

    true_observation = torch.zeros((1, num_dim))
    num_samples = 200

    if prior_str == "gaussian":
        prior = distributions.MultivariateNormal(
            loc=torch.zeros(num_dim), covariance_matrix=torch.eye(num_dim))
        target_samples = get_true_posterior_samples_linear_gaussian_mvn_prior(
            true_observation, num_samples=num_samples)
    else:
        prior = utils.BoxUniform(-1.0 * torch.ones(num_dim),
                                 torch.ones(num_dim))
        target_samples = get_true_posterior_samples_linear_gaussian_uniform_prior(
            true_observation, num_samples=num_samples, prior=prior)

    neural_likelihood = utils.likelihood_nn(
        model="maf",
        prior=prior,
        context=true_observation,
    )

    infer = SNL(
        simulator=linear_gaussian,
        prior=prior,
        true_observation=true_observation,
        density_estimator=neural_likelihood,
        mcmc_method="slice-np",
    )

    posterior = infer(num_rounds=1, num_simulations_per_round=1000)

    samples = posterior.sample(num_samples=num_samples)

    # compute the mmd
    mmd = utils.unbiased_mmd_squared(target_samples, samples)

    # check if mmd is larger than expected
    # NOTE: the mmd is calculated based on a number of test runs
    max_mmd = 0.02

    assert (mmd < max_mmd
            ), f"MMD={mmd} is more than 2 stds above the average performance."
Example #4
0
def test_multi_round_snl_on_linearGaussian_based_on_mmd():
    """Test SNL on linear Gaussian, comparing to ground truth posterior via MMD.

    NOTE: The MMD threshold is calculated based on a number of test runs and taking the mean plus 2 stds. 

    """

    num_dim = 3
    true_observation = torch.zeros((1, num_dim))
    num_samples = 200

    prior = distributions.MultivariateNormal(
        loc=torch.zeros(num_dim), covariance_matrix=torch.eye(num_dim))
    target_samples = get_true_posterior_samples_linear_gaussian_mvn_prior(
        true_observation, num_samples=num_samples)

    neural_likelihood = utils.likelihood_nn(
        model="maf",
        prior=prior,
        context=true_observation,
    )

    infer = SNL(
        simulator=linear_gaussian,
        prior=prior,
        true_observation=true_observation,
        density_estimator=neural_likelihood,
        simulation_batch_size=50,
        mcmc_method="slice",
    )

    posterior = infer(num_rounds=2, num_simulations_per_round=1000)

    samples = posterior.sample(num_samples=500)

    mmd = utils.unbiased_mmd_squared(target_samples, samples)

    # check if mmd is larger than expected
    # NOTE: the mmd is calculated based on a number of test runs
    max_mmd = 0.02

    assert (mmd < max_mmd
            ), f"MMD={mmd} is more than 2 stds above the average performance."
Example #5
0
def test_apt_on_linearGaussian_based_on_mmd(num_dim: int, prior_str: str,
                                            algorithm_str: str,
                                            simulation_batch_size: int):
    """Test whether APT infers well a simple example where ground truth is available."""

    true_observation = torch.zeros(num_dim)
    num_samples = 100

    if prior_str == "gaussian":
        prior = distributions.MultivariateNormal(
            loc=torch.zeros(num_dim), covariance_matrix=torch.eye(num_dim))
        target_samples = get_true_posterior_samples_linear_gaussian_mvn_prior(
            true_observation, num_samples=num_samples)
    else:
        prior = utils.BoxUniform(-1.0 * torch.ones(num_dim),
                                 torch.ones(num_dim))
        target_samples = get_true_posterior_samples_linear_gaussian_uniform_prior(
            true_observation, num_samples=num_samples, prior=prior)

    neural_net = utils.posterior_nn(model="maf",
                                    prior=prior,
                                    context=true_observation)

    snpe_common_args = dict(
        simulator=linear_gaussian,
        true_observation=true_observation,
        density_estimator=neural_net,
        prior=prior,
        z_score_obs=True,
        simulation_batch_size=simulation_batch_size,
        use_combined_loss=False,
        retrain_from_scratch_each_round=False,
        discard_prior_samples=False,
    )

    if algorithm_str == "snpe_b":
        infer = SnpeB(**snpe_common_args)
    elif algorithm_str == "snpe_c":
        infer = SnpeC(num_atoms=-1, sample_with_mcmc=False, **snpe_common_args)

    # Run inference.
    num_rounds, num_simulations_per_round = 1, 1000
    posterior = infer(
        num_rounds=num_rounds,
        num_simulations_per_round=num_simulations_per_round,
    )

    # Draw from posterior.
    samples = posterior.sample(num_samples)

    # Compute the mmd, and check if larger than expected
    mmd = utils.unbiased_mmd_squared(target_samples, samples)
    max_mmd = 0.03

    print(f"mmd for {algorithm_str} is {mmd}.")

    assert (mmd < max_mmd
            ), f"MMD={mmd} is more than 2 stds above the average performance."

    # Checks for log_prob()
    if prior_str == "gaussian":
        # For the Gaussian prior, we compute the KLd between ground truth and posterior.
        dkl = test_utils.get_dkl_gaussian_prior(posterior, true_observation,
                                                num_dim)

        max_dkl = 0.05 if num_dim == 1 else 0.8

        assert (
            dkl < max_dkl
        ), f"D-KL={dkl} is more than 2 stds above the average performance."

    elif prior_str == "uniform":
        # Check whether the returned probability outside of the support is zero.
        posterior_prob = test_utils.get_prob_outside_uniform_prior(
            posterior, num_dim)
        assert (
            posterior_prob == 0.0
        ), "The posterior probability outside of the prior support is not zero"

        # Check whether normalization (i.e. scaling up the density due
        # to leakage into regions without prior support) scales up the density by the
        # correct factor.
        (
            posterior_likelihood_unnorm,
            posterior_likelihood_norm,
            acceptance_prob,
        ) = test_utils.get_normalization_uniform_prior(posterior, prior,
                                                       true_observation)
        # The acceptance probability should be *exactly* the ratio of the unnormalized
        # and the normalized likelihood. However, we allow for an error margin of 1%,
        # since the estimation of the acceptance probability is random (based on
        # rejection sampling).
        assert (
            acceptance_prob * 0.99 < posterior_likelihood_unnorm /
            posterior_likelihood_norm < acceptance_prob * 1.01
        ), "Normalizing the posterior density using the acceptance probability failed."
Example #6
0
def summarize(
    summary_writer,
    summary,
    round_,
    true_observation,
    parameter_bank,
    observation_bank,
    simulator,
    posterior_samples_acceptance_rate=None,
):
    # get ground truth if available
    try:
        (
            _,
            prior,
            ground_truth_parameters,
            ground_truth_observation,
        ) = simulators.get_simulator_prior_and_groundtruth(simulator.name)
    # Update summaries.
    except:
        pass

    try:
        mmd = utils.unbiased_mmd_squared(
            parameter_bank[-1],
            simulator.get_ground_truth_posterior_samples(num_samples=1000),
        )
        summary["mmds"].append(mmd.item())
    except:
        pass

    try:
        # Median |x - x0| for most recent round.
        median_observation_distance = torch.median(
            torch.sqrt(
                torch.sum(
                    (observation_bank[-1] -
                     true_observation.reshape(1, -1))**2,
                    dim=-1,
                )))
        summary["median_observation_distances"].append(
            median_observation_distance.item())

        summary_writer.add_scalar(
            tag="median_observation_distance",
            scalar_value=summary["median_observation_distances"][-1],
            global_step=round_ + 1,
        )

    except:
        pass

    try:
        # KDE estimate of negative log prob true parameters using
        # parameters from most recent round.

        negative_log_prob_true_parameters = -utils.gaussian_kde_log_eval(
            samples=parameter_bank[-1],
            query=ground_truth_parameters.reshape(1, -1),
        )
        summary["negative_log_probs_true_parameters"].append(
            negative_log_prob_true_parameters.item())

        summary_writer.add_scalar(
            tag="negative_log_prob_true_parameters",
            scalar_value=summary["negative_log_probs_true_parameters"][-1],
            global_step=round_ + 1,
        )
    except:
        pass

    try:
        # Rejection sampling acceptance rate
        summary["rejection_sampling_acceptance-rates"].append(
            posterior_samples_acceptance_rate)

        summary_writer.add_scalar(
            tag="rejection_sampling_acceptance_rate",
            scalar_value=summary["rejection_sampling_acceptance_rates"][-1],
            global_step=round_ + 1,
        )
    except:
        pass

    try:
        # Plot most recently sampled parameters.
        parameters = utils.tensor2numpy(parameter_bank[-1])
        figure = utils.plot_hist_marginals(
            data=parameters,
            ground_truth=utils.tensor2numpy(ground_truth_parameters).reshape(
                -1),
            lims=simulator.parameter_plotting_limits,
        )
        summary_writer.add_figure(tag="posterior_samples",
                                  figure=figure,
                                  global_step=round_ + 1)
    except:
        pass

    # Write quantities using SummaryWriter.
    summary_writer.add_scalar(
        tag="epochs_trained",
        scalar_value=summary["epochs"][-1],
        global_step=round_ + 1,
    )

    summary_writer.add_scalar(
        tag="best_validation_log_prob",
        scalar_value=summary["best_validation_log_probs"][-1],
        global_step=round_ + 1,
    )

    if summary["mmds"]:
        summary_writer.add_scalar(
            tag="mmd",
            scalar_value=summary["mmds"][-1],
            global_step=round_ + 1,
        )

    summary_writer.flush()

    return summary_writer, summary
Example #7
0
def test_sre_on_linearGaussian_based_on_mmd(num_dim: int, prior_str: str,
                                            classifier_loss: str):
    """Test MMD accuracy of inference with SRE on linear Gaussian model. 

    NOTE: The mmd threshold is calculated based on a number of test runs and taking the mean plus 2 stds. 
    
    Args:
        num_dim: parameter dimension of the gaussian model
        prior_str: one of "gaussian" or "uniform"
    """

    true_observation = torch.zeros(num_dim)
    num_samples = 300

    if prior_str == "gaussian":
        prior = distributions.MultivariateNormal(
            loc=torch.zeros(num_dim), covariance_matrix=torch.eye(num_dim))
        target_samples = get_true_posterior_samples_linear_gaussian_mvn_prior(
            true_observation[None, ], num_samples=num_samples)
    else:
        prior = utils.BoxUniform(-1.0 * torch.ones(num_dim),
                                 torch.ones(num_dim))
        target_samples = get_true_posterior_samples_linear_gaussian_uniform_prior(
            true_observation[None, ], num_samples=num_samples, prior=prior)

    classifier = utils.classifier_nn(
        "resnet",
        prior=prior,
        context=true_observation,
    )

    num_atoms = 2 if classifier_loss == "aalr" else -1

    infer = SRE(
        simulator=linear_gaussian,
        prior=prior,
        true_observation=true_observation,
        num_atoms=num_atoms,
        classifier=classifier,
        classifier_loss=classifier_loss,
        simulation_batch_size=50,
        mcmc_method="slice-np",
    )

    posterior = infer(num_rounds=1, num_simulations_per_round=1000)

    samples = posterior.sample(num_samples=num_samples)

    # Check if mmd is larger than expected.
    mmd = utils.unbiased_mmd_squared(target_samples, samples)
    max_mmd = 0.045
    assert (mmd < max_mmd
            ), f"MMD={mmd} is more than 2 stds above the average performance."

    # Checks for log_prob()
    if prior_str == "gaussian" and classifier_loss == "aalr":
        # For the Gaussian prior, we compute the KLd between ground truth and
        # posterior. We can do this only if the classifier_loss was as described in
        # Hermans et al. 2019 ('aalr') since Durkan et al. 2019 version only allows
        # evaluation up to a constant.
        # For the Gaussian prior, we compute the KLd between ground truth and posterior
        dkl = test_utils.get_dkl_gaussian_prior(posterior, true_observation,
                                                num_dim)

        max_dkl = 0.05 if num_dim == 1 else 0.8

        assert (
            dkl < max_dkl
        ), f"KLd={dkl} is more than 2 stds above the average performance."
    if prior_str == "uniform":
        # Check whether the returned probability outside of the support is zero.
        posterior_prob = test_utils.get_prob_outside_uniform_prior(
            posterior, num_dim)
        assert (
            posterior_prob == 0.0
        ), "The posterior probability outside of the prior support is not zero"