Hi there,
I would like to combine a Linear Regression Model with Garch(1,1) noise, and I have seen that there is already a built-in pm.Garch11
distribution in PYMC. I started with a standard Linear Regression Model, and replaced the std parameter for the data with np.sqrt( (σ_t) ** 2 )
, where ‘σ_t’ is a random variable from pm.Garch11
.
Unfortunately, the sampling speed even for a very simple dummy model is extremely slow, and I was wondering if someone has advice on improving the model stated below. I assume it has something to do with my standard deviation formulation np.sqrt( (σ_t) ** 2 )
for the data, but I could not figure out what I did wrong here.
Data Generation:
import numpy as np, pandas as pd, matplotlib.pyplot as plt
import pymc as pm, arviz as az
np.random.seed(42)
n, phi, sigma = 500, 0.8, 1
X1, X2 = np.linspace(0, 10, n), np.linspace(5, 15, n)
Y_linear = 1.0 + 0.25 * X1 + 0.5 * X2
# Generate AR(1) noise
ar_noise = np.zeros(n)
ar_noise[0] = np.random.normal(0, sigma) # First element in AR(1) series
for i in range(1, n):
ar_noise[i] = phi * ar_noise[i - 1] + np.random.normal(0, sigma)
Y = Y_linear + ar_noise
df_target = pd.DataFrame(Y, columns = ['Target'])
df_features = pd.concat([pd.Series(X1, name="Feature1"), pd.Series(X2, name="Feature2")], axis=1)
coords = {"T_features": df_features.index, "N_features": df_features.columns, "N_target": df_target.columns}
Model Parameterization:
with pm.Model() as LinearRegressionGARCH:
# Data for features and target
feature_array = pm.Data("feature", df_features.values, mutable=True, dims=("T_features", "N_features"))
target_array = pm.Data("target", df_target.values.squeeze(), mutable=True, dims="T_features")
# Linear Regression Part
β0 = pm.Normal("β0", mu=0.0, sigma=10)
β = pm.Normal("β", mu= 0, sigma=10, dims="N_features")
mu_t = pm.Deterministic("mu_t", β0 + pm.math.dot(feature_array, β), dims="T_features")
# GARCH Parameter
# GARCH(1,1) Model for volatility
ω = pm.HalfNormal("ω", sigma=5.0) # Constant innovation variance
## these two's sum is bounded by 1
α = pm.Beta("α", alpha=1, beta=1) # ARCH parameter (constrained between 0 and 1)
β_garch = pm.Uniform('β_garch', 0, (1-α)) # GARCH parameter (constrained between 0 and 1)
initial_vol = pm.HalfNormal("initial_vol", sigma=5.0)
σ_t = pm.GARCH11("σ_t", omega=ω, alpha_1=α, beta_1=β_garch, initial_vol=initial_vol, dims="T_features")
## Evaluate Model
observed = pm.Normal("observed", mu=mu_t, sigma= np.sqrt( (σ_t) ** 2 ), observed=target_array, dims = "T_features")
Sampling:
draws = 1000
tune = 650
cores = 4
target_accept = 0.90
DEFAULT_RNG = np.random.default_rng(123)
with LinearRegressionGARCH:
trace = pm.sample(draws, tune=tune, cores=cores, target_accept = target_accept, random_seed = DEFAULT_RNG)
trace_predictive_oos = pm.sample_posterior_predictive(trace, predictions=True, var_names = ['observed'])