Beispiel #1
0
    def fit(self, x_train, y_train):
        self.num_bv = min(len(x_train), self.max_bv)
        self._x_bv = np.empty((self.max_bv + 1, x_train.shape[1]))
        self._y_bv = np.empty((self.max_bv + 1, y_train.shape[1]))
        self.deleted_bv = GrowingArray(
            (x_train.shape[1],), expected_rows=2 * self.max_bv)
        self._C.fill(0.0)
        self._alpha.fill(0.0)
        self._K_inv.fill(0.0)

        if len(x_train) > self.max_bv:
            more_x = x_train[self.max_bv:, :]
            more_y = y_train[self.max_bv:, :]
            x_train = x_train[:self.max_bv, :]
            y_train = y_train[:self.max_bv, :]
        else:
            more_x = more_y = None

        self.x_bv[:] = x_train
        self.y_bv[:] = y_train

        L_inv = inv(cholesky(
            self.kernel(x_train, x_train, y_train, y_train) +
            np.eye(len(x_train)) * self.noise_var))
        self.K_inv[:, :] = np.dot(L_inv.T, L_inv)
        self.alpha[:] = np.squeeze(np.dot(self.K_inv, y_train))
        self.C[:, :] = -self.K_inv

        self.updates += 1

        if more_x is not None:
            self.add_observations(more_x, more_y)
Beispiel #2
0
class BatchPredictionUpdater(object):
    def __init__(self, predictor, plume_recorder, max_train_size=3000):
        self.predictor = predictor
        self.plume_recorder = plume_recorder
        self.max_train_size = max_train_size
        self.last_update = None
        self.noisy_positions = None
        self.on_hold = False

    def step(self, noisy_states):
        if self.last_update is None:
            self.last_update = np.array(len(noisy_states) * [0])
        if self.noisy_positions is None:
            self.noisy_positions = GrowingArray(
                (len(noisy_states), 3),
                expected_rows=self.plume_recorder.expected_steps)

        self.noisy_positions.append([s.position for s in noisy_states])
        can_do_first_training = self.plume_recorder.num_recorded > 1
        do_update = not self.predictor.trained and can_do_first_training and \
            not self.on_hold
        if do_update:
            self.update_prediction()

    def target_reached(self):
        if self.predictor.trained and not self.on_hold:
            self.update_prediction()

    def update_prediction(self, uavs=None):
        is_limit_reached = self.predictor.y_train is not None and \
            len(self.predictor.y_train.data) > self.max_train_size
        if is_limit_reached:
            raise TrainDataSizeLimitReachedError()
        if uavs is None:
            uavs = range(len(self.plume_recorder.plume_measurements))
        for uav in uavs:
            self.predictor.add_observations(
                self.noisy_positions.data[self.last_update[uav]:, uav, :],
                self.get_uncommited_measurements(uav)[:, None])
        self.dismiss_measurements(uavs)

    def get_uncommited_measurements(self, uav):
        return self.plume_recorder.plume_measurements[
            uav, self.last_update[uav]:]

    def dismiss_measurements(self, uav):
        self.last_update[uav] = len(self.noisy_positions.data)
Beispiel #3
0
    def step(self, noisy_states):
        if self.last_update is None:
            self.last_update = np.array(len(noisy_states) * [0])
        if self.noisy_positions is None:
            self.noisy_positions = GrowingArray(
                (len(noisy_states), 3),
                expected_rows=self.plume_recorder.expected_steps)

        self.noisy_positions.append([s.position for s in noisy_states])
        can_do_first_training = self.plume_recorder.num_recorded > 1
        do_update = not self.predictor.trained and can_do_first_training and \
            not self.on_hold
        if do_update:
            self.update_prediction()
Beispiel #4
0
 def _create_data_array(self, initial_data):
     growing_array = GrowingArray(
         initial_data.shape[1:], expected_rows=self.expected_samples)
     growing_array.extend(initial_data)
     return growing_array
Beispiel #5
0
class SparseGP(object):
    def __init__(self, kernel=None, tolerance=0, noise_var=1.0, max_bv=1000):
        self.kernel = kernel
        self.tolerance = tolerance
        self.noise_var = noise_var
        self.max_bv = max_bv
        self.num_bv = 0
        self._x_bv = None
        self._y_bv = None
        self._alpha = np.zeros(max_bv + 1)
        self._C = np.zeros((max_bv + 1, max_bv + 1))
        self._K_inv = np.zeros((max_bv + 1, max_bv + 1))
        self.updates = 0

    x_bv = property(lambda self: self._x_bv[:self.num_bv])
    y_bv = property(lambda self: self._y_bv[:self.num_bv])
    alpha = property(lambda self: self._alpha[:self.num_bv])
    C = property(lambda self: self._C[:self.num_bv, :self.num_bv])
    K_inv = property(lambda self: self._K_inv[:self.num_bv, :self.num_bv])

    trained = property(lambda self: self.updates > 0)

    def fit(self, x_train, y_train):
        self.num_bv = min(len(x_train), self.max_bv)
        self._x_bv = np.empty((self.max_bv + 1, x_train.shape[1]))
        self._y_bv = np.empty((self.max_bv + 1, y_train.shape[1]))
        self.deleted_bv = GrowingArray(
            (x_train.shape[1],), expected_rows=2 * self.max_bv)
        self._C.fill(0.0)
        self._alpha.fill(0.0)
        self._K_inv.fill(0.0)

        if len(x_train) > self.max_bv:
            more_x = x_train[self.max_bv:, :]
            more_y = y_train[self.max_bv:, :]
            x_train = x_train[:self.max_bv, :]
            y_train = y_train[:self.max_bv, :]
        else:
            more_x = more_y = None

        self.x_bv[:] = x_train
        self.y_bv[:] = y_train

        L_inv = inv(cholesky(
            self.kernel(x_train, x_train, y_train, y_train) +
            np.eye(len(x_train)) * self.noise_var))
        self.K_inv[:, :] = np.dot(L_inv.T, L_inv)
        self.alpha[:] = np.squeeze(np.dot(self.K_inv, y_train))
        self.C[:, :] = -self.K_inv

        self.updates += 1

        if more_x is not None:
            self.add_observations(more_x, more_y)

    def add_observations(self, x_train, y_train):
        if not self.trained:
            self.fit(x_train, y_train)
        else:
            for x, y in zip(x_train, y_train):
                self.add_single_observation(x, y)
            self.updates += 1

    def add_single_observation(self, x, y):
        x = np.atleast_2d(x)
        k = self.kernel(self.x_bv, x, self.y_bv.T, y)
        k_star = np.squeeze(self.kernel(x, x, y, y))

        gamma = np.squeeze(k_star - np.einsum('ij,jk,kl', k.T, self.K_inv, k))
        e_hat = np.atleast_1d(np.squeeze(np.dot(self.K_inv, k)))

        if self.num_bv > 0:
            sigma_x_sq = np.squeeze(self.noise_var + np.einsum(
                'ij,jk,kl->il', k.T, self.C, k) + k_star)
        else:
            sigma_x_sq = self.noise_var + k_star
        q = (y - np.dot(self.alpha, k)) / sigma_x_sq
        r = -1.0 / sigma_x_sq

        if gamma < self.tolerance:
            self._reduced_update(k, e_hat, q, r)
        else:
            self._extend_basis(x, y, k, q, r)
            self._update_K(gamma, e_hat)
            if self.num_bv > self.max_bv:
                self._delete_bv()

    def _extend_basis(self, x, y, k, q, r):
        s = np.concatenate((np.atleast_1d(np.squeeze(np.dot(self.C, k))), [1]))

        self.num_bv += 1
        self.x_bv[-1] = x
        self.y_bv[-1] = y

        self._update_alpha_and_C(q, r, s)

    def _reduced_update(self, k, e_hat, q, r):
        s = np.squeeze(np.dot(self.C, k)) + e_hat
        self._update_alpha_and_C(q, r, s)

    def _update_alpha_and_C(self, q, r, s):
        self.alpha[:] += q * s
        self.C[:] += r * np.outer(s, s)

    def _update_K(self, gamma, e_hat):
        e_extended = np.concatenate((e_hat, [-1]))
        self.K_inv[:, :] += np.outer(e_extended, e_extended) / (
            self.noise_var + gamma)

    def _delete_bv(self):
        score = np.abs(self.alpha) / np.diag(self.K_inv)
        min_bv = np.argmin(score)
        self.deleted_bv.append(self.x_bv[min_bv])

        self._exclude_from_vec(self.x_bv, min_bv)
        self._exclude_from_vec(self.y_bv, min_bv)
        alpha_star = self._exclude_from_vec(self.alpha, min_bv)
        Q_star, q_star = self._exclude_from_mat(self.K_inv, min_bv)
        C_star, c_star = self._exclude_from_mat(self.C, min_bv)

        self.num_bv -= 1

        self.alpha[:] -= alpha_star / q_star * Q_star
        QQ_T = np.outer(Q_star, Q_star)
        QC_T = np.outer(Q_star, C_star)
        self.C[:, :] += c_star / (q_star ** 2) * QQ_T - \
            (QC_T + QC_T.T) / q_star
        self.K_inv[:, :] -= QQ_T / q_star

    def _exclude_from_vec(self, vec, idx, fill_value=0):
        excluded = vec[idx]
        vec[idx:-1] = vec[idx + 1:]
        vec[-1] = fill_value
        return excluded

    def _exclude_from_mat(self, mat, idx, fill_value=0):
        excluded_diag = mat[idx, idx]
        excluded_vec = np.empty(len(mat) - 1)
        excluded_vec[:idx] = mat[idx, :idx]
        excluded_vec[idx:] = mat[idx, idx + 1:]
        mat[idx:-1, :] = mat[idx + 1:, :]
        mat[:, idx:-1] = mat[:, idx + 1:]
        mat[-1, :] = fill_value
        mat[:, -1] = fill_value
        return excluded_vec, excluded_diag

    def predict(self, x, eval_MSE=False, eval_derivatives=False):
        if eval_derivatives:
            k, k_derivative = self.kernel(
                x, self.x_bv, eval_derivative=True)
        else:
            k = self.kernel(x, self.x_bv, np.zeros(len(x)), self.y_bv.T)
        pred = np.dot(k, np.atleast_2d(self.alpha).T)

        if eval_MSE is not False:
            noise_part = self.noise_var
            if eval_MSE == 'err':
                density = gaussian_kde(self.x_bv.T)
                count = len(self.x_bv) * np.apply_along_axis(
                    density.integrate_gaussian, 1, x,
                    np.eye(3) * self.kernel.lengthscale)
                noise_part /= (1 + count)
            mse = np.maximum(
                noise_part,
                noise_part + self.kernel.diag(
                    x, x, np.zeros(len(x)), np.zeros(len(x))) + np.einsum(
                    'ij,jk,ki->i', k, self.C, k.T))

        if eval_derivatives:
            pred_derivative = np.einsum(
                'ijk,lj->ilk', k_derivative, np.atleast_2d(self.alpha))
            if eval_MSE:
                mse_derivative = 2 * np.einsum(
                    'ijk,jl,li->ik', k_derivative, self.C, k.T)
                return pred, pred_derivative, mse, mse_derivative
            else:
                return pred, pred_derivative
        elif eval_MSE:
            return pred, mse
        else:
            return pred

    def calc_neg_log_likelihood(self, eval_derivative=False):
        L_inv = cholesky(-self.C)
        svs = np.dot(self.y_bv.T, L_inv)
        log_likelihood = -0.5 * np.dot(svs, svs.T) + \
            np.sum(np.log(np.diag(L_inv))) - \
            0.5 * self.num_bv * np.log(2 * np.pi)

        if eval_derivative:
            alpha = np.dot(svs, L_inv.T)
            grad_weighting = np.dot(alpha.T, alpha) + self.C
            kernel_derivative = np.array([
                0.5 * np.sum(np.einsum(
                    'ij,ji->i', grad_weighting, param_deriv))
                for param_deriv in self.kernel.param_derivatives(
                    self.x_bv, self.x_bv)])

            return -np.squeeze(log_likelihood), -kernel_derivative
        else:
            return -np.squeeze(log_likelihood)