Probability: How to access continuous approximation of a Poisson p.m.f. in v0.4.0+ (compared to v0.3.0)

Created on 9 Oct 2018  Â·  14Comments  Â·  Source: tensorflow/probability

This is not a formal bug request yet, but rather a question to help guide us on our debugging of a failing test (reported in diana-hep/pyhf#302).

We rely on tensorflow probability to calculate some probability distributions and notice that our test fails in a very specific test case in v0.4.0 that did not fail in v0.3.0. The failure is here: https://travis-ci.org/diana-hep/pyhf/jobs/438404350#L674-L908.

In particular, we see a tensorflow.python.framework.errors_impl.InvalidArgumentError: Input is not invertible error.

We use:

  • tfp.distributions.Poisson
  • tfp.distributions.Normal

However, it seems that the error comes from tensorflow itself - but related to a change in TFP that we would like to understand. Thanks!

self = <tensorflow.python.client.session.Session object at 0x7f885b39c898>
fn = <function BaseSession._do_run.<locals>._run_fn at 0x7f8854e15510>
args = ({<tensorflow.python.pywrap_tensorflow_internal.TF_Output; proxy of <Swig Object of type 'TF_Output *' at 0x7f88549393...ywrap_tensorflow_internal.TF_Output; proxy of <Swig Object of type 'TF_Output *' at 0x7f88549392a0> >], [], None, None)
message = 'Input is not invertible.\n\t [[{{node MatrixInverse_3}} = MatrixInverse[T=DT_FLOAT, adjoint=false, _device="/job:localhost/replica:0/task:0/device:CPU:0"](Reshape_7)]]'
m = <_sre.SRE_Match object; span=(27, 55), match='[[{{node MatrixInverse_3}} ='>
    def _do_call(self, fn, *args):
      try:
        return fn(*args)
      except errors.OpError as e:
        message = compat.as_text(e.message)
        m = BaseSession._NODEDEF_NAME_RE.search(message)
        node_def = None
        op = None
        if m is not None:
          node_name = m.group(3)
          try:
            op = self._graph.get_operation_by_name(node_name)
            node_def = op.node_def
          except KeyError:
            pass
        if (self._config is not None and
            self._config.experimental.client_handles_error_formatting):
          message = error_interpolation.interpolate(message, self._graph)
>       raise type(e)(node_def, op, message)
E       tensorflow.python.framework.errors_impl.InvalidArgumentError: Input is not invertible.
E            [[{{node MatrixInverse_3}} = MatrixInverse[T=DT_FLOAT, adjoint=false, _device="/job:localhost/replica:0/task:0/device:CPU:0"](Reshape_7)]]
E       
E       Caused by op 'MatrixInverse_3', defined at:
E         File "/home/travis/virtualenv/python3.6.3/bin/pytest", line 11, in <module>
E           sys.exit(main())
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/_pytest/config/__init__.py", line 67, in main
E           return config.hook.pytest_cmdline_main(config=config)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/hooks.py", line 258, in __call__
E           return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/manager.py", line 67, in _hookexec
E           return self._inner_hookexec(hook, methods, kwargs)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/manager.py", line 61, in <lambda>
E           firstresult=hook.spec_opts.get('firstresult'),
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/callers.py", line 180, in _multicall
E           res = hook_impl.function(*args)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/_pytest/main.py", line 208, in pytest_cmdline_main
E           return wrap_session(config, _main)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/_pytest/main.py", line 178, in wrap_session
E           session.exitstatus = doit(config, session) or 0
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/_pytest/main.py", line 215, in _main
E           config.hook.pytest_runtestloop(session=session)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/hooks.py", line 258, in __call__
E           return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/manager.py", line 67, in _hookexec
E           return self._inner_hookexec(hook, methods, kwargs)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/manager.py", line 61, in <lambda>
E           firstresult=hook.spec_opts.get('firstresult'),
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/callers.py", line 180, in _multicall
E           res = hook_impl.function(*args)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/_pytest/main.py", line 236, in pytest_runtestloop
E           item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/hooks.py", line 258, in __call__
E           return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/manager.py", line 67, in _hookexec
E           return self._inner_hookexec(hook, methods, kwargs)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/manager.py", line 61, in <lambda>
E           firstresult=hook.spec_opts.get('firstresult'),
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/callers.py", line 180, in _multicall
E           res = hook_impl.function(*args)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/_pytest/runner.py", line 66, in pytest_runtest_protocol
E           runtestprotocol(item, nextitem=nextitem)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/_pytest/runner.py", line 81, in runtestprotocol
E           reports.append(call_and_report(item, "call", log))
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/_pytest/runner.py", line 161, in call_and_report
E           call = call_runtest_hook(item, when, **kwds)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/_pytest/runner.py", line 185, in call_runtest_hook
E           treat_keyboard_interrupt_as_exception=item.config.getvalue("usepdb"),
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/_pytest/runner.py", line 201, in __init__
E           self.result = func()
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/_pytest/runner.py", line 183, in <lambda>
E           lambda: ihook(item=item, **kwds),
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/hooks.py", line 258, in __call__
E           return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/manager.py", line 67, in _hookexec
E           return self._inner_hookexec(hook, methods, kwargs)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/manager.py", line 61, in <lambda>
E           firstresult=hook.spec_opts.get('firstresult'),
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/callers.py", line 180, in _multicall
E           res = hook_impl.function(*args)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/_pytest/runner.py", line 111, in pytest_runtest_call
E           item.runtest()
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/_pytest/python.py", line 1459, in runtest
E           self.ihook.pytest_pyfunc_call(pyfuncitem=self)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/hooks.py", line 258, in __call__
E           return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/manager.py", line 67, in _hookexec
E           return self._inner_hookexec(hook, methods, kwargs)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/manager.py", line 61, in <lambda>
E           firstresult=hook.spec_opts.get('firstresult'),
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/pluggy/callers.py", line 180, in _multicall
E           res = hook_impl.function(*args)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/_pytest/python.py", line 198, in pytest_pyfunc_call
E           testfunction(**testargs)
E         File "/home/travis/build/diana-hep/pyhf/tests/test_backend_consistency.py", line 113, in test_runOnePoint_q_mu
E           pdf.config.suggested_bounds())[0]
E         File "/home/travis/build/diana-hep/pyhf/pyhf/utils.py", line 163, in runOnePoint
E           qmu(muTest, asimov_data, pdf, init_pars, par_bounds), 0, max=None)
E         File "/home/travis/build/diana-hep/pyhf/pyhf/utils.py", line 79, in qmu
E           loglambdav, mu, data, pdf, init_pars, par_bounds)
E         File "/home/travis/build/diana-hep/pyhf/pyhf/optimize/opt_tflow.py", line 48, in constrained_bestfit
E           invhess   = tf.linalg.inv(hessian)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/tensorflow/python/ops/gen_linalg_ops.py", line 1041, in matrix_inverse
E           "MatrixInverse", input=input, adjoint=adjoint, name=name)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/tensorflow/python/framework/op_def_library.py", line 787, in _apply_op_helper
E           op_def=op_def)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/tensorflow/python/util/deprecation.py", line 488, in new_func
E           return func(*args, **kwargs)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/tensorflow/python/framework/ops.py", line 3272, in create_op
E           op_def=op_def)
E         File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/tensorflow/python/framework/ops.py", line 1768, in __init__
E           self._traceback = tf_stack.extract_stack()
E       
E       InvalidArgumentError (see above for traceback): Input is not invertible.
E            [[{{node MatrixInverse_3}} = MatrixInverse[T=DT_FLOAT, adjoint=false, _device="/job:localhost/replica:0/task:0/device:CPU:0"](Reshape_7)]]

Most helpful comment

Hi all! As of https://github.com/tensorflow/probability/commit/891b15144a3d94d587ab178259cf0c590b404cff
we've enabled a class-level flag interpolate_nondiscrete to expose the (obvious) continuous relaxation of Poisson densities. This may eventually make its way to other integer-valued distributions (Bernoulli, Geometric, etc). Anyone who wants other relaxations is welcome to define your own subclass. :)

All 14 comments

Can you say more about the TFP features you're using and how you think they relate to this error? I don't see TFP anywhere in your stack trace.

Can you say more about the TFP features you're using and how you think they relate to this error? I don't see TFP anywhere in your stack trace.

This is what's confusing us a little. Naively, looking at the stack trace, it appears tensorflow would be the issue. When we freeze the tensorflow version and only change tensorflow-probability to v0.4.0 from v0.3.0, the error shows up. I think we should only be using the probability distributions from tfp - but I guess I'm wondering if tfp is injecting some code into tensorflow in v0.4.0 that it didn't before in v0.3.0? [Additionally, if you think this should be reported to tensorflow instead, that would be fine].

From the stack trace, it looks like you're trying to invert a matrix that's not invertible (or at least, with small enough eigenvalues that the inversion runs into numerical problems). It looks like it's a Hessian matrix defined in constrained_bestfit (/home/travis/build/diana-hep/pyhf/pyhf/optimize/opt_tflow.py, line 48). Are you using TFP upstream to build this matrix (or perhaps to define the objective function of which this matrix is the Hessian)?

In general, TFP is a layer on top of TF and shouldn't modify TF in any way, so if the behavior is isolated to a TFP version change, you're almost certainly running TFP code somewhere.

I haven't proven this with a code example yet, but I think the issue that we (the pyhf dev team) are seeing is related to a change in the definition of the Poisson distribution _log_prob between release v0.3.0 and v0.4.0.

We had been relying on the continuous approximation of the Poisson p.m.f. that v0.3.0 uses, but it seems that v0.4.0 is really enforcing a true p.m.f.

$ pip install tensorflow_probability==0.3.0
>>> import tensorflow as tf
>>> import tensorflow_probability as tfp
>>> with tf.Session() as sess:
...     sess.run(tfp.distributions.Poisson(5.).log_prob(6.2))
... 
-1.978333
$ pip install --upgrade tensorflow_probability
>>> import tensorflow as tf
>>> import tensorflow_probability as tfp
>>> tf.__version__
'1.10.1'
>>> tfp.__version__
'0.4.0'
>>> with tf.Session() as sess:
...     sess.run(tfp.distributions.Poisson(5.).log_prob(6.2))
... 
-inf

Assuming that I haven't messed up, @davmre do you have any suggestions on how we can continue to use a continuous approximation beyond just calling

poisson = tfp.distributions.Poisson(lam)
return poisson._log_unnormalized_prob(n) - poisson._log_normalization()

for our TensorFlow backend?

I don't think we've committed to any convention for exposing continuous relaxations of discrete log_prob methods.

One possibility would be to add an class-level enforce_discrete_support property specified at initialization (I think this'd make more sense than an argument specific to log_prob, since log_prob often get called mechanistically by other APIs like TransformedDistribution or Edward2 that wouldn't have any way to pass through such a parameter). @jvdillon what do you think?

@mfigurnov

Hi. I'm pinging this Issue as I was just wondering if there was any additional thoughts on this from the tfp team on this subject.

Sorry for breaking your code. The relaxation of pmf/cdf was not conforming to the mathematical definition of pmf/cdf (and the scipy.stats convention) which caused some confusion to me, so I fixed it. I agree with @davmre that a constructor flag would be the best solution. enforce_discrete_support sounds vague, maybe we could call it relax_prob_and_cdf?

Sorry for breaking your code.

No worries. This is why we have unit tests :) If I may, I suggest a name like continous_approximation that allow values like None, normal, and gamma. At least since there's multiple ways to provide a continuous approximation.

there's multiple ways to provide a continuous approximation

That's a good point, actually. For example, for x ~ Bernoulli(p) we usually define the log-probability as x log p + (1 - x) log (1 - p), which leads to one relaxation. However, you could also define it as x^2 log p + (1 - x)^2 log (1 - p), which would result in the same values for x in {0, 1}, but a different relaxation. Furthermore, if you only plug in the samples from the distribution to log_prob and cdf, then you don't care about the relaxation. Maybe the right solution is to have a base class for each discrete distribution that does not relax log_prob and cdf, and then implement the relaxed versions as sub-classes?

I guess there will be some resistance to subclassing. For example, we're deprecating the FooWithSoftplusBar subclass distributions. How many of the inifinity possible relaxations are interesting?

Hi all! As of https://github.com/tensorflow/probability/commit/891b15144a3d94d587ab178259cf0c590b404cff
we've enabled a class-level flag interpolate_nondiscrete to expose the (obvious) continuous relaxation of Poisson densities. This may eventually make its way to other integer-valued distributions (Bernoulli, Geometric, etc). Anyone who wants other relaxations is welcome to define your own subclass. :)

Many thanks! This is excellent news and we're looking forward to seeing this in an upcoming release. :)

Thanks again Dave!

On Fri, Oct 19, 2018, 4:42 PM Matthew Feickert <[email protected]
wrote:

Many thanks! This is excellent news and we're looking forward to seeing
this in an upcoming release. :)

—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/tensorflow/probability/issues/182#issuecomment-431528190,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABABB8SZKVkVNeYB5UvjNR_69YfsvkSaks5ummN8gaJpZM4XTSd-
.

Was this page helpful?
0 / 5 - 0 ratings