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.Poissontfp.distributions.NormalHowever, 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)]]
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()
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-
.
Most helpful comment
Hi all! As of https://github.com/tensorflow/probability/commit/891b15144a3d94d587ab178259cf0c590b404cff
we've enabled a class-level flag
interpolate_nondiscreteto 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. :)