TypeError fitting Beta-binomial mixture density network

I am trying to fit a Mixture Density Network model using tensorflow, following the example posted in Taboola’s blog - Predicting Probability Distributions Using Neural Networks (acompaniying notebook here - https://github.com/taboola/mdn-tensorflow-notebook-example/blob/master/mdn-tf2.ipynb).

The data I am fitting are positive integers so I initially modified the blog example to use a Poisson distribution instead of a Normal from the blog example:

def poisson_mdn_cost(lambda_, y_):
    dist = tfp.distributions.Poisson(rate = lambda_)
    return tf.reduce_mean(-dist.log_prob(y_))

epochs = 500
batch_size = 50
learning_rate = 0.0003
InputLayer = Input(shape=(1,))
Layer_1 = Dense(12,activation="tanh")(InputLayer)
Layer_2 = Dense(12,activation="tanh")(Layer_1)
lambda_ = Dense(1, activation=lambda x: tf.nn.elu(x) + 1)(Layer_2)

Y = Input(shape=(1,))
lossF =  poisson_mdn_cost(lambda_,Y)
model2 = Model(inputs=[InputLayer, Y], outputs=[lambda_])
model2.add_loss(lossF)
model2.compile(optimizer = 'adam',
               metrics = ['mse'],
             )
history_cache2 = model2.fit([x_data, y_data], 
                            verbose=0, 
                            epochs=epochs,
                            batch_size=batch_size)
print('Final cost: {0:.4f}'.format(history_cache2.history['loss'][-1]))

The Poisson MDN fitted fine and I am now running an experiment with a Beta-binomial MDN using the following code:

def betabin_mdn_cost( a_,b_, y):
   dist = tfp.distributions.BetaBinomial(total_count =10,
                                         concentration0=a_,
                                         concentration1=b_) 
   return tf.reduce_mean(-dist.log_prob(y))

epochs = 500
batch_size = 50
learning_rate = 0.0003
InputLayer = Input(shape=(1,))
Layer_1 = Dense(12,activation="tanh")(InputLayer)
Layer_2 = Dense(12,activation="tanh")(Layer_1)
a = Dense(1, activation="relu")(Layer_2)
b = Dense(1, activation="relu")(Layer_2)

Y = Input(shape=(1,))
lossF =  betabin_mdn_cost(a,b,Y)
model3 = Model(inputs=[InputLayer, Y], outputs=[a,b])
model3.add_loss(lossF)
model3.compile(optimizer = 'adam',
               metrics = ['mse'],
             )
history_cache3 = model3.fit([x_data, y_data], 
                            verbose=0, 
                            epochs=epochs,
                            batch_size=batch_size)
print('Final cost: {0:.4f}'.format(history_cache3.history['loss'][-1]))

The data is truncated between 0 and 10, so fixed the Beta-binomial total_count parameter to 10. As far as I can see, the latter code has minimal changes compared with the Poisson above, essentially just swapping the Poisson cost function by the Beta-binomial and adding the additional parameter. However this time I can’t get it to run and get the error:

  TypeError: Cannot convert a symbolic Keras input/output to a numpy array. This error may indicate that you're trying to pass a symbolic value to a NumPy call, which is not supported. Or, you may be trying to pass Keras symbolic inputs/outputs to a TF API that does not register dispatching, preventing Keras from automatically converting the API call to a lambda layer in the Functional Model.

I an new to tf and have exhausted my ability to try to get this to work. I Was wondering if there is anybody out there with experience in fitting these types of models using tf, that might have some insights?

[tf.version.VERSION = 2.6.0]

/cc @Christopher_Suter

I’m afraid I’m not sure what the issue is here. Keras does some kind of tracing that I don’t understand, and it seems not to like one of the functions we’re using there. Pasting the full stack trace below. It seems like some crucial information is being lost at the point where the real error is detected. Maybe an exception is being caught and re-raised, losing some context? Maybe someone from Keras can weigh in? I don’t think the TFP code is doing anything unusual here.

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-17-c3244dc752a3> in <module>()
     18 
     19 Y = Input(shape=(1,))
---> 20 lossF =  betabin_mdn_cost(a,b,Y)
     21 model3 = tf.keras.Model(inputs=[InputLayer, Y], outputs=[a,b])
     22 model3.add_loss(lossF)

<ipython-input-17-c3244dc752a3> in betabin_mdn_cost(a_, b_, y, total_count)
      6                                          concentration0=a_,
      7                                          concentration1=b_) 
----> 8    return tf.reduce_mean(-dist.log_prob(y))
      9 
     10 epochs = 500

google3/third_party/py/tensorflow_probability/python/distributions/distribution.py in log_prob(self, value, name, **kwargs)
   1282         values of type `self.dtype`.
   1283     """
-> 1284     return self._call_log_prob(value, name, **kwargs)
   1285 
   1286   def _call_prob(self, value, name, **kwargs):

google3/third_party/py/tensorflow_probability/python/distributions/distribution.py in _call_log_prob(self, value, name, **kwargs)
   1264     with self._name_and_control_scope(name, value, kwargs):
   1265       if hasattr(self, '_log_prob'):
-> 1266         return self._log_prob(value, **kwargs)
   1267       if hasattr(self, '_prob'):
   1268         return tf.math.log(self._prob(value, **kwargs))

google3/third_party/py/tensorflow_probability/python/internal/distribution_util.py in _fn(*args, **kwargs)
   1364     @functools.wraps(fn)
   1365     def _fn(*args, **kwargs):
-> 1366       return fn(*args, **kwargs)
   1367 
   1368     if _fn.__doc__ is None:

google3/third_party/py/tensorflow_probability/python/distributions/beta_binomial.py in _log_prob(self, counts)
    271     return (_log_combinations(n, counts)
    272             + tfp_math.lbeta(c1 + counts, (n - counts) + c0)
--> 273             - tfp_math.lbeta(c1, c0))
    274 
    275   @distribution_util.AppendDocstring(_beta_binomial_sample_note)

google3/third_party/py/tensorflow_probability/python/distributions/beta_binomial.py in _log_combinations(n, k)
    329 def _log_combinations(n, k):
    330   """Computes log(Gamma(n+1) / (Gamma(k+1) * Gamma(n-k+1))."""
--> 331   return -tfp_math.lbeta(k + 1, n - k + 1) - tf.math.log(n + 1)

google3/third_party/tensorflow/python/util/traceback_utils.py in error_handler(*args, **kwargs)
    151     except Exception as e:
    152       filtered_tb = _process_traceback_frames(e.__traceback__)
--> 153       raise e.with_traceback(filtered_tb) from None
    154     finally:
    155       del filtered_tb

google3/third_party/py/keras/engine/keras_tensor.py in __array__(self)
    243   def __array__(self):
    244     raise TypeError(
--> 245         'Cannot convert a symbolic Keras input/output to a numpy array. '
    246         'This error may indicate that you\'re trying to pass a symbolic value '
    247         'to a NumPy call, which is not supported. Or, '

TypeError: Cannot convert a symbolic Keras input/output to a numpy array. This error may indicate that you're trying to pass a symbolic value to a NumPy call, which is not supported. Or, you may be trying to pass Keras symbolic inputs/outputs to a TF API that does not register dispatching, preventing Keras from automatically converting the API call to a lambda layer in the Functional Model.

Looking at the stacktrace @Christopher_Suter shared: I think some traceback filtering got added to TF recently that is removing some callframes that would maybe also be useful here. Taking a look though, it looks like that tfp_math.lbeta method in the stacktrace actually calls a tf.function:

It is not supported to call tf.function directly on symbolic intermediate values during Functional model construction (hence the KerasTensors error message). What you can do is put the tf.function call / offending piece of code in a Keras lambda layer or a custom Keras layer’s call method. (In this case that would be putting betabin_mdn_cost(a, b, Y) in the call method of a custom layer that takes three inputs)

We’ll update this error message to mention that as the workaround whenever it gets raised.

1 Like

Hmm, I’m not sure why we (TFP) would have put tf.function here. I’ll raise this on our end.

1 Like