[active dims] kernel active dims now the real active dims

This commit is contained in:
mzwiessele 2015-10-03 19:39:46 +01:00
parent c3afb4eaaf
commit 7d5283314a
4 changed files with 36 additions and 31 deletions

View file

@ -121,7 +121,7 @@ class Add(CombinationKernel):
#ffrom fixed import Fixed #ffrom fixed import Fixed
for p1, p2 in itertools.combinations(self.parts, 2): for p1, p2 in itertools.combinations(self.parts, 2):
# i1, i2 = p1.active_dims, p2.active_dims # i1, i2 = p1._all_dims_active, p2._all_dims_active
# white doesn;t combine with anything # white doesn;t combine with anything
if isinstance(p1, White) or isinstance(p2, White): if isinstance(p1, White) or isinstance(p2, White):
pass pass
@ -135,7 +135,7 @@ class Add(CombinationKernel):
tmp = p1.psi1(Z, variational_posterior).sum(axis=0) tmp = p1.psi1(Z, variational_posterior).sum(axis=0)
psi2 += p2.variance * (tmp[:,None]+tmp[None,:]) #(tmp[:, :, None] + tmp[:, None, :]) psi2 += p2.variance * (tmp[:,None]+tmp[None,:]) #(tmp[:, :, None] + tmp[:, None, :])
elif isinstance(p2, (RBF, Linear)) and isinstance(p1, (RBF, Linear)): elif isinstance(p2, (RBF, Linear)) and isinstance(p1, (RBF, Linear)):
assert np.intersect1d(p1.active_dims, p2.active_dims).size == 0, "only non overlapping kernel dimensions allowed so far" assert np.intersect1d(p1._all_dims_active, p2._all_dims_active).size == 0, "only non overlapping kernel dimensions allowed so far"
tmp1 = p1.psi1(Z, variational_posterior) tmp1 = p1.psi1(Z, variational_posterior)
tmp2 = p2.psi1(Z, variational_posterior) tmp2 = p2.psi1(Z, variational_posterior)
psi2 += np.einsum('nm,no->mo',tmp1,tmp2)+np.einsum('nm,no->mo',tmp2,tmp1) psi2 += np.einsum('nm,no->mo',tmp1,tmp2)+np.einsum('nm,no->mo',tmp2,tmp1)
@ -157,7 +157,7 @@ class Add(CombinationKernel):
#ffrom fixed import Fixed #ffrom fixed import Fixed
for p1, p2 in itertools.combinations(self.parts, 2): for p1, p2 in itertools.combinations(self.parts, 2):
# i1, i2 = p1.active_dims, p2.active_dims # i1, i2 = p1._all_dims_active, p2._all_dims_active
# white doesn;t combine with anything # white doesn;t combine with anything
if isinstance(p1, White) or isinstance(p2, White): if isinstance(p1, White) or isinstance(p2, White):
pass pass
@ -171,7 +171,7 @@ class Add(CombinationKernel):
tmp = p1.psi1(Z, variational_posterior) tmp = p1.psi1(Z, variational_posterior)
psi2 += p2.variance * (tmp[:, :, None] + tmp[:, None, :]) psi2 += p2.variance * (tmp[:, :, None] + tmp[:, None, :])
elif isinstance(p2, (RBF, Linear)) and isinstance(p1, (RBF, Linear)): elif isinstance(p2, (RBF, Linear)) and isinstance(p1, (RBF, Linear)):
assert np.intersect1d(p1.active_dims, p2.active_dims).size == 0, "only non overlapping kernel dimensions allowed so far" assert np.intersect1d(p1._all_dims_active, p2._all_dims_active).size == 0, "only non overlapping kernel dimensions allowed so far"
tmp1 = p1.psi1(Z, variational_posterior) tmp1 = p1.psi1(Z, variational_posterior)
tmp2 = p2.psi1(Z, variational_posterior) tmp2 = p2.psi1(Z, variational_posterior)
psi2 += np.einsum('nm,no->nmo',tmp1,tmp2)+np.einsum('nm,no->nmo',tmp2,tmp1) psi2 += np.einsum('nm,no->nmo',tmp1,tmp2)+np.einsum('nm,no->nmo',tmp2,tmp1)
@ -193,7 +193,7 @@ class Add(CombinationKernel):
continue continue
elif isinstance(p2, Bias): elif isinstance(p2, Bias):
eff_dL_dpsi1 += dL_dpsi2.sum(1) * p2.variance * 2. eff_dL_dpsi1 += dL_dpsi2.sum(1) * p2.variance * 2.
else:# np.setdiff1d(p1.active_dims, ar2, assume_unique): # TODO: Careful, not correct for overlapping active_dims else:# np.setdiff1d(p1._all_dims_active, ar2, assume_unique): # TODO: Careful, not correct for overlapping _all_dims_active
eff_dL_dpsi1 += dL_dpsi2.sum(1) * p2.psi1(Z, variational_posterior) * 2. eff_dL_dpsi1 += dL_dpsi2.sum(1) * p2.psi1(Z, variational_posterior) * 2.
p1.update_gradients_expectations(dL_dpsi0, eff_dL_dpsi1, dL_dpsi2, Z, variational_posterior) p1.update_gradients_expectations(dL_dpsi0, eff_dL_dpsi1, dL_dpsi2, Z, variational_posterior)
@ -244,17 +244,17 @@ class Add(CombinationKernel):
self.link_parameters(*other_params) self.link_parameters(*other_params)
else: else:
self.link_parameter(other) self.link_parameter(other)
self.input_dim, self.active_dims = self.get_input_dim_active_dims(self.parts) self.input_dim, self._all_dims_active = self.get_input_dim_active_dims(self.parts)
return self return self
def input_sensitivity(self, summarize=True): def input_sensitivity(self, summarize=True):
if summarize: if summarize:
i_s = np.zeros((self.input_dim)) i_s = np.zeros((self.input_dim))
for k in self.parts: for k in self.parts:
i_s[k.active_dims] += k.input_sensitivity(summarize) i_s[k._all_dims_active] += k.input_sensitivity(summarize)
return i_s return i_s
else: else:
i_s = np.zeros((len(self.parts), self.input_dim)) i_s = np.zeros((len(self.parts), self.input_dim))
from operator import setitem from operator import setitem
[setitem(i_s, (i, k.active_dims), k.input_sensitivity(summarize)) for i, k in enumerate(self.parts)] [setitem(i_s, (i, k._all_dims_active), k.input_sensitivity(summarize)) for i, k in enumerate(self.parts)]
return i_s return i_s

View file

@ -34,15 +34,15 @@ class Kern(Parameterized):
If this is not an integer (!) we will work on the whole input matrix X, If this is not an integer (!) we will work on the whole input matrix X,
and not check whether dimensions match or not (!). and not check whether dimensions match or not (!).
active_dims: _all_dims_active:
is the active_dimensions of inputs X we will work on. is the active_dimensions of inputs X we will work on.
All kernels will get sliced Xes as inputs, if active_dims is not None All kernels will get sliced Xes as inputs, if _all_dims_active is not None
Only positive integers are allowed in active_dims! Only positive integers are allowed in _all_dims_active!
if active_dims is None, slicing is switched off and all X will be passed through as given. if _all_dims_active is None, slicing is switched off and all X will be passed through as given.
:param int input_dim: the number of input dimensions to the function :param int input_dim: the number of input dimensions to the function
:param array-like|None active_dims: list of indices on which dimensions this kernel works on, or none if no slicing :param array-like|None _all_dims_active: list of indices on which dimensions this kernel works on, or none if no slicing
Do not instantiate. Do not instantiate.
""" """
@ -52,19 +52,20 @@ class Kern(Parameterized):
if active_dims is None: if active_dims is None:
active_dims = np.arange(input_dim) active_dims = np.arange(input_dim)
self.active_dims = np.atleast_1d(active_dims).astype(int) self.active_dims = active_dims
self._all_dims_active = np.atleast_1d(active_dims).astype(int)
assert self.active_dims.size == self.input_dim, "input_dim={} does not match len(active_dim)={}, active_dims={}".format(self.input_dim, self.active_dims.size, self.active_dims) assert self._all_dims_active.size == self.input_dim, "input_dim={} does not match len(active_dim)={}, _all_dims_active={}".format(self.input_dim, self._all_dims_active.size, self._all_dims_active)
self._sliced_X = 0 self._sliced_X = 0
self.useGPU = self._support_GPU and useGPU self.useGPU = self._support_GPU and useGPU
from .psi_comp import PSICOMP_GH from .psi_comp import PSICOMP_GH
self.psicomp = PSICOMP_GH() self.psicomp = PSICOMP_GH()
@Cache_this(limit=20) @Cache_this(limit=20)
def _slice_X(self, X): def _slice_X(self, X):
return X[:, self.active_dims] return X[:, self._all_dims_active]
def K(self, X, X2): def K(self, X, X2):
""" """
@ -244,9 +245,9 @@ class Kern(Parameterized):
""" """
Shortcut for tensor `prod`. Shortcut for tensor `prod`.
""" """
assert np.all(self.active_dims == range(self.input_dim)), "Can only use kernels, which have their input_dims defined from 0" assert np.all(self._all_dims_active == range(self.input_dim)), "Can only use kernels, which have their input_dims defined from 0"
assert np.all(other.active_dims == range(other.input_dim)), "Can only use kernels, which have their input_dims defined from 0" assert np.all(other._all_dims_active == range(other.input_dim)), "Can only use kernels, which have their input_dims defined from 0"
other.active_dims += self.input_dim other._all_dims_active += self.input_dim
return self.prod(other) return self.prod(other)
def prod(self, other, name='mul'): def prod(self, other, name='mul'):
@ -268,10 +269,10 @@ class Kern(Parameterized):
return Prod([self, other], name) return Prod([self, other], name)
def _check_input_dim(self, X): def _check_input_dim(self, X):
assert X.shape[1] == self.input_dim, "{} did not specify active_dims and X has wrong shape: X_dim={}, whereas input_dim={}".format(self.name, X.shape[1], self.input_dim) assert X.shape[1] == self.input_dim, "{} did not specify _all_dims_active and X has wrong shape: X_dim={}, whereas input_dim={}".format(self.name, X.shape[1], self.input_dim)
def _check_active_dims(self, X): def _check_active_dims(self, X):
assert X.shape[1] >= len(self.active_dims), "At least {} dimensional X needed, X.shape={!s}".format(len(self.active_dims), X.shape) assert X.shape[1] >= len(self._all_dims_active), "At least {} dimensional X needed, X.shape={!s}".format(len(self._all_dims_active), X.shape)
class CombinationKernel(Kern): class CombinationKernel(Kern):
@ -303,15 +304,15 @@ class CombinationKernel(Kern):
return self.parameters return self.parameters
def get_input_dim_active_dims(self, kernels, extra_dims = None): def get_input_dim_active_dims(self, kernels, extra_dims = None):
#active_dims = reduce(np.union1d, (np.r_[x.active_dims] for x in kernels), np.array([], dtype=int)) self.active_dims = reduce(np.union1d, (np.r_[x.active_dims] for x in kernels), np.array([], dtype=int))
#active_dims = np.array(np.concatenate((active_dims, extra_dims if extra_dims is not None else [])), dtype=int) #_all_dims_active = np.array(np.concatenate((_all_dims_active, extra_dims if extra_dims is not None else [])), dtype=int)
input_dim = reduce(max, (k.active_dims.max() for k in kernels)) + 1 input_dim = reduce(max, (k._all_dims_active.max() for k in kernels)) + 1
if extra_dims is not None: if extra_dims is not None:
input_dim += extra_dims.size input_dim += extra_dims.size
active_dims = np.arange(input_dim) _all_dims_active = np.arange(input_dim)
return input_dim, active_dims return input_dim, _all_dims_active
def input_sensitivity(self, summarize=True): def input_sensitivity(self, summarize=True):
""" """

View file

@ -5,7 +5,7 @@ Created on 11 Mar 2014
This module provides a meta class for the kernels. The meta class is for This module provides a meta class for the kernels. The meta class is for
slicing the inputs (X, X2) for the kernels, before K (or any other method involving X) slicing the inputs (X, X2) for the kernels, before K (or any other method involving X)
gets calls. The `active_dims` of a kernel decide which dimensions the kernel works on. gets calls. The `_all_dims_active` of a kernel decide which dimensions the kernel works on.
''' '''
from ...core.parameterization.parameterized import ParametersChangedMeta from ...core.parameterization.parameterized import ParametersChangedMeta
import numpy as np import numpy as np
@ -47,7 +47,7 @@ class _Slice_wrap(object):
assert X.ndim == 2, "only matrices are allowed as inputs to kernels for now, given X.shape={!s}".format(X.shape) assert X.ndim == 2, "only matrices are allowed as inputs to kernels for now, given X.shape={!s}".format(X.shape)
if X2 is not None: if X2 is not None:
assert X2.ndim == 2, "only matrices are allowed as inputs to kernels for now, given X2.shape={!s}".format(X2.shape) assert X2.ndim == 2, "only matrices are allowed as inputs to kernels for now, given X2.shape={!s}".format(X2.shape)
if (self.k.active_dims is not None) and (self.k._sliced_X == 0): if (self.k._all_dims_active is not None) and (self.k._sliced_X == 0):
self.k._check_active_dims(X) self.k._check_active_dims(X)
self.X = self.k._slice_X(X) self.X = self.k._slice_X(X)
self.X2 = self.k._slice_X(X2) if X2 is not None else X2 self.X2 = self.k._slice_X(X2) if X2 is not None else X2
@ -66,9 +66,9 @@ class _Slice_wrap(object):
if self.ret: if self.ret:
ret = np.zeros(self.shape) ret = np.zeros(self.shape)
if len(self.shape) == 2: if len(self.shape) == 2:
ret[:, self.k.active_dims] = return_val ret[:, self.k._all_dims_active] = return_val
elif len(self.shape) == 3: elif len(self.shape) == 3:
ret[:, :, self.k.active_dims] = return_val ret[:, :, self.k._all_dims_active] = return_val
return ret return ret
return return_val return return_val

View file

@ -346,6 +346,10 @@ class KernelTestsMiscellaneous(unittest.TestCase):
self.assertTrue(np.allclose(self.sumkern.K(self.X, which_parts=[self.linear, self.rbf]), self.linear.K(self.X)+self.rbf.K(self.X))) self.assertTrue(np.allclose(self.sumkern.K(self.X, which_parts=[self.linear, self.rbf]), self.linear.K(self.X)+self.rbf.K(self.X)))
self.assertTrue(np.allclose(self.sumkern.K(self.X, which_parts=self.sumkern.parts[0]), self.rbf.K(self.X))) self.assertTrue(np.allclose(self.sumkern.K(self.X, which_parts=self.sumkern.parts[0]), self.rbf.K(self.X)))
def test_active_dims(self):
np.testing.assert_array_equal(self.sumkern.active_dims, [0,1,2,3,7,9])
np.testing.assert_array_equal(self.sumkern._all_dims_active, range(10))
class KernelTestsNonContinuous(unittest.TestCase): class KernelTestsNonContinuous(unittest.TestCase):
def setUp(self): def setUp(self):
N0 = 3 N0 = 3