How do I evaluate posterior density, and get Hessians thereof?

I’d like the parameters φ of my guide q(z|φ) to correspond to ẑ, an SVI estimate of the MAP of z. The guide would thus be a multivariate normal 𝒩(ẑ,ℐ ⁻¹(ẑ)), where the variance ℐ ⁻¹(ẑ) is the inverse of the observed information of z at ẑ; that is, the inverse of the Hessian of the posterior density.

Thus, in order to construct the guide, I need to be able to evaluate the posterior density of the model, and to get the Hessian of that posterior density. I’d like to do that Hessian for a subset of variables at a time, so that I can use what I know about conditional independence to deal with this high-dimensional matrix one low-dimensional part at a time.

Reading the documents, it’s clear to me how to run inference, but when I do so, all the evaluations of the density of the model are inside a black box. Can anybody tell me how I’d evaluate the density of a model m, and how I’d get the Hessian of that density?

Hi @Jameson, I think that what you are looking for is Laplace Approximation which is available in autoguide module. Note that the approximation happens in unconstrained space, so the result covariance (generated from the param auto_scale_tril) might be different from what you want if your z support is not real.

If your z support is not real and you want to get covariance in constrained space then I think there are two ways to do:

  • generate 1000 samples from LaplaceApproximation, then calculate covariance of these samples (you can use Welford scheme to calculate it).
  • start from z_MAP
from pyro.contrib.util import hessian

MAP_model = pyro.condition(model, data={"z"=z_MAP})
trace = poutine.trace(MAP_model).get_trace(*args, **kwargs)
loss = -trace.log_prob_sum()
H = hessian(loss, z_MAP)
covariance = H.inverse()

Thanks. That’s helpful. I can’t use LaplaceApproximation as-is, because it does MAP estimation and then LA in two steps instead of simultaneously, but it definitely helps point me in the right direction.

But your code doesn’t work. Aside from the easy-to-fix issue of using = instead of : in your dict, there’s the line ....get_trace(*args, **kwargs). What should I use for args and/or kwargs in this case?

Note that the un-informative *args, **kwargs appears in the docs a lot. For instance, in AutoLaplaceApproximation, both get_posterior and laplace_approximation have this. It would be one thing if these uniformly meant the parameters were purely optional, but I’m getting an error on pyro/poutine/trace_messenger.py line 147 where that’s not the case.

In almost all cases in the Pyro source code and documentation, including the use of pyro.poutine.trace here, *args, **kwargs are the arguments and keyword arguments that are passed to your model.