Numpyro: Predictive distribution fails on model with lax.scan

Created on 7 Apr 2020  路  8Comments  路  Source: pyro-ppl/numpyro

Refer to the forum discussion

The following snippet:

def target(T=10, q=1., r=1., phi=0.5, beta=0.5):

    def transition(state, xs):
        i, key = xs
        key1, key2 = jax.random.split(key)
        x0, mu0 = state
        x1 = numpyro.sample(f'x_{i}', dist.Normal(phi * x0, q), rng_key=key1)
        mu1 = beta * mu0 + x1
        y1 = numpyro.sample(f'y_{i}', dist.Normal(mu1, r), rng_key=key2)
        return (x1, mu1), (x1, y1)

    mu0 = x0 = numpyro.sample('x_0', dist.Normal(0, q))
    y0 = numpyro.sample('y_0', dist.Normal(mu0, r))

    key = numpyro.sample('key', dist.PRNGIdentity())
    _, xy = jax.lax.scan(transition, (x0, mu0), (np.arange(1, T), jax.random.split(key, T-1)))
    x, y = xy

    return np.append(x0, x), np.append(y0, y)


prior = Predictive(target, posterior_samples = {}, num_samples = 10)
prior_samples = prior(PRNGKey(2), T=10)

fails with:

UnexpectedTracerError: Encountered an unexpected tracer. Perhaps this tracer escaped through global state from a previously traced function.
The functions being transformed should not save traced values to global state.
Details: Can't lift level Traced<ShapedArray(float32[]):JaxprTrace(level=1/0)> to JaxprTrace(level=0/0).
bug

All 8 comments

@neerajprad I think we can't trace this function

numpyro.handlers.trace(numpyro.handlers.seed(target, 0)).get_trace()

Hmm..yes, that makes sense. We can't really collect x_i, y_i samples unfortunately, so this doesn't work. I would like to keep this issue open to explore better workarounds for such use cases where we may need to use a jax control flow primitive inside a model. I think the only workaround is to make sure that the body of the control flow doesn't contain any pyro primitives, but I wonder if there's a better solution.

I think that we can follow your direction to rewrite scan such that it takes rng as carry and moves the drawn samples to collection. For log_density, given the collection of samples, we use an index i in carry to select the corresponding samples at each scan step. I think a similar approach can be used for fori_loop.

I am trying to port the DMM example to NumPyro and experiencing the same issues when using SVI. I really support the idea of making NumPyro primitives work within Jax loops 馃槃

@ahmadsalim - we agree that this will be important to have. I think having effect handlers work generically within jax control flow primitives might be hard, but there may be a way to rewrite some version of these control flow primitives in a way that makes writing such loops and collecting samples easier, like @fehiepsi mentioned above. We will look into it.

It looks like JAX has a new experimental library loops that might play nicely with effect handlers: https://github.com/google/jax/blob/master/jax/experimental/loops.py

Thanks for pointing this out, @eb8680! I'll definitely take a look at this as I find time. It will be really cool if effect handlers would work with this.

Looks interesting! I have been thinking about this issue for a while. I think we can mimic its ideas: rewrite content inside loops.Scope() into lax.control_flow primitives.

I think I can come up with a solution for a loop with simple content. Let's see how it goes. :) (I'll be pretty excited if it works)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fehiepsi picture fehiepsi  路  4Comments

fehiepsi picture fehiepsi  路  6Comments

justindomke picture justindomke  路  6Comments

peterroelants picture peterroelants  路  5Comments

LysSanzMoreta picture LysSanzMoreta  路  3Comments