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)
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)
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 _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
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)