Hi @bwengals, just a quick update. Iâve implemented the MarginalSparse
and it works fine. But if I try to increase the dummy data size (e.g. n=100, s=20, n\_gps=10), it just hangs forever with one core of the CPU at 100%. I believe that the model would take a reasonable time to run (MAP is pretty fast) but it wonât even start. The full code is below. What could be the cause?
Also, I am not able to run it with the latest PyMC3 version (3.11.2 from conda and python=3.9.7), are you? Actually, I have to go back as far as pymc3=3.10.0. Is this a bug or am I missing something?
Full traceback with latest pymc3 version
You can find the C code in this temporary file: /tmp/theano_compilation_error_k5ixfvsi
Traceback (most recent call last):
File "/home/mach1ne/WIP/multi_observed_GPs_with_mixture_in_cov.py", line 80, in <module>
piece = PiecewiseLinearMatrix(k=np.array([0.2]),
File "/home/mach1ne/anaconda3/envs/pymc3_test/lib/python3.9/site-packages/theano/graph/basic.py", line 554, in eval
self._fn_cache[inputs] = theano.function(inputs, self)
File "/home/mach1ne/anaconda3/envs/pymc3_test/lib/python3.9/site-packages/theano/compile/function/__init__.py", line 337, in function
fn = pfunc(
File "/home/mach1ne/anaconda3/envs/pymc3_test/lib/python3.9/site-packages/theano/compile/function/pfunc.py", line 524, in pfunc
return orig_function(
File "/home/mach1ne/anaconda3/envs/pymc3_test/lib/python3.9/site-packages/theano/compile/function/types.py", line 1981, in orig_function
fn = m.create(defaults)
File "/home/mach1ne/anaconda3/envs/pymc3_test/lib/python3.9/site-packages/theano/compile/function/types.py", line 1836, in create
_fn, _i, _o = self.linker.make_thunk(
File "/home/mach1ne/anaconda3/envs/pymc3_test/lib/python3.9/site-packages/theano/link/basic.py", line 266, in make_thunk
return self.make_all(
File "/home/mach1ne/anaconda3/envs/pymc3_test/lib/python3.9/site-packages/theano/link/vm.py", line 1131, in make_all
node.op.make_thunk(node, storage_map, compute_map, [], impl=impl)
File "/home/mach1ne/anaconda3/envs/pymc3_test/lib/python3.9/site-packages/theano/graph/op.py", line 634, in make_thunk
return self.make_c_thunk(node, storage_map, compute_map, no_recycling)
File "/home/mach1ne/anaconda3/envs/pymc3_test/lib/python3.9/site-packages/theano/graph/op.py", line 600, in make_c_thunk
outputs = cl.make_thunk(
File "/home/mach1ne/anaconda3/envs/pymc3_test/lib/python3.9/site-packages/theano/link/c/basic.py", line 1203, in make_thunk
cthunk, module, in_storage, out_storage, error_storage = self.__compile__(
File "/home/mach1ne/anaconda3/envs/pymc3_test/lib/python3.9/site-packages/theano/link/c/basic.py", line 1138, in __compile__
thunk, module = self.cthunk_factory(
File "/home/mach1ne/anaconda3/envs/pymc3_test/lib/python3.9/site-packages/theano/link/c/basic.py", line 1634, in cthunk_factory
module = get_module_cache().module_from_key(key=key, lnk=self)
File "/home/mach1ne/anaconda3/envs/pymc3_test/lib/python3.9/site-packages/theano/link/c/cmodule.py", line 1191, in module_from_key
module = lnk.compile_cmodule(location)
File "/home/mach1ne/anaconda3/envs/pymc3_test/lib/python3.9/site-packages/theano/link/c/basic.py", line 1543, in compile_cmodule
module = c_compiler.compile_str(
File "/home/mach1ne/anaconda3/envs/pymc3_test/lib/python3.9/site-packages/theano/link/c/cmodule.py", line 2546, in compile_str
raise Exception(
Exception: ('The following error happened while compiling the node', Elemwise{Composite{((i0 + i1) * i2)}}[(0, 1)](TensorConstant{(1, 1) of 0.2}, Dot22.0, Reshape{2}.0), '\n', 'Compilation failed (return status=1): /home/mach1ne/.theano/compiledir_Linux-5.4--generic-x86_64-with-glibc2.31-x86_64-3.9.7-64/tmpkmb9d2k0/mod.cpp: In member function âint {anonymous}::__struct_compiled_op_m0e832eaf80de42bf9790ad948edc032faf7da093be86d8ce155fb3c56a0d3a2d::run()â:. /home/mach1ne/.theano/compiledir_Linux-5.4--generic-x86_64-with-glibc2.31-x86_64-3.9.7-64/tmpkmb9d2k0/mod.cpp:589:60: error: expected primary-expression before â{â token. 589 | #pragma omp parallel for if(n>={int(config.openmp_elemwise_minsize)}). | ^. /home/mach1ne/.theano/compiledir_Linux-5.4--generic-x86_64-with-glibc2.31-x86_64-3.9.7-64/tmpkmb9d2k0/mod.cpp:627:27: warning: narrowing conversion of âV5_n0â from ânpy_intpâ {aka âlong intâ} to âintâ [-Wnarrowing]. 627 | int init_totals[2] = {V5_n0, V5_n1};. | ^~~~~. /home/mach1ne/.theano/compiledir_Linux-5.4--generic-x86_64-with-glibc2.31-x86_64-3.9.7-64/tmpkmb9d2k0/mod.cpp:627:34: warning: narrowing conversion of âV5_n1â from ânpy_intpâ {aka âlong intâ} to âintâ [-Wnarrowing]. 627 | int init_totals[2] = {V5_n0, V5_n1};. | ^~~~~. /home/mach1ne/.theano/compiledir_Linux-5.4--generic-x86_64-with-glibc2.31-x86_64-3.9.7-64/tmpkmb9d2k0/mod.cpp:640:1: warning: narrowing conversion of âV5_stride0â from âssize_tâ {aka âlong intâ} to âintâ [-Wnarrowing]. 640 | V5_stride0, V5_stride1,. | ^~~~~~~~~~. /home/mach1ne/.theano/compiledir_Linux-5.4--generic-x86_64-with-glibc2.31-x86_64-3.9.7-64/tmpkmb9d2k0/mod.cpp:640:13: warning: narrowing conversion of âV5_stride1â from âssize_tâ {aka âlong intâ} to âintâ [-Wnarrowing]. 640 | V5_stride0, V5_stride1,. | ^~~~~~~~~~. /home/mach1ne/.theano/compiledir_Linux-5.4--generic-x86_64-with-glibc2.31-x86_64-3.9.7-64/tmpkmb9d2k0/mod.cpp:641:1: warning: narrowing conversion of âV3_stride0â from âssize_tâ {aka âlong intâ} to âintâ [-Wnarrowing]. 641 | V3_stride0, V3_stride1. | ^~~~~~~~~~. /home/mach1ne/.theano/compiledir_Linux-5.4--generic-x86_64-with-glibc2.31-x86_64-3.9.7-64/tmpkmb9d2k0/mod.cpp:641:13: warning: narrowing conversion of âV3_stride1â from âssize_tâ {aka âlong intâ} to âintâ [-Wnarrowing]. 641 | V3_stride0, V3_stride1. | ^~~~~~~~~~. At global scope:. cc1plus: warning: unrecognized command line option â-Wno-c++11-narrowingâ. ', 'FunctionGraph(Elemwise{Composite{((i0 + i1) * i2)}}[(0, 1)](TensorConstant{(1, 1) of 0.2}, <TensorType(float64, matrix)>, <TensorType(int64, matrix)>))')
Full code with Marginal Sparse:
import numpy as np
import pymc3 as pm
import matplotlib.pyplot as plt
from pymc3.gp.util import plot_gp_dist
import theano
import theano.tensor as tt
from scipy import stats
import pickle
import warnings
warnings.filterwarnings("ignore")
class PiecewiseLinearMatrix():
def __init__(self,
k,
m,
b,
changepoints,
n_series):
self.k = k
self.m = m
self.b = b
self.changepoints = changepoints
self.n_series = n_series
def create_changepoints(self, X, changepoints):
return (0.5 * (1.0 + tt.sgn(tt.tile(X.reshape((-1,1)), (1,len(changepoints))) - changepoints)))
def build(self, X):
X = theano.shared(X)
A = self.create_changepoints(X, self.changepoints)
growth = (self.k.reshape((1, -1)) + tt.dot(A, self.b))*tt.tile(X, (1, self.n_series))
offset = (self.m.reshape((1,-1)) + tt.dot(A, (-self.changepoints.reshape((-1,1)) * self.b)))
piecewise = growth + offset
return piecewise
class PiecewiseLinear(pm.gp.mean.Mean):
def __init__(self, changepoints, k, m, delta):
self.changepoints = changepoints
self.k = k
self.m = m
self.delta = delta
def dm_changepoints_theano(self, X, changepoints_t):
return 0.5 * (1.0 + tt.sgn(tt.tile(X, (1, len(changepoints_t))) - changepoints_t))
def __call__(self, X):
A = self.dm_changepoints_theano(X, self.changepoints)
return (self.k + tt.dot(A, self.delta)) * X.flatten() + (
self.m + tt.dot(A, -self.changepoints * self.delta)
)
## generate the data
# number of time series
n = 100
# length of each series
s = 20
# number of "basis" GPs
n_gps = 10
# domain
x = np.arange(s)
# noise standard deviation
sigma = 0.1
# each gp has a different covariance matrix
eta = stats.halfnorm.rvs(2, size=n_gps)
ell = stats.halfnorm.rvs(2, size=n_gps)
covs = []
piece = PiecewiseLinearMatrix(k=np.array([0.2]),
m=np.array([0.2]),
b=np.random.normal(0.1, 0.3, size=(4,n)),
changepoints = np.linspace(0, s, 4),
n_series = n
).build(x[:, None]).eval()
for i in range(n_gps):
covs.append(eta[i]**2 * pm.gp.cov.ExpQuad(1, ls=ell[i]))
# construct the four "true" gps
gps = []
for i in range(n_gps):
gps.append(np.random.multivariate_normal(np.zeros(s), covs[i](x[:,None]).eval(), size=1).flatten())
# construct y_set, the set of n time series, each one a mixture of the four gps
y_set = []
f_set = []
known_mixtures = [] # Question, are the mixture weights known? Answer: No
for i in range(n):
mixture_weights = np.random.randint(0, 2, size=n_gps)
# make sure its not all zeros...
if sum(mixture_weights) == 0:
ix = np.random.randint(3) # 0, 1 or 2
mixture_weights[ix] = 1
known_mixtures.append(mixture_weights)
# define mixture
f = np.zeros(s)
for i, w in enumerate(mixture_weights):
f += gps[i] * w
noise = sigma * np.random.randn(s)
y = f + noise + piece[:,i]
f_set.append(f)
y_set.append(y)
# With MarginalSparse
s_u = 10
Xu_init = np.linspace(0, 50, s_u)
## set up the pymc model
with pm.Model() as model:
k = pm.Normal("k", mu=0, sigma=1, shape = n)
m = pm.Normal("m", mu=0, sigma=1, shape = n)
delta = pm.Normal("delta", mu=0, sigma=2, shape=(4,n))
covs = []
for i in range(1, n_gps + 1):
eta = pm.HalfNormal(f'eta_{i}', sd=5)
ell = pm.Gamma(f'ell_{i}', alpha=2, beta=1)
cov = eta**2 * pm.gp.cov.ExpQuad(input_dim=1, ls=ell)
covs.append(cov)
# apply mixtures to covariances
selected_covs = []
mixed_covs = []
for i in range(n):
mixture_weights = known_mixtures[i]
for w_ix in range(len(mixture_weights)):
w = mixture_weights[w_ix]
if w == 1.0:
selected_covs.append(covs[w_ix])
mixed_cov = sum(selected_covs) # because GP(cov1 + cov2) = GP(cov1) + GP(cov2)
mixed_covs.append(mixed_cov)
selected_covs = [] # clear out cov list
# set flat prior for Xu
Xu = pm.Flat("Xu", shape=s_u, testval=Xu_init)
gps = []
for i in range(n):
piece = PiecewiseLinear(k = k[i],
m = m[i],
delta = delta[:,i],
changepoints = np.linspace(0, s, 4))
gp = pm.gp.MarginalSparse(mean_func = piece,
cov_func=mixed_covs[i],
approx="VFE")
gps.append(gp)
noise = pm.HalfNormal('noise', sd=2)
for i in range(n):
lik = gps[i].marginal_likelihood(f"lik_{i}", X=x[:, None], Xu=Xu[:, None], y=y_set[i], noise=noise)
mp = pm.find_MAP()
# predict length of each series
s_new = 60
x_new = np.arange(s_new)[:,None]
f_new = []
with model:
for i in range(n):
f_new.append(gps[i].conditional(f"f_new_{i}", Xnew=x_new, pred_noise=True))
with model:
y_pred = pm.sample_posterior_predictive([mp],
vars=[*f_new],
samples=500)
with open(f'results_gp_cov_{n_gps}.pickle', 'wb') as handle:
pickle.dump(y_pred, handle, pickle.HIGHEST_PROTOCOL)
Thanks,
Luis