├── .gitignore ├── LICENSE ├── Nested Deep GPs.ipynb ├── README.md ├── choleskies.py ├── coldeep.py ├── layers.py ├── plotting.py ├── special_einsum.py └── step_fn_demo.py /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | *# 3 | *~ 4 | .#* 5 | .ipynb_checkpoints 6 | 7 | # Byte-compiled / optimized / DLL files 8 | __pycache__/ 9 | *.py[cod] 10 | 11 | # C extensions 12 | *.so 13 | 14 | # Distribution / packaging 15 | .Python 16 | env/ 17 | build/ 18 | develop-eggs/ 19 | dist/ 20 | downloads/ 21 | eggs/ 22 | .eggs/ 23 | lib/ 24 | lib64/ 25 | parts/ 26 | sdist/ 27 | var/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *,cover 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | 59 | # Sphinx documentation 60 | docs/_build/ 61 | 62 | # PyBuilder 63 | target/ 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, James Hensman 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of deepGPy nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # deepGPy 2 | Deep GPs with GPy 3 | 4 | http://arxiv.org/abs/1412.1370 5 | 6 | ### Requires 7 | - GPy (development branch) 8 | - openMP 9 | 10 | 11 | ### How to run on OS X (10.9+) ### 12 | deepGPy requires OS X does not support openMP by default. To have openMP support on your OS X, you can use a clang-omp lib (i.e., an implementation of the OpenMP C/C++ language extensions in Clang/LLVM compiler). There are two ways: 13 | 14 | a) Build from source. To download and build your own clang-omp, please refer to: http://clang-omp.github.io/ 15 | 16 | b) Using Homebrew version: 17 | 1. brew update 18 | 2. brew install clang-omp 19 | 20 | After installing clang-omp, copy the omp.h from your clang-omp installation path to your python enviroment. e.g.: 21 | 22 | cp /usr/local/Cellar/libiomp/20150701/include/libiomp/omp.h /Users//anaconda2/include/. 23 | 24 | At last, leave 'extra_link_args' and 'libraries' empty in special_einsum.py. e.g.: 25 | 26 | 'extra_link_args':[], 27 | 'libraries': [] 28 | -------------------------------------------------------------------------------- /choleskies.py: -------------------------------------------------------------------------------- 1 | # Copyright James Hensman and Max Zwiessele 2014 2 | # Licensed under the GNU GPL version 3.0 3 | 4 | import numpy as np 5 | from scipy import weave 6 | try: 7 | from scipy.linalg.lapack import dpotri 8 | except: 9 | from scipy import linalg 10 | dpotri = linalg.lapack.clapack.dpotri 11 | 12 | import GPy 13 | 14 | def safe_root(N): 15 | i = np.sqrt(N) 16 | j = int(i) 17 | if i != j: 18 | raise ValueError, "N is not square!" 19 | return j 20 | 21 | def flat_to_triang(flat): 22 | """take a matrix N x D and return a M X M x D array where 23 | 24 | N = M(M+1)/2 25 | 26 | the lower triangluar portion of the d'th slice of the result is filled by the d'th column of flat. 27 | """ 28 | N, D = flat.shape 29 | M = (-1 + safe_root(8*N+1))/2 30 | ret = np.zeros((M, M, D)) 31 | flat = np.ascontiguousarray(flat) 32 | 33 | code = """ 34 | int count = 0; 35 | for(int m=0; milk', Ki, LL) 131 | self._loglik = np.sum([np.sum(np.log(np.abs(np.diag(LL[:,:,i])))) for i in range(self.L.shape[-1])]) 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /coldeep.py: -------------------------------------------------------------------------------- 1 | import GPy 2 | import numpy as np 3 | from layers import ObservedLayer, InputLayer, HiddenLayer, InputLayerFixed 4 | try: 5 | from mpi4py import MPI 6 | except: 7 | print "mpi not found" 8 | import sys 9 | import plotting 10 | from GPy.util.choleskies import indexes_to_fix_for_low_rank 11 | 12 | class ColDeep(GPy.core.Model): 13 | def __init__(self, layers, name='deepgp'): 14 | super(ColDeep, self).__init__(name) 15 | self.layers = layers 16 | self.link_parameters(*layers) 17 | 18 | def parameters_changed(self): 19 | self.layers[0].feed_forward() 20 | 21 | def log_likelihood(self): 22 | return sum([l._log_marginal_contribution for l in self.layers]) 23 | 24 | def predict_sampling(self, Xtest, Ns): 25 | """ 26 | pushes the Xtest points through the model, sampling at each layer. 27 | 28 | For every points in the matrix Xtest, we produce Ns samples. 29 | 30 | Returns a 3D array where the first dimension indexes the samples, the 31 | next indexes thepoints in Xtest and the last dimensino indexes the 32 | output dimensinos of the model. 33 | """ 34 | XX = np.repeat(Xtest, Ns, axis=0) 35 | return self.layers[0].predict_forward_sampling(XX).reshape(Ns, Xtest.shape[0], -1, order='F') 36 | 37 | def posterior_sample(self, X): 38 | """ 39 | like predict_sampling, but draw a correlated sample for every layer 40 | """ 41 | return self.layers[0].predict_forward_sampling(X, correlated=True, noise_off=True) 42 | 43 | def log_density_sampling(self, X, Y, Ns): 44 | XX = np.repeat(X, Ns, axis=0) 45 | return self.layers[0].log_density_sampling(XX, Y) 46 | 47 | def predict_means(self, X): 48 | #predict the output layer from the input, throwing away the variance at each level 49 | return self.layers[0].predict_means(X) 50 | 51 | def get_natgrad(self): 52 | return np.hstack([np.hstack([l.dL_dEu.flatten(), l.dL_duuT.flatten()]) for l in self.layers]) 53 | 54 | def set_vb_param(self, p): 55 | count = 0 56 | for l in self.layers: 57 | size = l.q_of_U_mean.size 58 | l.q_yyPof_U_mean.param_array = p[count:count+size] 59 | l.q_yyPof_U_mean.param_array = p[count:count+size] 60 | count += size 61 | 62 | size = l.q_of_U_precision.size 63 | #L = 64 | l.q_of_U_mean.param_array = p[count:count+size] 65 | count += size 66 | 67 | def plot(self, xlim=None, Nsamples=0): 68 | plotting.plot_deep(self, xlim, Nsamples) 69 | 70 | 71 | class ColDeepStochastic(ColDeep): 72 | """ 73 | A Deep GP that can be optimized by stochastic methods. Only the 74 | supervised case is considered, i.e. the top layer is an InputLayerFixed 75 | instance. 76 | """ 77 | def __init__(self, layers, X, Y, name='deepgp'): 78 | ColDeep.__init__(self, layers, name) 79 | self.X, self.Y = X, Y # keep a copy of X and Y her to select batches from 80 | self.set_batchsize(10) 81 | import climin.util 82 | self.slicer = climin.util.draw_mini_slices(self.X.shape[0], self._batchsize) 83 | 84 | def set_batchsize(self, n): 85 | """ 86 | set the batch size to n 87 | """ 88 | self._batchsize = int(n) 89 | N = float(self.X.shape[0]) 90 | for l in self.layers: 91 | l.KL_scaling = self._batchsize/N 92 | 93 | def set_batch(self): 94 | """ 95 | Select a random sub-set of the data, and call parameters_changed() to 96 | update the gradients and objective function 97 | """ 98 | index = self.slicer.next() 99 | Xbatch, Ybatch = self.X[index], self.Y[index] 100 | self.layers[0].X = Xbatch 101 | self.layers[-1].Y = Ybatch 102 | 103 | def stochastic_fprime(self, w): 104 | self.set_batch() 105 | return self._grads(w) 106 | 107 | class ColDeepMPI(ColDeep): 108 | def __init__(self, layers, name, comm): 109 | self.mpi_comm = comm 110 | super(ColDeepMPI, self).__init__(layers, name) 111 | 112 | def synch(self, x): 113 | xx = np.ascontiguousarray(x) 114 | self.mpi_comm.Bcast([xx, MPI.DOUBLE], root=0) 115 | x[:] = xx 116 | 117 | def _grads(self, x): 118 | if self.mpi_comm.rank==0: 119 | self.mpi_comm.Bcast([np.int32(3), MPI.INT]) 120 | #synchroize across all mpi nodes 121 | self.synch(x) 122 | g = super(ColDeepMPI, self)._grads(x) 123 | g_all = g.copy() 124 | self.mpi_comm.Reduce([g, MPI.DOUBLE], [g_all, MPI.DOUBLE], root=0) 125 | return g_all 126 | 127 | def _objective(self, x): 128 | if self.mpi_comm.rank==0: 129 | self.mpi_comm.Bcast([np.int32(2), MPI.INT]) 130 | self.synch(x) 131 | o = super(ColDeepMPI, self)._objective(x) 132 | o_all = np.zeros(1) 133 | self.mpi_comm.Reduce([np.float64(o), MPI.DOUBLE], [o_all, MPI.DOUBLE], root=0) 134 | return o_all 135 | 136 | def _objective_grads(self, x): 137 | if self.mpi_comm.rank==0: 138 | self.mpi_comm.Bcast([np.int32(1), MPI.INT]) 139 | self.synch(x) 140 | o, g = super(ColDeepMPI, self)._objective_grads(x) 141 | g_all = g.copy() 142 | o_all = np.zeros(1) 143 | self.mpi_comm.Reduce([g, MPI.DOUBLE], [g_all, MPI.DOUBLE], root=0) 144 | self.mpi_comm.Reduce([np.float64(o), MPI.DOUBLE], [o_all, MPI.DOUBLE], root=0) 145 | return o_all, g_all 146 | 147 | def optimize(self,*a, **kw): 148 | if self.mpi_comm.rank==0: 149 | super(ColDeepMPI, self).optimize(*a, **kw) 150 | self.mpi_comm.Bcast([np.int32(-1), MPI.INT])#after optimization , tell all the mpi processes to exit 151 | else: 152 | x = self.optimizer_array.copy() 153 | while True: 154 | flag = np.zeros(1,dtype=np.int32) 155 | self.mpi_comm.Bcast(flag,root=0) 156 | if flag==1: 157 | self._objective_grads(x) 158 | elif flag==2: 159 | self._objective(x) 160 | elif flag==3: 161 | self._grads(x) 162 | elif flag==-1: 163 | break 164 | else: 165 | raise ValueError, "bad integer broadcast" 166 | 167 | #TODO: move this helper to its own utility file 168 | def divide_data(datanum, comm): 169 | residue = (datanum)%comm.size 170 | datanum_list = np.empty((comm.size),dtype=np.int32) 171 | for i in xrange(comm.size): 172 | if i