ValueError: Not enough samples to build a trace

I keep getting an error on a small test case. See attached file for dataset df3.csv (301 Bytes). What’s going on?

Relevant packages:

python                    3.8.6           h60c2a47_0_cpython    conda-forge
pymc3                     3.9.3                      py_1    conda-forge
theano                    1.0.5            py38h7ae7562_0    conda-forge

Source code:

import numpy as np
import pandas as pd
import pymc3 as pm
import theano
import theano.tensor as tt


def fit(df: pd.DataFrame, time_column: str = "time", event_column: str = "event", vague_prior_sd: float = 20.0):
    # Extract censoring and predictor data
    y = df[time_column].values
    X_censor = df[event_column].values
    X_ones = np.ones_like(X_censor).reshape((len(X_censor), 1))
    X_predictors = df[df.columns[~df.columns.isin((time_column, event_column))]]
    if X_predictors.empty:
        X_predictors = X_ones
    else:
        X_predictors = np.concatenate((X_ones, X_predictors.values), axis=1)
    X_predictors_ = theano.shared(X_predictors)

    cens = X_censor == 0
    cens_ = theano.shared(cens)

    with pm.Model() as bayesian_model:
        # Defining Log complementary cdf of Weibull distribution
        def weibull_lccdf(x, beta, eta):
            return -(x / eta) ** beta

        # Priors for unknown model parameters
        beta = pm.Gamma("beta", mu=3, sigma=vague_prior_sd)  # alpha=0.001, beta=0.001)
        eta = pm.Normal("eta", 0, vague_prior_sd, shape=X_predictors.shape[1])
        reg = X_predictors_.dot(eta)

        # Expected value of lambda parameter
        lambda_obs = pm.Deterministic("lambda_obs", tt.exp(reg[~cens_]))
        lambda_cens = pm.Deterministic("lambda_cens", tt.exp(reg[cens_]))

        # Likelihood (sampling distribution) of observations
        y_obs = pm.Weibull("y_obs", alpha=beta, beta=lambda_obs, observed=y[~cens])
        y_cens = pm.Potential("y_cens", weibull_lccdf(y[cens], beta, lambda_cens))

    draws = 2000
    warmup_ratio = 1.0
    tune = int(warmup_ratio * draws)
    with bayesian_model:
        trace = pm.sample(target_accept=0.9, draws=draws, tune=tune, random_seed=123, return_inferencedata=True)


def main():
    df = pd.read_csv("df3.csv")
    fit(df, time_column="T", event_column="c")


if __name__ == "__main__":
    main()

Error message:

Auto-assigning NUTS sampler...
Initializing NUTS using jitter+adapt_diag...
Multiprocess sampling (4 chains in 4 jobs)
NUTS: [eta, beta]
Traceback (most recent call last):
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\pymc3\sampling.py", line 1486, in _mp_sample
    for draw in sampler:
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\pymc3\parallel_sampling.py", line 492, in __iter__
    draw = ProcessAdapter.recv_draw(self._active)
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\pymc3\parallel_sampling.py", line 352, in recv_draw
    ready = multiprocessing.connection.wait(pipes)
  File "C:\Users\abreucbr\Miniconda3\lib\multiprocessing\connection.py", line 849, in wait
    fileno = getattr(o, 'fileno')
KeyboardInterrupt

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:/Users/abreucbr/Projects/Scratch/WeibullAFTBayesianFitter.py", line 294, in <module>
    main()
  File "C:/Users/abreucbr/Projects/Scratch/WeibullAFTBayesianFitter.py", line 290, in main
    fit(df, time_column="T", event_column="c")
  File "C:/Users/abreucbr/Projects/Scratch/WeibullAFTBayesianFitter.py", line 285, in fit
    trace = pm.sample(target_accept=0.9, draws=draws, tune=tune, random_seed=123, return_inferencedata=True)
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\pymc3\sampling.py", line 545, in sample
    trace = _mp_sample(**sample_args, **parallel_args)
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\pymc3\sampling.py", line 1512, in _mp_sample
    traces, length = _choose_chains(traces, tune)
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\pymc3\sampling.py", line 1530, in _choose_chains
    raise ValueError("Not enough samples to build a trace.")
ValueError: Not enough samples to build a trace.

Hi Bruno,
It seems like your interrupting the sampling with your keyboard, and it must be at the very beginning of the sampling, which means there isn’t enough samples to get an accurate estimation of the posterior, as indicated by the error message.
You shouldn’t have this error if you let sampling go uninterrupted – 2000 draws times 4 chains is clearly enough.
Hope this helps :vulcan_salute:

But of course I’m not doing anything with my keyboard! :stuck_out_tongue: I did see that part of the error message and was confused by it. The error occurs regardless if I execute the script in Command Prompt or PyCharm. I simply run it and don’t touch any keyboard or mouse until the error comes up.

Ha ha of course, otherwise it wouldn’t be fun :sweat_smile:
I’m wondering if it’s not because of the context manager: try to return bayesian_model in fit, and then:

def main():
    df = pd.read_csv("df3.csv")
    bayesian_model = fit(df, time_column="T", event_column="c")
    with bayesian_model:
        trace = pm.sample(target_accept=0.9, draws=draws, tune=tune, random_seed=123, return_inferencedata=True)
   return trace

If this still doesn’t work, try returning the model from fit and then from main and putting the context manager and sample under the if __name__ == "__main__" clause.

You can also try running the model raw, without the custom python functions to test that it does sample and that the problem comes from the function definitions themselves.
Hope this helps :vulcan_salute:

None of that worked. And I actually wouldn’t be happy if it did, because the original code is inside a class. The code I pasted was just to simplify things as a minimal working example. So (I hope) it shouldn’t matter where the pm.sample is called from.

This is very weird. I have another model (parameterization) that works just fine in a very similar code structure.

Yeah, that’s weird to me too, it’s the first time I this. Does the model sample when you get rid of all the functional programming?

No. Even if I put all the code in if __name__ == "__main__", I still get the same error. Is there any debugging flag I could enable to help figure out what’s happening?

To be sure, you did:

    with pm.Model() as bayesian_model:
        # Defining Log complementary cdf of Weibull distribution
        def weibull_lccdf(x, beta, eta):
            return -(x / eta) ** beta

        # Priors for unknown model parameters
        beta = pm.Gamma("beta", mu=3, sigma=vague_prior_sd)  # alpha=0.001, beta=0.001)
        eta = pm.Normal("eta", 0, vague_prior_sd, shape=X_predictors.shape[1])
        reg = X_predictors_.dot(eta)

        # Expected value of lambda parameter
        lambda_obs = pm.Deterministic("lambda_obs", tt.exp(reg[~cens_]))
        lambda_cens = pm.Deterministic("lambda_cens", tt.exp(reg[cens_]))

        # Likelihood (sampling distribution) of observations
        y_obs = pm.Weibull("y_obs", alpha=beta, beta=lambda_obs, observed=y[~cens])
        y_cens = pm.Potential("y_cens", weibull_lccdf(y[cens], beta, lambda_cens))

        trace = pm.sample(target_accept=0.9, draws=draws, tune=tune, random_seed=123, return_inferencedata=True)

and it still didn’t work?

Correct. All of that inside if __name__ == "__main__". No auxiliary functions.

And when outside the __main__ clause? Like in a plain Jupyter notebook cell?

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Users\abreucbr\Miniconda3\lib\multiprocessing\spawn.py", line 116, in spawn_main
    exitcode = _main(fd, parent_sentinel)
  File "C:\Users\abreucbr\Miniconda3\lib\multiprocessing\spawn.py", line 125, in _main
    prepare(preparation_data)
  File "C:\Users\abreucbr\Miniconda3\lib\multiprocessing\spawn.py", line 236, in prepare
    _fixup_main_from_path(data['init_main_from_path'])
  File "C:\Users\abreucbr\Miniconda3\lib\multiprocessing\spawn.py", line 287, in _fixup_main_from_path
    main_content = runpy.run_path(main_path,
  File "C:\Users\abreucbr\Miniconda3\lib\runpy.py", line 265, in run_path
    return _run_module_code(code, init_globals, run_name,
  File "C:\Users\abreucbr\Miniconda3\lib\runpy.py", line 97, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "C:\Users\abreucbr\Miniconda3\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "C:\Users\abreucbr\Projects\Scratch\WeibullAFTBayesianFitter.py", line 338, in <module>
    trace = pm.sample(target_accept=0.9, init="adapt_diag", draws=draws, tune=tune, random_seed=123,
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\pymc3\sampling.py", line 545, in sample
    trace = _mp_sample(**sample_args, **parallel_args)
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\pymc3\sampling.py", line 1470, in _mp_sample
    sampler = ps.ParallelSampler(
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\pymc3\parallel_sampling.py", line 442, in __init__
    self._samplers = [
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\pymc3\parallel_sampling.py", line 443, in <listcomp>
    ProcessAdapter(
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\pymc3\parallel_sampling.py", line 294, in __init__
    self._process.start()
  File "C:\Users\abreucbr\Miniconda3\lib\multiprocessing\process.py", line 121, in start
    self._popen = self._Popen(self)
  File "C:\Users\abreucbr\Miniconda3\lib\multiprocessing\context.py", line 327, in _Popen
    return Popen(process_obj)
  File "C:\Users\abreucbr\Miniconda3\lib\multiprocessing\popen_spawn_win32.py", line 45, in __init__
    prep_data = spawn.get_preparation_data(process_obj._name)
  File "C:\Users\abreucbr\Miniconda3\lib\multiprocessing\spawn.py", line 154, in get_preparation_data
    _check_not_importing_main()
  File "C:\Users\abreucbr\Miniconda3\lib\multiprocessing\spawn.py", line 134, in _check_not_importing_main
    raise RuntimeError('''
RuntimeError: 
        An attempt has been made to start a new process before the
        current process has finished its bootstrapping phase.

        This probably means that you are not using fork to start your
        child processes and you have forgotten to use the proper idiom
        in the main module:

            if __name__ == '__main__':
                freeze_support()
                ...

        The "freeze_support()" line can be omitted if the program
        is not going to be frozen to produce an executable.

Aha, different error and one that makes more sense! Are you on Windows? If yes, try pm.sample(cores=1, ...)

Yes for Windows. After setting cores=1 (which I’d hope I wouldn’t have to when placing the code in a class and using the __main__ clause), I get:

Traceback (most recent call last):
  File "C:/Users/abreucbr/Projects/Scratch/WeibullAFTBayesianFitter.py", line 338, in <module>
    trace = pm.sample(target_accept=0.9, cores=1, init="adapt_diag", draws=draws, tune=tune, random_seed=123,
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\pymc3\sampling.py", line 586, in sample
    trace = _sample_many(**sample_args)
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\pymc3\sampling.py", line 700, in _sample_many
    trace = _sample(
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\pymc3\sampling.py", line 846, in _sample
    for it, (strace, diverging) in enumerate(sampling):
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\fastprogress\fastprogress.py", line 47, in __iter__
    raise e
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\fastprogress\fastprogress.py", line 41, in __iter__
    for i,o in enumerate(self.gen):
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\pymc3\sampling.py", line 1003, in _iter_sample
    point, stats = step.step(point)
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\pymc3\step_methods\arraystep.py", line 263, in step
    apoint, stats = self.astep(array)
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\pymc3\step_methods\hmc\base_hmc.py", line 154, in astep
    self.potential.raise_ok(self._logp_dlogp_func._ordering.vmap)
  File "C:\Users\abreucbr\Miniconda3\lib\site-packages\pymc3\step_methods\hmc\quadpotential.py", line 263, in raise_ok
    raise ValueError('\n'.join(errmsg))
ValueError: Mass matrix contains zeros on the diagonal. 
The derivative of RV `eta`.ravel()[0] is zero.
The derivative of RV `eta`.ravel()[1] is zero.
The derivative of RV `eta`.ravel()[2] is zero.

Ok, that’s good actually: this error means you’re sampling now :tada:
I’m out of my league regarding the Windows and __main__ clause here though, so pinging @aseyboldt as he dug a lot about these issues :wave: (see Bruno’s penultimate message Adrian, and then the first error with a weird KeyboardInterrupt).

The ValueError: Mass matrix contains zeros on the diagonal is very probably because your prior is too flat for eta (so flat that there is no derivative). I think you’re taking an std of 20 and then exponentiating it, so this is really huge. Here are good recommendations for prior choices. hope this helps :vulcan_salute:

Thanks! But even setting vague_prior_sd to 1.0, I get the same error.

OK, the Gamma distribution prior with alpha=0.001, beta=0.001 avoided that error.

Usually, doing prior predictive checks is really useful in these cases, to see what your priors mean on the outcome space. You can see this notebook for an example, and McElreath’s Statistical Rethinking for more :vulcan_salute:

1 Like