mirror of
https://github.com/SheffieldML/GPy.git
synced 2026-06-11 15:15:15 +02:00
unoptimized parameter, still slower than current implementation
This commit is contained in:
parent
89c0d7e8eb
commit
ed3b3414f8
1 changed files with 222 additions and 55 deletions
|
|
@ -9,44 +9,87 @@ import numpy
|
|||
from GPy.core.transformations import Logexp, NegativeLogexp
|
||||
from GPy.core.index_operations import ParameterIndexOperations
|
||||
from types import FunctionType
|
||||
from re import compile, _pattern_type
|
||||
_index_re = re.compile('(?:_(\d+))+') # pattern match for indices
|
||||
|
||||
###### printing
|
||||
__constraints_name__ = "Constraint"
|
||||
__index_name__ = "Index"
|
||||
__tie_name__ = "Tied to"
|
||||
__precision__ = numpy.get_printoptions()['precision'] # numpy printing precision used, sublassing numpy ndarray after all
|
||||
######
|
||||
|
||||
def translate_param_names_to_parameters(param_names):
|
||||
"""
|
||||
Naive translation from _get_param_names return to Parameters object.
|
||||
Naive translation from _get_param_names return to Parameterized object.
|
||||
Assumptions:
|
||||
- array indices are at the and matching _\d+_\d+...
|
||||
- names are in order and names match field names
|
||||
"""
|
||||
|
||||
|
||||
class Parameters(object):
|
||||
class Parameterized(object):
|
||||
def __init__(self, parameterlist, prefix=None, *args, **kwargs):
|
||||
self._params = parameterlist
|
||||
self._params = []
|
||||
for p in parameterlist:
|
||||
if isinstance(p, Parameterized):
|
||||
self._params.extend(p._params)
|
||||
else:
|
||||
self._params.append(p)
|
||||
sizes = numpy.cumsum([0] + self.sizes)
|
||||
self._param_slices = itertools.starmap(lambda start,stop: slice(start, stop), zip(sizes, sizes[1:]))
|
||||
for p in parameterlist:
|
||||
for p in self._params:
|
||||
p._parent = self
|
||||
self.__setattr__(p.name, p)
|
||||
|
||||
def grep_param_names(self, regexp):
|
||||
"""
|
||||
Wrapper for parameterized.grep_param_names
|
||||
"""
|
||||
pass
|
||||
|
||||
def _get_params(self):
|
||||
return numpy.fromiter(itertools.chain(*itertools.imap(lambda x: x._get_params(), self._params)), dtype=numpy.float64, count=sum(self.sizes))
|
||||
return numpy.hstack([x._get_params() for x in self._params])#numpy.fromiter(itertools.chain(*itertools.imap(lambda x: x._get_params(), self._params)), dtype=numpy.float64, count=sum(self.sizes))
|
||||
|
||||
def _set_params(self, params):
|
||||
[p._set_params(params[s]) for s in self._param_slices]
|
||||
[p._set_params(params[s]) for p,s in itertools.izip(self._params,self._param_slices)]
|
||||
|
||||
def _get_params_transformed(self):
|
||||
return numpy.fromiter(itertools.chain(*itertools.imap(lambda x: x._get_params_transformed(), self._params)), dtype=numpy.float64, count=sum(self.sizes))
|
||||
return numpy.hstack([x._get_params_transformed() for x in self._params])
|
||||
return numpy.fromiter(itertools.chain(*itertools.imap(lambda x: x._get_params_transformed(), self._params)), dtype=numpy.float64, count=self.num_params_transformed)
|
||||
|
||||
def _set_params_transformed(self, params):
|
||||
current_index = 0
|
||||
for p in self._params:
|
||||
s = p.num_params_transformed
|
||||
p._set_params_transformed(params[current_index:current_index+s])
|
||||
current_index += s
|
||||
self._handle_ties()
|
||||
|
||||
def _handle_ties(self):
|
||||
[p._handle_ties() for p in self._params]
|
||||
|
||||
def __getitem__(self, name):
|
||||
return self.__getattr__(name)
|
||||
|
||||
def grep_param_names(self, regexp):
|
||||
if not isinstance(regexp, _pattern_type):
|
||||
regexp = compile(regexp)
|
||||
paramlist = [param for param in self._params if regexp.search(param.name) is not None]
|
||||
return paramlist
|
||||
|
||||
def tie_params(self, regexp):
|
||||
paramlist = self.grep_param_names(regexp)
|
||||
|
||||
|
||||
def __getattr__(self, name, *args, **kwargs):
|
||||
if name in self.__dict__:
|
||||
return object.__getattribute__(self, name, *args, **kwargs)
|
||||
else:
|
||||
paramlist = self.grep_param_names(name)
|
||||
if len(paramlist) < 1:
|
||||
raise AttributeError("'{:s}' object has no attribute '{:s}'".format(str(self.__class__.__name__), name))
|
||||
if len(paramlist) == 1:
|
||||
return paramlist[-1]
|
||||
return ParamConcatenation(paramlist)
|
||||
|
||||
@property
|
||||
def names(self):
|
||||
return [x.name for x in self._params]
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
return sum(self.sizes)
|
||||
|
|
@ -54,6 +97,12 @@ class Parameters(object):
|
|||
def sizes(self):
|
||||
return [x.size for x in self._params]
|
||||
@property
|
||||
def sizes_transformed(self):
|
||||
return [x.num_params_transformed() for x in self._params]
|
||||
@property
|
||||
def num_params_transformed(self):
|
||||
return reduce(lambda a,b: a+b.num_params_transformed, self._params, 0)
|
||||
@property
|
||||
def constraints(self):
|
||||
return [x.constraints for x in self._params]
|
||||
@property
|
||||
|
|
@ -65,12 +114,12 @@ class Parameters(object):
|
|||
@property
|
||||
def _descs(self):
|
||||
return [x._desc for x in self._params]
|
||||
|
||||
|
||||
def __str__(self, header=True):
|
||||
nl = max([len(str(x)) for x in self.names + ["Name"]])
|
||||
sl = max([len(str(x)) for x in self._descs + ["Value"]])
|
||||
cl = max([len(str(x)) if x else 0 for x in self._constrs + ["Constraint"]])
|
||||
format_spec = " {{self.name:^{0}s}} | {{self._desc:^{1}s}} | {{self._constr:^{2}s}} ".format(nl, sl, cl)
|
||||
format_spec = " \033[1m{{self.name:^{0}s}}\033[0;0m | {{self._desc:^{1}s}} | {{self._constr:^{2}s}} ".format(nl, sl, cl)
|
||||
if header:
|
||||
header = " {{0:^{0}s}} | {{1:^{1}s}} | {{2:^{2}s}} ".format(nl, sl, cl).format("Name", "Value", "Constraint")
|
||||
header += '\n' + '-'*len(header)
|
||||
|
|
@ -78,21 +127,42 @@ class Parameters(object):
|
|||
return '\n'.join([x.__str__(format_spec=format_spec) for x in self._params])
|
||||
pass
|
||||
|
||||
|
||||
class Param(numpy.ndarray):
|
||||
tied_to = [] # list of parameters this parameter is tied to
|
||||
fixed = False # if this parameter is fixed
|
||||
__array_priority__ = -1
|
||||
"""
|
||||
Parameter object for GPy models.
|
||||
|
||||
def __new__(cls, name, input_array, constraints=None):
|
||||
obj = numpy.array(input_array).view(cls)
|
||||
- constraining singular entries:
|
||||
- param[1:2,1:2].constrain{_..}() to keep the shape
|
||||
- param[1,1,...].constrain{_..}(), which tells numpy to keep the shapes
|
||||
- param[[1],[1]] to trigger advanced indexin, which keeps shapes
|
||||
|
||||
this is because of numpy's ndarray functionality:
|
||||
it gives back a float, when indexing singular entries
|
||||
we want a Param object though, with the right shape
|
||||
workarounds:
|
||||
|
||||
- slice paramters and constrain the slices.
|
||||
|
||||
- unconstrain slices
|
||||
|
||||
"""
|
||||
fixed = False # if this parameter is fixed
|
||||
__array_priority__ = -1 # Only give back a param if slicing
|
||||
|
||||
def __new__(cls, name, input_array, constraints=None, ties=None):
|
||||
obj = numpy.atleast_1d(numpy.array(input_array)).view(cls)
|
||||
obj.name = name
|
||||
obj._current_slice = slice(None)
|
||||
obj._realshape = input_array.shape
|
||||
obj._realshape = obj.shape
|
||||
#obj.parameters = parameters
|
||||
if constraints is None:
|
||||
obj.constraints = ParameterIndexOperations(obj)
|
||||
else:
|
||||
obj.constraints = constraints
|
||||
obj.constraints = constraints
|
||||
if ties is None:
|
||||
obj.ties = ParameterIndexOperations(obj)
|
||||
else:
|
||||
obj.ties = ties
|
||||
return obj
|
||||
|
||||
def __array_finalize__(self, obj):
|
||||
|
|
@ -101,6 +171,8 @@ class Param(numpy.ndarray):
|
|||
self.name = getattr(obj, 'name', None)
|
||||
self._realshape = getattr(obj, '_realshape', None)
|
||||
self.constraints = getattr(obj, 'constraints', None)
|
||||
self._parent = getattr(obj, '_parent', None)
|
||||
self.ties = getattr(obj, 'ties', None)
|
||||
self._current_slice = getattr(obj, '_current_slice', None)
|
||||
|
||||
@property
|
||||
|
|
@ -115,7 +187,7 @@ class Param(numpy.ndarray):
|
|||
return self.shape
|
||||
@property
|
||||
def _constr(self):
|
||||
return ' '.join([str(c) if c else '' for c in self.constraints.keys()])
|
||||
return ' '.join([str(c) if c else '' for c in self.constraints.properties()])
|
||||
|
||||
def _set_params(self, param):
|
||||
self.value.flat = param
|
||||
|
|
@ -123,11 +195,44 @@ class Param(numpy.ndarray):
|
|||
def _get_params(self):
|
||||
return self.value.flat
|
||||
|
||||
@property
|
||||
def num_params_transformed(self):
|
||||
return self.size - self.ties.size()
|
||||
|
||||
def _get_params_transformed(self):
|
||||
params = self.value.copy()
|
||||
params = self.base.flatten()
|
||||
[numpy.put(params, indices, constr.finv(params[indices])) for constr, indices in self.constraints.iteritems()]
|
||||
return numpy.delete(params, reduce(numpy.union1d, self.ties.iterindices(), numpy.array([],dtype=self.dtype)))
|
||||
|
||||
def _set_params_transformed(self, params):
|
||||
numpy.put(self.base, numpy.setdiff1d(numpy.r_[:self.size],reduce(numpy.union1d, self.ties.iterindices(), numpy.array([],dtype=self.dtype))), params)
|
||||
[numpy.put(self.base, indices, constraint.f(self.base.flat[indices])) for constraint, indices in self.constraints.iteritems()]
|
||||
|
||||
def constrain(self, constraint):
|
||||
self.constraints.add(constraint, self._current_slice)
|
||||
def _handle_ties(self):
|
||||
# handle all tied values
|
||||
for tie, indices in self.ties.iteritems():
|
||||
self.base.flat[indices] = tie.flat
|
||||
|
||||
def tie_to(self, param):
|
||||
assert isinstance(param, Param), "Argument {1} not of type {0}".format(Param,param.__class__)
|
||||
try:
|
||||
rav_index = self.ties.create_raveled_indices(self._current_slice)
|
||||
self.base.flat[rav_index] = param.flat
|
||||
self.ties.add(param, self._current_slice)
|
||||
self._handle_ties()
|
||||
except ValueError:
|
||||
raise ValueError("Trying to tie params with shape {} to params with shape {}".format(self.shape, param.shape))
|
||||
|
||||
def untie(self, *params):
|
||||
if len(params) == 0:
|
||||
params = self.ties.properties()
|
||||
for p in self.ties.properties():
|
||||
if any((p == x).all() for x in params):
|
||||
self.ties.remove(p, self._current_slice)
|
||||
|
||||
def constrain(self, transform):
|
||||
self.constraints.add(transform, self._current_slice)
|
||||
self[:] = transform.initialize(self)
|
||||
|
||||
def constrain_positive(self):
|
||||
self.constrain(Logexp())
|
||||
|
|
@ -135,16 +240,19 @@ class Param(numpy.ndarray):
|
|||
def constrain_negative(self):
|
||||
self.constrain(NegativeLogexp())
|
||||
|
||||
def unconstrain(self, constraints=None):
|
||||
if constraints is None:
|
||||
constraints = self.constraints.keys()
|
||||
elif not isinstance(constraints, (tuple, list, numpy.ndarray)):
|
||||
constraints = [constraints]
|
||||
for constr in constraints:
|
||||
def unconstrain(self, transforms=None):
|
||||
if transforms is None:
|
||||
transforms = self.constraints.properties()
|
||||
elif not isinstance(transforms, (tuple, list, numpy.ndarray)):
|
||||
transforms = [transforms]
|
||||
for constr in transforms:
|
||||
self.constraints.remove(constr, self._current_slice)
|
||||
|
||||
def unconstrain_positive(self):
|
||||
self.unconstrain(Logexp())
|
||||
|
||||
def unconstrain_negative(self):
|
||||
self.unconstrain(NegativeLogexp())
|
||||
|
||||
def __getitem__(self, s, *args, **kwargs):
|
||||
new_arr = numpy.ndarray.__getitem__(self, s, *args, **kwargs)
|
||||
|
|
@ -155,37 +263,96 @@ class Param(numpy.ndarray):
|
|||
pass
|
||||
return new_arr
|
||||
|
||||
def __setitem__(self, *args, **kwargs):
|
||||
numpy.ndarray.__setitem__(self, *args, **kwargs)
|
||||
self._parent._handle_ties()
|
||||
|
||||
def __repr__(self, *args, **kwargs):
|
||||
return super(Param, self).__repr__(*args, **kwargs)
|
||||
view = repr(self.value)
|
||||
return view
|
||||
|
||||
def __str__(self, format_spec=None):
|
||||
|
||||
def _constr_matrix_str(self):
|
||||
constr_matrix = numpy.empty(self._realshape, dtype=object) # we need the whole constraints matrix
|
||||
constr_matrix[:] = ''
|
||||
for constr, indices in self.constraints.iteritems(): # put in all the constraints:
|
||||
constr_matrix[indices] = numpy.vectorize(lambda x:" ".join([x, str(constr)]) if x else str(constr))(constr_matrix[indices])
|
||||
return constr_matrix.astype(numpy.string_)[self._current_slice] # and get the slice we did before
|
||||
def _ties_matrix_str(self):
|
||||
ties_matr = numpy.empty(self._realshape, dtype=object) # we need the whole constraints matrix
|
||||
ties_matr[:] = ''
|
||||
for tie, indices in self.ties.iteritems(): # go through all ties:
|
||||
tie_cycle = itertools.cycle(tie._indices())
|
||||
ties_matr[indices] = numpy.vectorize(lambda x:" ".join([x, str(tie.name) + str(str(tie_cycle.next()))]) if x else str(tie.name)+str(str(tie_cycle.next())), otypes=[str])(ties_matr[indices])
|
||||
return ties_matr.astype(numpy.string_)[self._current_slice] # and get the slice we did before
|
||||
def _indices(self):
|
||||
return numpy.array(list(itertools.product(*itertools.imap(range, self._realshape))))[self.constraints.create_raveled_indices(self._current_slice),...] # find out which indices to print
|
||||
def _max_len_names(self, constr_matrix, header):
|
||||
return max(reduce(lambda a, b:max(a, len(b)), constr_matrix.flat, 0), len(header))
|
||||
def _max_len_values(self):
|
||||
return max(reduce(lambda a, b:max(a, len("{x:=.{0}G}".format(__precision__, x=b))), self.value.flat, 0), len(self.name))
|
||||
def _max_len_index(self, ind):
|
||||
return max(reduce(lambda a, b:max(a, len(str(b))), ind, 0), len(__index_name__))
|
||||
def __str__(self, format_spec=None, constr_matrix=None, indices=None, ties=None, lc=None, lx=None, li=None, lt=None):
|
||||
if format_spec is None:
|
||||
constr_matrix = numpy.empty(self._realshape, dtype=object)
|
||||
constr_matrix[:] = ''
|
||||
for constr, indices in self.constraints.iteritems():
|
||||
constr_matrix[indices] = numpy.vectorize(lambda x: " ".join([x,str(constr)]) if x else str(constr))(constr_matrix[indices])
|
||||
constr_matrix = constr_matrix.astype(numpy.string_)[self._current_slice]
|
||||
p = numpy.get_printoptions()['precision']
|
||||
if indices is None:
|
||||
indices = self._indices()
|
||||
if constr_matrix is None:
|
||||
constr_matrix = self._constr_matrix_str()
|
||||
if ties is None:
|
||||
ties = self._ties_matrix_str()
|
||||
if lc is None:
|
||||
lc = self._max_len_names(constr_matrix, __constraints_name__)
|
||||
if lx is None:
|
||||
lx = self._max_len_values()
|
||||
if li is None:
|
||||
li = self._max_len_index(indices)
|
||||
if lt is None:
|
||||
lt = self._max_len_names(ties, __tie_name__)
|
||||
constr = constr_matrix.flat
|
||||
ind = numpy.array(list(itertools.product(*itertools.imap(range, self._realshape))))[self.constraints.create_raveled_indices(self._current_slice),...]
|
||||
c_name, x_name, i_name = "Constraint", "Value", "Index"
|
||||
lc = max(reduce(lambda a,b: max(a, len(b)), constr_matrix.flat, 0), len(c_name))
|
||||
lx = max(reduce(lambda a,b: max(a, len("{x:=.{0}G}".format(p,x=b))), self.value.flat, 0), len(x_name))
|
||||
li = max(reduce(lambda a,b: max(a, len(str(b))), ind, 0), len(i_name))
|
||||
header = " {i:^{3}s} | {x:^{1}s} | {c:^{0}s}".format(lc,lx,p,li, x=x_name, c=c_name, i=i_name)
|
||||
return "\n".join([header]+[" {i:^{3}s} | {x: >{1}.{2}G} | {c:^{0}s}".format(lc,lx,p,li, x=x, c=constr.next(), i=i) for i,x in itertools.izip(ind,self.value.flat)])
|
||||
ties = ties.flat
|
||||
header = " {i:^{2}s} | \033[1m{x:^{1}s}\033[0;0m | {c:^{0}s} | {t:^{3}s}".format(lc,lx,li,lt, x=self.name, c=__constraints_name__, i=__index_name__, t=__tie_name__) # nice header for printing
|
||||
return "\n".join([header]+[" {i:^{3}s} | {x: >{1}.{2}G} | {c:^{0}s} | {t:^{4}} ".format(lc,lx,__precision__,li,lt, x=x, c=constr.next(), t=ties.next(), i=i) for i,x in itertools.izip(indices,self.value.flat)]) # return all the constraints with right indices
|
||||
return format_spec.format(self=self)
|
||||
|
||||
class ParamConcatenation(object):
|
||||
def __init__(self, params):
|
||||
self.params = params
|
||||
def __getitem__(self, s):
|
||||
raise AttributeError("Cannot index a concatenation of parameters!")
|
||||
def constrain(self, constraint):
|
||||
[param.constrain(constraint) for param in self.params]
|
||||
def constrain_positive(self):
|
||||
[param.constrain_positive() for param in self.params]
|
||||
def constrain_negative(self):
|
||||
[param.constrain_negative() for param in self.params]
|
||||
def unconstrain(self, constraints=None):
|
||||
[param.unconstrain(constraints) for param in self.params]
|
||||
def unconstrain_negative(self):
|
||||
[param.unconstrain_negative() for param in self.params]
|
||||
def __str__(self, *args, **kwargs):
|
||||
constr_matrices = [p._constr_matrix_str() for p in self.params]
|
||||
ties_matrices = [p._ties_matrix_str() for p in self.params]
|
||||
indices = [p._indices() for p in self.params]
|
||||
lc = max([p._max_len_names(cm, __constraints_name__) for p, cm in itertools.izip(self.params, constr_matrices)])
|
||||
lx = max([p._max_len_values() for p in self.params])
|
||||
li = max([p._max_len_index(i) for p, i in itertools.izip(self.params, indices)])
|
||||
lt = max([p._max_len_names(tm, __tie_name__) for p, tm in itertools.izip(self.params, ties_matrices)])
|
||||
return "\n".join([p.__str__(None, cm, i, tm, lc, lx, li, lt) for p, cm, i, tm in itertools.izip(self.params,constr_matrices,indices,ties_matrices)])
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
if __name__ == '__main__':
|
||||
X = numpy.random.randn(2,4)
|
||||
X = numpy.random.randn(100,8)
|
||||
p = Param("X", X)
|
||||
p1 = Param("X_variance", numpy.random.rand(*X.shape))
|
||||
p2 = Param("Y", numpy.random.randn(3,1))
|
||||
p3 = Param("rbf_variance", numpy.random.rand(1))
|
||||
p4 = Param("rbf_lengthscale", numpy.random.rand(2))
|
||||
params = Parameters([p,p2,p3,p4])
|
||||
params.X[1].constrain_positive()
|
||||
print params.X
|
||||
#params.X[1,1].constrain_positive()
|
||||
|
||||
params = Parameterized([p,p1,p2,p3,p4])
|
||||
params.rbf.constrain_positive()
|
||||
params[".*variance"].constrain_positive()
|
||||
params.rbf_l.tie_to(params.rbf_va)
|
||||
pt = numpy.array(params._get_params_transformed())
|
||||
ptr = numpy.random.randn(*pt.shape)
|
||||
# params.X.tie_to(params.rbf_v)
|
||||
Loading…
Add table
Add a link
Reference in a new issue