This commit is contained in:
Nicolo Fusi 2012-11-29 16:26:21 +00:00
parent e1b766a8dd
commit aa13e095a9
7 changed files with 619 additions and 0 deletions

166
GPy/util/Tango.py Normal file
View file

@ -0,0 +1,166 @@
import matplotlib as mpl
import pylab as pb
import sys
#sys.path.append('/home/james/mlprojects/sitran_cluster/')
#from switch_pylab_backend import *
#this stuff isn;t really Tango related: maybe it could be moved out? TODO
def removeRightTicks(ax=None):
ax = ax or pb.gca()
for i, line in enumerate(ax.get_yticklines()):
if i%2 == 1: # odd indices
line.set_visible(False)
def removeUpperTicks(ax=None):
ax = ax or pb.gca()
for i, line in enumerate(ax.get_xticklines()):
if i%2 == 1: # odd indices
line.set_visible(False)
def fewerXticks(ax=None,divideby=2):
ax = ax or pb.gca()
ax.set_xticks(ax.get_xticks()[::divideby])
coloursHex = {\
"Aluminium6":"#2e3436",\
"Aluminium5":"#555753",\
"Aluminium4":"#888a85",\
"Aluminium3":"#babdb6",\
"Aluminium2":"#d3d7cf",\
"Aluminium1":"#eeeeec",\
"lightPurple":"#ad7fa8",\
"mediumPurple":"#75507b",\
"darkPurple":"#5c3566",\
"lightBlue":"#729fcf",\
"mediumBlue":"#3465a4",\
"darkBlue": "#204a87",\
"lightGreen":"#8ae234",\
"mediumGreen":"#73d216",\
"darkGreen":"#4e9a06",\
"lightChocolate":"#e9b96e",\
"mediumChocolate":"#c17d11",\
"darkChocolate":"#8f5902",\
"lightRed":"#ef2929",\
"mediumRed":"#cc0000",\
"darkRed":"#a40000",\
"lightOrange":"#fcaf3e",\
"mediumOrange":"#f57900",\
"darkOrange":"#ce5c00",\
"lightButter":"#fce94f",\
"mediumButter":"#edd400",\
"darkButter":"#c4a000"}
darkList = [coloursHex['darkBlue'],coloursHex['darkRed'],coloursHex['darkGreen'], coloursHex['darkOrange'], coloursHex['darkButter'], coloursHex['darkPurple'], coloursHex['darkChocolate'], coloursHex['Aluminium6']]
mediumList = [coloursHex['mediumBlue'], coloursHex['mediumRed'],coloursHex['mediumGreen'], coloursHex['mediumOrange'], coloursHex['mediumButter'], coloursHex['mediumPurple'], coloursHex['mediumChocolate'], coloursHex['Aluminium5']]
lightList = [coloursHex['lightBlue'], coloursHex['lightRed'],coloursHex['lightGreen'], coloursHex['lightOrange'], coloursHex['lightButter'], coloursHex['lightPurple'], coloursHex['lightChocolate'], coloursHex['Aluminium4']]
def currentDark():
return darkList[-1]
def currentMedium():
return mediumList[-1]
def currentLight():
return lightList[-1]
def nextDark():
darkList.append(darkList.pop(0))
return darkList[-1]
def nextMedium():
mediumList.append(mediumList.pop(0))
return mediumList[-1]
def nextLight():
lightList.append(lightList.pop(0))
return lightList[-1]
def reset():
while not darkList[0]==coloursHex['darkBlue']:
darkList.append(darkList.pop(0))
while not mediumList[0]==coloursHex['mediumBlue']:
mediumList.append(mediumList.pop(0))
while not lightList[0]==coloursHex['lightBlue']:
lightList.append(lightList.pop(0))
def setLightFigures():
mpl.rcParams['axes.edgecolor']=coloursHex['Aluminium6']
mpl.rcParams['axes.facecolor']=coloursHex['Aluminium2']
mpl.rcParams['axes.labelcolor']=coloursHex['Aluminium6']
mpl.rcParams['figure.edgecolor']=coloursHex['Aluminium6']
mpl.rcParams['figure.facecolor']=coloursHex['Aluminium2']
mpl.rcParams['grid.color']=coloursHex['Aluminium6']
mpl.rcParams['savefig.edgecolor']=coloursHex['Aluminium2']
mpl.rcParams['savefig.facecolor']=coloursHex['Aluminium2']
mpl.rcParams['text.color']=coloursHex['Aluminium6']
mpl.rcParams['xtick.color']=coloursHex['Aluminium6']
mpl.rcParams['ytick.color']=coloursHex['Aluminium6']
def setDarkFigures():
mpl.rcParams['axes.edgecolor']=coloursHex['Aluminium2']
mpl.rcParams['axes.facecolor']=coloursHex['Aluminium6']
mpl.rcParams['axes.labelcolor']=coloursHex['Aluminium2']
mpl.rcParams['figure.edgecolor']=coloursHex['Aluminium2']
mpl.rcParams['figure.facecolor']=coloursHex['Aluminium6']
mpl.rcParams['grid.color']=coloursHex['Aluminium2']
mpl.rcParams['savefig.edgecolor']=coloursHex['Aluminium6']
mpl.rcParams['savefig.facecolor']=coloursHex['Aluminium6']
mpl.rcParams['text.color']=coloursHex['Aluminium2']
mpl.rcParams['xtick.color']=coloursHex['Aluminium2']
mpl.rcParams['ytick.color']=coloursHex['Aluminium2']
def hex2rgb(hexcolor):
hexcolor = [hexcolor[1+2*i:1+2*(i+1)] for i in range(3)]
r,g,b = [int(n,16) for n in hexcolor]
return (r,g,b)
coloursRGB = dict([(k,hex2rgb(i)) for k,i in coloursHex.items()])
cdict_RB = {'red' :((0.,coloursRGB['mediumRed'][0]/256.,coloursRGB['mediumRed'][0]/256.),
(.5,coloursRGB['mediumPurple'][0]/256.,coloursRGB['mediumPurple'][0]/256.),
(1.,coloursRGB['mediumBlue'][0]/256.,coloursRGB['mediumBlue'][0]/256.)),
'green':((0.,coloursRGB['mediumRed'][1]/256.,coloursRGB['mediumRed'][1]/256.),
(.5,coloursRGB['mediumPurple'][1]/256.,coloursRGB['mediumPurple'][1]/256.),
(1.,coloursRGB['mediumBlue'][1]/256.,coloursRGB['mediumBlue'][1]/256.)),
'blue':((0.,coloursRGB['mediumRed'][2]/256.,coloursRGB['mediumRed'][2]/256.),
(.5,coloursRGB['mediumPurple'][2]/256.,coloursRGB['mediumPurple'][2]/256.),
(1.,coloursRGB['mediumBlue'][2]/256.,coloursRGB['mediumBlue'][2]/256.))}
cmap_RB = mpl.colors.LinearSegmentedColormap('TangoRedBlue',cdict_RB,256)
cdict_BGR = {'red' :((0.,coloursRGB['mediumBlue'][0]/256.,coloursRGB['mediumBlue'][0]/256.),
(.5,coloursRGB['mediumGreen'][0]/256.,coloursRGB['mediumGreen'][0]/256.),
(1.,coloursRGB['mediumRed'][0]/256.,coloursRGB['mediumRed'][0]/256.)),
'green':((0.,coloursRGB['mediumBlue'][1]/256.,coloursRGB['mediumBlue'][1]/256.),
(.5,coloursRGB['mediumGreen'][1]/256.,coloursRGB['mediumGreen'][1]/256.),
(1.,coloursRGB['mediumRed'][1]/256.,coloursRGB['mediumRed'][1]/256.)),
'blue':((0.,coloursRGB['mediumBlue'][2]/256.,coloursRGB['mediumBlue'][2]/256.),
(.5,coloursRGB['mediumGreen'][2]/256.,coloursRGB['mediumGreen'][2]/256.),
(1.,coloursRGB['mediumRed'][2]/256.,coloursRGB['mediumRed'][2]/256.))}
cmap_BGR = mpl.colors.LinearSegmentedColormap('TangoRedBlue',cdict_BGR,256)
cdict_Alu = {'red' :((0./5,coloursRGB['Aluminium1'][0]/256.,coloursRGB['Aluminium1'][0]/256.),
(1./5,coloursRGB['Aluminium2'][0]/256.,coloursRGB['Aluminium2'][0]/256.),
(2./5,coloursRGB['Aluminium3'][0]/256.,coloursRGB['Aluminium3'][0]/256.),
(3./5,coloursRGB['Aluminium4'][0]/256.,coloursRGB['Aluminium4'][0]/256.),
(4./5,coloursRGB['Aluminium5'][0]/256.,coloursRGB['Aluminium5'][0]/256.),
(5./5,coloursRGB['Aluminium6'][0]/256.,coloursRGB['Aluminium6'][0]/256.)),
'green' :((0./5,coloursRGB['Aluminium1'][1]/256.,coloursRGB['Aluminium1'][1]/256.),
(1./5,coloursRGB['Aluminium2'][1]/256.,coloursRGB['Aluminium2'][1]/256.),
(2./5,coloursRGB['Aluminium3'][1]/256.,coloursRGB['Aluminium3'][1]/256.),
(3./5,coloursRGB['Aluminium4'][1]/256.,coloursRGB['Aluminium4'][1]/256.),
(4./5,coloursRGB['Aluminium5'][1]/256.,coloursRGB['Aluminium5'][1]/256.),
(5./5,coloursRGB['Aluminium6'][1]/256.,coloursRGB['Aluminium6'][1]/256.)),
'blue' :((0./5,coloursRGB['Aluminium1'][2]/256.,coloursRGB['Aluminium1'][2]/256.),
(1./5,coloursRGB['Aluminium2'][2]/256.,coloursRGB['Aluminium2'][2]/256.),
(2./5,coloursRGB['Aluminium3'][2]/256.,coloursRGB['Aluminium3'][2]/256.),
(3./5,coloursRGB['Aluminium4'][2]/256.,coloursRGB['Aluminium4'][2]/256.),
(4./5,coloursRGB['Aluminium5'][2]/256.,coloursRGB['Aluminium5'][2]/256.),
(5./5,coloursRGB['Aluminium6'][2]/256.,coloursRGB['Aluminium6'][2]/256.))}
cmap_Alu = mpl.colors.LinearSegmentedColormap('TangoAluminium',cdict_Alu,256)
if __name__=='__main__':
import pylab as pb
pb.figure()
pb.pcolor(pb.rand(10,10),cmap=cmap_RB)
pb.colorbar()
pb.show()

7
GPy/util/__init__.py Normal file
View file

@ -0,0 +1,7 @@
import linalg
import misc
import plot
import squashers
import Tango
import misc
import warping_functions

150
GPy/util/linalg.py Normal file
View file

@ -0,0 +1,150 @@
import numpy as np
from scipy import linalg, optimize
import pylab as pb
import Tango
import sys
import re
import pdb
import cPickle
import types
import scipy.lib.lapack.flapack
import scipy as sp
def mdot(*args):
"""Multiply all the arguments using matrix product rules.
The output is equivalent to multiplying the arguments one by one
from left to right using dot().
Precedence can be controlled by creating tuples of arguments,
for instance mdot(a,((b,c),d)) multiplies a (a*((b*c)*d)).
Note that this means the output of dot(a,b) and mdot(a,b) will differ if
a or b is a pure tuple of numbers.
"""
if len(args)==1:
return args[0]
elif len(args)==2:
return _mdot_r(args[0],args[1])
else:
return _mdot_r(args[:-1],args[-1])
def _mdot_r(a,b):
"""Recursive helper for mdot"""
if type(a)==types.TupleType:
if len(a)>1:
a = mdot(*a)
else:
a = a[0]
if type(b)==types.TupleType:
if len(b)>1:
b = mdot(*b)
else:
b = b[0]
return np.dot(a,b)
def jitchol(A,maxtries=5):
"""
Arguments
---------
A : An almost pd square matrix
Returns
-------
cholesky(K)
Notes
-----
Adds jitter to K, to enforce positive-definiteness
if stuff breaks, please check:
np.allclose(sp.linalg.cholesky(XXT, lower = True), np.triu(sp.linalg.cho_factor(XXT)[0]).T)
"""
try:
return linalg.cholesky(A, lower = True)
except linalg.LinAlgError:
diagA = np.diag(A)
if np.any(diagA<0.):
raise linalg.LinAlgError, "not pd: negative diagonal elements"
jitter= diagA.mean()*1e-6
for i in range(1,maxtries+1):
try:
print 'Warning: adding jitter of '+str(jitter)
return linalg.cholesky(A+np.eye(A.shape[0])*jitter, lower = True)
except:
jitter *= 10
raise linalg.LinAlgError,"not positive definite, even with jitter."
def pdinv(A):
"""
Arguments
---------
:param A: A DxD pd numpy array
Returns
-------
inv : the inverse of A
hld: 0.5* the log of the determinant of A
"""
L = jitchol(A)
hld = np.sum(np.log(np.diag(L)))
inv = sp.lib.lapack.flapack.dpotri(L)[0]
# inv = linalg.flapack.dpotri(L,lower = 1)[0]
inv = np.tril(inv)+np.tril(inv,-1).T
return inv, hld
def chol_inv(L):
"""
Inverts a Cholesky lower triangular matrix
:param L: lower triangular matrix
:rtype: inverse of L
"""
return linalg.flapack.dtrtri(L, lower = True)[0]
def multiple_pdinv(A):
"""
Arguments
---------
:param A: A DxDxN numpy array (each A[:,:,i] is pd)
Returns
-------
invs : the inverses of A
hld: 0.5* the log of the determinants of A
"""
N = A.shape[-1]
chols = [jitchol(A[:,:,i]) for i in range(N)]
halflogdets = [np.sum(np.log(np.diag(L[0]))) for L in chols]
invs = [linalg.flapack.dpotri(L[0],True)[0] for L in chols]
invs = [np.triu(I)+np.triu(I,1).T for I in invs]
return np.dstack(invs),np.array(halflogdets)
def PCA(Y, Q):
"""
Principal component analysis: maximum likelihood solution by SVD
Arguments
---------
:param Y: NxD np.array of data
:param Q: int, dimension of projection
Returns
-------
X - NxQ np.array of dimensionality reduced data
W - QxD mapping from X to Y
"""
if not np.allclose(Y.mean(axis=0), 0.0):
print "Y is not zero mean, centering it locally (GPy.util.linalg.PCA)"
Y -= Y.mean(axis=0)
Z = linalg.svd(Y, full_matrices = False)
[X, W] = [Z[0][:,0:Q], np.dot(np.diag(Z[1]), Z[2]).T[:,0:Q]]
v = X.std(axis=0)
X /= v;
W *= v;
return X, W.T

56
GPy/util/misc.py Normal file
View file

@ -0,0 +1,56 @@
import numpy as np
def linear_grid(D, n = 100, min_max = (-100, 100)):
"""
Creates a D-dimensional grid of n linearly spaced points
Parameters:
D: dimension of the grid
n: number of points
min_max: (min, max) list
"""
g = np.linspace(min_max[0], min_max[1], n)
G = np.ones((n, D))
return G*g[:,None]
def kmm_init(X, m = 10):
"""
This is the same initialization algorithm that is used
in Kmeans++. It's quite simple and very useful to initialize
the locations of the inducing points in sparse GPs.
:param X: data
:param m: number of inducing points
"""
# compute the distances
XXT = np.dot(X, X.T)
D = (-2.*XXT + np.diag(XXT)[:,np.newaxis] + np.diag(XXT)[np.newaxis,:])
# select the first point
s = np.random.permutation(X.shape[0])[0]
inducing = [s]
prob = D[s]/D[s].sum()
for z in range(m-1):
s = np.random.multinomial(1, prob.flatten()).argmax()
inducing.append(s)
prob = D[s]/D[s].sum()
inducing = np.array(inducing)
return X[inducing]
if __name__ == '__main__':
import pylab as plt
X = np.linspace(1,10, 100)[:, None]
X = X[np.random.permutation(X.shape[0])[:20]]
inducing = kmm_init(X, m = 5)
plt.figure()
plt.plot(X.flatten(), np.ones((X.shape[0],)), 'x')
plt.plot(inducing, 0.5* np.ones((len(inducing),)), 'o')
plt.ylim((0.0, 10.0))

73
GPy/util/plot.py Normal file
View file

@ -0,0 +1,73 @@
import Tango
import pylab as pb
import numpy as np
def gpplot(x,mu,var,edgecol=Tango.coloursHex['darkBlue'],fillcol=Tango.coloursHex['lightBlue'],axes=None,**kwargs):
if axes is None:
axes = pb.gca()
mu = mu.flatten()
x = x.flatten()
#here's the mean
axes.plot(x,mu,color=edgecol,linewidth=2)
#ensure variance is a vector
if len(var.shape)>1:
err = 2*np.sqrt(np.diag(var))
else:
err = 2*np.sqrt(var)
#here's the 2*std box
kwargs['linewidth']=0.5
if not 'alpha' in kwargs.keys():
kwargs['alpha'] = 0.3
axes.fill(np.hstack((x,x[::-1])),np.hstack((mu+err,mu[::-1]-err[::-1])),color=fillcol,**kwargs)
#this is the edge:
axes.plot(x,mu+err,color=edgecol,linewidth=0.2)
axes.plot(x,mu-err,color=edgecol,linewidth=0.2)
def removeRightTicks(ax=None):
ax = ax or pb.gca()
for i, line in enumerate(ax.get_yticklines()):
if i%2 == 1: # odd indices
line.set_visible(False)
def removeUpperTicks(ax=None):
ax = ax or pb.gca()
for i, line in enumerate(ax.get_xticklines()):
if i%2 == 1: # odd indices
line.set_visible(False)
def fewerXticks(ax=None,divideby=2):
ax = ax or pb.gca()
ax.set_xticks(ax.get_xticks()[::divideby])
def align_subplots(N,M,xlim=None, ylim=None):
"""make all of the subplots have the same limits, turn off unnecessary ticks"""
#find sensible xlim,ylim
if xlim is None:
xlim = [np.inf,-np.inf]
for i in range(N*M):
pb.subplot(N,M,i+1)
xlim[0] = min(xlim[0],pb.xlim()[0])
xlim[1] = max(xlim[1],pb.xlim()[1])
if ylim is None:
ylim = [np.inf,-np.inf]
for i in range(N*M):
pb.subplot(N,M,i+1)
ylim[0] = min(ylim[0],pb.ylim()[0])
ylim[1] = max(ylim[1],pb.ylim()[1])
for i in range(N*M):
pb.subplot(N,M,i+1)
pb.xlim(xlim)
pb.ylim(ylim)
if (i)%M:
pb.yticks([])
else:
removeRightTicks()
if i<(M*(N-1)):
pb.xticks([])
else:
removeUpperTicks()

14
GPy/util/squashers.py Normal file
View file

@ -0,0 +1,14 @@
import numpy as np
def sigmoid(x):
return 1./(1.+np.exp(-x))
def softmax(x):
ex = np.exp(x-x.max(1)[:,None])
return ex/ex.sum(1)[:,np.newaxis]
def single_softmax(x):
ex = np.exp(x)
return ex/ex.sum()

View file

@ -0,0 +1,153 @@
import numpy as np
import scipy as sp
import pylab as plt
class WarpingFunction(object):
"""
abstract function for warping
z = f(y)
"""
def __init__(self):
raise NotImplementedError
def f(self,y,psi):
"""function transformation
y is a list of values (GP training data) of shpape [N,1]
"""
raise NotImplementedError
def fgrad_y(self,y,psi):
"""gradient of f w.r.t to y"""
raise NotImplementedError
def fgrad_y_psi(self,y,psi):
"""gradient of f w.r.t to y"""
raise NotImplementedError
def f_inv(self,z,psi):
"""inverse function transformation"""
raise NotImplementedError
def get_param_names(self):
raise NotImplementedError
def plot(self, psi, xmin, xmax):
y = np.arange(xmin, xmax, 0.01)
f_y = self.f(y, psi)
plt.figure()
plt.plot(y, f_y)
plt.xlabel('y')
plt.ylabel('f(y)')
plt.title('warping function')
class TanhWarpingFunction(WarpingFunction):
def __init__(self,n_terms=3):
"""n_terms specifies the number of tanh terms to be used"""
self.n_terms = n_terms
self.num_parameters = 3 * self.n_terms
def f(self,y,psi):
"""transform y with f using parameter vector psi
psi = [[a,b,c]]
f = \sum_{terms} a * tanh(b*(y+c))
"""
#1. check that number of params is consistent
assert psi.shape[0] == self.n_terms, 'inconsistent parameter dimensions'
assert psi.shape[1] == 3, 'inconsistent parameter dimensions'
#2. exponentiate the a and b (positive!)
mpsi = psi.copy()
#3. transform data
z = y.copy()
for i in range(len(mpsi)):
a,b,c = mpsi[i]
z += a*np.tanh(b*(y+c))
return z
def f_inv(self, y, psi, iterations = 10):
"""
calculate the numerical inverse of f
== input ==
iterations: number of N.R. iterations
"""
y = y.copy()
z = np.ones_like(y)
for i in range(iterations):
z -= (self.f(z, psi) - y)/self.fgrad_y(z,psi)
return z
def fgrad_y(self, y, psi, return_precalc = False):
"""
gradient of f w.r.t to y ([N x 1])
returns: Nx1 vector of derivatives, unless return_precalc is true,
then it also returns the precomputed stuff
"""
mpsi = psi.copy()
# vectorized version
# S = (mpsi[:,1]*(y + mpsi[:,2])).T
S = (mpsi[:,1]*(y[:,:,None] + mpsi[:,2])).T
R = np.tanh(S)
D = 1-R**2
# GRAD = (1+(mpsi[:,0:1]*mpsi[:,1:2]*D).sum(axis=0))[:,np.newaxis]
GRAD = (1+(mpsi[:,0:1][:,:,None]*mpsi[:,1:2][:,:,None]*D).sum(axis=0)).T
if return_precalc:
# return GRAD,S.sum(axis=1),R.sum(axis=1),D.sum(axis=1)
return GRAD, S, R, D
return GRAD
def fgrad_y_psi(self, y, psi, return_covar_chain = False):
"""
gradient of f w.r.t to y and psi
returns: NxIx3 tensor of partial derivatives
"""
# 1. exponentiate the a and b (positive!)
mpsi = psi.copy()
w, s, r, d = self.fgrad_y(y, psi, return_precalc = True)
gradients = np.zeros((y.shape[0], y.shape[1], len(mpsi), 3))
for i in range(len(mpsi)):
a,b,c = mpsi[i]
gradients[:,:,i,0] = (b*(1.0/np.cosh(s[i]))**2).T
gradients[:,:,i,1] = a*(d[i] - 2.0*s[i]*r[i]*(1.0/np.cosh(s[i]))**2).T
gradients[:,:,i,2] = (-2.0*a*(b**2)*r[i]*((1.0/np.cosh(s[i]))**2)).T
if return_covar_chain:
covar_grad_chain = np.zeros((y.shape[0], y.shape[1], len(mpsi), 3))
for i in range(len(mpsi)):
a,b,c = mpsi[i]
covar_grad_chain[:, :, i, 0] = (r[i]).T
covar_grad_chain[:, :, i, 1] = (a*(y + c) * ((1.0/np.cosh(s[i]))**2).T)
covar_grad_chain[:, :, i, 2] = a*b*((1.0/np.cosh(s[i]))**2).T
return gradients, covar_grad_chain
return gradients
def get_param_names(self):
variables = ['a', 'b', 'c']
names = sum([['warp_tanh_%s_t%i' % (variables[n],q) for n in range(3)] for q in range(self.n_terms)],[])
return names