I am a newbie in pyro, so please bear with me in case I miss anything obvious.
I have a generative model for a group of time series that is basically a single latent Gaussian Process summed with a number of piecewise linear functions equal to the number of time series. I want to know if this is possible to implement in pyro and if I should expect performance gains.
Here you can find an example of the generative process and plot the series:
import pymc3 as pm
import numpy as np
import matplotlib.pyplot as plt
import theano
import theano.tensor as tt
class OutPiecewiseLinearChangepoints():
# Piecewise linear function applied outside of the GPs and added in the likelihood parameter
def __init__(self,
k,
m,
b,
changepoints,
n_series = 4):
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
piece = OutPiecewiseLinearChangepoints(k=np.array([0.1]),
m=np.array([0.1]),
b=np.random.normal(0.1, 0.4, size=(4,4)),
changepoints = changepoints_t,
).build(np.arange(10).reshape((-1,1))).eval()
lengthscale = 0.2
eta = 2.0
cov = eta ** 2 * pm.gp.cov.ExpQuad(1, lengthscale)
X = np.arange(10)[:, None]
K = cov(X).eval()
gpl = pm.MvNormal.dist(mu=np.zeros(K.shape[0]), cov=K).random(size=10).T
x_train = np.random.normal(pm.MvNormal.dist(mu=np.zeros(K.shape[0]), cov=K).random(size=4).T + piece, 0.1)
plt.figure(figsize=(14, 4))
plt.plot(X, x_train)
plt.title("time series generated")
plt.ylabel("y")
plt.xlabel("X");
With pymc3 I can build the model as:
with pm.Model() as model:
period = pm.Gamma('period', 40, 10)
l_t = pm.InverseGamma('l_t', 4, 40)
l_p = pm.HalfNormal('l_p', 0.5)
η_trend = pm.HalfNormal('η_trend',1)
σ = pm.HalfNormal("σ", sigma=0.1)
mu_func = pm.gp.mean.Zero()
# cov function for the GP
cov = (η_trend**2 * pm.gp.cov.ExpQuad(input_dim=1, ls=l_t)
+ pm.gp.cov.WhiteNoise(σ))
gp = pm.gp.Latent(mean_func=mu_func, cov_func=cov)
f = gp.prior('f', X=X, reparameterize=True)
k = pm.Normal("k", mu=0, sigma=0.01, shape = s)
m = pm.Normal("m", mu=0, sigma=0.01, shape = s)
delta = pm.Normal("delta", mu=0, sigma=0.01, shape=(len(changepoints_t),s))
piece = OutPiecewiseLinearChangepoints(k = k,
m = m,
b = delta,
changepoints = changepoints_t).build(X)
# Likelihood
y_pred = pm.Normal('y_pred', mu=f.reshape((-1,1)) + piece, sigma = σ,observed=x_train)
# Fitting using ADVI
trace_vi = pm.fit(100000,
method='advi',
obj_n_mc=1,
obj_optimizer=pm.adamax(),
callbacks=[pm.callbacks.CheckParametersConvergence(every=50, diff='absolute',tolerance=1e-3)],
progressbar = True)
# Sampling
trace_vi_samples = trace_vi.sample()
pred_samples_fit = pm.sample_posterior_predictive(trace_vi_samples,
vars=[y_pred],
samples=n_samples,
progressbar = True)
# Conditional of the GP on new points
f_n = gp.conditional('f_n', Xnew=X_new)
piece = OutPiecewiseLinearChangepoints(k = k,
m = m,
b = delta,
changepoints = changepoints_t).build(X_new)
y_pred_new = pm.Normal("y_pred_new",
mu=f_n.reshape((-1,1)) + piece,
shape=(X_new.shape[0], s))
# Sampling of predictions
pred_samples_predict = pm.sample_posterior_predictive(trace_vi_samples,
vars=[y_pred_new],
samples=n_samples,
progressbar = True)
Once again, Is this possible to implement in pyro? Should I expect performance gains with such a model in pyro?
Thanks a lot!
luis