I am looking to get some advice on fitting a large(ish) gaussian process regression. The problem involves fitting a GP at ~6000 locations, with two predictors.
From some early trials it is clear that to fit a GP to these data in Stan I will need to make some efficiency gains. Options I have been looking at include...
The second of these approaches seems like it could work, as a separable covariance function should not be problematic (as far as I can tell), and I can organize my data to lie on a grid of 100 X 100. I have been working through the appendix of Seth's paper, but the model takes a long time to evaluate, and longer than the speed results shown in his paper.
I am copying my code below (for transparency, and in case I have made some obvious mistake), but my first question relates to the missingness pattern. While there is 10 to 20% missing in the data, extending the surface to create a grid produces a large area with no observations (see attached plot). Is it problematic that there are no data to fit the GP in this region? I have no interest in making inference for this part of the surface, but I am wondering if this is causing problems with the sampler.
My more general question is how people proceed with this kind of problem (ie GPs that are too big to fit with the methods described the Stan manual) -- I have seen discussions on this list suggesting that it may be nice but not necessary to estimate the covariance function parameters simultaneously, and instead used fixed values for these. My overall analysis has similarities to the 'birthday problem' that has featured on Andrew's blog a number of times, in that there is a lot of structure in the data to deal with and the GP described is just step one. For that analysis they used GPstuff -- is that the way to go when the problem becomes sufficiently large? Anyway, sorry some of these questions are general, but I didnt want to focus on optimizing the details if there are larger issues.
ps - thanks all for providing Stan, and for being so responsive on this list.
///////////////////////////////////////////////////////////
functions {
// return (A * B) v where:
// A is n1 x n1, B = n2 x n2, V = n2 x n1 = reshape(v,n2,n1)
matrix kron_mvprod(matrix A, matrix B, matrix V) {
return transpose(A * transpose(B * V));
}
// A is a length n1 vector, B is a length n2 vector.
// Treating them as diagonal matrices, this calculates:
// v = (A * B + sigma2)ˆ{-1}
// and returns the n1 x n2 matrix V = reshape(v,n1,n2)
matrix calculate_eigenvalues(vector A, vector B, int n1, int n2, real sigma2) {
matrix[n1,n2] e;
for(i in 1:n1) {
for(j in 1:n2) {
e[i,j] = (A[i] * B[j] + sigma2);
}
}
return(e);
}
}
///////////////////////////////////////////////////////////
data {
int<lower=1> n1; // number of locations on dimension 1
int<lower=1> n2; // number of locations on dimension 2
int<lower=0> nmissing; // number of missing obs
vector[n1] x1; // locations on dimension 1
vector[n2] x2; // locations on dimension 2
matrix[n2,n1] y; // matrix with response values for dimension 1 (cols) & 2 (rows)
int x1missing[nmissing]; // dimension 1 locations for missing values
int x2missing[nmissing]; // dimension 2 locations for missing values
}
///////////////////////////////////////////////////////////
transformed data {
matrix[n1, n1] xd1;
matrix[n2, n2] xd2;
for(i in 1:n1) {
xd1[i, i] = 0;
for (j in (i+1):n1) {
xd1[i, j] = -pow(x1[i]-x1[j],2);
xd1[j, i] = xd1[i, j];
}
}
for (i in 1:n2) {
xd2[i, i] = 0;
for (j in (i+1):n2) {
xd2[i, j] = -pow(x2[i]-x2[j],2);
xd2[j, i] = xd2[i, j];
}
}
}
///////////////////////////////////////////////////////////
parameters {
real<lower=0> var1;
real<lower=0> inv_rho1;
real<lower=0> inv_rho2;
real<lower=0.0001> sigma1;
vector[nmissing] ymissing;
}
///////////////////////////////////////////////////////////
transformed parameters {
matrix[n2,n1] ystar;
ystar = y;
for(i in 1:nmissing) {
ystar[x2missing[i],x1missing[i]] = ymissing[i];
}
}
///////////////////////////////////////////////////////////
model {
matrix[n1, n1] Sigma1;
matrix[n2, n2] Sigma2;
matrix[n1, n1] Q1;
vector[n1] R1;
matrix[n2, n2] Q2;
vector[n2] R2;
matrix[n2,n1] eigenvalues;
Sigma1 = var1 * exp(xd1 * inv_rho1);
for(i in 1:n1)
Sigma1[i,i] = Sigma1[i,i] + 0.0001;
Sigma2 = exp(xd2 * inv_rho2);
for(i in 1:n2)
Sigma2[i,i] = Sigma2[i,i] + 0.0001;
Q1 = eigenvectors_sym(Sigma1);
R1 = eigenvalues_sym(Sigma1);
Q2 = eigenvectors_sym(Sigma2);
R2 = eigenvalues_sym(Sigma2);
eigenvalues = calculate_eigenvalues(R2,R1,n2,n1,sigma1);
var1 ~ lognormal(0,1);
inv_rho1 ~ normal(0, 1);
inv_rho2 ~ normal(0, 1);
sigma1 ~ lognormal(0,1);
target += -0.5 * sum(ystar .* kron_mvprod(Q1,Q2,
kron_mvprod(transpose(Q1),transpose(Q2),ystar) ./ eigenvalues)) // calculates -0.5 * y’ (K1 * K2) y
-0.5 * sum(log(eigenvalues)); // calculates logdet(K1 * K2)
}