Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TrajectorySampler amenable to any Kernel/IndVar #606

Closed
wants to merge 17 commits into from

Conversation

SebastianPopescu
Copy link
Collaborator

Rewrite of Decoupled Trajectory Sampling such that it also works for SeparateIndependent kernels and SeparteIndependentInducingVariables. The goal is to eventually use this for heteroskedastic likelihoods.

Copy link
Collaborator

@hstojic hstojic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems ok in terms of the code, I haven't checked the maths, left some minor comments

more general notes:

  • there are lots of if-else statements, I wonder if it would be a bit cleaner with kernel type dependent methods - perhaps @uri-granta has some suggestions here?
  • you are missing unit tests for all of this...

P.S I don't know if it matters but gpflow is at the moment changing how heteroskedastic modeling is done

tests/requirements.txt Outdated Show resolved Hide resolved
trieste/models/gpflux/sampler.py Outdated Show resolved Hide resolved
trieste/models/gpflux/sampler.py Outdated Show resolved Hide resolved
trieste/models/gpflux/sampler.py Show resolved Hide resolved
trieste/models/gpflux/sampler.py Outdated Show resolved Hide resolved

q_mu = self._layer.q_mu # [M, P]
q_sqrt = self._layer.q_sqrt # [P, M, M]

#NOTE -- I don't understand why in the original code this approach was used and not the gpflow.covariances.Kuu dispatcher
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leave this as a comment for @sebastianober here in github rather than as a comment in the code :)

# Build the RFF objects
if isinstance(self._kernel, list):

self._rff = defaultdict()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you'll need to provide a type here most likely (see below), though not sure why you need defaultdict here, it seems like you can simply do dict()...

self._rff: DefaultDict[int, RFF] = defaultdict()

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added it

@SebastianPopescu
Copy link
Collaborator Author

thanks for the comments @hstojic , I will take care of adding unit tests now

I don't think that the change in heteroskedastic modelling in GPflow would influence anything here since this is just TrajectorySampling.

lengthscales = [2.0] * input_dim
return SquaredExponential(lengthscales=lengthscales, variance=variance)

def build_constant_input_dim_flexible_deep_gp(X: np.ndarray, num_layers: int, config: Config, separate_case: bool, output_dim: bool) -> DeepGP:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like it should go to gpflux rather than being here in trieste, if its generally useful and not just for trieste needs

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will probably move this architecture to tests..since that's the only place it will be ever used

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great!



@dataclass
class Config:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Config seems like a copy-paste, do we need to redefine it here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no..it should just be an import

Comment on lines 171 to 173
Build a :class:`~gpflux.models.DeepGP` model with sensible initial parameters. We found the
default configuration used here to work well in most situation, but it should not be taken as a
universally good solution.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whats the key difference with the ohter builder?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commented a bit below

Comment on lines +15 to +23
"""
In this module, we test the *behaviour* of Trieste models against reference GPflux models (thus
implicitly assuming the latter are correct).
*NOTE:* Where GPflux models are used as the underlying model in an Trieste model, we should
*not* test that the underlying model is used in any particular way. To do so would break
encapsulation. For example, we should *not* test that methods on the GPflux models are called
(except in the rare case that such behaviour is an explicitly documented behaviour of the
Trieste model).
"""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this file an accident or there should be another test file for sampler?

whiten=True, # whiten = False not supported yet in GPflux for this model
)

model = build_constant_input_dim_flexible_deep_gp(query_points, num_layers, config, True, dim_output)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hstojic this is the only difference..if you can think of a more efficient way of doing this, that would be grand

Copy link
Collaborator

@hstojic hstojic Sep 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you said you need build_constant_input_dim_flexible_deep_gp only for the tests, meaning that the builder build_vanilla_flexible_deep_gp is needed only for a test, right? you can then move the whole thing there?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

otherwise there are indeed more efficient ways to do this, e.g. having a private function _build_deep_gp which takes a function as an input, either build_constant_input_dim_flexible_deep_gp or build_constant_input_dim_deep_gp and then you have two public functions build_vanilla_flexible_deep_gp and build_vanilla_deep_gp that call the private one with these two different functions

Comment on lines 215 to +219
class DeepGaussianProcessDecoupledLayer(ABC):
"""
Layer that samples an approximate decoupled trajectory for a GPflux
:class:`~gpflux.layers.GPLayer` using Matheron's rule (:cite:`wilson2020efficiently`). Note
that the only multi-output kernel that is supported is a
:class:`~gpflow.kernels.SharedIndependent` kernel.
:class:`~gpflux.layers.GPLayer` using Matheron's rule (:cite:`wilson2020efficiently`).
Supports multi-output kernels of :class:`~gpflow.kernels.SharedIndependent`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this a layer rather than just a sampler?

] # [N, B, L + M, 1]


# TODO -- probably have to re-write unflatten to accomodate for this case as well
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

feature_evaluations.append(
unflatten(flattened_feature_evaluations[counter, :, :])[..., None]
) # [N, B, L + M, 1] if Shared or [N, B, L + M, P] if separate
# TODO -- check that this is acutally true
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

# TODO -- check that this is acutally true
feature_evaluations = tf.concat(feature_evaluations, axis=-1)

# TODO -- should probably introduce a tf.debugging.assert_equal just to be sure
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Comment on lines +371 to +374
"""
Kmm = self._kernel.K(inducing_points, inducing_points) # [M, M]
Kmm += tf.eye(tf.shape(inducing_points)[0], dtype=Kmm.dtype) * DEFAULTS.JITTER
"""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove?

Comment on lines +452 to +457
tf.debugging.assert_shapes(
[
(u_sample, ["B", "M", "P"]),
]
)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this would be nicer with check shapes!

Comment on lines +584 to +591
self._rff[counter].b.assign(self._rff[counter]._sample_bias(tf.shape(self._rff[
counter].b), dtype=self._rff[counter]._dtype))
self._rff[counter].W.assign(self._rff[counter]._sample_weights(tf.shape(
self._rff[counter].W), dtype=self._rff[counter]._dtype))
else:
self._rff[counter].b.assign(self._rff[counter]._bias_init(tf.shape(self._rff[
counter].b), dtype=self._rff[counter]._dtype))
self._rff[counter].W.assign(self._rff[counter]._weights_init(tf.shape(self._rff[
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Comment on lines +619 to +623
# fourier_feature_eval = []
# for counter, ker in enumerate(self._kernel):
# fourier_feature_eval.append(self._rff[counter].__call__(x)) # [N, L]

# fourier_feature_eval = tf.stack(fourier_feature_eval, axis = 0) # [P, N, L]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this PR needs some tidying!

@uri-granta uri-granta removed their request for review March 16, 2023 09:37
@hstojic
Copy link
Collaborator

hstojic commented Apr 13, 2023

@sebastianober has made a different set of changes in gpflux to accomodate separate independent kernels, and made corresponding changes here in GPflux package, so I'll close this PR - but we cannot handle sep ind inducing variables, so we might need to return to this at some point

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants