Skip to content

Commit e09674d

Browse files
Merge branch 'master' into feature/online-learning-improvements
2 parents 93d87cd + 9551eba commit e09674d

File tree

14 files changed

+265
-20
lines changed

14 files changed

+265
-20
lines changed

docs/source/distributions.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ gpytorch.distributions
55
===================================
66

77
GPyTorch distribution objects are essentially the same as torch distribution objects.
8-
For the most part, GpyTorch relies on torch's distribution library.
8+
For the most part, GPyTorch relies on torch's distribution library.
99
However, we offer two custom distributions.
1010

1111
We implement a custom :obj:`~gpytorch.distributions.MultivariateNormal` that accepts

docs/source/kernels.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ gpytorch.kernels
99

1010

1111
If you don't know what kernel to use, we recommend that you start out with a
12-
:code:`gpytorch.kernels.ScaleKernel(gpytorch.kernels.RBFKernel)`.
12+
:code:`gpytorch.kernels.ScaleKernel(gpytorch.kernels.RBFKernel()) + gpytorch.kernel.ConstantKernel()`.
1313

1414

1515
Kernel
@@ -22,6 +22,13 @@ Kernel
2222
Standard Kernels
2323
-----------------------------
2424

25+
:hidden:`ConstantKernel`
26+
~~~~~~~~~~~~~~~~~~~~~~~~~
27+
28+
.. autoclass:: ConstantKernel
29+
:members:
30+
31+
2532
:hidden:`CosineKernel`
2633
~~~~~~~~~~~~~~~~~~~~~~
2734

examples/01_Exact_GPs/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Exact GPs (Regression)
22
========================
33

4-
Regression with a Gaussian noise model is the cannonical example of Gaussian processes.
4+
Regression with a Gaussian noise model is the canonical example of Gaussian processes.
55
These examples will work for small to medium sized datasets (~2,000 data points).
66
All examples here use exact GP inference.
77

gpytorch/kernels/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from . import keops
33
from .additive_structure_kernel import AdditiveStructureKernel
44
from .arc_kernel import ArcKernel
5+
from .constant_kernel import ConstantKernel
56
from .cosine_kernel import CosineKernel
67
from .cylindrical_kernel import CylindricalKernel
78
from .distributional_input_kernel import DistributionalInputKernel
@@ -38,6 +39,7 @@
3839
"ArcKernel",
3940
"AdditiveKernel",
4041
"AdditiveStructureKernel",
42+
"ConstantKernel",
4143
"CylindricalKernel",
4244
"MultiDeviceKernel",
4345
"CosineKernel",
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
#!/usr/bin/env python3
2+
3+
from typing import Optional, Tuple
4+
5+
import torch
6+
from torch import Tensor
7+
8+
from ..constraints import Interval, Positive
9+
from ..priors import Prior
10+
from .kernel import Kernel
11+
12+
13+
class ConstantKernel(Kernel):
14+
"""
15+
Constant covariance kernel for the probabilistic inference of constant coefficients.
16+
17+
ConstantKernel represents the prior variance `k(x1, x2) = var(c)` of a constant `c`.
18+
The prior variance of the constant is optimized during the GP hyper-parameter
19+
optimization stage. The actual value of the constant is computed (implicitly) using
20+
the linear algebraic approaches for the computation of GP samples and posteriors.
21+
22+
The constant kernel `k_constant` is most useful as a modification of an arbitrary
23+
base kernel `k_base`:
24+
1) Additive constants: The modification `k_base + k_constant` allows the GP to
25+
infer a non-zero asymptotic value far from the training data, which generally
26+
leads to more accurate extrapolation. Notably, the uncertainty in this constant
27+
value affects the posterior covariances through the posterior inference equations.
28+
This is not the case when a constant prior mean is not used, since the prior mean
29+
does not show up the posterior covariance and is regularized by the log-determinant
30+
during the optimization of the marginal likelihood.
31+
2) Multiplicative constants: The modification `k_base * k_constant` allows the GP to
32+
modulate the variance of the kernel `k_base`, and is mathematically identical to
33+
`ScaleKernel(base_kernel)` with the same constant.
34+
"""
35+
36+
has_lengthscale = False
37+
38+
def __init__(
39+
self,
40+
batch_shape: Optional[torch.Size] = None,
41+
constant_prior: Optional[Prior] = None,
42+
constant_constraint: Optional[Interval] = None,
43+
active_dims: Optional[Tuple[int, ...]] = None,
44+
):
45+
"""Constructor of ConstantKernel.
46+
47+
Args:
48+
batch_shape: The batch shape of the kernel.
49+
constant_prior: Prior over the constant parameter.
50+
constant_constraint: Constraint to place on constant parameter.
51+
active_dims: The dimensions of the input with which to evaluate the kernel.
52+
This is mute for the constant kernel, but added for compatability with
53+
the Kernel API.
54+
"""
55+
super().__init__(batch_shape=batch_shape, active_dims=active_dims)
56+
57+
self.register_parameter(
58+
name="raw_constant",
59+
parameter=torch.nn.Parameter(torch.zeros(*self.batch_shape, 1)),
60+
)
61+
62+
if constant_prior is not None:
63+
if not isinstance(constant_prior, Prior):
64+
raise TypeError("Expected gpytorch.priors.Prior but got " + type(constant_prior).__name__)
65+
self.register_prior(
66+
"constant_prior",
67+
constant_prior,
68+
lambda m: m.constant,
69+
lambda m, v: m._set_constant(v),
70+
)
71+
72+
if constant_constraint is None:
73+
constant_constraint = Positive()
74+
self.register_constraint("raw_constant", constant_constraint)
75+
76+
@property
77+
def constant(self) -> Tensor:
78+
return self.raw_constant_constraint.transform(self.raw_constant)
79+
80+
@constant.setter
81+
def constant(self, value: Tensor) -> None:
82+
self._set_constant(value)
83+
84+
def _set_constant(self, value: Tensor) -> None:
85+
value = value.view(*self.batch_shape, 1)
86+
self.initialize(raw_constant=self.raw_constant_constraint.inverse_transform(value))
87+
88+
def forward(
89+
self,
90+
x1: Tensor,
91+
x2: Tensor,
92+
diag: Optional[bool] = False,
93+
last_dim_is_batch: Optional[bool] = False,
94+
) -> Tensor:
95+
"""Evaluates the constant kernel.
96+
97+
Args:
98+
x1: First input tensor of shape (batch_shape x n1 x d).
99+
x2: Second input tensor of shape (batch_shape x n2 x d).
100+
diag: If True, returns the diagonal of the covariance matrix.
101+
last_dim_is_batch: If True, the last dimension of size `d` of the input
102+
tensors are treated as a batch dimension.
103+
104+
Returns:
105+
A (batch_shape x n1 x n2)-dim, resp. (batch_shape x n1)-dim, tensor of
106+
constant covariance values if diag is False, resp. True.
107+
"""
108+
if last_dim_is_batch:
109+
x1 = x1.transpose(-1, -2).unsqueeze(-1)
110+
x2 = x2.transpose(-1, -2).unsqueeze(-1)
111+
112+
dtype = torch.promote_types(x1.dtype, x2.dtype)
113+
batch_shape = torch.broadcast_shapes(x1.shape[:-2], x2.shape[:-2])
114+
shape = batch_shape + (x1.shape[-2],) + (() if diag else (x2.shape[-2],))
115+
constant = self.constant.to(dtype=dtype, device=x1.device)
116+
117+
if not diag:
118+
constant = constant.unsqueeze(-1)
119+
120+
if last_dim_is_batch:
121+
constant = constant.unsqueeze(-1)
122+
123+
return constant.expand(shape)

gpytorch/kernels/kernel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ def forward(
236236
) -> Union[Tensor, LinearOperator]:
237237
r"""
238238
Computes the covariance between :math:`\mathbf x_1` and :math:`\mathbf x_2`.
239-
This method should be imlemented by all Kernel subclasses.
239+
This method should be implemented by all Kernel subclasses.
240240
241241
:param x1: First set of data (... x N x D).
242242
:param x2: Second set of data (... x M x D).

gpytorch/kernels/periodic_kernel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class PeriodicKernel(Kernel):
7878
>>> covar = covar_module(x) # Output: LazyVariable of size (2 x 10 x 10)
7979
8080
.. _David Mackay's Introduction to Gaussian Processes equation 47:
81-
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.81.1927&rep=rep1&type=pdf
81+
https://inference.org.uk/mackay/gpB.pdf
8282
"""
8383

8484
has_lengthscale = True

gpytorch/kernels/rff_kernel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class RFFKernel(Kernel):
3535
3636
.. math::
3737
\begin{equation}
38-
k(\Delta) = \exp{(-\frac{\Delta^2}{2\sigma^2})}$ and $p(\omega) = \exp{(-\frac{\sigma^2\omega^2}{2})}
38+
k(\Delta) = \exp{(-\frac{\Delta^2}{2\sigma^2})} \text{ and } p(\omega) = \exp{(-\frac{\sigma^2\omega^2}{2})}
3939
\end{equation}
4040
4141
where :math:`\Delta = x - x'`.

gpytorch/mlls/leave_one_out_pseudo_likelihood.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def __init__(self, likelihood, model):
4747

4848
def forward(self, function_dist: MultivariateNormal, target: Tensor, *params) -> Tensor:
4949
r"""
50-
Computes the leave one out likelihood given :math:`p(\mathbf f)` and `\mathbf y`
50+
Computes the leave one out likelihood given :math:`p(\mathbf f)` and :math:`\mathbf y`
5151
5252
:param ~gpytorch.distributions.MultivariateNormal output: the outputs of the latent function
5353
(the :obj:`~gpytorch.models.GP`)

gpytorch/test/base_keops_test_case.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def test_forward_x1_neq_x2(self, use_keops=True, ard=False, **kwargs):
6666
# The patch makes sure that we're actually using KeOps
6767
k1 = kern1(x1, x2).to_dense()
6868
k2 = kern2(x1, x2).to_dense()
69-
self.assertLess(torch.norm(k1 - k2), 1e-4)
69+
self.assertLess(torch.norm(k1 - k2), 1e-3)
7070

7171
if use_keops:
7272
self.assertTrue(keops_mock.called)
@@ -86,7 +86,7 @@ def test_batch_matmul(self, use_keops=True, **kwargs):
8686
# The patch makes sure that we're actually using KeOps
8787
res1 = kern1(x1, x1).matmul(rhs)
8888
res2 = kern2(x1, x1).matmul(rhs)
89-
self.assertLess(torch.norm(res1 - res2), 1e-4)
89+
self.assertLess(torch.norm(res1 - res2), 1e-3)
9090

9191
if use_keops:
9292
self.assertTrue(keops_mock.called)
@@ -115,7 +115,7 @@ def test_gradient(self, use_keops=True, ard=False, **kwargs):
115115
# stack all gradients into a tensor
116116
grad_s1 = torch.vstack(torch.autograd.grad(s1, [*kern1.hyperparameters()]))
117117
grad_s2 = torch.vstack(torch.autograd.grad(s2, [*kern2.hyperparameters()]))
118-
self.assertAllClose(grad_s1, grad_s2, rtol=1e-4, atol=1e-5)
118+
self.assertAllClose(grad_s1, grad_s2, rtol=1e-3, atol=1e-3)
119119

120120
if use_keops:
121121
self.assertTrue(keops_mock.called)

0 commit comments

Comments
 (0)