Beispiel #1
0
class ProbabilityOfImprovement(Acquisition):
    """
    Probability of Improvement acquisition function for single-objective global optimization.

    .. math::
       \\alpha(\\mathbf x_{\\star}) = \\int_{-\\infty}^{f_{\\min}} \\, p( f_{\\star}\\,|\\, \\mathbf x, \\mathbf y, \\mathbf x_{\\star} ) \\, d f_{\\star}
    """
    def __init__(self, model):
        """
        :param model: GPflow model (single output) representing our belief of the objective 
        """
        super(ProbabilityOfImprovement, self).__init__(model)
        self.fmin = DataHolder(np.zeros(1))
        self.setup()

    def setup(self):
        super(ProbabilityOfImprovement, self).setup()
        samples_mean, _ = self.models[0].predict_f(self.data[0])
        self.fmin.set_data(np.min(samples_mean, axis=0))

    def build_acquisition(self, Xcand):
        candidate_mean, candidate_var = self.models[0].build_predict(Xcand)
        candidate_var = tf.maximum(candidate_var, stability)
        normal = tf.contrib.distributions.Normal(candidate_mean,
                                                 tf.sqrt(candidate_var))
        return normal.cdf(self.fmin, name=self.__class__.__name__)
Beispiel #2
0
 def __init__(self, model):
     """
     :param model: GPflow model (single output) representing our belief of the objective
     """
     super(ExpectedImprovement, self).__init__(model)
     self.fmin = DataHolder(np.zeros(1))
     self.setup()
Beispiel #3
0
 def _diag_tr_helper(self, node_features, adj_mat, x_tr):
     z = np.asarray([node_features[a == 1.] for a in adj_mat[x_tr.flatten()]])
     max_n = np.max([t.shape[0] for t in z])
     out = np.zeros((len(z), max_n, node_features.shape[1]))
     masks = np.zeros((len(z), max_n, max_n), dtype=np_float_type)
     for i in range(len(z)):
         out[i,:len(z[i]),:] = z[i]
         masks[i, :len(z[i]), :len(z[i])] = 1
     return DataHolder(out), DataHolder(masks), DataHolder(np.sum(np.sum(masks, 2), 1))
Beispiel #4
0
class ExpectedImprovement(Acquisition):
    """
    Expected Improvement acquisition function for single-objective global optimization.
    Introduced by (Mockus et al, 1975).

    Key reference:

    ::

       @article{Jones:1998,
            title={Efficient global optimization of expensive black-box functions},
            author={Jones, Donald R and Schonlau, Matthias and Welch, William J},
            journal={Journal of Global optimization},
            volume={13},
            number={4},
            pages={455--492},
            year={1998},
            publisher={Springer}
       }

    This acquisition function is the expectation of the improvement over the current best observation
    w.r.t. the predictive distribution. The definition is closely related to the :class:`.ProbabilityOfImprovement`,
    but adds a multiplication with the improvement w.r.t the current best observation to the integral.

    .. math::
       \\alpha(\\mathbf x_{\\star}) = \\int \\max(f_{\\min} - f_{\\star}, 0) \\, p( f_{\\star}\\,|\\, \\mathbf x, \\mathbf y, \\mathbf x_{\\star} ) \\, d f_{\\star}
    """
    def __init__(self, model):
        """
        :param model: GPflow model (single output) representing our belief of the objective
        """
        super(ExpectedImprovement, self).__init__(model)
        assert (isinstance(model, Model))
        self.fmin = DataHolder(np.zeros(1))
        self.setup()

    def setup(self):
        super(ExpectedImprovement, self).setup()
        # Obtain the lowest posterior mean for the previous - feasible - evaluations
        feasible_samples = self.data[0][
            self.highest_parent.feasible_data_index(), :]
        samples_mean, _ = self.models[0].predict_f(feasible_samples)
        self.fmin.set_data(np.min(samples_mean, axis=0))

    def build_acquisition(self, Xcand):
        # Obtain predictive distributions for candidates
        candidate_mean, candidate_var = self.models[0].build_predict(Xcand)
        candidate_var = tf.maximum(candidate_var, stability)

        # Compute EI
        normal = tf.contrib.distributions.Normal(candidate_mean,
                                                 tf.sqrt(candidate_var))
        t1 = (self.fmin - candidate_mean) * normal.cdf(self.fmin)
        t2 = candidate_var * normal.prob(self.fmin)
        return tf.add(t1, t2, name=self.__class__.__name__)
Beispiel #5
0
    def __init__(self, lb, ub):
        """
        Construct bounded volumes.

        :param lb: the lowerbounds of the volumes
        :param ub: the upperbounds of the volumes
        """
        super(BoundedVolumes, self).__init__()
        assert np.all(lb.shape == lb.shape)
        self.lb = DataHolder(np.atleast_2d(lb), 'pass')
        self.ub = DataHolder(np.atleast_2d(ub), 'pass')
Beispiel #6
0
    def Y(self):
        """
        Returns the output data of the wrapped model, unscaled.

        :return: :class:`.DataHolder`: unscaled output data
        """
        return DataHolder(self.output_transform.backward(self.wrapped.Y.value))
Beispiel #7
0
    def X(self):
        """
        Returns the input data of the model, unscaled.

        :return: :class:`.DataHolder`: unscaled input data
        """
        return DataHolder(self.input_transform.backward(self.wrapped.X.value))
Beispiel #8
0
 def __init__(self, models):
     """
     :param models: A list of (possibly multioutput) GPflow representing our belief of the objectives.
     """
     super(HVProbabilityOfImprovement, self).__init__(models)
     assert self.data[1].shape[1] > 1
     self.pareto = Pareto(np.hstack((m.predict_f(self.data[0])[0] for m in self.models)))
     self.reference = DataHolder(self._estimate_reference())
    def __init__(self,
                 X,
                 Y,
                 Z,
                 kernels,
                 likelihood,
                 num_latent_Y=None,
                 minibatch_size=None,
                 num_samples=1,
                 mean_function=Zero()):
        Model.__init__(self)

        assert X.shape[0] == Y.shape[0]
        assert Z.shape[1] == X.shape[1]
        assert kernels[0].input_dim == X.shape[1]

        self.num_data, D_X = X.shape
        self.num_samples = num_samples
        self.D_Y = num_latent_Y or Y.shape[1]

        self.dims = [k.input_dim for k in kernels] + [
            self.D_Y,
        ]
        q_mus, q_sqrts, Zs, mean_functions = init_layers(
            X, Z, self.dims, mean_function)

        layers = []
        for q_mu, q_sqrt, Z, mean_function, kernel in zip(
                q_mus, q_sqrts, Zs, mean_functions, kernels):
            layers.append(Layer(kernel, q_mu, q_sqrt, Z, mean_function))
        self.layers = ParamList(layers)

        for layer in self.layers[:-1]:  # fix the inner layer mean functions
            layer.mean_function.fixed = True

        self.likelihood = likelihood

        if minibatch_size is not None:
            self.X = MinibatchData(X, minibatch_size)
            self.Y = MinibatchData(Y, minibatch_size)
        else:
            self.X = DataHolder(X)
            self.Y = DataHolder(Y)
Beispiel #10
0
    def __init__(self,
                 Y,
                 latent_dim,
                 X_mean=None,
                 kern=None,
                 back_kern=None,
                 mean_function=Zero()):
        """
        Initialise GPLVM object. This method only works with a Gaussian likelihood.
        :param Y: data matrix (N x D)
        :param X_mean: latent positions (N x Q), by default initialized using PCA.
        :param kern: kernel specification, by default RBF
        :param mean_function: mean function, by default None.
        """

        # define kernel function
        if kern is None:
            kern = kernels.RBF(latent_dim)
            back_kern = kernels.RBF(latent_dim)

        # initialize latent_positions
        if X_mean is None:
            X_mean = PCA_reduce(Y, latent_dim)

        # initialize variables
        self.num_latent = X_mean.shape[1]

        # initialize variables
        likelihood = likelihoods.Gaussian()
        Y = DataHolder(Y, on_shape_change='pass')
        X = DataHolder(X_mean, on_shape_change='pass')

        # initialize parent GPModel
        GPModel.__init__(self, X, Y, kern, likelihood, mean_function)

        # initialize back constraint model
        self.back_kern = back_kern
        self.back_mean_function = Zero()
        self.back_likelihood = likelihoods.Gaussian()

        # set latent positions as model param
        del self.X
        self.X = Param(X_mean)
Beispiel #11
0
 def __init__(self, denseAdjMat, denseFeatureMat, X_tr, degree=3.0, variance=1.0, offset=1.0):
     GPflow.kernels.Kern.__init__(self, 1)
     self.degree = degree
     self.offset = Param(offset, transform=transforms.positive)
     self.variance = Param(variance, transforms.positive)
     denseAdjMat[np.diag_indices(len(denseAdjMat))] = 1.
     self.tr_features, self.tr_masks, self.tr_masks_counts = self._diag_tr_helper(denseFeatureMat, denseAdjMat, X_tr)
     self.sparse_P = SparseDataHolder(sparse_to_tuple(sparse.csr_matrix(
                     denseAdjMat/np.sum(denseAdjMat, 1, keepdims=True))))
     self.sparseFeatureMat = SparseDataHolder(sparse_to_tuple(sparse.csr_matrix(denseFeatureMat)))
     self.denseFeatureMat = DataHolder(denseFeatureMat)
Beispiel #12
0
    def __init__(self, A, b):
        """
        :param A: scaling matrix. Either a P-dimensional vector, or a P x P transformation matrix. For the latter, 
            the inverse and backward methods are not guaranteed to work as A must be invertible.
            
            It is also possible to specify a matrix with size P x Q with Q != P to achieve 
            a lower dimensional representation of X.
            In this case, A is not invertible, hence inverse and backward transforms are not supported.
        :param b: A P-dimensional offset vector.
        """
        super(LinearTransform, self).__init__()
        assert A is not None
        assert b is not None

        b = np.atleast_1d(b)
        A = np.atleast_1d(A)
        if len(A.shape) == 1:
            A = np.diag(A)

        assert (len(b.shape) == 1)
        assert (len(A.shape) == 2)

        self.A = DataHolder(A)
        self.b = DataHolder(b)
Beispiel #13
0
    def __init__(self, Y, threshold=0):
        """
        Construct a Pareto set.

        Stores a Pareto set and calculates the cell bounds covering the non-dominated region.
        The latter is needed for certain multiobjective acquisition functions.
        E.g., the :class:`~.acquisition.HVProbabilityOfImprovement`.

        :param Y: output data points, size N x R
        :param threshold: approximation threshold for the generic divide and conquer strategy
            (default 0: exact calculation)
        """
        super(Pareto, self).__init__()
        self.threshold = threshold
        self.Y = Y

        # Setup data structures
        self.bounds = BoundedVolumes.empty(Y.shape[1], np_int_type)
        self.front = DataHolder(np.zeros((0, Y.shape[1])), 'pass')

        # Initialize
        self.update()
Beispiel #14
0
class LinearTransform(DataTransform):
    """
    A simple linear transform of the form
    
    .. math::
       \\mathbf Y = (\\mathbf A \\mathbf X^{T})^{T} + \\mathbf b \\otimes \\mathbf 1_{N}^{T}

    """

    def __init__(self, A, b):
        """
        :param A: scaling matrix. Either a P-dimensional vector, or a P x P transformation matrix. For the latter, 
            the inverse and backward methods are not guaranteed to work as A must be invertible.
            
            It is also possible to specify a matrix with size P x Q with Q != P to achieve 
            a lower dimensional representation of X.
            In this case, A is not invertible, hence inverse and backward transforms are not supported.
        :param b: A P-dimensional offset vector.
        """
        super(LinearTransform, self).__init__()
        assert A is not None
        assert b is not None

        b = np.atleast_1d(b)
        A = np.atleast_1d(A)
        if len(A.shape) == 1:
            A = np.diag(A)

        assert (len(b.shape) == 1)
        assert (len(A.shape) == 2)

        self.A = DataHolder(A)
        self.b = DataHolder(b)

    def build_forward(self, X):
        return tf.matmul(X, tf.transpose(self.A)) + self.b

    @AutoFlow((float_type, [None, None]))
    def backward(self, Y):
        """
        Overwrites the default backward approach, to avoid an explicit matrix inversion.
        """
        return self.build_backward(Y)

    def build_backward(self, Y):
        """
        TensorFlow implementation of the inverse mapping
        """
        L = tf.cholesky(tf.transpose(self.A))
        XT = tf.cholesky_solve(L, tf.transpose(Y-self.b))
        return tf.transpose(XT)

    def build_backward_variance(self, Yvar):
        """
        Additional method for scaling variance backward (used in :class:`.Normalizer`). Can process both the diagonal
        variances returned by predict_f, as well as full covariance matrices.

        :param Yvar: size N x N x P or size N x P
        :return: Yvar scaled, same rank and size as input
        """
        rank = tf.rank(Yvar)
        # Because TensorFlow evaluates both fn1 and fn2, the transpose can't be in the same line. If a full cov
        # matrix is provided fn1 turns it into a rank 4, then tries to transpose it as a rank 3.
        # Splitting it in two steps however works fine.
        Yvar = tf.cond(tf.equal(rank, 2), lambda: tf.matrix_diag(tf.transpose(Yvar)), lambda: Yvar)
        Yvar = tf.cond(tf.equal(rank, 2), lambda: tf.transpose(Yvar, perm=[1, 2, 0]), lambda: Yvar)

        N = tf.shape(Yvar)[0]
        D = tf.shape(Yvar)[2]
        L = tf.cholesky(tf.square(tf.transpose(self.A)))
        Yvar = tf.reshape(Yvar, [N * N, D])
        scaled_var = tf.reshape(tf.transpose(tf.cholesky_solve(L, tf.transpose(Yvar))), [N, N, D])
        return tf.cond(tf.equal(rank, 2), lambda: tf.reduce_sum(scaled_var, axis=1), lambda: scaled_var)

    def assign(self, other):
        """
        Assign the parameters of another :class:`LinearTransform`. Can be useful to avoid graph
        re-compilation.

        :param other: :class:`.LinearTransform` object
        """
        assert other is not None
        assert isinstance(other, LinearTransform)
        self.A.set_data(other.A.value)
        self.b.set_data(other.b.value)

    def __invert__(self):
        A_inv = np.linalg.inv(self.A.value.T)
        return LinearTransform(A_inv, -np.dot(self.b.value, A_inv))

    def __str__(self):
        return 'XA + b'